From cc059bf6aae06c9d14331c392cccfd7ff9c05670 Mon Sep 17 00:00:00 2001 From: Danny Su Date: Tue, 5 Sep 2023 21:00:22 -0700 Subject: [PATCH] Remove deprecated inspector and jsinspector (#39300) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/39300 Changelog: [Internal] `ReactCommon/hermes/inspector` and `ReactCommon/jsinspector` are unused in the React Native repo as of D48897203 and D48966244, respectively. Now that we've removed the last remaining references to them from internal Meta code, we can safely delete them from React Native. Reviewed By: christophpurrer Differential Revision: D48983212 fbshipit-source-id: 9a70178b19fb461c00a2304697b647b7bebe74c3 --- .../hermes/inspector/.clang-format | 87 - .../hermes/inspector/AsyncPauseState.h | 34 - .../ReactCommon/hermes/inspector/Exceptions.h | 60 - .../hermes/inspector/Inspector.cpp | 745 ----- .../ReactCommon/hermes/inspector/Inspector.h | 384 --- .../hermes/inspector/InspectorState.cpp | 511 --- .../hermes/inspector/InspectorState.h | 412 --- .../ReactCommon/hermes/inspector/README.md | 119 - .../hermes/inspector/RuntimeAdapter.cpp | 30 - .../hermes/inspector/RuntimeAdapter.h | 76 - .../hermes/inspector/chrome/Connection.cpp | 1667 ---------- .../hermes/inspector/chrome/Connection.h | 67 - .../inspector/chrome/ConnectionDemux.cpp | 131 - .../hermes/inspector/chrome/ConnectionDemux.h | 59 - .../inspector/chrome/MessageConverters.cpp | 378 --- .../inspector/chrome/MessageConverters.h | 136 - .../inspector/chrome/MessageInterfaces.h | 74 - .../hermes/inspector/chrome/MessageTypes.cpp | 1948 ----------- .../hermes/inspector/chrome/MessageTypes.h | 1129 ------- .../inspector/chrome/MessageTypesInlines.h | 190 -- .../hermes/inspector/chrome/Registration.cpp | 38 - .../hermes/inspector/chrome/Registration.h | 43 - .../inspector/chrome/RemoteObjectsTable.cpp | 146 - .../inspector/chrome/RemoteObjectsTable.h | 124 - .../hermes/inspector/chrome/cli/main.cpp | 274 -- .../chrome/tests/AsyncHermesRuntime.cpp | 166 - .../chrome/tests/AsyncHermesRuntime.h | 148 - .../chrome/tests/ConnectionDemuxTests.cpp | 147 - .../chrome/tests/ConnectionTests.cpp | 2955 ----------------- .../inspector/chrome/tests/MessageTests.cpp | 1549 --------- .../chrome/tests/RemoteObjectsTableTest.cpp | 111 - .../inspector/chrome/tests/SyncConnection.cpp | 133 - .../inspector/chrome/tests/SyncConnection.h | 69 - .../inspector/detail/CallbackOStream.cpp | 77 - .../hermes/inspector/detail/CallbackOStream.h | 89 - .../inspector/detail/SerialExecutor.cpp | 60 - .../hermes/inspector/detail/SerialExecutor.h | 57 - .../hermes/inspector/detail/Thread.cpp | 39 - .../hermes/inspector/detail/Thread.h | 85 - .../detail/tests/CallbackOStreamTests.cpp | 104 - .../detail/tests/SerialExecutorTests.cpp | 41 - .../inspector/docs/GenerateInspectorFSM.sh | 7 - .../hermes/inspector/docs/InspectorFSM.gv | 39 - .../hermes/inspector/docs/InspectorFSM.pdf | Bin 18808 -> 0 bytes .../hermes/inspector/tests/InspectorTests.cpp | 410 --- .../ReactCommon/hermes/inspector/tools/format | 10 - .../hermes/inspector/tools/message_types.txt | 41 - .../hermes/inspector/tools/run_msggen | 32 - .../tools/sandcastle/build_and_test.sh | 13 - .../inspector/tools/sandcastle/setup.sh | 31 - 50 files changed, 15275 deletions(-) delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/.clang-format delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/AsyncPauseState.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/Exceptions.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/Inspector.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/Inspector.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/InspectorState.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/InspectorState.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/README.md delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/MessageInterfaces.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/cli/main.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionDemuxTests.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/RemoteObjectsTableTest.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/Thread.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/Thread.h delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/tests/CallbackOStreamTests.cpp delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/detail/tests/SerialExecutorTests.cpp delete mode 100755 packages/react-native/ReactCommon/hermes/inspector/docs/GenerateInspectorFSM.sh delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.gv delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.pdf delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/tests/InspectorTests.cpp delete mode 100755 packages/react-native/ReactCommon/hermes/inspector/tools/format delete mode 100644 packages/react-native/ReactCommon/hermes/inspector/tools/message_types.txt delete mode 100755 packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen delete mode 100755 packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/build_and_test.sh delete mode 100755 packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/setup.sh diff --git a/packages/react-native/ReactCommon/hermes/inspector/.clang-format b/packages/react-native/ReactCommon/hermes/inspector/.clang-format deleted file mode 100644 index 550292f9506..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/.clang-format +++ /dev/null @@ -1,87 +0,0 @@ ---- -AccessModifierOffset: -1 -AlignAfterOpenBracket: AlwaysBreak -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: true -AlignOperands: false -AlignTrailingComments: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: true -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterClass: false - AfterControlStatement: false - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: false -ColumnLimit: 80 -CommentPragmas: '^ IWYU pragma:' -ConstructorInitializerAllOnOneLineOrOnePerLine: true -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, ] -IncludeCategories: - - Regex: '^<.*\.h(pp)?>' - Priority: 1 - - Regex: '^<.*' - Priority: 2 - - Regex: '.*' - Priority: 3 -IndentCaseLabels: true -IndentWidth: 2 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: false -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBlockIndentWidth: 2 -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: false -PenaltyBreakBeforeFirstCallParameter: 1 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 200 -PointerAlignment: Right -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 8 -UseTab: Never -... diff --git a/packages/react-native/ReactCommon/hermes/inspector/AsyncPauseState.h b/packages/react-native/ReactCommon/hermes/inspector/AsyncPauseState.h deleted file mode 100644 index b040780f5c9..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/AsyncPauseState.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -namespace facebook { -namespace hermes { -namespace inspector { - -/** - * AsyncPauseState is used to track whether we requested an async pause from a - * running VM, and whether the pause was initiated by us or by the client. - */ -enum class AsyncPauseState { - /// None means there is no pending async pause in the VM. - None, - - /// Implicit means we requested an async pause from the VM to service an op - /// that can only be performed while paused, like setting a breakpoint. An - /// impliict pause can be upgraded to an explicit pause if the client later - /// explicitly requests a pause. - Implicit, - - /// Explicit means that the client requested the pause by calling pause(). - Explicit -}; - -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/Exceptions.h b/packages/react-native/ReactCommon/hermes/inspector/Exceptions.h deleted file mode 100644 index 928b795448e..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/Exceptions.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// using include guards instead of #pragma once due to compile issues -// with MSVC and BUCK -#ifndef HERMES_INSPECTOR_EXCEPTIONS_H -#define HERMES_INSPECTOR_EXCEPTIONS_H - -#include - -namespace facebook { -namespace hermes { -namespace inspector { - -class AlreadyEnabledException : public std::runtime_error { - public: - AlreadyEnabledException() - : std::runtime_error("can't enable: debugger already enabled") {} -}; - -class NotEnabledException : public std::runtime_error { - public: - NotEnabledException(const std::string &cmd) - : std::runtime_error("debugger can't perform " + cmd + ": not enabled") {} -}; - -class InvalidStateException : public std::runtime_error { - public: - InvalidStateException( - const std::string &cmd, - const std::string &curState, - const std::string &expectedState) - : std::runtime_error( - "debugger can't perform " + cmd + ": in " + curState + - ", expected " + expectedState) {} -}; - -class MultipleCommandsPendingException : public std::runtime_error { - public: - MultipleCommandsPendingException(const std::string &cmd) - : std::runtime_error( - "debugger can't perform " + cmd + - ": a step or resume is already pending") {} -}; - -class UserCallbackException : public std::runtime_error { - public: - UserCallbackException(const std::exception &e) - : std::runtime_error(std::string("callback exception: ") + e.what()) {} -}; - -} // namespace inspector -} // namespace hermes -} // namespace facebook - -#endif // HERMES_INSPECTOR_EXCEPTIONS_H diff --git a/packages/react-native/ReactCommon/hermes/inspector/Inspector.cpp b/packages/react-native/ReactCommon/hermes/inspector/Inspector.cpp deleted file mode 100644 index bf35a4e85f6..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/Inspector.cpp +++ /dev/null @@ -1,745 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "Inspector.h" -#include "Exceptions.h" -#include "InspectorState.h" - -#include -#include - -#include -#include -#include - -#ifdef HERMES_INSPECTOR_FOLLY_KLUDGE -// This is here, instead of linking against -// folly/futures/Future.cpp, to avoid pulling in another pile of -// dependencies, including the separate dependency libevent. This is -// likely specific to the version of folly RN uses, so may need to be -// changed. Even better, perhaps folly can be refactored to simplify -// this. Providing a RN-specific Timekeeper impl may also help. - -template class folly::Future; -template class folly::Future; - -namespace folly { -namespace futures { - -SemiFuture sleep(Duration, Timekeeper *) { - LOG(FATAL) << "folly::futures::sleep() not implemented"; -} - -} // namespace futures - -namespace detail { - -std::shared_ptr getTimekeeperSingleton() { - LOG(FATAL) << "folly::detail::getTimekeeperSingleton() not implemented"; -} - -} // namespace detail -} // namespace folly - -// -#endif - -namespace facebook { -namespace hermes { -namespace inspector { - -using folly::Unit; - -namespace debugger = ::facebook::hermes::debugger; - -/** - * Threading notes: - * - * 1. mutex_ must be held before using state_ or any InspectorState methods. - * 2. Methods that are callable by the client (like enable, resume, etc.) call - * various InspectorState methods via state_. This implies that they must - * acquire mutex_. - * 3. Since some InspectorState methods call back out to the client (e.g. via - * fulfilling promises, or via the InspectorObserver callbacks), we have to - * be careful about reentrancy from a callback causing a deadlock when (1) - * and (2) interact. Consider: - * - * 1) Debugger pauses, which causes InspectorObserve::onPause to fire. - * onPause is called by InspectorState::Paused::onEnter on the JS - * thread with mutex_ held. - * 2) Client calls setBreakpoint from the onPause callback. - * 3) If setBreakpoint directly tried to acquire mutex_ here, we would - * deadlock since our thread already owns the mutex_ (see 1). - * - * For this reason, all client-facing methods are executed on executor_, which - * runs on its own thread. The pattern is: - * - * 1. The client-facing method foo (e.g. enable) enqueues a call to - * fooOnExecutor (e.g. enableOnExecutor) on executor_. - * 2. fooOnExecutor is responsible for acquiring mutex_. - * - */ - -// TODO: read this out of an env variable or config -static constexpr bool kShouldLog = false; - -// Logging state transitions is done outside of transition() in a macro so that -// function and line numbers in the log will be accurate. -#define TRANSITION(nextState) \ - do { \ - if (kShouldLog) { \ - if (state_ == nullptr) { \ - LOG(INFO) << "Inspector::" << __func__ \ - << " transitioning to initial state " << *(nextState); \ - } else { \ - LOG(INFO) << "Inspector::" << __func__ << " transitioning from " \ - << *state_ << " to " << *(nextState); \ - } \ - } \ - transition((nextState)); \ - } while (0) - -Inspector::Inspector( - std::shared_ptr adapter, - InspectorObserver &observer, - bool pauseOnFirstStatement) - : adapter_(adapter), - debugger_(adapter->getRuntime().getDebugger()), - observer_(observer), - executor_(std::make_unique("hermes-inspector")) { - // TODO (t26491391): make tickleJs a real Hermes runtime API - std::string src = "function __tickleJs() { return Math.random(); }"; - adapter->getRuntime().evaluateJavaScript( - std::make_shared(src), "__tickleJsHackUrl"); - - { - std::scoped_lock lock(mutex_); - - if (pauseOnFirstStatement) { - awaitingDebuggerOnStart_ = true; - TRANSITION(std::make_unique(*this)); - } else { - TRANSITION(std::make_unique(*this)); - } - } - - debugger_.setShouldPauseOnScriptLoad(true); - debugger_.setEventObserver(this); -} - -Inspector::~Inspector() { - debugger_.setEventObserver(nullptr); -} - -static bool toBoolean(jsi::Runtime &runtime, const jsi::Value &val) { - // Based on Operations.cpp:toBoolean in the Hermes VM. - if (val.isUndefined() || val.isNull()) { - return false; - } - if (val.isBool()) { - return val.getBool(); - } - if (val.isNumber()) { - double m = val.getNumber(); - return m != 0 && !std::isnan(m); - } - if (val.isSymbol() || val.isObject()) { - return true; - } - if (val.isString()) { - std::string s = val.getString(runtime).utf8(runtime); - return !s.empty(); - } - assert(false && "All cases should be covered"); - return false; -} - -void Inspector::installConsoleFunction( - jsi::Object &console, - std::shared_ptr &originalConsole, - const std::string &name, - const std::string &chromeTypeDefault = "") { - jsi::Runtime &rt = adapter_->getRuntime(); - auto chromeType = chromeTypeDefault == "" ? name : chromeTypeDefault; - auto nameID = jsi::PropNameID::forUtf8(rt, name); - auto weakInspector = std::weak_ptr(shared_from_this()); - console.setProperty( - rt, - nameID, - jsi::Function::createFromHostFunction( - rt, - nameID, - 1, - [weakInspector, originalConsole, name, chromeType]( - jsi::Runtime &runtime, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count) { - if (originalConsole) { - auto val = originalConsole->getProperty(runtime, name.c_str()); - if (val.isObject()) { - auto obj = val.getObject(runtime); - if (obj.isFunction(runtime)) { - auto func = obj.getFunction(runtime); - func.callWithThis(runtime, *originalConsole, args, count); - } - } - } - - if (auto inspector = weakInspector.lock()) { - if (name != "assert") { - // All cases other than assert just log a simple message. - jsi::Array argsArray(runtime, count); - for (size_t index = 0; index < count; ++index) - argsArray.setValueAtIndex(runtime, index, args[index]); - inspector->logMessage( - ConsoleMessageInfo{chromeType, std::move(argsArray)}); - return jsi::Value::undefined(); - } - // console.assert needs to check the first parameter before - // logging. - if (count == 0) { - // No parameters, throw a blank assertion failed message. - inspector->logMessage( - ConsoleMessageInfo{chromeType, jsi::Array(runtime, 0)}); - } else if (!toBoolean(runtime, args[0])) { - // Shift the message array down by one to not include the - // condition. - jsi::Array argsArray(runtime, count - 1); - for (size_t index = 1; index < count; ++index) - argsArray.setValueAtIndex(runtime, index, args[index]); - inspector->logMessage( - ConsoleMessageInfo{chromeType, std::move(argsArray)}); - } - } - - return jsi::Value::undefined(); - })); -} - -void Inspector::installLogHandler() { - jsi::Runtime &rt = adapter_->getRuntime(); - auto console = jsi::Object(rt); - auto val = rt.global().getProperty(rt, "console"); - std::shared_ptr originalConsole; - if (val.isObject()) { - originalConsole = std::make_shared(val.getObject(rt)); - } - installConsoleFunction(console, originalConsole, "assert"); - installConsoleFunction(console, originalConsole, "clear"); - installConsoleFunction(console, originalConsole, "debug"); - installConsoleFunction(console, originalConsole, "dir"); - installConsoleFunction(console, originalConsole, "dirxml"); - installConsoleFunction(console, originalConsole, "error"); - installConsoleFunction(console, originalConsole, "group", "startGroup"); - installConsoleFunction( - console, originalConsole, "groupCollapsed", "startGroupCollapsed"); - installConsoleFunction(console, originalConsole, "groupEnd", "endGroup"); - installConsoleFunction(console, originalConsole, "info"); - installConsoleFunction(console, originalConsole, "log"); - installConsoleFunction(console, originalConsole, "profile"); - installConsoleFunction(console, originalConsole, "profileEnd"); - installConsoleFunction(console, originalConsole, "table"); - installConsoleFunction(console, originalConsole, "trace"); - installConsoleFunction(console, originalConsole, "warn", "warning"); - rt.global().setProperty(rt, "console", console); -} - -void Inspector::triggerAsyncPause(bool andTickle) { - // In order to ensure that we pause soon, we both set the async pause flag on - // the runtime, and we run a bit of dummy JS to ensure we enter the Hermes - // interpreter loop. - debugger_.triggerAsyncPause( - pendingPauseState_ == AsyncPauseState::Implicit - ? debugger::AsyncPauseKind::Implicit - : debugger::AsyncPauseKind::Explicit); - - if (andTickle) { - // We run the dummy JS on a background thread to avoid any reentrancy issues - // in case this thread is called with the inspector mutex held. - std::shared_ptr adapter = adapter_; - detail::Thread tickleJsLater( - "inspectorTickleJs", [adapter]() { adapter->tickleJs(); }); - tickleJsLater.detach(); - } -} - -void Inspector::notifyContextCreated() { - observer_.onContextCreated(*this); -} - -ScriptInfo Inspector::getScriptInfoFromTopCallFrame() { - ScriptInfo info{}; - auto stackTrace = debugger_.getProgramState().getStackTrace(); - - if (stackTrace.callFrameCount() > 0) { - debugger::SourceLocation loc = stackTrace.callFrameForIndex(0).location; - - info.fileId = loc.fileId; - info.fileName = loc.fileName; - info.sourceMappingUrl = debugger_.getSourceMappingUrl(info.fileId); - } - - return info; -} - -void Inspector::addCurrentScriptToLoadedScripts() { - ScriptInfo info = getScriptInfoFromTopCallFrame(); - auto fileId = info.fileId; - - if (!loadedScripts_.count(fileId)) { - loadedScriptIdByName_[info.fileName] = fileId; - loadedScripts_[fileId] = LoadedScriptInfo{std::move(info), false}; - } -} - -void Inspector::removeAllBreakpoints() { - debugger_.deleteAllBreakpoints(); -} - -void Inspector::resetScriptsLoaded() { - for (auto &it : loadedScripts_) { - it.second.notifiedClient = false; - } -} - -void Inspector::notifyScriptsLoaded() { - for (auto &it : loadedScripts_) { - LoadedScriptInfo &loadedScriptInfo = it.second; - - if (!loadedScriptInfo.notifiedClient) { - loadedScriptInfo.notifiedClient = true; - observer_.onScriptParsed(*this, loadedScriptInfo.info); - } - } -} - -folly::Future Inspector::disable() { - auto promise = std::make_shared>(); - - executor_->add([this, promise] { disableOnExecutor(promise); }); - - return promise->getFuture(); -} - -folly::Future Inspector::enable() { - auto promise = std::make_shared>(); - - executor_->add([this, promise] { enableOnExecutor(promise); }); - - return promise->getFuture(); -} - -folly::Future Inspector::executeIfEnabled( - const std::string &description, - folly::Function func) { - auto promise = std::make_shared>(); - - executor_->add( - [this, description, func = std::move(func), promise]() mutable { - executeIfEnabledOnExecutor(description, std::move(func), promise); - }); - - return promise->getFuture(); -} - -folly::Future Inspector::setBreakpoint( - debugger::SourceLocation loc, - std::optional condition) { - auto promise = std::make_shared>(); - // Automatically re-enable breakpoints since the user presumably wants this - // to start triggering. - breakpointsActive_ = true; - - executor_->add([this, loc, condition, promise] { - setBreakpointOnExecutor(loc, condition, promise); - }); - - return promise->getFuture(); -} - -folly::Future Inspector::removeBreakpoint( - debugger::BreakpointID breakpointId) { - auto promise = std::make_shared>(); - - executor_->add([this, breakpointId, promise] { - removeBreakpointOnExecutor(breakpointId, promise); - }); - - return promise->getFuture(); -} - -folly::Future Inspector::logMessage(ConsoleMessageInfo info) { - auto promise = std::make_shared>(); - - executor_->add([this, - pInfo = std::make_unique(std::move(info)), - promise] { logOnExecutor(std::move(*pInfo), promise); }); - - return promise->getFuture(); -} - -folly::Future Inspector::setPendingCommand(debugger::Command command) { - auto promise = std::make_shared>(); - - executor_->add([this, promise, cmd = std::move(command)]() mutable { - setPendingCommandOnExecutor(std::move(cmd), promise); - }); - - return promise->getFuture(); -} - -folly::Future Inspector::resume() { - return setPendingCommand(debugger::Command::continueExecution()); -} - -folly::Future Inspector::stepIn() { - return setPendingCommand(debugger::Command::step(debugger::StepMode::Into)); -} - -folly::Future Inspector::stepOver() { - return setPendingCommand(debugger::Command::step(debugger::StepMode::Over)); -} - -folly::Future Inspector::stepOut() { - return setPendingCommand(debugger::Command::step(debugger::StepMode::Out)); -} - -folly::Future Inspector::pause() { - auto promise = std::make_shared>(); - - executor_->add([this, promise]() { pauseOnExecutor(promise); }); - - return promise->getFuture(); -} - -folly::Future Inspector::evaluate( - uint32_t frameIndex, - const std::string &src, - folly::Function - resultTransformer) { - auto promise = std::make_shared>(); - - executor_->add([this, - frameIndex, - src, - promise, - resultTransformer = std::move(resultTransformer)]() mutable { - evaluateOnExecutor(frameIndex, src, promise, std::move(resultTransformer)); - }); - - return promise->getFuture(); -} - -folly::Future Inspector::setPauseOnExceptions( - const debugger::PauseOnThrowMode &mode) { - auto promise = std::make_shared>(); - - executor_->add([this, mode, promise]() mutable { - setPauseOnExceptionsOnExecutor(mode, promise); - }); - - return promise->getFuture(); -}; - -folly::Future Inspector::setPauseOnLoads( - const PauseOnLoadMode mode) { - // This flag does not touch the runtime, so it doesn't need the executor. - // Return a future anyways for consistency. - auto promise = std::make_shared>(); - pauseOnLoadMode_ = mode; - promise->setValue(); - return promise->getFuture(); -}; - -folly::Future Inspector::setBreakpointsActive(bool active) { - // Same logic as setPauseOnLoads. - auto promise = std::make_shared>(); - breakpointsActive_ = active; - 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_); - - if (kShouldLog) { - LOG(INFO) << "received didPause for reason: " - << static_cast(debugger.getProgramState().getPauseReason()) - << " in state: " << *state_; - } - - while (true) { - /* - * Keep sending the onPause event to the current state until we get a - * command to return. For instance, this handles the transition from - * Running to Paused to Running: - * - * 1) (R => P) We're currently in Running, so we call Running::didPause, - * which returns {nextState: Paused, command: null}. There isn't a - * command to return yet. - * 2) (P => R) Now we're in Paused, so we call Paused::didPause, which - * returns {nextState: Running, command: someCommand} where someCommand - * is non-null (e.g. continue or step over). This terminates the loop. - */ - auto result = state_->didPause(lock); - - std::unique_ptr nextState = std::move(result.first); - if (nextState) { - TRANSITION(std::move(nextState)); - } - - std::unique_ptr command = std::move(result.second); - if (command) { - return std::move(*command); - } - } -} - -void Inspector::breakpointResolved( - debugger::Debugger &debugger, - debugger::BreakpointID breakpointId) { - std::unique_lock lock(mutex_); - - debugger::BreakpointInfo info = debugger.getBreakpointInfo(breakpointId); - observer_.onBreakpointResolved(*this, info); -} - -void Inspector::transition(std::unique_ptr nextState) { - assert(nextState); - assert(state_ != nextState); - - std::unique_ptr prevState = std::move(state_); - state_ = std::move(nextState); - state_->onEnter(prevState.get()); -} - -void Inspector::disableOnExecutor( - std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - debugger_.setIsDebuggerAttached(false); - - state_->detach(promise); -} - -void Inspector::enableOnExecutor( - std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - auto result = state_->enable(); - - /** - * We fulfill the promise before changing state because fulfilling the promise - * responds to the Debugger.enable request, and changing state could send a - * notification (like Debugger.paused). It seems like a good idea to respond - * to enable before sending out any notifications. - */ - bool enabled = result.second; - if (enabled) { - debugger_.setIsDebuggerAttached(true); - promise->setValue(); - } else { - promise->setException(AlreadyEnabledException()); - } - - std::unique_ptr nextState = std::move(result.first); - if (nextState) { - TRANSITION(std::move(nextState)); - } -} - -void Inspector::executeIfEnabledOnExecutor( - const std::string &description, - folly::Function func, - std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - if (!state_->isPaused() && !state_->isRunning()) { - promise->setException(InvalidStateException( - description, state_->description(), "paused or running")); - return; - } - - folly::Func wrappedFunc = [this, func = std::move(func)]() mutable { - func(debugger_.getProgramState()); - }; - - state_->pushPendingFunc( - [wrappedFunc = std::move(wrappedFunc), promise]() mutable { - if (auto userCallbackException = runUserCallback(wrappedFunc)) { - promise->setException(*userCallbackException); - } else { - promise->setValue(); - } - }); -} - -void Inspector::setBreakpointOnExecutor( - debugger::SourceLocation loc, - std::optional condition, - std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - bool pushed = state_->pushPendingFunc([this, loc, condition, promise] { - debugger::BreakpointID id = debugger_.setBreakpoint(loc); - debugger::BreakpointInfo info{debugger::kInvalidBreakpoint}; - if (id != debugger::kInvalidBreakpoint) { - info = debugger_.getBreakpointInfo(id); - - if (condition) { - debugger_.setBreakpointCondition(id, condition.value()); - } - } - - promise->setValue(std::move(info)); - }); - - if (!pushed) { - promise->setException(NotEnabledException("setBreakpoint")); - } -} - -void Inspector::removeBreakpointOnExecutor( - debugger::BreakpointID breakpointId, - std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - bool pushed = state_->pushPendingFunc([this, breakpointId, promise] { - debugger_.deleteBreakpoint(breakpointId); - promise->setValue(); - }); - - if (!pushed) { - promise->setException(NotEnabledException("removeBreakpoint")); - } -} - -void Inspector::logOnExecutor( - ConsoleMessageInfo info, - std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - state_->pushPendingFunc([this, info = std::move(info)] { - observer_.onMessageAdded(*this, info); - }); - - promise->setValue(); -} - -void Inspector::setPendingCommandOnExecutor( - debugger::Command command, - std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - state_->setPendingCommand(std::move(command), promise); -} - -void Inspector::pauseOnExecutor(std::shared_ptr> promise) { - std::scoped_lock lock(mutex_); - - bool canPause = state_->pause(); - - if (canPause) { - promise->setValue(); - } else { - promise->setException(NotEnabledException("pause")); - } -} - -void Inspector::evaluateOnExecutor( - uint32_t frameIndex, - const std::string &src, - std::shared_ptr> promise, - folly::Function - resultTransformer) { - std::scoped_lock lock(mutex_); - - state_->pushPendingEval( - frameIndex, src, promise, std::move(resultTransformer)); -} - -void Inspector::setPauseOnExceptionsOnExecutor( - const debugger::PauseOnThrowMode &mode, - std::shared_ptr> promise) { - std::scoped_lock local(mutex_); - - state_->pushPendingFunc([this, mode, promise] { - debugger_.setPauseOnThrowMode(mode); - promise->setValue(); - }); -} - -static const char *kSuppressionVariable = "_hermes_suppress_superseded_warning"; -void Inspector::alertIfPausedInSupersededFile() { - if (isExecutingSupersededFile() && - !shouldSuppressAlertAboutSupersededFiles()) { - ScriptInfo info = getScriptInfoFromTopCallFrame(); - std::string warning = - "You have loaded the current file multiple times, and you are " - "now paused in one of the previous instances. The source " - "code you see may not correspond to what's being executed " - "(set JS variable " + - std::string(kSuppressionVariable) + - "=true to " - "suppress this warning. Filename: " + - info.fileName + ")."; - jsi::Array jsiArray(adapter_->getRuntime(), 1); - jsiArray.setValueAtIndex(adapter_->getRuntime(), 0, warning); - - ConsoleMessageInfo logMessage("warning", std::move(jsiArray)); - observer_.onMessageAdded(*this, logMessage); - } -} - -bool Inspector::shouldSuppressAlertAboutSupersededFiles() { - jsi::Runtime &rt = adapter_->getRuntime(); - jsi::Value setting = rt.global().getProperty(rt, kSuppressionVariable); - - if (setting.isUndefined() || !setting.isBool()) - return false; - return setting.getBool(); -} - -bool Inspector::isExecutingSupersededFile() { - ScriptInfo info = getScriptInfoFromTopCallFrame(); - if (info.fileName.empty()) - return false; - - auto it = loadedScriptIdByName_.find(info.fileName); - if (it != loadedScriptIdByName_.end()) { - return it->second > info.fileId; - } - return false; -} - -bool Inspector::isAwaitingDebuggerOnStart() { - return awaitingDebuggerOnStart_; -} - -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/Inspector.h b/packages/react-native/ReactCommon/hermes/inspector/Inspector.h deleted file mode 100644 index 672bfad6b4a..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/Inspector.h +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// using include guards instead of #pragma once due to compile issues -// with MSVC and BUCK -#ifndef HERMES_INSPECTOR_INSPECTOR_H -#define HERMES_INSPECTOR_INSPECTOR_H - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { - -class Inspector; -class InspectorState; - -/** - * ScriptInfo contains info about loaded scripts. - */ -struct ScriptInfo { - uint32_t fileId{}; - std::string fileName; - std::string sourceMappingUrl; -}; - -struct ConsoleMessageInfo { - std::string source; - std::string level; - std::string url; - int line; - int column; - - jsi::Array args; - - ConsoleMessageInfo(std::string level, jsi::Array args) - : source("console-api"), - level(level), - url(""), - line(-1), - column(-1), - args(std::move(args)) {} -}; - -enum PauseOnLoadMode { None, Smart, All }; - -/** - * InspectorObserver notifies the observer of events that occur in the VM. - */ -class InspectorObserver { - public: - virtual ~InspectorObserver() = default; - - /// onContextCreated fires when the VM is created. - virtual void onContextCreated(Inspector &inspector) = 0; - - /// onBreakpointResolve fires when a lazy breakpoint is resolved. - virtual void onBreakpointResolved( - Inspector &inspector, - const facebook::hermes::debugger::BreakpointInfo &info) = 0; - - /// onPause fires when VM transitions from running to paused state. This is - /// called directly on the JS thread while the VM is paused, so the receiver - /// can call debugger::ProgramState methods safely. - virtual void onPause( - Inspector &inspector, - const facebook::hermes::debugger::ProgramState &state) = 0; - - /// onResume fires when VM transitions from paused to running state. - virtual void onResume(Inspector &inspector) = 0; - - /// onScriptParsed fires when after the VM parses a script. - virtual void onScriptParsed(Inspector &inspector, const ScriptInfo &info) = 0; - - // onMessageAdded fires when new console message is added. - virtual void onMessageAdded( - Inspector &inspector, - const ConsoleMessageInfo &info) = 0; -}; - -/** - * Inspector implements a future-based interface over the low-level Hermes - * debugging API. - */ -class Inspector : public facebook::hermes::debugger::EventObserver, - public std::enable_shared_from_this { - public: - /** - * Inspector's constructor should be used to install the inspector on the - * provided runtime before any JS executes in the runtime. - */ - Inspector( - std::shared_ptr adapter, - InspectorObserver &observer, - bool pauseOnFirstStatement); - ~Inspector() override; - - /** - * disable turns off the inspector. All of the subsequent methods will not do - * anything unless the inspector is enabled. - */ - folly::Future disable(); - - /** - * enable turns on the inspector. All of the subsequent methods will not do - * anything unless the inspector is enabled. The returned future succeeds when - * the debugger is enabled, or fails with AlreadyEnabledException if the - * debugger was already enabled. - */ - folly::Future enable(); - - /** - * installs console log handler. Ideally this should be done inside - * constructor, but because it uses shared_from_this we can't do this - * in constructor. - */ - void installLogHandler(); - - /** - * executeIfEnabled executes the provided callback *on the JS thread with the - * inspector lock held*. Execution can be implicitly requested while running. - * The inspector lock: - * - * 1) Protects VM state transitions. This means that the VM is guaranteed to - * stay in the paused or running state for the duration of the callback. - * 2) Protects InspectorObserver callbacks. This means that if some shared - * data is accessed only in InspectorObserver and executeIfEnabled - * callbacks, it does not need to be locked, since it's already protected - * by the inspector lock. - * - * The returned future resolves to true in the VM can be paused, or - * fails with IllegalStateException otherwise. The description is only used - * to populate the IllegalStateException with more useful info on failure. - */ - folly::Future executeIfEnabled( - const std::string &description, - folly::Function - func); - - /** - * setBreakpoint can be called at any time after the debugger is enabled to - * set a breakpoint in the VM. The future is fulfilled with the resolved - * breakpoint info. - * - * Resolving a breakpoint takes an indeterminate amount of time since Hermes - * only resolves breakpoints when the debugger is able to actively pause JS - * execution. - */ - folly::Future setBreakpoint( - facebook::hermes::debugger::SourceLocation loc, - std::optional condition = std::nullopt); - - folly::Future removeBreakpoint( - facebook::hermes::debugger::BreakpointID loc); - - /** - * logs console message. - */ - folly::Future logMessage(ConsoleMessageInfo info); - - /** - * resume and step methods are only valid when the VM is currently paused. The - * returned future succeeds when the VM resumes execution, or fails with an - * InvalidStateException otherwise. - */ - folly::Future resume(); - folly::Future stepIn(); - folly::Future stepOver(); - folly::Future stepOut(); - - /** - * pause can be issued at any time while the inspector is enabled. It requests - * the VM to asynchronously break execution. The returned future succeeds if - * the VM can be paused in this state and fails with InvalidStateException if - * otherwise. - */ - folly::Future pause(); - - /** - * evaluate runs JavaScript code within the context of a call frame. The - * returned promise is fulfilled with an eval result if it's possible to - * evaluate code in the current state or fails with InvalidStateException - * otherwise. - */ - folly::Future evaluate( - uint32_t frameIndex, - const std::string &src, - folly::Function - resultTransformer); - - 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); - - /** - * Set whether breakpoints are active (pause when hit). This does not require - * runtime modifications, but returns a future for consistency. - */ - folly::Future setBreakpointsActive(bool active); - - /** - * 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. - */ - facebook::hermes::debugger::Command didPause( - facebook::hermes::debugger::Debugger &debugger) override; - - /** - * breakpointResolved implements the breakpointResolved callback from Hermes. - */ - void breakpointResolved( - facebook::hermes::debugger::Debugger &debugger, - facebook::hermes::debugger::BreakpointID breakpointId) override; - - /** - * Get whether we started with pauseOnFirstStatement, and have not yet had a - * debugger attach and ask to resume from that point. This matches the - * semantics of when CDP Debugger.runIfWaitingForDebugger should resume. - * - * It's not named "isPausedOnStart" because the VM and inspector is not - * necessarily paused; we could be in a RunningWaitPause state. - */ - bool isAwaitingDebuggerOnStart(); - - private: - friend class InspectorState; - - void triggerAsyncPause(bool andTickle); - - void notifyContextCreated(); - - ScriptInfo getScriptInfoFromTopCallFrame(); - - void addCurrentScriptToLoadedScripts(); - void removeAllBreakpoints(); - void resetScriptsLoaded(); - void notifyScriptsLoaded(); - - folly::Future setPendingCommand(debugger::Command command); - - void transition(std::unique_ptr nextState); - - // All methods that end with OnExecutor run on executor_. - void disableOnExecutor(std::shared_ptr> promise); - - void enableOnExecutor(std::shared_ptr> promise); - - void executeIfEnabledOnExecutor( - const std::string &description, - folly::Function - func, - std::shared_ptr> promise); - - void setBreakpointOnExecutor( - debugger::SourceLocation loc, - std::optional condition, - std::shared_ptr< - folly::Promise> promise); - - void removeBreakpointOnExecutor( - debugger::BreakpointID breakpointId, - std::shared_ptr> promise); - - void logOnExecutor( - ConsoleMessageInfo info, - std::shared_ptr> promise); - - void setPendingCommandOnExecutor( - facebook::hermes::debugger::Command command, - std::shared_ptr> promise); - - void pauseOnExecutor(std::shared_ptr> promise); - - void evaluateOnExecutor( - uint32_t frameIndex, - const std::string &src, - std::shared_ptr> - promise, - folly::Function - resultTransformer); - - void setPauseOnExceptionsOnExecutor( - const facebook::hermes::debugger::PauseOnThrowMode &mode, - std::shared_ptr> promise); - - void installConsoleFunction( - jsi::Object &console, - std::shared_ptr &originalConsole, - const std::string &name, - const std::string &chromeType); - - std::shared_ptr adapter_; - facebook::hermes::debugger::Debugger &debugger_; - InspectorObserver &observer_; - - // All of the following member variables are guarded by mutex_. - std::mutex mutex_; - std::unique_ptr state_; - - // See the InspectorState::Running implementation for an explanation for why - // 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; - - // Whether or not we should pause on breakpoints. - bool breakpointsActive_ = true; - - // All scripts loaded in to the VM, along with whether we've notified the - // client about the script yet. - struct LoadedScriptInfo { - ScriptInfo info; - bool notifiedClient; - }; - std::unordered_map loadedScripts_; - std::unordered_map loadedScriptIdByName_; - - // Returns true if we are executing a file instance that has since been - // reloaded. I.e. we are running an old version of the file. - bool isExecutingSupersededFile(); - - // Allow the user to suppress warnings about superseded files. - bool shouldSuppressAlertAboutSupersededFiles(); - - // Trigger a fake console.log if we're currently in a superseded file. - void alertIfPausedInSupersededFile(); - - // Are we currently waiting for a debugger to attach, because we - // requested 'pauseOnFirstStatement'? - bool awaitingDebuggerOnStart_; - - // All client methods (e.g. enable, setBreakpoint, resume, etc.) are executed - // on executor_ to prevent deadlocking on mutex_. See the implementation for - // more comments on the threading invariants used in this class. - // NOTE: This needs to be declared LAST because it should be destroyed FIRST. - std::unique_ptr executor_; -}; - -/// Helper function that guards user code execution in a try-catch block. -template -std::optional runUserCallback(C &cb, A &&...arg) { - try { - cb(std::forward(arg)...); - } catch (const std::exception &e) { - return UserCallbackException(e); - } - - return {}; -} - -} // namespace inspector -} // namespace hermes -} // namespace facebook - -#endif // HERMES_INSPECTOR_INSPECTOR_H diff --git a/packages/react-native/ReactCommon/hermes/inspector/InspectorState.cpp b/packages/react-native/ReactCommon/hermes/inspector/InspectorState.cpp deleted file mode 100644 index 8ee53e9005c..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/InspectorState.cpp +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "InspectorState.h" - -#include - -namespace facebook { -namespace hermes { -namespace inspector { - -using folly::Unit; - -namespace debugger = ::facebook::hermes::debugger; - -namespace { - -std::unique_ptr makeContinueCommand() { - return std::make_unique( - debugger::Command::continueExecution()); -} - -} // namespace - -std::ostream &operator<<(std::ostream &os, const InspectorState &state) { - return os << state.description(); -} - -/* - * InspectorState::RunningDetached - */ - -std::pair InspectorState::RunningDetached::didPause( - MonitorLock &lock) { - debugger::PauseReason reason = getPauseReason(); - - if (reason == debugger::PauseReason::DebuggerStatement) { - return std::make_pair( - InspectorState::PausedWaitEnable::make(inspector_), nullptr); - } - - if (reason == debugger::PauseReason::ScriptLoaded) { - inspector_.addCurrentScriptToLoadedScripts(); - } - - return std::make_pair( - nullptr, makeContinueCommand()); -} - -void InspectorState::RunningDetached::onEnter(InspectorState *previous) { - inspector_.awaitingDebuggerOnStart_ = false; -} - -std::pair InspectorState::RunningDetached::enable() { - return std::make_pair( - InspectorState::Running::make(inspector_), true); -} - -/* - * InspectorState::RunningWaitEnable - */ - -std::pair InspectorState::RunningWaitEnable::didPause( - MonitorLock &lock) { - // If we started in RWE, then we asked for the VM to break on the first - // statement, and the first pause should be because of a script load. - assert(getPauseReason() == debugger::PauseReason::ScriptLoaded); - inspector_.addCurrentScriptToLoadedScripts(); - - return std::make_pair( - InspectorState::PausedWaitEnable::make(inspector_), nullptr); -} - -std::pair InspectorState::RunningWaitEnable::enable() { - return std::make_pair( - InspectorState::RunningWaitPause::make(inspector_), true); -} - -/* - * InspectorState::RunningWaitPause - */ -std::pair InspectorState::RunningWaitPause::didPause( - MonitorLock &lock) { - // If we are in RWP, then we asked for the VM to break on the first - // statement, and the first pause should be because of a script load. - assert(getPauseReason() == debugger::PauseReason::ScriptLoaded); - inspector_.addCurrentScriptToLoadedScripts(); - - return std::make_pair( - InspectorState::Paused::make(inspector_), nullptr); -} - -/* - * InspectorState::PausedWaitEnable - */ - -std::pair InspectorState::PausedWaitEnable::didPause( - MonitorLock &lock) { - if (getPauseReason() == debugger::PauseReason::ScriptLoaded) { - inspector_.addCurrentScriptToLoadedScripts(); - } - - while (!enabled_) { - /* - * The call to wait temporarily relinquishes the inspector mutex. This is - * safe because no other PausedWaitEnable event handler directly transitions - * out of PausedWaitEnable. So we know that our state is the active state - * both before and after the call to wait. This preserves the invariant that - * the inspector state is not modified during the execution of this method. - * - * Instead, PausedWaitEnable::enable indirectly induces the state transition - * out of PausedWaitEnable by signaling us via enabledCondition_. - */ - enabledCondition_.wait(lock); - - assert(inspector_.state_.get() == this); - } - - return std::make_pair( - InspectorState::Paused::make(inspector_), nullptr); -} - -std::pair InspectorState::PausedWaitEnable::enable() { - if (enabled_) { - // Someone already called enable before and we're just waiting for the - // condition variable to wake up didPause. - return std::make_pair(nullptr, false); - } - - enabled_ = true; - enabledCondition_.notify_one(); - return std::make_pair(nullptr, true); -} - -/* - * InspectorState::Running - * - * # Async Pauses - * - * We distinguish between implicit and explicit async pauses. An implicit async - * pause is requested by the inspector itself to service a request that requires - * the VM to be paused (e.g. to set a breakpoint). This is different from an - * explicit async pause requested by the user by hitting the pause button in the - * debugger UI. - * - * The async pause state must live in the Inspector class instead of the Running - * class because of potential races between when the implicit pause is requested - * and when it's serviced. Consider: - * - * 1. We request an implicit pause (e.g. to set a breakpoint). - * 2. An existing breakpoint fires, moving us from Running => Paused. - * 3. Client resumes execution, moving us from Paused => Running. - * 4. Now the debugger notices the async pause flag we set in (1), which pauses - * us again, causing Running::didPause to run. - * - * In this case, the Running state instance from (1) is no longer the same as - * the Running state instance in (4). But the running state instance in (4) - * needs to know that we requested the async break sometime in the past so it - * knows to automatically continue in the didPause callback. Therefore the async - * break state has to be stored in the long-lived Inspector class rather than in - * the short-lived Running class. - */ - -void InspectorState::Running::onEnter(InspectorState *prevState) { - if (prevState) { - if (prevState->isPaused()) { - inspector_.observer_.onResume(inspector_); - } else { - // send context created and script load notifications if we just enabled - // the debugger - inspector_.notifyContextCreated(); - inspector_.notifyScriptsLoaded(); - } - } - - inspector_.awaitingDebuggerOnStart_ = false; -} - -void InspectorState::Running::detach( - std::shared_ptr> promise) { - pushPendingFunc([this, promise] { - pendingDetach_ = promise; - - inspector_.removeAllBreakpoints(); - inspector_.resetScriptsLoaded(); - }); -} - -std::pair InspectorState::Running::didPause( - MonitorLock &lock) { - debugger::PauseReason reason = getPauseReason(); - - for (auto &func : pendingFuncs_) { - func(); - } - pendingFuncs_.clear(); - - if (pendingDetach_) { - // Clear any pending pause state back to no requests for the next attach - inspector_.pendingPauseState_ = AsyncPauseState::None; - - // Ensure we fulfill any pending ScriptLoaded requests - if (reason == debugger::PauseReason::ScriptLoaded) { - inspector_.addCurrentScriptToLoadedScripts(); - } - - // Fail any in-flight Eval requests - if (pendingEvalPromise_) { - pendingEvalPromise_->setException(NotEnabledException("eval")); - } - - // if we requested the break implicitly to clear state and detach, - // transition to RunningDetached - pendingDetach_->setValue(); - - return std::make_pair( - InspectorState::RunningDetached::make(inspector_), - makeContinueCommand()); - } - - if (reason == debugger::PauseReason::AsyncTrigger) { - AsyncPauseState &pendingPauseState = inspector_.pendingPauseState_; - - switch (pendingPauseState) { - case AsyncPauseState::None: - // shouldn't ever async break without us asking first - assert(false); - break; - case AsyncPauseState::Implicit: - pendingPauseState = AsyncPauseState::None; - break; - case AsyncPauseState::Explicit: - // explicit break was requested by user, so go to Paused state - pendingPauseState = AsyncPauseState::None; - return std::make_pair( - InspectorState::Paused::make(inspector_), nullptr); - } - } 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_); - - if (auto userCallbackException = runUserCallback( - pendingEvalResultTransformer_, - inspector_.debugger_.getProgramState().getEvalResult())) { - pendingEvalPromise_->setException(*userCallbackException); - } else { - pendingEvalPromise_->setValue( - inspector_.debugger_.getProgramState().getEvalResult()); - } - pendingEvalPromise_.reset(); - } else if ( - reason == debugger::PauseReason::Breakpoint && - !inspector_.breakpointsActive_) { - // We hit a user defined breakpoint, but breakpoints have been deactivated. - return std::make_pair( - nullptr, makeContinueCommand()); - } else /* other cases imply a transition to Pause */ { - return std::make_pair( - InspectorState::Paused::make(inspector_), nullptr); - } - - if (!pendingEvals_.empty()) { - assert(!pendingEvalPromise_); - - auto eval = std::make_unique(std::move(pendingEvals_.front())); - pendingEvals_.pop(); - - pendingEvalPromise_ = eval->promise; - pendingEvalResultTransformer_ = std::move(eval->resultTransformer); - - return std::make_pair( - nullptr, std::make_unique(std::move(eval->command))); - } - - return std::make_pair( - nullptr, makeContinueCommand()); -} - -bool InspectorState::Running::pushPendingFunc(folly::Func func) { - pendingFuncs_.emplace_back(std::move(func)); - - if (inspector_.pendingPauseState_ == AsyncPauseState::None) { - inspector_.pendingPauseState_ = AsyncPauseState::Implicit; - inspector_.triggerAsyncPause(true); - } - - return true; -} - -void InspectorState::Running::pushPendingEval( - uint32_t frameIndex, - const std::string &src, - std::shared_ptr> promise, - folly::Function - resultTransformer) { - PendingEval pendingEval{ - debugger::Command::eval(src, frameIndex), - promise, - std::move(resultTransformer)}; - - pendingEvals_.emplace(std::move(pendingEval)); - - if (inspector_.pendingPauseState_ == AsyncPauseState::None) { - inspector_.pendingPauseState_ = AsyncPauseState::Implicit; - } - - inspector_.triggerAsyncPause(true); -} - -bool InspectorState::Running::pause() { - AsyncPauseState &pendingPauseState = inspector_.pendingPauseState_; - bool canPause = false; - - switch (pendingPauseState) { - case AsyncPauseState::None: - // haven't yet requested a pause, so do it now - pendingPauseState = AsyncPauseState::Explicit; - inspector_.triggerAsyncPause(false); - canPause = true; - break; - case AsyncPauseState::Implicit: - // already requested an implicit pause on our own, upgrade it to an - // explicit pause - pendingPauseState = AsyncPauseState::Explicit; - inspector_.triggerAsyncPause(false); - canPause = true; - break; - case AsyncPauseState::Explicit: - // client already requested a pause that hasn't occurred yet - canPause = false; - break; - } - - return canPause; -} - -/* - * InspectorState::Paused - */ - -void InspectorState::Paused::onEnter(InspectorState *prevState) { - // send script load notifications if we just enabled the debugger - if (prevState && !prevState->isRunning()) { - inspector_.notifyContextCreated(); - inspector_.notifyScriptsLoaded(); - } - - const debugger::ProgramState &state = inspector_.debugger_.getProgramState(); - inspector_.alertIfPausedInSupersededFile(); - inspector_.observer_.onPause(inspector_, state); -} - -std::pair InspectorState::Paused::didPause( - std::unique_lock &lock) { - switch (getPauseReason()) { - case debugger::PauseReason::AsyncTrigger: - inspector_.pendingPauseState_ = AsyncPauseState::None; - break; - case debugger::PauseReason::EvalComplete: { - assert(pendingEvalPromise_); - if (auto userCallbackException = runUserCallback( - pendingEvalResultTransformer_, - inspector_.debugger_.getProgramState().getEvalResult())) { - pendingEvalPromise_->setException(*userCallbackException); - } else { - pendingEvalPromise_->setValue( - inspector_.debugger_.getProgramState().getEvalResult()); - } - pendingEvalPromise_.reset(); - } break; - case debugger::PauseReason::ScriptLoaded: - inspector_.addCurrentScriptToLoadedScripts(); - inspector_.notifyScriptsLoaded(); - break; - default: - break; - } - - std::unique_ptr eval; - std::unique_ptr resumeOrStep; - - while (!eval && !resumeOrStep && !pendingDetach_) { - { - while (!pendingCommand_ && pendingEvals_.empty() && - pendingFuncs_.empty()) { - /* - * The call to wait temporarily relinquishes the inspector mutex. This - * is safe because no other Paused event handler directly transitions - * out of Paused. So we know that our state is the active state both - * before and after the call to wait. This preserves the invariant that - * the inspector state is not modified during the execution of this - * method. - */ - hasPendingWork_.wait(lock); - } - - assert(inspector_.state_.get() == this); - } - - if (!pendingEvals_.empty()) { - eval = std::make_unique(std::move(pendingEvals_.front())); - pendingEvals_.pop(); - } else if (pendingCommand_) { - resumeOrStep.swap(pendingCommand_); - } - - for (auto &func : pendingFuncs_) { - func(); - } - pendingFuncs_.clear(); - } - - if (pendingDetach_) { - if (pendingEvalPromise_) { - pendingEvalPromise_->setException(NotEnabledException("eval")); - } - - if (resumeOrStep) { - resumeOrStep->promise->setValue(); - } - - pendingDetach_->setValue(); - - // Send resume so client-side UI doesn't stay stuck at the breakpoint UI - inspector_.observer_.onResume(inspector_); - - return std::make_pair( - InspectorState::RunningDetached::make(inspector_), - makeContinueCommand()); - } - - if (eval) { - assert(!pendingEvalPromise_); - pendingEvalPromise_ = eval->promise; - pendingEvalResultTransformer_ = std::move(eval->resultTransformer); - - return std::make_pair( - nullptr, std::make_unique(std::move(eval->command))); - } - - assert(resumeOrStep); - resumeOrStep->promise->setValue(); - - return std::make_pair( - InspectorState::Running::make(inspector_), - std::make_unique(std::move(resumeOrStep->command))); -} - -void InspectorState::Paused::detach( - std::shared_ptr> promise) { - pushPendingFunc([this, promise] { - pendingDetach_ = promise; - - inspector_.removeAllBreakpoints(); - inspector_.resetScriptsLoaded(); - }); -} - -bool InspectorState::Paused::pushPendingFunc(folly::Func func) { - pendingFuncs_.emplace_back(std::move(func)); - hasPendingWork_.notify_one(); - - return true; -} - -void InspectorState::Paused::pushPendingEval( - uint32_t frameIndex, - const std::string &src, - std::shared_ptr> promise, - folly::Function - resultTransformer) { - // Shouldn't allow the client to eval if there's already a pending resume/step - if (pendingCommand_) { - promise->setException(MultipleCommandsPendingException("eval")); - return; - } - - PendingEval pendingEval{ - debugger::Command::eval(src, frameIndex), - promise, - std::move(resultTransformer)}; - pendingEvals_.emplace(std::move(pendingEval)); - hasPendingWork_.notify_one(); -} - -void InspectorState::Paused::setPendingCommand( - debugger::Command command, - std::shared_ptr> promise) { - if (pendingCommand_) { - promise->setException(MultipleCommandsPendingException("cmd")); - return; - } - - pendingCommand_ = - std::make_unique(std::move(command), promise); - hasPendingWork_.notify_one(); -} - -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/InspectorState.h b/packages/react-native/ReactCommon/hermes/inspector/InspectorState.h deleted file mode 100644 index 3552b5c1877..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/InspectorState.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// using include guards instead of #pragma once due to compile issues -// with MSVC and BUCK -#ifndef HERMES_INSPECTOR_INSPECTOR_STATE_H -#define HERMES_INSPECTOR_INSPECTOR_STATE_H - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { - -using NextStatePtr = std::unique_ptr; -using CommandPtr = std::unique_ptr; -using MonitorLock = std::unique_lock; - -/** - * InspectorState encapsulates a single state in the Inspector FSM. Events in - * the FSM are modeled as methods in InspectorState. - * - * Some events may cause state transitions. The next state is returned via a - * pointer to the next InspectorState. - * - * We assume that the Inspector's mutex is held across all calls to - * InspectorState methods. For more threading notes, see the Inspector - * implementation. - */ -class InspectorState { - public: - InspectorState(Inspector &inspector) : inspector_(inspector) {} - virtual ~InspectorState() = default; - /** - * onEnter is called when entering the state. prevState may be null when - * transitioning into an initial state. - */ - virtual void onEnter(InspectorState *prevState) {} - - /* - * Events that may cause a state transition. - */ - - /** - * detach clears all debugger state and transitions to RunningDetached. - */ - virtual void detach(std::shared_ptr> promise) { - // As we're not attached we'd like for the operation to be idempotent - promise->setValue(); - } - - /** - * didPause handles the didPause callback from the debugger. It takes the lock - * associated with the Inspector's mutex by reference in case we need to - * temporarily relinquish the lock (e.g. via condition_variable::wait). - */ - virtual std::pair didPause(MonitorLock &lock) = 0; - - /** - * enable handles the enable event from the client. - */ - virtual std::pair enable() { - return std::make_pair(nullptr, false); - } - - /* - * Events that don't cause a state transition. - */ - - /** - * pushPendingFunc appends a function to run the next time the debugger - * pauses, either explicitly while paused or implicitly while running. - * Returns false if it's not possible to push a func in this state. - */ - virtual bool pushPendingFunc(folly::Func func) { - return false; - } - - /** - * pushPendingEval appends an eval request to run the next time the debugger - * pauses, either explicitly while paused or implicitly while running. - * resultTransformer function will be called with EvalResult before returning - * result so that we can manipulate EvalResult while the VM is paused. - */ - virtual void pushPendingEval( - uint32_t frameIndex, - const std::string &src, - std::shared_ptr> - promise, - folly::Function - resultTransformer) { - promise->setException( - InvalidStateException("eval", description(), "paused or running")); - } - - /** - * setPendingCommand sets a command to break the debugger out of the didPause - * run loop. If it's not possible to set a pending command in this state, the - * promise fails with InvalidStateException. Otherwise, the promise resolves - * to true when the command actually executes. - */ - virtual void setPendingCommand( - debugger::Command command, - std::shared_ptr> promise) { - promise->setException( - InvalidStateException("cmd", description(), "paused")); - } - - /** - * pause requests an async pause from the VM. - */ - virtual bool pause() { - return false; - } - - /* - * Convenience functions for determining the concrete type and description - * for a state instance without RTTI. - */ - - virtual bool isRunningDetached() const { - return false; - } - virtual bool isRunningWaitEnable() const { - return false; - } - virtual bool isRunningWaitPause() const { - return false; - } - virtual bool isPausedWaitEnable() const { - return false; - } - virtual bool isRunning() const { - return false; - } - virtual bool isPaused() const { - return false; - } - - virtual const char *description() const = 0; - friend std::ostream &operator<<( - std::ostream &os, - const InspectorState &state); - - class RunningDetached; - class RunningWaitEnable; - class RunningWaitPause; - class PausedWaitEnable; - class Running; - class Paused; - - protected: - debugger::PauseReason getPauseReason() { - return inspector_.debugger_.getProgramState().getPauseReason(); - } - - private: - Inspector &inspector_; -}; - -extern std::ostream &operator<<(std::ostream &os, const InspectorState &state); - -/** - * RunningDetached is the initial state when we're associated with a VM that - * initially has no breakpoints. - */ -class InspectorState::RunningDetached : public InspectorState { - public: - static std::unique_ptr make(Inspector &inspector) { - return std::make_unique(inspector); - } - - RunningDetached(Inspector &inspector) : InspectorState(inspector) {} - ~RunningDetached() override {} - - std::pair didPause(MonitorLock &lock) override; - std::pair enable() override; - - void onEnter(InspectorState *prevState) override; - - bool isRunningDetached() const override { - return true; - } - - const char *description() const override { - return "RunningDetached"; - } -}; - -/** - * RunningWaitEnable is the initial state when we're associated with a VM that - * has a breakpoint on the first statement. - */ -class InspectorState::RunningWaitEnable : public InspectorState { - public: - static std::unique_ptr make(Inspector &inspector) { - return std::make_unique(inspector); - } - - RunningWaitEnable(Inspector &inspector) : InspectorState(inspector) {} - ~RunningWaitEnable() override {} - - std::pair didPause(MonitorLock &lock) override; - std::pair enable() override; - - bool isRunningWaitEnable() const override { - return true; - } - - const char *description() const override { - return "RunningWaitEnable"; - } -}; - -/** - * RunningWaitPause is the state when we've received enable call, but - * waiting for didPause because we need to pause on the first statement. - */ -class InspectorState::RunningWaitPause : public InspectorState { - public: - static std::unique_ptr make(Inspector &inspector) { - return std::make_unique(inspector); - } - - std::pair didPause(MonitorLock &lock) override; - - RunningWaitPause(Inspector &inspector) : InspectorState(inspector) {} - ~RunningWaitPause() {} - - bool isRunningWaitPause() const override { - return true; - } - - const char *description() const override { - return "RunningWaitPause"; - } -}; - -/** - * PausedWaitEnable is the state when we're in a didPause callback and we're - * waiting for the client to call enable. - */ -class InspectorState::PausedWaitEnable : public InspectorState { - public: - static std::unique_ptr make(Inspector &inspector) { - return std::make_unique(inspector); - } - - PausedWaitEnable(Inspector &inspector) : InspectorState(inspector) {} - ~PausedWaitEnable() override {} - - std::pair didPause(MonitorLock &lock) override; - std::pair enable() override; - - bool isPausedWaitEnable() const override { - return true; - } - - const char *description() const override { - return "PausedWaitEnable"; - } - - private: - bool enabled_ = false; - std::condition_variable enabledCondition_; -}; - -/** - * PendingEval holds an eval command and a promise that is fulfilled with the - * eval result. - */ -struct PendingEval { - debugger::Command command; - std::shared_ptr> - promise; - folly::Function - resultTransformer; -}; - -/** - * Running is the state when we're enabled and not currently paused, e.g. when - * we're actively executing JS. - * - * Note that we can be in the running state even if we're not actively running - * JS. For instance, React Native could be blocked in a native message queue - * waiting for the next message to process outside of the call in to Hermes. - * That still counts as Running in this FSM. - */ -class InspectorState::Running : public InspectorState { - public: - static std::unique_ptr make(Inspector &inspector) { - return std::make_unique(inspector); - } - - Running(Inspector &inspector) : InspectorState(inspector) {} - ~Running() override {} - - void onEnter(InspectorState *prevState) override; - - void detach(std::shared_ptr> promise) override; - - std::pair didPause(MonitorLock &lock) override; - bool pushPendingFunc(folly::Func func) override; - void pushPendingEval( - uint32_t frameIndex, - const std::string &src, - std::shared_ptr> - promise, - folly::Function - resultTransformer) override; - bool pause() override; - - bool isRunning() const override { - return true; - } - - const char *description() const override { - return "Running"; - } - - private: - std::vector pendingFuncs_; - std::queue pendingEvals_; - std::shared_ptr> - pendingEvalPromise_; - folly::Function - pendingEvalResultTransformer_; - std::shared_ptr> pendingDetach_; -}; - -/** - * PendingCommand holds a resume or step command and a promise that is fulfilled - * just before the debugger resumes or steps. - */ -struct PendingCommand { - PendingCommand( - debugger::Command command, - std::shared_ptr> promise) - : command(std::move(command)), promise(promise) {} - - debugger::Command command; - std::shared_ptr> promise; -}; - -/** - * Paused is the state when we're enabled and and currently in a didPause - * callback. - */ -class InspectorState::Paused : public InspectorState { - public: - static std::unique_ptr make(Inspector &inspector) { - return std::make_unique(inspector); - } - - Paused(Inspector &inspector) : InspectorState(inspector) {} - ~Paused() override {} - - void onEnter(InspectorState *prevState) override; - - void detach(std::shared_ptr> promise) override; - - std::pair didPause(MonitorLock &lock) override; - bool pushPendingFunc(folly::Func func) override; - void pushPendingEval( - uint32_t frameIndex, - const std::string &src, - std::shared_ptr> - promise, - folly::Function - resultTransformer) override; - void setPendingCommand( - debugger::Command command, - std::shared_ptr> promise) override; - - bool isPaused() const override { - return true; - } - - const char *description() const override { - return "Paused"; - } - - private: - std::condition_variable hasPendingWork_; - std::vector pendingFuncs_; - std::queue pendingEvals_; - std::shared_ptr> - pendingEvalPromise_; - folly::Function - pendingEvalResultTransformer_; - std::unique_ptr pendingCommand_; - std::shared_ptr> pendingDetach_; -}; - -} // namespace inspector -} // namespace hermes -} // namespace facebook - -#endif // HERMES_INSPECTOR_INSPECTOR_STATE_H diff --git a/packages/react-native/ReactCommon/hermes/inspector/README.md b/packages/react-native/ReactCommon/hermes/inspector/README.md deleted file mode 100644 index 3b56811530c..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# DEPRECATED! - -This is a **deprecated, frozen version of the Hermes inspector support code in React Native**. See `ReactCommon/hermes/inspector-modern` instead. - ---- - -hermes-inspector provides a bridge between the low-level debugging API exposed -by Hermes and higher-level debugging protocols such as the Chrome DevTools -protocol. - -# Targets - - - chrome: classes that implement the Chrome DevTools Protocol adapter. Sits on - top of classes provided by the inspector target. - - detail: utility classes and functions - - inspector: protocol-independent classes that sit on top of the low-level - Hermes debugging API. - -# Testing - -Tests are implemented using gtest. Debug logging is enabled for tests, and you -can get debug logs to show even when tests are passing by running the test -executable directly: - -``` -$ buck build //xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector:chrome-tests -$ buck-out/gen/xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector/chrome-tests -[...] -``` - -You can use standard gtest filters to only execute a particular set of tests: - -``` -$ buck-out/gen/xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector/chrome-tests \ - --gtest_filter='ConnectionTests.testSetBreakpoint' -``` - -You can debug the tests using lldb or gdb: - -``` -$ lldb buck-out/gen/xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector/chrome-tests -$ gdb buck-out/gen/xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector/chrome-tests -``` - -# Formatting - -Make sure the code is formatted using the hermes clang-format rules before -committing: - -``` -$ xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector/tools/format -``` - -We follow the clang format rules used by the rest of the Hermes project. - -# Adding Support For New Message Types - -To add support for a new Chrome DevTools protocol message, add the message you -want to add to tools/message_types.txt, and re-run the message types generator: - -``` -$ xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen -``` - -This will generate C++ structs for the new message type in -`chrome/MessageTypes.{h,cpp}`. - -You'll then need to: - -1. Implement a message handler for the new message type in `chrome::Connection`. -2. Implement a public API for the new message type in `Inspector`. This will - most likely return a `folly::Future` that the message handler in (1) can use - for chaining. -3. Implement a private API for the new message type in `Inspector` that performs - the logic in Inspector's executor. (Inspector.cpp contains a comment - explaining why the executor is necessary.) -4. Optionally, implement a method for the new message type in `InspectorState`. - In most cases this is probably not necessary--one of the existing methods in - `InspectorState` will work. - -For a diff that illustrates these steps, take a look at D6601459. - -# Testing Integration With Nuclide and Apps - -For now, the quickest way to use hermes-inspector in an app is with Eats. First, -make sure the packager is running: - -``` -$ js1 run -``` - -Then, on Android, build the fbeats target: - -``` -$ buck install --run fbeats -``` - -On iOS, build the `//Apps/Internal/Eats:Eats` target: - -``` -$ buck install --run //Apps/Internal/Eats:Eats -``` - -You can also build `Eats` in Xcode using `arc focus` if you prefer an -IDE: - -``` -$ arc focus --force-build \ - -b //Apps/Internal/Eats:Eats \ - cxxreact //xplat/hermes/API:HermesAPI //xplat/hermes/lib/VM:VM jsi \ - jsinspector hermes-inspector FBReactKit FBReactModule FBCatalystWrapper \ - //xplat/js:React //xplat/js/react-native-github:ReactInternal -``` - -For all the above commands, if you want to build the inspector `-O0` for better -debug info, add the argument `--config hermes.build_mode=dbg`. - -You should then be able to launch the app and see it listed in the list of -Mobile JS contexts in the Nuclide debugger. diff --git a/packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.cpp b/packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.cpp deleted file mode 100644 index 0085acee372..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "RuntimeAdapter.h" - -namespace facebook { -namespace hermes { -namespace inspector { - -RuntimeAdapter::~RuntimeAdapter() = default; - -void RuntimeAdapter::tickleJs() {} - -SharedRuntimeAdapter::SharedRuntimeAdapter( - std::shared_ptr runtime) - : runtime_(std::move(runtime)) {} - -SharedRuntimeAdapter::~SharedRuntimeAdapter() = default; - -HermesRuntime &SharedRuntimeAdapter::getRuntime() { - return *runtime_; -} - -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.h b/packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.h deleted file mode 100644 index 253c9879b92..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/RuntimeAdapter.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include - -#ifndef INSPECTOR_EXPORT -#ifdef _MSC_VER -#ifdef CREATE_SHARED_LIBRARY -#define INSPECTOR_EXPORT __declspec(dllexport) -#else -#define INSPECTOR_EXPORT -#endif // CREATE_SHARED_LIBRARY -#else // _MSC_VER -#define INSPECTOR_EXPORT __attribute__((visibility("default"))) -#endif // _MSC_VER -#endif // !defined(INSPECTOR_EXPORT) - -namespace facebook { -namespace hermes { -namespace inspector { - -/** - * RuntimeAdapter encapsulates a HermesRuntime object. The underlying Hermes - * runtime object should stay alive for at least as long as the RuntimeAdapter - * is alive. - */ -class INSPECTOR_EXPORT RuntimeAdapter { - public: - virtual ~RuntimeAdapter() = 0; - - /// getRuntime should return the runtime encapsulated by this adapter. - virtual HermesRuntime &getRuntime() = 0; - - /// tickleJs is a method that subclasses can choose to override to make the - /// inspector more responsive. If overridden, it should call the "__tickleJs" - /// function. The call should occur with appropriate locking (e.g. via a - /// thread-safe runtime instance, or by enqueuing the call on to a dedicated - /// JS thread). - /// - /// This makes the inspector more responsive because it gives the inspector - /// the ability to force the process to enter the Hermes interpreter loop - /// soon. This is important because the inspector can only do a number of - /// important operations (like manipulating breakpoints) within the context of - /// a Hermes interpreter loop. - /// - /// The default implementation does nothing. - virtual void tickleJs(); -}; - -/** - * SharedRuntimeAdapter is a simple implementation of RuntimeAdapter that - * uses shared_ptr to hold on to the runtime. It's generally only used in tests, - * since it does not implement tickleJs. - */ -class INSPECTOR_EXPORT SharedRuntimeAdapter : public RuntimeAdapter { - public: - SharedRuntimeAdapter(std::shared_ptr runtime); - ~SharedRuntimeAdapter() override; - - HermesRuntime &getRuntime() override; - - private: - std::shared_ptr runtime_; -}; - -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp deleted file mode 100644 index 94ccfc01344..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ /dev/null @@ -1,1667 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "Connection.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -using ::facebook::react::ILocalConnection; -using ::facebook::react::IRemoteConnection; -using ::folly::Unit; - -namespace debugger = ::facebook::hermes::debugger; -namespace inspector = ::facebook::hermes::inspector; -namespace m = ::facebook::hermes::inspector::chrome::message; - -static const char *const kVirtualBreakpointPrefix = "virtualbreakpoint-"; -static const char *const kBeforeScriptWithSourceMapExecution = - "beforeScriptWithSourceMapExecution"; -static const char *const kUserEnteredScriptPrefix = "userScript"; - -/* - * Connection::Impl - */ - -class Connection::Impl : public inspector::InspectorObserver, - public message::RequestHandler { - public: - Impl( - std::unique_ptr adapter, - const std::string &title, - bool waitForDebugger); - ~Impl(); - - HermesRuntime &getRuntime(); - std::string getTitle() const; - - bool connect(std::unique_ptr remoteConn); - bool disconnect(); - void sendMessage(std::string str); - - /* InspectorObserver overrides */ - void onBreakpointResolved( - Inspector &inspector, - const debugger::BreakpointInfo &info) override; - void onContextCreated(Inspector &inspector) override; - void onPause(Inspector &inspector, const debugger::ProgramState &state) - override; - void onResume(Inspector &inspector) override; - void onScriptParsed(Inspector &inspector, const ScriptInfo &info) override; - void onMessageAdded(Inspector &inspector, const ConsoleMessageInfo &info) - override; - - /* RequestHandler overrides */ - void handle(const m::UnknownRequest &req) override; - void handle(const m::debugger::DisableRequest &req) override; - void handle(const m::debugger::EnableRequest &req) override; - void handle(const m::debugger::EvaluateOnCallFrameRequest &req) override; - void handle(const m::debugger::PauseRequest &req) override; - void handle(const m::debugger::RemoveBreakpointRequest &req) override; - void handle(const m::debugger::ResumeRequest &req) override; - void handle(const m::debugger::SetBreakpointRequest &req) override; - void handle(const m::debugger::SetBreakpointByUrlRequest &req) override; - void handle(const m::debugger::SetBreakpointsActiveRequest &req) override; - void handle( - const m::debugger::SetInstrumentationBreakpointRequest &req) override; - void handle(const m::debugger::SetPauseOnExceptionsRequest &req) override; - void handle(const m::debugger::StepIntoRequest &req) override; - void handle(const m::debugger::StepOutRequest &req) override; - void handle(const m::debugger::StepOverRequest &req) override; - void handle(const m::heapProfiler::TakeHeapSnapshotRequest &req) override; - void handle( - const m::heapProfiler::StartTrackingHeapObjectsRequest &req) override; - void handle( - const m::heapProfiler::StopTrackingHeapObjectsRequest &req) override; - void handle(const m::heapProfiler::StartSamplingRequest &req) override; - void handle(const m::heapProfiler::StopSamplingRequest &req) override; - void handle(const m::heapProfiler::CollectGarbageRequest &req) override; - void handle( - const m::heapProfiler::GetObjectByHeapObjectIdRequest &req) override; - void handle(const m::heapProfiler::GetHeapObjectIdRequest &req) override; - void handle(const m::profiler::StartRequest &req) override; - void handle(const m::profiler::StopRequest &req) override; - void handle(const m::runtime::CallFunctionOnRequest &req) override; - void handle(const m::runtime::CompileScriptRequest &req) override; - 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: - // The execution context id reported back by the ExecutionContextCreated - // notification. We only ever expect this execution context id. - static constexpr int32_t kHermesExecutionContextId = 1; - std::vector makePropsFromScope( - std::pair frameAndScopeIndex, - const std::string &objectGroup, - const debugger::ProgramState &state, - bool generatePreview); - std::vector makePropsFromValue( - const jsi::Value &value, - const std::string &objectGroup, - bool onlyOwnProperties, - bool generatePreview); - - void sendSnapshot( - int reqId, - std::string message, - bool reportProgress, - bool stopStackTraceCapture); - void sendToClient(const std::string &str); - void sendResponseToClient(const m::Response &resp); - void sendNotificationToClient(const m::Notification &resp); - folly::Function sendErrorToClient(int id); - void sendResponseToClientViaExecutor(int id); - void sendResponseToClientViaExecutor(folly::Future future, int id); - void sendResponseToClientViaExecutor(const m::Response &resp); - void sendNotificationToClientViaExecutor(const m::Notification ¬e); - void sendErrorToClientViaExecutor(int id, const std::string &error); - - template - void runInExecutor(int id, C callback) { - executor_->add([cb = std::move(callback)]() { cb(); }); - } - - std::shared_ptr runtimeAdapter_; - std::string title_; - - // connected_ is protected by connectionMutex_. - std::mutex connectionMutex_; - bool connected_; - - // parsedScripts_ list stores file names of all scripts that have been - // parsed so that we could find script's file name by regex. - // This is similar to Inspector's loadedScripts_ map but we want to - // store this info here because searching file name that matches - // given regex (on setBreakpointByUrl command) is more related to Chrome - // protocol than to Hermes inspector. - // Access is protected by parsedScriptsMutex_. - std::mutex parsedScriptsMutex_; - std::vector parsedScripts_; - - // preparedScripts_ stores user-entered scripts that have been prepared for - // execution, and may be invoked by a later command. - std::vector> preparedScripts_; - - // Some events are represented as a mode in Hermes but a breakpoint in CDP, - // e.g. "beforeScriptExecution" and "beforeScriptWithSourceMapExecution". - // Keep track of these separately. The caller should lock the - // virtualBreakpointMutex_. - std::mutex virtualBreakpointMutex_; - uint32_t nextVirtualBreakpoint_ = 1; - const std::string &createVirtualBreakpoint(const std::string &category); - bool isVirtualBreakpointId(const std::string &id); - bool hasVirtualBreakpoint(const std::string &category); - bool removeVirtualBreakpoint(const std::string &id); - std::unordered_map> - virtualBreakpoints_; - - // The rest of these member variables are only accessed via executor_. - std::unique_ptr executor_; - std::unique_ptr remoteConn_; - std::shared_ptr inspector_; - - // objTable_ is protected by the inspector lock. It should only be accessed - // when the VM is paused, e.g. in an InspectorObserver callback or in an - // executeIfEnabled callback. - RemoteObjectsTable objTable_; -}; - -Connection::Impl::Impl( - std::unique_ptr adapter, - const std::string &title, - bool waitForDebugger) - : runtimeAdapter_(std::move(adapter)), - title_(title), - connected_(false), - executor_(std::make_unique( - "hermes-chrome-inspector-conn")), - remoteConn_(nullptr), - inspector_(std::make_shared( - runtimeAdapter_, - *this, - waitForDebugger)) { - inspector_->installLogHandler(); -} - -Connection::Impl::~Impl() = default; - -HermesRuntime &Connection::Impl::getRuntime() { - return runtimeAdapter_->getRuntime(); -} - -std::string Connection::Impl::getTitle() const { - return title_; -} - -bool Connection::Impl::connect(std::unique_ptr remoteConn) { - assert(remoteConn); - std::scoped_lock lock(connectionMutex_); - - if (connected_) { - return false; - } - - connected_ = true; - executor_->add([this, remoteConn = std::move(remoteConn)]() mutable { - remoteConn_ = std::move(remoteConn); - }); - - return true; -} - -bool Connection::Impl::disconnect() { - std::scoped_lock lock(connectionMutex_); - - if (!connected_) { - return false; - } - - connected_ = false; - - inspector_->disable().via(executor_.get()).thenValue([this](auto &&) { - // HACK: We purposely call RemoteConnection::onDisconnect on a *different* - // thread, rather than on this thread (the executor thread). This is to - // prevent this scenario: - // - // 1. RemoteConnection::onDisconnect runs on the executor thread - // 2. onDisconnect through a long chain of calls causes the Connection - // destructor to run - // 3. The Connection destructor causes the SerialExecutor destructor to run. - // 4. The SerialExecutor destructor waits for all outstanding work items to - // finish via a call to join(). - // 5. join() fails, since the executor thread is trying to join against - // itself. - // - // To prevent this chain of events, we always call onDisconnect on a - // different thread. - // - // See P59135203 for an example stack trace. - // - // One more hack: we use release() and delete instead of unique_ptr because - // detail::Thread expects a std::function, and std::function cannot capture - // move-only types like unique_ptr. - auto conn = remoteConn_.release(); - inspector::detail::Thread disconnectLaterThread{ - "hermes-chrome-inspector-conn-disconnect", [conn] { - conn->onDisconnect(); - delete conn; - }}; - disconnectLaterThread.detach(); - }); - - return true; -} - -void Connection::Impl::sendMessage(std::string str) { - executor_->add([this, str = std::move(str)]() mutable { - folly::Try> maybeReq = - m::Request::fromJson(str); - - if (maybeReq.hasException()) { - LOG(ERROR) << "Invalid request `" << str - << "`: " << maybeReq.exception().what(); - return; - } - - auto &req = maybeReq.value(); - if (req) { - req->accept(*this); - } - }); -} - -/* - * InspectorObserver overrides - */ - -void Connection::Impl::onBreakpointResolved( - Inspector &inspector, - const debugger::BreakpointInfo &info) { - m::debugger::BreakpointResolvedNotification note; - note.breakpointId = folly::to(info.id); - note.location = m::debugger::makeLocation(info.resolvedLocation); - sendNotificationToClientViaExecutor(note); -} - -void Connection::Impl::onContextCreated(Inspector &inspector) { - // Right now, Hermes only has the notion of one JS context per VM instance, - // so we just always name the single JS context with id=1 and name=hermes. - m::runtime::ExecutionContextCreatedNotification note; - note.context.id = kHermesExecutionContextId; - note.context.name = "hermes"; - - sendNotificationToClientViaExecutor(note); -} - -void Connection::Impl::onPause( - Inspector &inspector, - const debugger::ProgramState &state) { - bool sendNotification = true; - m::debugger::PausedNotification note; - note.callFrames = m::debugger::makeCallFrames(state, objTable_, getRuntime()); - - switch (state.getPauseReason()) { - case debugger::PauseReason::Breakpoint: - // use other, chrome protocol has no reason specifically for breakpoints - note.reason = "other"; - -// TODO: hermes hasn't implemented ProgramState::getBreakpoint yet -#if HERMES_SUPPORTS_STATE_GET_BREAKPOINT - note.hitBreakpoints = std::vector(); - note.hitBreakpoints->emplace_back( - folly::to(state.getBreakpoint())); -#endif - - break; - case debugger::PauseReason::Exception: - note.reason = "exception"; - break; - case debugger::PauseReason::ScriptLoaded: { - // This case covers both wait-for-debugger and instrumentation - // breakpoints, since both are implemented as pauses on script load. - - note.reason = "other"; - note.hitBreakpoints = std::vector(); - - std::scoped_lock lock(virtualBreakpointMutex_); - for (auto &bp : - virtualBreakpoints_[kBeforeScriptWithSourceMapExecution]) { - note.hitBreakpoints->emplace_back(bp); - } - - // Debuggers don't tend to ever remove these kinds of breakpoints, but - // in the extremely unlikely event that it did *and* did it exactly - // between us 1. checking that we should stop, and 2. adding the stop - // reason here, then just resume and skip sending a pause notification. - if (!inspector_->isAwaitingDebuggerOnStart() && - note.hitBreakpoints->empty()) { - sendNotification = false; - inspector_->resume(); - } - }; - // This will be toggled back on in the next onScriptParsed if applicable - // Locking is handled by didPause in the inspector - inspector_->setPauseOnLoads(PauseOnLoadMode::None); - break; - default: - note.reason = "other"; - break; - } - - if (sendNotification) { - sendNotificationToClientViaExecutor(note); - } -} - -void Connection::Impl::onResume(Inspector &inspector) { - objTable_.releaseObjectGroup(BacktraceObjectGroup); - - m::debugger::ResumedNotification note; - sendNotificationToClientViaExecutor(note); -} - -void Connection::Impl::onScriptParsed( - Inspector &inspector, - const ScriptInfo &info) { - m::debugger::ScriptParsedNotification note; - note.scriptId = folly::to(info.fileId); - note.url = info.fileName; - note.executionContextId = kHermesExecutionContextId; - - if (!info.sourceMappingUrl.empty()) { - note.sourceMapURL = info.sourceMappingUrl; - - std::scoped_lock lock(virtualBreakpointMutex_); - if (hasVirtualBreakpoint(kBeforeScriptWithSourceMapExecution)) { - // We are precariously relying on the fact that onScriptParsed - // is invoked immediately before the pause load mode is checked. - // That means that we can check for breakpoints and toggle the - // mode here, and then immediately turn it off in onPause. - inspector_->setPauseOnLoads(PauseOnLoadMode::All); - } - } - - { - std::scoped_lock lock(parsedScriptsMutex_); - parsedScripts_.push_back(info.fileName); - } - - sendNotificationToClientViaExecutor(note); -} - -void Connection::Impl::onMessageAdded( - facebook::hermes::inspector::Inspector &inspector, - const ConsoleMessageInfo &info) { - m::runtime::ConsoleAPICalledNotification apiCalledNote; - apiCalledNote.type = info.level; - apiCalledNote.timestamp = - std::chrono::duration>( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - // TODO(jpporto): fix test cases sending invalid context id. - // apiCalledNote.executionContextId = kHermesExecutionContextId; - - size_t argsSize = info.args.size(getRuntime()); - for (size_t index = 0; index < argsSize; ++index) { - apiCalledNote.args.push_back(m::runtime::makeRemoteObject( - getRuntime(), - info.args.getValueAtIndex(getRuntime(), index), - objTable_, - "ConsoleObjectGroup", - false, - false)); - } - - sendNotificationToClientViaExecutor(apiCalledNote); -} - -/* - * RequestHandler overrides - */ - -void Connection::Impl::handle(const m::UnknownRequest &req) { - LOG(INFO) << "responding ok to unknown request: " << req.toDynamic(); - sendResponseToClientViaExecutor(req.id); -} - -void Connection::Impl::handle(const m::debugger::DisableRequest &req) { - sendResponseToClientViaExecutor(inspector_->disable(), req.id); -} - -void Connection::Impl::handle(const m::debugger::EnableRequest &req) { - sendResponseToClientViaExecutor(inspector_->enable(), req.id); -} - -void Connection::Impl::handle( - const m::debugger::EvaluateOnCallFrameRequest &req) { - auto remoteObjPtr = std::make_shared(); - - inspector_ - ->evaluate( - atoi(req.callFrameId.c_str()), - req.expression, - [this, - remoteObjPtr, - objectGroup = req.objectGroup, - byValue = req.returnByValue.value_or(false), - generatePreview = req.generatePreview.value_or(false)]( - const facebook::hermes::debugger::EvalResult - &evalResult) mutable { - *remoteObjPtr = m::runtime::makeRemoteObject( - getRuntime(), - evalResult.value, - objTable_, - objectGroup.value_or(""), - byValue, - generatePreview); - }) - .via(executor_.get()) - .thenValue( - [this, id = req.id, remoteObjPtr](debugger::EvalResult result) { - m::debugger::EvaluateOnCallFrameResponse resp; - resp.id = id; - - if (result.isException) { - resp.exceptionDetails = - m::runtime::makeExceptionDetails(result.exceptionDetails); - } else { - resp.result = std::move(*remoteObjPtr); - } - - sendResponseToClient(resp); - }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::sendSnapshot( - int reqId, - std::string message, - bool reportProgress, - bool stopStackTraceCapture) { - inspector_ - ->executeIfEnabled( - message, - [this, reportProgress, stopStackTraceCapture]( - const debugger::ProgramState &) { - // Stop taking any new traces before sending out the heap snapshot. - if (stopStackTraceCapture) { - getRuntime() - .instrumentation() - .stopTrackingHeapObjectStackTraces(); - } - - if (reportProgress) { - // A progress notification with finished = true indicates the - // snapshot has been captured and is ready to be sent. Our - // implementation streams the snapshot as it is being captured, so - // we must send this notification first. - m::heapProfiler::ReportHeapSnapshotProgressNotification note; - note.done = 1; - note.total = 1; - note.finished = true; - sendNotificationToClient(note); - } - - // Size picked to conform to Chrome's own implementation, at the - // time of writing. - inspector::detail::CallbackOStream cos( - /* sz */ 100 << 10, [this](std::string s) { - m::heapProfiler::AddHeapSnapshotChunkNotification note; - note.chunk = std::move(s); - sendNotificationToClient(note); - return true; - }); - - getRuntime().instrumentation().createSnapshotToStream(cos); - }) - .via(executor_.get()) - .thenValue([this, reqId](auto &&) { - sendResponseToClient(m::makeOkResponse(reqId)); - }) - .thenError(sendErrorToClient(reqId)); -} - -void Connection::Impl::handle( - const m::heapProfiler::TakeHeapSnapshotRequest &req) { - sendSnapshot( - req.id, - "HeapSnapshot.takeHeapSnapshot", - req.reportProgress && *req.reportProgress, - /* stopStackTraceCapture */ false); -} - -void Connection::Impl::handle( - const m::heapProfiler::StartTrackingHeapObjectsRequest &req) { - const auto id = req.id; - - inspector_ - ->executeIfEnabled( - "HeapProfiler.startTrackingHeapObjects", - [this](const debugger::ProgramState &) { - getRuntime().instrumentation().startTrackingHeapObjectStackTraces( - [this]( - uint64_t lastSeenObjectId, - std::chrono::microseconds timestamp, - std::vector stats) { - // Send the last object ID notification first. - m::heapProfiler::LastSeenObjectIdNotification note; - note.lastSeenObjectId = lastSeenObjectId; - // The protocol uses milliseconds with a fraction for - // microseconds. - note.timestamp = - static_cast(timestamp.count()) / 1000; - sendNotificationToClient(note); - - m::heapProfiler::HeapStatsUpdateNotification heapStatsNote; - // Flatten the HeapStatsUpdate list. - heapStatsNote.statsUpdate.reserve(stats.size() * 3); - for (const jsi::Instrumentation::HeapStatsUpdate &fragment : - stats) { - // Each triplet is the fragment number, the total count of - // objects for the fragment, and the total size of objects - // for the fragment. - heapStatsNote.statsUpdate.push_back( - static_cast(std::get<0>(fragment))); - heapStatsNote.statsUpdate.push_back( - static_cast(std::get<1>(fragment))); - heapStatsNote.statsUpdate.push_back( - static_cast(std::get<2>(fragment))); - } - assert( - heapStatsNote.statsUpdate.size() == stats.size() * 3 && - "Should be exactly 3x the stats vector"); - // TODO: Chunk this if there are too many fragments to update. - // Unlikely to be a problem in practice unless there's a huge - // amount of allocation and freeing. - sendNotificationToClient(heapStatsNote); - }); - // At this point we need the equivalent of a setInterval, where each - // interval samples the existing - }) - .via(executor_.get()) - .thenValue( - [this, id](auto &&) { sendResponseToClient(m::makeOkResponse(id)); }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle( - const m::heapProfiler::StopTrackingHeapObjectsRequest &req) { - sendSnapshot( - req.id, - "HeapSnapshot.stopTrackingHeapObjects", - req.reportProgress && *req.reportProgress, - /* stopStackTraceCapture */ true); -} - -void Connection::Impl::handle( - const m::heapProfiler::StartSamplingRequest &req) { - const auto id = req.id; - // This is the same default sampling interval that Chrome uses. - // https://chromedevtools.github.io/devtools-protocol/tot/HeapProfiler/#method-startSampling - constexpr size_t kDefaultSamplingInterval = 1 << 15; - const size_t samplingInterval = - req.samplingInterval.value_or(kDefaultSamplingInterval); - - inspector_ - ->executeIfEnabled( - "HeapProfiler.startSampling", - [this, samplingInterval](const debugger::ProgramState &) { - getRuntime().instrumentation().startHeapSampling(samplingInterval); - }) - .via(executor_.get()) - .thenValue( - [this, id](auto &&) { sendResponseToClient(m::makeOkResponse(id)); }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle(const m::heapProfiler::StopSamplingRequest &req) { - inspector_ - ->executeIfEnabled( - "HeapProfiler.stopSampling", - [this, id = req.id](const debugger::ProgramState &) { - std::ostringstream stream; - getRuntime().instrumentation().stopHeapSampling(stream); - folly::dynamic json = folly::parseJson(stream.str()); - m::heapProfiler::StopSamplingResponse resp; - resp.id = id; - m::heapProfiler::SamplingHeapProfile profile{json}; - resp.profile = std::move(profile); - sendResponseToClient(resp); - }) - .via(executor_.get()) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle( - const m::heapProfiler::CollectGarbageRequest &req) { - const auto id = req.id; - - inspector_ - ->executeIfEnabled( - "HeapProfiler.collectGarbage", - [this](const debugger::ProgramState &) { - getRuntime().instrumentation().collectGarbage("inspector"); - }) - .via(executor_.get()) - .thenValue( - [this, id](auto &&) { sendResponseToClient(m::makeOkResponse(id)); }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle( - const m::heapProfiler::GetObjectByHeapObjectIdRequest &req) { - uint64_t objID = atoi(req.objectId.c_str()); - std::optional group = req.objectGroup; - auto remoteObjPtr = std::make_shared(); - - inspector_ - ->executeIfEnabled( - "HeapProfiler.getObjectByHeapObjectId", - [this, remoteObjPtr, objID, group](const debugger::ProgramState &) { - jsi::Runtime *rt = &getRuntime(); - if (auto *hermesRT = dynamic_cast(rt)) { - jsi::Value val = hermesRT->getObjectForID(objID); - if (val.isNull()) { - return; - } - *remoteObjPtr = m::runtime::makeRemoteObject( - getRuntime(), - val, - objTable_, - group.value_or(""), - false, - false); - } - }) - .via(executor_.get()) - .thenValue([this, id = req.id, remoteObjPtr](auto &&) { - if (!remoteObjPtr->type.empty()) { - m::heapProfiler::GetObjectByHeapObjectIdResponse resp; - resp.id = id; - resp.result = std::move(*remoteObjPtr); - sendResponseToClient(resp); - } else { - sendResponseToClient(m::makeErrorResponse( - id, m::ErrorCode::ServerError, "Object is not available")); - } - }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle( - const m::heapProfiler::GetHeapObjectIdRequest &req) { - // Use a shared_ptr because the stack frame will go away. - std::shared_ptr snapshotID = std::make_shared(0); - - inspector_ - ->executeIfEnabled( - "HeapProfiler.getHeapObjectId", - [this, req, snapshotID](const debugger::ProgramState &) { - if (const jsi::Value *valuePtr = objTable_.getValue(req.objectId)) { - jsi::Runtime *rt = &getRuntime(); - if (auto *hermesRT = dynamic_cast(rt)) { - *snapshotID = hermesRT->getUniqueID(*valuePtr); - } - } - }) - .via(executor_.get()) - .thenValue([this, id = req.id, snapshotID](auto &&) { - if (*snapshotID) { - m::heapProfiler::GetHeapObjectIdResponse resp; - resp.id = id; - // std::to_string is not available on Android, use a std::ostream - // instead. - std::ostringstream stream; - stream << *snapshotID; - resp.heapSnapshotObjectId = stream.str(); - sendResponseToClient(resp); - } else { - sendResponseToClient(m::makeErrorResponse( - id, m::ErrorCode::ServerError, "Object is not available")); - } - }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle(const m::profiler::StartRequest &req) { - runInExecutor(req.id, [this, id = req.id]() { - HermesRuntime::enableSamplingProfiler(); - sendResponseToClient(m::makeOkResponse(id)); - }); -} - -void Connection::Impl::handle(const m::profiler::StopRequest &req) { - HermesRuntime *hermesRT = &getRuntime(); - - runInExecutor(req.id, [this, id = req.id, hermesRT]() { - HermesRuntime::disableSamplingProfiler(); - - std::ostringstream profileStream; - // HermesRuntime instance methods are usually unsafe to be called with a - // running VM, but sampledTraceToStreamInDevToolsFormat is an exception to - // that rule -- it synchronizes access to shared resources so it can be - // safely invoked with a running VM. - hermesRT->sampledTraceToStreamInDevToolsFormat(profileStream); - - // Hermes can emit the proper format directly, but it still needs to - // be parsed into a dynamic. - try { - m::profiler::StopResponse resp; - resp.id = id; - // parseJson throws on errors, so make sure we don't crash the app - // if somehow the sampling profiler output is borked. - resp.profile = m::profiler::Profile( - folly::parseJson(std::move(profileStream).str())); - sendResponseToClient(resp); - } catch (const std::exception &) { - LOG(ERROR) << "Failed to parse Sampling Profiler output"; - sendResponseToClient(m::makeErrorResponse( - id, - m::ErrorCode::InternalError, - "Hermes profile output could not be parsed.")); - } - }); -} - -namespace { -/// Runtime.CallArguments can have their values specified "inline", or they can -/// have remote references. The inline values are eval'd together with the -/// Runtime.CallFunctionOn.functionDeclaration (see CallFunctionOnBuilder -/// below), while remote object Ids need to be resolved outside of the VM. -class CallFunctionOnArgument { - public: - explicit CallFunctionOnArgument( - std::optional maybeObjectId) - : maybeObjectId_(std::move(maybeObjectId)) {} - - /// Computes the real value for this argument, which can be an object - /// referenced by maybeObjectId_, or the given evaldValue. Throws if - /// maybeObjectId_ is not empty but references an unknown object. - jsi::Value value( - jsi::Runtime &rt, - RemoteObjectsTable &objTable, - jsi::Value evaldValue) const { - if (maybeObjectId_) { - assert(evaldValue.isUndefined() && "expected undefined placeholder"); - return getValueFromId(rt, objTable, *maybeObjectId_); - } - - return evaldValue; - } - - private: - /// Returns the jsi::Object for the given objId. Throws if such object can't - /// be found. - static jsi::Value getValueFromId( - jsi::Runtime &rt, - RemoteObjectsTable &objTable, - m::runtime::RemoteObjectId objId) { - if (const jsi::Value *ptr = objTable.getValue(objId)) { - return jsi::Value(rt, *ptr); - } - - throw std::runtime_error("unknown object id " + objId); - } - - std::optional maybeObjectId_; -}; - -/// Functor that should be used to run the result of eval-ing a CallFunctionOn -/// request. -class CallFunctionOnRunner { - public: - static constexpr size_t kJsThisIndex = 0; - static constexpr size_t kFirstArgIndex = 1; - - // N.B.: constexpr char[] broke react-native-oss-android. - static const char *kJsThisArgPlaceholder; - - CallFunctionOnRunner() = default; - CallFunctionOnRunner(CallFunctionOnRunner &&) = default; - CallFunctionOnRunner &operator=(CallFunctionOnRunner &&) = default; - - /// Performs the actual Runtime.CallFunctionOn request. It assumes. - /// \p evalResult is the result of invoking the Inspector's evaluate() method - /// on the expression built by the CallFunctionOnBuilder below. - jsi::Value operator()( - jsi::Runtime &rt, - RemoteObjectsTable &objTable, - const facebook::hermes::debugger::EvalResult &evalResult) { - // The eval result is an array [a0, a1, ..., an, func] (see - // CallFunctionOnBuilder below). - auto argsAndFunc = evalResult.value.getObject(rt).getArray(rt); - assert( - argsAndFunc.length(rt) == thisAndArguments_.size() + 1 && - "Unexpected result size"); - - // now resolve the arguments to the call, including "this". - std::vector arguments(thisAndArguments_.size() - 1); - - jsi::Object jsThis = - getJsThis(rt, objTable, argsAndFunc.getValueAtIndex(rt, kJsThisIndex)); - - int i = kFirstArgIndex; - for (/*i points to the first param*/; i < thisAndArguments_.size(); ++i) { - arguments[i - kFirstArgIndex] = thisAndArguments_[i].value( - rt, objTable, argsAndFunc.getValueAtIndex(rt, i)); - } - - // i is now func's index. - jsi::Function func = - argsAndFunc.getValueAtIndex(rt, i).getObject(rt).getFunction(rt); - - return func.callWithThis( - rt, - std::move(jsThis), - static_cast(arguments.data()), - arguments.size()); - } - - private: - friend class CallFunctionOnBuilder; - - CallFunctionOnRunner(const CallFunctionOnRunner &) = delete; - CallFunctionOnRunner &operator=(const CallFunctionOnRunner &) = delete; - - CallFunctionOnRunner( - std::vector thisAndArguments, - std::optional executionContextId) - : thisAndArguments_(std::move(thisAndArguments)), - executionContextId_(std::move(executionContextId)) {} - - /// Resolves the js "this" for the request, which lives in - /// thisAndArguments_[kJsThisIndex]. \p evaldThis should either be undefined, - /// or the placeholder indicating that globalThis should be used. - jsi::Object getJsThis( - jsi::Runtime &rt, - RemoteObjectsTable &objTable, - jsi::Value evaldThis) const { - // In the future we may support multiple execution context ids; for now, - // there's only one. - (void)executionContextId_; - - // Either evaldThis is undefined (because the request had an object id - // specifying "this"), or it should be a string (i.e., the placeholder - // kJsThisArgPlaceholder). - assert( - (evaldThis.isUndefined() || - (evaldThis.isString() && - evaldThis.getString(rt).utf8(rt) == kJsThisArgPlaceholder)) && - "unexpected value for jsThis argument placeholder"); - - // Need to save this information because of the std::move() below. - const bool useGlobalThis = evaldThis.isString(); - jsi::Value value = thisAndArguments_[kJsThisIndex].value( - rt, objTable, std::move(evaldThis)); - - return useGlobalThis ? rt.global() : value.getObject(rt); - } - - std::vector thisAndArguments_; - std::optional executionContextId_; -}; - -/*static*/ const char *CallFunctionOnRunner::kJsThisArgPlaceholder = - "jsThis is Execution Context"; - -/// Returns true if \p str is a number-like string value (e.g., Infinity), -/// and false otherwise. -bool unserializableValueLooksLikeNumber(const std::string &str) { - return str == "Infinity" || str == "-Infinity" || str == "NaN"; -} - -/// Helper class that processes a Runtime.CallFunctionOn request, and -/// builds an expression string that, once eval()d, yields an Array with the -/// CallArguments as well as the function to run. The generated array is -/// -/// [JsThis, P0, P1, P2, P3, Pn, F] -/// -/// where: -/// * F is the functionDeclaration in the request -/// * JsThis is either: -/// * undefined (if the request has an object ID); or -/// * the placeholder kJsThisArgPlaceholder -/// * Pi is either: -/// * the string in CallArgument[i].unserializableValue; or -/// * the string in CallArgument[i].value; or -/// * arguments[j] (i.e., the j-th argument passed to the newly built -/// function), j being the j-th CallArgument with an ObjectId. This is -/// needed because there's no easy way to express the objects referred -/// to by object ids by name. -class CallFunctionOnBuilder { - public: - explicit CallFunctionOnBuilder(const m::runtime::CallFunctionOnRequest &req) - : executionContextId_(req.executionContextId) { - out_ << "["; - thisAndArguments_.emplace_back(CallFunctionOnArgument(req.objectId)); - if (req.objectId) { - out_ << "undefined, "; - } else { - out_ << '\'' << CallFunctionOnRunner::kJsThisArgPlaceholder << "', "; - } - - addParams(req.arguments); - out_ << req.functionDeclaration; - out_ << "]"; - }; - - /// Extracts the functions that handles the CallFunctionOn requests, as well - /// as the list of object ids that must be passed when calling it. - std::pair expressionAndRunner() && { - return std::make_pair( - std::move(out_).str(), - CallFunctionOnRunner( - std::move(thisAndArguments_), std::move(executionContextId_))); - } - - private: - void addParams(const std::optional> - &maybeArguments) { - if (maybeArguments) { - for (const auto &ca : *maybeArguments) { - addParam(ca); - thisAndArguments_.emplace_back(CallFunctionOnArgument(ca.objectId)); - out_ << ", "; - } - } - } - - void addParam(const m::runtime::CallArgument &ca) { - if (ca.objectId) { - out_ << "undefined"; - } else if (ca.value) { - // TODO: this may throw if ca.value is a CBOR (see RFC 8949), but the - // chrome debugger doesn't seem to send those. - out_ << "(" << folly::toJson(*ca.value) << ")"; - } else if (ca.unserializableValue) { - if (unserializableValueLooksLikeNumber(*ca.unserializableValue)) { - out_ << "+(" << *ca.unserializableValue << ")"; - } else { - out_ << *ca.unserializableValue; - } - } else { - throw std::runtime_error("unknown payload for CallParam"); - } - } - - std::ostringstream out_; - - std::vector thisAndArguments_; - std::optional executionContextId_; -}; - -} // namespace - -void Connection::Impl::handle(const m::runtime::CallFunctionOnRequest &req) { - std::string expression; - CallFunctionOnRunner runner; - - auto validateAndParseRequest = - [&expression, &runner](const m::runtime::CallFunctionOnRequest &req) - -> std::optional { - if (req.objectId.has_value() == req.executionContextId.has_value()) { - return std::string( - "The request must specify either object id or execution context id."); - } - - if (!req.objectId) { - assert( - req.executionContextId && - "should not be here if both object id and execution context id are missing"); - if (*req.executionContextId != kHermesExecutionContextId) { - return "unknown execution context id " + - std::to_string(*req.executionContextId); - } - } - - try { - std::tie(expression, runner) = - CallFunctionOnBuilder(req).expressionAndRunner(); - } catch (const std::exception &e) { - return std::string(e.what()); - } - - return {}; - }; - - if (auto errMsg = validateAndParseRequest(req)) { - sendErrorToClientViaExecutor(req.id, *errMsg); - return; - } - - auto remoteObjPtr = std::make_shared(); - inspector_ - ->evaluate( - 0, // Top of the stackframe - expression, - [this, - remoteObjPtr, - objectGroup = req.objectGroup, - jsThisId = req.objectId, - byValue = req.returnByValue.value_or(false), - generatePreview = req.generatePreview.value_or(false), - runner = - std::move(runner)](const facebook::hermes::debugger::EvalResult - &evalResult) mutable { - if (evalResult.isException) { - return; - } - - *remoteObjPtr = m::runtime::makeRemoteObject( - getRuntime(), - runner(getRuntime(), objTable_, evalResult), - objTable_, - objectGroup.value_or("ConsoleObjectGroup"), - byValue, - generatePreview); - }) - .via(executor_.get()) - .thenValue( - [this, id = req.id, remoteObjPtr](debugger::EvalResult result) { - m::debugger::EvaluateOnCallFrameResponse resp; - resp.id = id; - - if (result.isException) { - resp.exceptionDetails = - m::runtime::makeExceptionDetails(result.exceptionDetails); - } else { - resp.result = std::move(*remoteObjPtr); - } - - sendResponseToClient(resp); - }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle(const m::runtime::CompileScriptRequest &req) { - auto resp = std::make_shared(); - resp->id = req.id; - - inspector_ - ->executeIfEnabled( - "Runtime.compileScriptRequest", - [this, req, resp](const debugger::ProgramState &state) { - if (req.executionContextId.has_value() && - req.executionContextId.value() != kHermesExecutionContextId) { - throw std::invalid_argument("Invalid execution context"); - } - - auto source = std::make_shared(req.expression); - std::shared_ptr preparedScript; - try { - preparedScript = - getRuntime().prepareJavaScript(source, req.sourceURL); - } catch (const facebook::jsi::JSIException &err) { - resp->exceptionDetails = m::runtime::ExceptionDetails(); - resp->exceptionDetails->text = err.what(); - return; - } - - if (req.persistScript) { - auto scriptId = folly::to( - kUserEnteredScriptPrefix, preparedScripts_.size()); - preparedScripts_.push_back(std::move(preparedScript)); - resp->scriptId = scriptId; - } - }) - .via(executor_.get()) - .thenValue([this, resp](auto &&) { sendResponseToClient(*resp); }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle(const m::runtime::EvaluateRequest &req) { - auto remoteObjPtr = std::make_shared(); - - inspector_ - ->evaluate( - 0, // Top of the stackframe - req.expression, - [this, - remoteObjPtr, - objectGroup = req.objectGroup, - byValue = req.returnByValue.value_or(false), - generatePreview = req.generatePreview.value_or(false)]( - const facebook::hermes::debugger::EvalResult - &evalResult) mutable { - *remoteObjPtr = m::runtime::makeRemoteObject( - getRuntime(), - evalResult.value, - objTable_, - objectGroup.value_or("ConsoleObjectGroup"), - byValue, - generatePreview); - }) - .via(executor_.get()) - .thenValue( - [this, id = req.id, remoteObjPtr](debugger::EvalResult result) { - m::debugger::EvaluateOnCallFrameResponse resp; - resp.id = id; - - if (result.isException) { - resp.exceptionDetails = - m::runtime::makeExceptionDetails(result.exceptionDetails); - } else { - resp.result = std::move(*remoteObjPtr); - } - - sendResponseToClient(resp); - }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle(const m::debugger::PauseRequest &req) { - sendResponseToClientViaExecutor(inspector_->pause(), req.id); -} - -void Connection::Impl::handle(const m::debugger::RemoveBreakpointRequest &req) { - if (isVirtualBreakpointId(req.breakpointId)) { - std::scoped_lock lock(virtualBreakpointMutex_); - if (!removeVirtualBreakpoint(req.breakpointId)) { - sendErrorToClientViaExecutor( - req.id, "Unknown breakpoint ID: " + req.breakpointId); - } - sendResponseToClientViaExecutor(req.id); - } else { - auto breakpointId = folly::to(req.breakpointId); - sendResponseToClientViaExecutor( - inspector_->removeBreakpoint(breakpointId), req.id); - } -} - -void Connection::Impl::handle(const m::debugger::ResumeRequest &req) { - sendResponseToClientViaExecutor(inspector_->resume(), req.id); -} - -void Connection::Impl::handle(const m::debugger::SetBreakpointRequest &req) { - debugger::SourceLocation loc; - - auto scriptId = folly::tryTo(req.location.scriptId); - if (!scriptId) { - sendErrorToClientViaExecutor( - req.id, "Expected integer scriptId: " + req.location.scriptId); - return; - } - - loc.fileId = scriptId.value(); - // CDP Locations are 0-based, Hermes lines/columns are 1-based - loc.line = req.location.lineNumber + 1; - if (req.location.columnNumber) { - loc.column = req.location.columnNumber.value() + 1; - } - - inspector_->setBreakpoint(loc, req.condition) - .via(executor_.get()) - .thenValue([this, id = req.id](debugger::BreakpointInfo info) { - m::debugger::SetBreakpointResponse resp; - resp.id = id; - resp.breakpointId = folly::to(info.id); - - if (info.resolved) { - resp.actualLocation = - m::debugger::makeLocation(info.resolvedLocation); - } - - sendResponseToClient(resp); - }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle( - const m::debugger::SetBreakpointByUrlRequest &req) { - debugger::SourceLocation loc; - - { - std::scoped_lock lock(parsedScriptsMutex_); - setHermesLocation(loc, req, parsedScripts_); - } - - inspector_->setBreakpoint(loc, req.condition) - .via(executor_.get()) - .thenValue([this, id = req.id](debugger::BreakpointInfo info) { - m::debugger::SetBreakpointByUrlResponse resp; - resp.id = id; - resp.breakpointId = folly::to(info.id); - - if (info.resolved) { - resp.locations.emplace_back( - m::debugger::makeLocation(info.resolvedLocation)); - } - - sendResponseToClient(resp); - }) - .thenError(sendErrorToClient(req.id)); -} - -void Connection::Impl::handle( - const m::debugger::SetBreakpointsActiveRequest &req) { - inspector_->setBreakpointsActive(req.active) - .via(executor_.get()) - .thenValue([this, id = req.id](const Unit &unit) { - sendResponseToClient(m::makeOkResponse(id)); - }) - .thenError(sendErrorToClient(req.id)); -} - -bool Connection::Impl::isVirtualBreakpointId(const std::string &id) { - return id.rfind(kVirtualBreakpointPrefix, 0) == 0; -} - -const std::string &Connection::Impl::createVirtualBreakpoint( - const std::string &category) { - auto ret = virtualBreakpoints_[category].insert(folly::to( - kVirtualBreakpointPrefix, nextVirtualBreakpoint_++)); - return *ret.first; -} - -bool Connection::Impl::hasVirtualBreakpoint(const std::string &category) { - auto pos = virtualBreakpoints_.find(category); - if (pos == virtualBreakpoints_.end()) - return false; - return !pos->second.empty(); -} - -bool Connection::Impl::removeVirtualBreakpoint(const std::string &id) { - // We expect roughly 1 category, so just iterate over all the sets - for (auto &kv : virtualBreakpoints_) { - if (kv.second.erase(id) > 0) { - return true; - } - } - return false; -} - -void Connection::Impl::handle( - const m::debugger::SetInstrumentationBreakpointRequest &req) { - if (req.instrumentation != kBeforeScriptWithSourceMapExecution) { - sendErrorToClientViaExecutor( - req.id, "Unknown instrumentation breakpoint: " + req.instrumentation); - return; - } - - // The act of creating and registering the breakpoint ID is enough - // to "set" it. We merely check for the existence of them later. - std::scoped_lock lock(virtualBreakpointMutex_); - m::debugger::SetInstrumentationBreakpointResponse resp; - resp.id = req.id; - resp.breakpointId = createVirtualBreakpoint(req.instrumentation); - sendResponseToClientViaExecutor(resp); -} - -void Connection::Impl::handle( - const m::debugger::SetPauseOnExceptionsRequest &req) { - debugger::PauseOnThrowMode mode = debugger::PauseOnThrowMode::None; - - if (req.state == "none") { - mode = debugger::PauseOnThrowMode::None; - } else if (req.state == "all") { - mode = debugger::PauseOnThrowMode::All; - } else if (req.state == "uncaught") { - mode = debugger::PauseOnThrowMode::Uncaught; - } else { - sendErrorToClientViaExecutor( - req.id, "Unknown pause-on-exception state: " + req.state); - return; - } - - sendResponseToClientViaExecutor( - inspector_->setPauseOnExceptions(mode), req.id); -} - -void Connection::Impl::handle(const m::debugger::StepIntoRequest &req) { - sendResponseToClientViaExecutor(inspector_->stepIn(), req.id); -} - -void Connection::Impl::handle(const m::debugger::StepOutRequest &req) { - sendResponseToClientViaExecutor(inspector_->stepOut(), req.id); -} - -void Connection::Impl::handle(const m::debugger::StepOverRequest &req) { - sendResponseToClientViaExecutor(inspector_->stepOver(), req.id); -} - -std::vector -Connection::Impl::makePropsFromScope( - std::pair frameAndScopeIndex, - const std::string &objectGroup, - const debugger::ProgramState &state, - bool generatePreview) { - // Chrome represents variables in a scope as properties on a dummy object. - // We don't instantiate such dummy objects, we just pretended to have one. - // Chrome has now asked for its properties, so it's time to synthesize - // descriptions of the properties that the dummy object would have had. - std::vector result; - - uint32_t frameIndex = frameAndScopeIndex.first; - uint32_t scopeIndex = frameAndScopeIndex.second; - debugger::LexicalInfo lexicalInfo = state.getLexicalInfo(frameIndex); - uint32_t varCount = lexicalInfo.getVariablesCountInScope(scopeIndex); - - // If this is the frame's local scope, include 'this'. - if (scopeIndex == 0) { - auto varInfo = state.getVariableInfoForThis(frameIndex); - m::runtime::PropertyDescriptor desc; - desc.name = varInfo.name; - desc.value = m::runtime::makeRemoteObject( - getRuntime(), - varInfo.value, - objTable_, - objectGroup, - false, - generatePreview); - // Chrome only shows enumerable properties. - desc.enumerable = true; - result.emplace_back(std::move(desc)); - } - - // Then add each of the variables in this lexical scope. - for (uint32_t varIndex = 0; varIndex < varCount; varIndex++) { - debugger::VariableInfo varInfo = - state.getVariableInfo(frameIndex, scopeIndex, varIndex); - - m::runtime::PropertyDescriptor desc; - desc.name = varInfo.name; - desc.value = m::runtime::makeRemoteObject( - getRuntime(), - varInfo.value, - objTable_, - objectGroup, - false, - generatePreview); - desc.enumerable = true; - - result.emplace_back(std::move(desc)); - } - - return result; -} - -std::vector -Connection::Impl::makePropsFromValue( - const jsi::Value &value, - const std::string &objectGroup, - bool onlyOwnProperties, - bool generatePreview) { - std::vector result; - - if (value.isObject()) { - jsi::Runtime &runtime = getRuntime(); - jsi::Object obj = value.getObject(runtime); - - // TODO(hypuk): obj.getPropertyNames only returns enumerable properties. - jsi::Array propNames = onlyOwnProperties - ? runtime.global() - .getPropertyAsObject(runtime, "Object") - .getPropertyAsFunction(runtime, "getOwnPropertyNames") - .call(runtime, obj) - .getObject(runtime) - .getArray(runtime) - : obj.getPropertyNames(runtime); - - size_t propCount = propNames.length(runtime); - for (size_t i = 0; i < propCount; i++) { - jsi::String propName = - propNames.getValueAtIndex(runtime, i).getString(runtime); - - m::runtime::PropertyDescriptor desc; - desc.name = propName.utf8(runtime); - - try { - // Currently, we fetch the property even if it runs code. - // Chrome instead detects getters and makes you click to invoke. - jsi::Value propValue = obj.getProperty(runtime, propName); - desc.value = m::runtime::makeRemoteObject( - runtime, propValue, objTable_, objectGroup, false, generatePreview); - } catch (const jsi::JSError &err) { - // We fetched a property with a getter that threw. Show a placeholder. - // We could have added additional info, but the UI quickly gets messy. - desc.value = m::runtime::makeRemoteObject( - runtime, - jsi::String::createFromUtf8(runtime, "(Exception)"), - objTable_, - objectGroup, - false, - generatePreview); - } - - result.emplace_back(std::move(desc)); - } - - if (onlyOwnProperties) { - jsi::Value proto = runtime.global() - .getPropertyAsObject(runtime, "Object") - .getPropertyAsFunction(runtime, "getPrototypeOf") - .call(runtime, obj); - if (!proto.isNull()) { - m::runtime::PropertyDescriptor desc; - desc.name = "__proto__"; - desc.value = m::runtime::makeRemoteObject( - runtime, proto, objTable_, objectGroup, false, generatePreview); - result.emplace_back(std::move(desc)); - } - } - } - - return result; -} - -void Connection::Impl::handle(const m::runtime::GetHeapUsageRequest &req) { - runInExecutor(req.id, [this, req]() { - auto heapInfo = getRuntime().instrumentation().getHeapInfo(false); - auto resp = std::make_shared(); - resp->id = req.id; - resp->usedSize = heapInfo["hermes_allocatedBytes"]; - resp->totalSize = heapInfo["hermes_heapSize"]; - sendResponseToClient(*resp); - }); -} - -void Connection::Impl::handle(const m::runtime::GetPropertiesRequest &req) { - auto resp = std::make_shared(); - resp->id = req.id; - - inspector_ - ->executeIfEnabled( - "Runtime.getProperties", - [this, - req, - resp, - generatePreview = req.generatePreview.value_or(false)]( - const debugger::ProgramState &state) { - std::string objGroup = objTable_.getObjectGroup(req.objectId); - auto scopePtr = objTable_.getScope(req.objectId); - auto valuePtr = objTable_.getValue(req.objectId); - - if (scopePtr != nullptr) { - resp->result = makePropsFromScope( - *scopePtr, objGroup, state, generatePreview); - } else if (valuePtr != nullptr) { - resp->result = makePropsFromValue( - *valuePtr, - objGroup, - req.ownProperties.value_or(true), - generatePreview); - } - }) - .via(executor_.get()) - .thenValue([this, resp](auto &&) { sendResponseToClient(*resp); }) - .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.has_value() && - 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()) { - sendResponseToClientViaExecutor(inspector_->resume(), req.id); - } else { - // We weren't awaiting a debugger. Just send an 'ok'. - sendResponseToClientViaExecutor(req.id); - } -} - -/* - * Send-to-client methods - */ - -void Connection::Impl::sendToClient(const std::string &str) { - if (remoteConn_) { - remoteConn_->onMessage(str); - } -} - -void Connection::Impl::sendResponseToClient(const m::Response &resp) { - sendToClient(resp.toJson()); -} - -void Connection::Impl::sendNotificationToClient(const m::Notification ¬e) { - sendToClient(note.toJson()); -} - -folly::Function -Connection::Impl::sendErrorToClient(int id) { - return [this, id](const std::exception &e) { - sendResponseToClient( - m::makeErrorResponse(id, m::ErrorCode::ServerError, e.what())); - }; -} - -void Connection::Impl::sendResponseToClientViaExecutor(int id) { - sendResponseToClientViaExecutor(folly::makeFuture(), id); -} - -void Connection::Impl::sendResponseToClientViaExecutor( - const m::Response &resp) { - std::string json = resp.toJson(); - - folly::makeFuture() - .via(executor_.get()) - .thenValue([this, json](const Unit &unit) { sendToClient(json); }); -} - -void Connection::Impl::sendResponseToClientViaExecutor( - folly::Future future, - int id) { - future.via(executor_.get()) - .thenValue([this, id](const Unit &unit) { - sendResponseToClient(m::makeOkResponse(id)); - }) - .thenError(sendErrorToClient(id)); -} - -void Connection::Impl::sendErrorToClientViaExecutor( - int id, - const std::string &error) { - folly::makeFuture() - .via(executor_.get()) - .thenValue([this, id, error](const Unit &unit) { - sendResponseToClient( - makeErrorResponse(id, m::ErrorCode::ServerError, error)); - }); -} - -void Connection::Impl::sendNotificationToClientViaExecutor( - const m::Notification ¬e) { - executor_->add( - [this, noteJson = note.toJson()]() { sendToClient(noteJson); }); -} - -/* - * Connection - */ -Connection::Connection( - std::unique_ptr adapter, - const std::string &title, - bool waitForDebugger) - : impl_( - std::make_unique(std::move(adapter), title, waitForDebugger)) {} - -Connection::~Connection() = default; - -HermesRuntime &Connection::getRuntime() { - return impl_->getRuntime(); -} - -std::string Connection::getTitle() const { - return impl_->getTitle(); -} - -bool Connection::connect(std::unique_ptr remoteConn) { - return impl_->connect(std::move(remoteConn)); -} - -bool Connection::disconnect() { - return impl_->disconnect(); -} - -void Connection::sendMessage(std::string str) { - impl_->sendMessage(std::move(str)); -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.h deleted file mode 100644 index 2bd8e7d084a..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// using include guards instead of #pragma once due to compile issues -// with MSVC and BUCK -#ifndef HERMES_INSPECTOR_CONNECTION_H -#define HERMES_INSPECTOR_CONNECTION_H - -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -/// Connection is a duplex connection between the client and the debugger. -class INSPECTOR_EXPORT Connection { - public: - /// Connection constructor enables the debugger on the provided runtime. This - /// should generally called before you start running any JS in the runtime. - Connection( - std::unique_ptr adapter, - const std::string &title, - bool waitForDebugger = false); - ~Connection(); - - /// getRuntime returns the underlying runtime being debugged. - HermesRuntime &getRuntime(); - - /// getTitle returns the name of the friendly name of the runtime that's shown - /// to users in Nuclide. - std::string getTitle() const; - - /// connect attaches this connection to the runtime's debugger. Requests to - /// the debugger sent via send(). Replies and notifications from the debugger - /// are sent back to the client via IRemoteConnection::onMessage. - bool connect( - std::unique_ptr<::facebook::react::IRemoteConnection> remoteConn); - - /// disconnect disconnects this connection from the runtime's debugger - bool disconnect(); - - /// sendMessage delivers a JSON-encoded Chrome DevTools Protocol request to - /// the debugger. - void sendMessage(std::string str); - - private: - class Impl; - std::unique_ptr impl_; -}; - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook - -#endif // HERMES_INSPECTOR_CONNECTION_H diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.cpp deleted file mode 100644 index dceeafe7ab0..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "ConnectionDemux.h" -#include "Connection.h" - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -using ::facebook::react::IInspector; -using ::facebook::react::ILocalConnection; -using ::facebook::react::IRemoteConnection; - -namespace { - -class LocalConnection : public ILocalConnection { - public: - LocalConnection( - std::shared_ptr conn, - std::shared_ptr> inspectedContexts); - ~LocalConnection(); - - void sendMessage(std::string message) override; - void disconnect() override; - - private: - std::shared_ptr conn_; - std::shared_ptr> inspectedContexts_; -}; - -LocalConnection::LocalConnection( - std::shared_ptr conn, - std::shared_ptr> inspectedContexts) - : conn_(conn), inspectedContexts_(inspectedContexts) { - inspectedContexts_->insert(conn->getTitle()); -} - -LocalConnection::~LocalConnection() = default; - -void LocalConnection::sendMessage(std::string str) { - conn_->sendMessage(std::move(str)); -} - -void LocalConnection::disconnect() { - inspectedContexts_->erase(conn_->getTitle()); - conn_->disconnect(); -} - -} // namespace - -ConnectionDemux::ConnectionDemux(facebook::react::IInspector &inspector) - : globalInspector_(inspector), - inspectedContexts_(std::make_shared>()) {} - -ConnectionDemux::~ConnectionDemux() = default; - -DebugSessionToken ConnectionDemux::enableDebugging( - std::unique_ptr adapter, - const std::string &title) { - std::scoped_lock lock(mutex_); - - // TODO(#22976087): workaround for ComponentScript contexts never being - // destroyed. - // - // After a reload, the old ComponentScript VM instance stays alive. When we - // register the new CS VM instance, check for any previous CS VM (via strcmp - // of title) and remove them. - std::vector pagesToDelete; - for (auto it = conns_.begin(); it != conns_.end(); ++it) { - if (it->second->getTitle() == title) { - pagesToDelete.push_back(it->first); - } - } - - for (auto pageId : pagesToDelete) { - removePage(pageId); - } - - auto waitForDebugger = - (inspectedContexts_->find(title) != inspectedContexts_->end()); - return addPage( - std::make_shared(std::move(adapter), title, waitForDebugger)); -} - -void ConnectionDemux::disableDebugging(DebugSessionToken session) { - std::scoped_lock lock(mutex_); - if (conns_.find(session) == conns_.end()) { - return; - } - removePage(session); -} - -int ConnectionDemux::addPage(std::shared_ptr conn) { - auto connectFunc = [conn, this](std::unique_ptr remoteConn) - -> std::unique_ptr { - if (!conn->connect(std::move(remoteConn))) { - return nullptr; - } - - return std::make_unique(conn, inspectedContexts_); - }; - - int pageId = globalInspector_.addPage( - conn->getTitle(), "Hermes", std::move(connectFunc)); - conns_[pageId] = std::move(conn); - - return pageId; -} - -void ConnectionDemux::removePage(int pageId) { - globalInspector_.removePage(pageId); - - auto conn = conns_.at(pageId); - std::string title = conn->getTitle(); - inspectedContexts_->erase(title); - conn->disconnect(); - conns_.erase(pageId); -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.h deleted file mode 100644 index e9510b4b060..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/ConnectionDemux.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -/* - * ConnectionDemux keeps track of all debuggable Hermes runtimes (called - * "pages" in the higher-level React Native API) in this process. See - * Registration.h for documentation of the public API. - */ -class ConnectionDemux { - public: - explicit ConnectionDemux(facebook::react::IInspector &inspector); - ~ConnectionDemux(); - - ConnectionDemux(const ConnectionDemux &) = delete; - ConnectionDemux &operator=(const ConnectionDemux &) = delete; - - DebugSessionToken enableDebugging( - std::unique_ptr adapter, - const std::string &title); - void disableDebugging(DebugSessionToken session); - - private: - int addPage(std::shared_ptr conn); - void removePage(int pageId); - - facebook::react::IInspector &globalInspector_; - - std::mutex mutex_; - std::unordered_map> conns_; - std::shared_ptr> inspectedContexts_; -}; - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.cpp deleted file mode 100644 index 4700427315f..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "MessageConverters.h" - -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -namespace h = ::facebook::hermes; -namespace m = ::facebook::hermes::inspector::chrome::message; - -constexpr size_t kMaxPreviewProperties = 10; - -m::ErrorResponse -m::makeErrorResponse(int id, m::ErrorCode code, const std::string &message) { - m::ErrorResponse resp; - resp.id = id; - resp.code = static_cast(code); - resp.message = message; - return resp; -} - -m::OkResponse m::makeOkResponse(int id) { - m::OkResponse resp; - resp.id = id; - return resp; -} - -std::string m::stripCachePrevention(const std::string &url) { - std::regex regex("&?cachePrevention=[0-9]*"); - return std::regex_replace(url, regex, ""); -} - -/* - * debugger message conversion helpers - */ - -m::debugger::Location m::debugger::makeLocation( - const h::debugger::SourceLocation &loc) { - m::debugger::Location result; - - result.scriptId = folly::to(loc.fileId); - m::setChromeLocation(result, loc); - - return result; -} - -m::debugger::CallFrame m::debugger::makeCallFrame( - uint32_t callFrameIndex, - const h::debugger::CallFrameInfo &callFrameInfo, - const h::debugger::LexicalInfo &lexicalInfo, - RemoteObjectsTable &objTable, - jsi::Runtime &runtime, - const facebook::hermes::debugger::ProgramState &state) { - m::debugger::CallFrame result; - - result.callFrameId = folly::to(callFrameIndex); - result.functionName = callFrameInfo.functionName; - result.location = makeLocation(callFrameInfo.location); - - uint32_t scopeCount = lexicalInfo.getScopesCount(); - - // First we have our local scope (unless we're in the global function) - if (scopeCount > 1) { - m::debugger::Scope scope; - scope.type = "local"; - scope.object.objectId = objTable.addScope( - std::make_pair(callFrameIndex, 0), BacktraceObjectGroup); - scope.object.type = "object"; - scope.object.className = "Object"; - result.scopeChain.emplace_back(std::move(scope)); - } - - // Then we have zero or more parent closure scopes - for (uint32_t scopeIndex = 1; scopeIndex < scopeCount - 1; scopeIndex++) { - m::debugger::Scope scope; - - scope.type = "closure"; - // TODO: Get the parent closure's name - scope.name = folly::to(scopeIndex); - scope.object.objectId = objTable.addScope( - std::make_pair(callFrameIndex, scopeIndex), BacktraceObjectGroup); - scope.object.type = "object"; - scope.object.className = "Object"; - result.scopeChain.emplace_back(std::move(scope)); - } - - // Finally, we always have the global scope - { - m::debugger::Scope scope; - scope.type = "global"; - scope.object.objectId = - objTable.addValue(runtime.global(), BacktraceObjectGroup); - scope.object.type = "object"; - scope.object.className = "Object"; - result.scopeChain.emplace_back(std::move(scope)); - } - - result.thisObj.type = "object"; - result.thisObj.objectId = objTable.addValue( - state.getVariableInfoForThis(callFrameIndex).value, BacktraceObjectGroup); - - return result; -} - -std::vector m::debugger::makeCallFrames( - const h::debugger::ProgramState &state, - RemoteObjectsTable &objTable, - jsi::Runtime &runtime) { - const h::debugger::StackTrace &stackTrace = state.getStackTrace(); - uint32_t count = stackTrace.callFrameCount(); - - std::vector result; - result.reserve(count); - - for (uint32_t i = 0; i < count; i++) { - h::debugger::CallFrameInfo callFrameInfo = stackTrace.callFrameForIndex(i); - h::debugger::LexicalInfo lexicalInfo = state.getLexicalInfo(i); - - result.emplace_back( - makeCallFrame(i, callFrameInfo, lexicalInfo, objTable, runtime, state)); - } - - return result; -} - -/* - * runtime message conversion helpers - */ - -m::runtime::CallFrame m::runtime::makeCallFrame( - const h::debugger::CallFrameInfo &info) { - m::runtime::CallFrame result; - - result.functionName = info.functionName; - result.scriptId = folly::to(info.location.fileId); - result.url = info.location.fileName; - m::setChromeLocation(result, info.location); - - return result; -} - -std::vector m::runtime::makeCallFrames( - const facebook::hermes::debugger::StackTrace &stackTrace) { - std::vector result; - result.reserve(stackTrace.callFrameCount()); - - for (size_t i = 0; i < stackTrace.callFrameCount(); i++) { - h::debugger::CallFrameInfo info = stackTrace.callFrameForIndex(i); - result.emplace_back(makeCallFrame(info)); - } - - return result; -} - -m::runtime::ExceptionDetails m::runtime::makeExceptionDetails( - const h::debugger::ExceptionDetails &details) { - m::runtime::ExceptionDetails result; - - result.text = details.text; - result.scriptId = folly::to(details.location.fileId); - result.url = details.location.fileName; - result.stackTrace = m::runtime::StackTrace(); - result.stackTrace->callFrames = makeCallFrames(details.getStackTrace()); - m::setChromeLocation(result, details.location); - - return result; -} - -static m::runtime::PropertyPreview generatePropertyPreview( - facebook::jsi::Runtime &runtime, - const std::string &name, - const facebook::jsi::Value &value) { - m::runtime::PropertyPreview preview; - preview.name = name; - if (value.isUndefined()) { - preview.type = "undefined"; - preview.value = "undefined"; - } else if (value.isNull()) { - preview.type = "object"; - preview.subtype = "null"; - preview.value = "null"; - } else if (value.isBool()) { - preview.type = "boolean"; - preview.value = value.toString(runtime).utf8(runtime); - } else if (value.isNumber()) { - preview.type = "number"; - preview.value = value.toString(runtime).utf8(runtime); - } else if (value.isSymbol()) { - preview.type = "symbol"; - preview.value = value.toString(runtime).utf8(runtime); - } else if (value.isBigInt()) { - preview.type = "bigint"; - preview.value = - value.getBigInt(runtime).toString(runtime).utf8(runtime) + 'n'; - } else if (value.isString()) { - preview.type = "string"; - preview.value = value.toString(runtime).utf8(runtime); - } else if (value.isObject()) { - jsi::Object obj = value.asObject(runtime); - if (obj.isFunction(runtime)) { - preview.type = "function"; - } else if (obj.isArray(runtime)) { - preview.type = "object"; - preview.subtype = "array"; - preview.value = "Array(" + - std::to_string(obj.getArray(runtime).length(runtime)) + ")"; - } else { - preview.type = "object"; - preview.value = "Object"; - } - } else { - preview.type = "string"; - preview.value = ""; - } - return preview; -} - -static m::runtime::ObjectPreview generateArrayPreview( - facebook::jsi::Runtime &runtime, - const facebook::jsi::Array &obj) { - m::runtime::ObjectPreview preview{}; - preview.type = "object"; - preview.subtype = "array"; - - size_t count = obj.length(runtime); - for (size_t i = 0; i < std::min(kMaxPreviewProperties, count); i++) { - m::runtime::PropertyPreview desc; - std::string indexString = std::to_string(i); - try { - desc = generatePropertyPreview( - runtime, indexString, obj.getValueAtIndex(runtime, i)); - } catch (const jsi::JSError &err) { - desc.name = indexString; - desc.type = "string"; - desc.value = ""; - } - preview.properties.push_back(std::move(desc)); - } - preview.description = - "Array(" + std::to_string(obj.getArray(runtime).length(runtime)) + ")"; - preview.overflow = count > kMaxPreviewProperties; - return preview; -} - -static m::runtime::ObjectPreview generateObjectPreview( - facebook::jsi::Runtime &runtime, - const facebook::jsi::Object &obj) { - m::runtime::ObjectPreview preview{}; - preview.type = "object"; - - // n.b. own properties only - jsi::Array propNames = - runtime.global() - .getPropertyAsObject(runtime, "Object") - .getPropertyAsFunction(runtime, "getOwnPropertyNames") - .call(runtime, obj) - .getObject(runtime) - .getArray(runtime); - - size_t propCount = propNames.length(runtime); - for (size_t i = 0; i < std::min(kMaxPreviewProperties, propCount); i++) { - jsi::String propName = - propNames.getValueAtIndex(runtime, i).getString(runtime); - - m::runtime::PropertyPreview desc; - try { - // Currently, we fetch the property even if it runs code. - // Chrome instead detects getters and makes you click to invoke. - desc = generatePropertyPreview( - runtime, propName.utf8(runtime), obj.getProperty(runtime, propName)); - } catch (const jsi::JSError &err) { - desc.name = propName.utf8(runtime); - desc.type = "string"; - desc.value = ""; - } - preview.properties.push_back(std::move(desc)); - } - preview.description = "Object"; - preview.overflow = propCount > kMaxPreviewProperties; - return preview; -} - -m::runtime::RemoteObject m::runtime::makeRemoteObject( - facebook::jsi::Runtime &runtime, - const facebook::jsi::Value &value, - RemoteObjectsTable &objTable, - const std::string &objectGroup, - bool byValue, - bool generatePreview) { - m::runtime::RemoteObject result; - if (value.isUndefined()) { - result.type = "undefined"; - } else if (value.isNull()) { - result.type = "object"; - result.subtype = "null"; - result.value = "null"; - } else if (value.isBool()) { - result.type = "boolean"; - result.value = value.getBool(); - } else if (value.isNumber()) { - double numberValue = value.getNumber(); - result.type = "number"; - if (std::isnan(numberValue)) { - result.description = result.unserializableValue = "NaN"; - } else if (numberValue == -std::numeric_limits::infinity()) { - result.description = result.unserializableValue = "-Infinity"; - } else if (numberValue == std::numeric_limits::infinity()) { - result.description = result.unserializableValue = "Infinity"; - } else if (numberValue == 0.0 && std::signbit(numberValue)) { - result.description = result.unserializableValue = "-0"; - } else { - result.value = numberValue; - } - } else if (value.isString()) { - result.type = "string"; - result.value = value.getString(runtime).utf8(runtime); - } else if (value.isSymbol()) { - result.type = "symbol"; - auto sym = value.getSymbol(runtime); - result.description = sym.toString(runtime); - result.objectId = - objTable.addValue(jsi::Value(std::move(sym)), objectGroup); - } else if (value.isBigInt()) { - auto strRepresentation = - value.getBigInt(runtime).toString(runtime).utf8(runtime) + 'n'; - result.description = result.unserializableValue = strRepresentation; - } else if (value.isObject()) { - jsi::Object obj = value.getObject(runtime); - if (obj.isFunction(runtime)) { - result.type = "function"; - result.value = ""; - } else if (obj.isArray(runtime)) { - auto array = obj.getArray(runtime); - size_t arrayCount = array.length(runtime); - - result.type = "object"; - result.subtype = "array"; - result.className = "Array"; - result.description = "Array(" + folly::to(arrayCount) + ")"; - if (generatePreview) { - result.preview = generateArrayPreview(runtime, array); - } - } else { - result.type = "object"; - result.description = result.className = "Object"; - if (generatePreview) { - result.preview = generateObjectPreview(runtime, obj); - } - } - - if (byValue) { - // FIXME: JSI currently does not handle cycles and functions well here - result.value = jsi::dynamicFromValue(runtime, value); - } else { - result.objectId = - objTable.addValue(jsi::Value(std::move(obj)), objectGroup); - } - } - - return result; -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.h deleted file mode 100644 index 522e12fd5f1..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageConverters.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { -namespace message { - -std::string stripCachePrevention(const std::string &url); - -template -void setHermesLocation( - facebook::hermes::debugger::SourceLocation &hermesLoc, - const T &chromeLoc, - const std::vector &parsedScripts) { - hermesLoc.line = chromeLoc.lineNumber + 1; - - if (chromeLoc.columnNumber.has_value()) { - if (chromeLoc.columnNumber.value() == 0) { - // TODO: When CDTP sends a column number of 0, we send Hermes a column - // number of 1. For some reason, this causes Hermes to not be - // able to resolve breakpoints. - hermesLoc.column = ::facebook::hermes::debugger::kInvalidLocation; - } else { - hermesLoc.column = chromeLoc.columnNumber.value() + 1; - } - } - - if (chromeLoc.url.has_value()) { - hermesLoc.fileName = stripCachePrevention(chromeLoc.url.value()); - } else if (chromeLoc.urlRegex.has_value()) { - const std::regex regex(stripCachePrevention(chromeLoc.urlRegex.value())); - auto it = parsedScripts.rbegin(); - - // We currently only support one physical breakpoint per location, so - // search backwards so that we find the latest matching file. - while (it != parsedScripts.rend()) { - if (std::regex_match(*it, regex)) { - hermesLoc.fileName = *it; - break; - } - it++; - } - } -} - -template -void setChromeLocation( - T &chromeLoc, - const facebook::hermes::debugger::SourceLocation &hermesLoc) { - if (hermesLoc.line != facebook::hermes::debugger::kInvalidLocation) { - chromeLoc.lineNumber = hermesLoc.line - 1; - } - - if (hermesLoc.column != facebook::hermes::debugger::kInvalidLocation) { - chromeLoc.columnNumber = hermesLoc.column - 1; - } -} - -/// ErrorCode magic numbers match JSC's (see InspectorBackendDispatcher.cpp) -enum class ErrorCode { - ParseError = -32700, - InvalidRequest = -32600, - MethodNotFound = -32601, - InvalidParams = -32602, - InternalError = -32603, - ServerError = -32000 -}; - -ErrorResponse -makeErrorResponse(int id, ErrorCode code, const std::string &message); - -OkResponse makeOkResponse(int id); - -namespace debugger { - -Location makeLocation(const facebook::hermes::debugger::SourceLocation &loc); - -CallFrame makeCallFrame( - uint32_t callFrameIndex, - const facebook::hermes::debugger::CallFrameInfo &callFrameInfo, - const facebook::hermes::debugger::LexicalInfo &lexicalInfo, - facebook::hermes::inspector::chrome::RemoteObjectsTable &objTable, - jsi::Runtime &runtime, - const facebook::hermes::debugger::ProgramState &state); - -std::vector makeCallFrames( - const facebook::hermes::debugger::ProgramState &state, - facebook::hermes::inspector::chrome::RemoteObjectsTable &objTable, - jsi::Runtime &runtime); - -} // namespace debugger - -namespace runtime { - -CallFrame makeCallFrame(const facebook::hermes::debugger::CallFrameInfo &info); - -std::vector makeCallFrames( - const facebook::hermes::debugger::StackTrace &stackTrace); - -ExceptionDetails makeExceptionDetails( - const facebook::hermes::debugger::ExceptionDetails &details); - -RemoteObject makeRemoteObject( - facebook::jsi::Runtime &runtime, - const facebook::jsi::Value &value, - facebook::hermes::inspector::chrome::RemoteObjectsTable &objTable, - const std::string &objectGroup, - bool byValue = false, - bool generatePreview = false); - -} // namespace runtime - -} // namespace message -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageInterfaces.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageInterfaces.h deleted file mode 100644 index 46cdd7388bd..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageInterfaces.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { -namespace message { - -struct RequestHandler; - -/// Serializable is an interface for objects that can be serialized to and from -/// JSON. -struct Serializable { - virtual ~Serializable() = default; - virtual folly::dynamic toDynamic() const = 0; - - std::string toJson() const { - return folly::toJson(toDynamic()); - } -}; - -/// Requests are sent from the debugger to the target. -struct Request : public Serializable { - static std::unique_ptr fromJsonThrowOnError(const std::string &str); - static folly::Try> fromJson(const std::string &str); - - Request() = default; - explicit Request(std::string method) : method(method) {} - - // accept dispatches to the appropriate handler method in RequestHandler based - // on the type of the request. - virtual void accept(RequestHandler &handler) const = 0; - - int id = 0; - std::string method; -}; - -/// Responses are sent from the target to the debugger in response to a Request. -struct Response : public Serializable { - Response() = default; - - int id = 0; -}; - -/// Notifications are sent from the target to the debugger. This is used to -/// notify the debugger about events that occur in the target, e.g. stopping -/// at a breakpoint. -struct Notification : public Serializable { - Notification() = default; - explicit Notification(std::string method) : method(method) {} - - std::string method; -}; - -} // namespace message -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp deleted file mode 100644 index e36a47344aa..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ /dev/null @@ -1,1948 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. -// @generated SignedSource<<499c1dc13c66e60377fd3e29460a2321>> - -#include "MessageTypes.h" - -#include "MessageTypesInlines.h" - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { -namespace message { - -using RequestBuilder = std::unique_ptr (*)(const dynamic &); - -namespace { - -template -std::unique_ptr makeUnique(const dynamic &obj) { - return std::make_unique(obj); -} - -} // namespace - -std::unique_ptr Request::fromJsonThrowOnError(const std::string &str) { - static std::unordered_map builders = { - {"Debugger.disable", makeUnique}, - {"Debugger.enable", makeUnique}, - {"Debugger.evaluateOnCallFrame", - makeUnique}, - {"Debugger.pause", makeUnique}, - {"Debugger.removeBreakpoint", - makeUnique}, - {"Debugger.resume", makeUnique}, - {"Debugger.setBreakpoint", makeUnique}, - {"Debugger.setBreakpointByUrl", - makeUnique}, - {"Debugger.setBreakpointsActive", - makeUnique}, - {"Debugger.setInstrumentationBreakpoint", - makeUnique}, - {"Debugger.setPauseOnExceptions", - makeUnique}, - {"Debugger.stepInto", makeUnique}, - {"Debugger.stepOut", makeUnique}, - {"Debugger.stepOver", makeUnique}, - {"HeapProfiler.collectGarbage", - makeUnique}, - {"HeapProfiler.getHeapObjectId", - makeUnique}, - {"HeapProfiler.getObjectByHeapObjectId", - makeUnique}, - {"HeapProfiler.startSampling", - makeUnique}, - {"HeapProfiler.startTrackingHeapObjects", - makeUnique}, - {"HeapProfiler.stopSampling", - makeUnique}, - {"HeapProfiler.stopTrackingHeapObjects", - makeUnique}, - {"HeapProfiler.takeHeapSnapshot", - makeUnique}, - {"Profiler.start", makeUnique}, - {"Profiler.stop", makeUnique}, - {"Runtime.callFunctionOn", makeUnique}, - {"Runtime.compileScript", makeUnique}, - {"Runtime.evaluate", makeUnique}, - {"Runtime.getHeapUsage", makeUnique}, - {"Runtime.getProperties", makeUnique}, - {"Runtime.globalLexicalScopeNames", - makeUnique}, - {"Runtime.runIfWaitingForDebugger", - makeUnique}, - }; - - dynamic obj = folly::parseJson(str); - std::string method = obj.at("method").asString(); - - auto it = builders.find(method); - if (it == builders.end()) { - return std::make_unique(obj); - } - - auto builder = it->second; - return builder(obj); -} - -folly::Try> Request::fromJson(const std::string &str) { - return folly::makeTryWith( - [&str] { return Request::fromJsonThrowOnError(str); }); -} - -/// Types -debugger::Location::Location(const dynamic &obj) { - assign(scriptId, obj, "scriptId"); - assign(lineNumber, obj, "lineNumber"); - assign(columnNumber, obj, "columnNumber"); -} - -dynamic debugger::Location::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "scriptId", scriptId); - put(obj, "lineNumber", lineNumber); - put(obj, "columnNumber", columnNumber); - return obj; -} - -runtime::PropertyPreview::PropertyPreview(const dynamic &obj) { - assign(name, obj, "name"); - assign(type, obj, "type"); - assign(value, obj, "value"); - assign(valuePreview, obj, "valuePreview"); - assign(subtype, obj, "subtype"); -} - -dynamic runtime::PropertyPreview::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "name", name); - put(obj, "type", type); - put(obj, "value", value); - put(obj, "valuePreview", valuePreview); - put(obj, "subtype", subtype); - return obj; -} - -runtime::EntryPreview::EntryPreview(const dynamic &obj) { - assign(key, obj, "key"); - assign(value, obj, "value"); -} - -dynamic runtime::EntryPreview::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "key", key); - put(obj, "value", value); - return obj; -} - -runtime::ObjectPreview::ObjectPreview(const dynamic &obj) { - assign(type, obj, "type"); - assign(subtype, obj, "subtype"); - assign(description, obj, "description"); - assign(overflow, obj, "overflow"); - assign(properties, obj, "properties"); - assign(entries, obj, "entries"); -} - -dynamic runtime::ObjectPreview::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "type", type); - put(obj, "subtype", subtype); - put(obj, "description", description); - put(obj, "overflow", overflow); - put(obj, "properties", properties); - put(obj, "entries", entries); - return obj; -} - -runtime::CustomPreview::CustomPreview(const dynamic &obj) { - assign(header, obj, "header"); - assign(bodyGetterId, obj, "bodyGetterId"); -} - -dynamic runtime::CustomPreview::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "header", header); - put(obj, "bodyGetterId", bodyGetterId); - return obj; -} - -runtime::RemoteObject::RemoteObject(const dynamic &obj) { - assign(type, obj, "type"); - assign(subtype, obj, "subtype"); - assign(className, obj, "className"); - assign(value, obj, "value"); - assign(unserializableValue, obj, "unserializableValue"); - assign(description, obj, "description"); - assign(objectId, obj, "objectId"); - assign(preview, obj, "preview"); - assign(customPreview, obj, "customPreview"); -} - -dynamic runtime::RemoteObject::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "type", type); - put(obj, "subtype", subtype); - put(obj, "className", className); - put(obj, "value", value); - put(obj, "unserializableValue", unserializableValue); - put(obj, "description", description); - put(obj, "objectId", objectId); - put(obj, "preview", preview); - put(obj, "customPreview", customPreview); - return obj; -} - -runtime::CallFrame::CallFrame(const dynamic &obj) { - assign(functionName, obj, "functionName"); - assign(scriptId, obj, "scriptId"); - assign(url, obj, "url"); - assign(lineNumber, obj, "lineNumber"); - assign(columnNumber, obj, "columnNumber"); -} - -dynamic runtime::CallFrame::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "functionName", functionName); - put(obj, "scriptId", scriptId); - put(obj, "url", url); - put(obj, "lineNumber", lineNumber); - put(obj, "columnNumber", columnNumber); - return obj; -} - -runtime::StackTrace::StackTrace(const dynamic &obj) { - assign(description, obj, "description"); - assign(callFrames, obj, "callFrames"); - assign(parent, obj, "parent"); -} - -dynamic runtime::StackTrace::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "description", description); - put(obj, "callFrames", callFrames); - put(obj, "parent", parent); - return obj; -} - -runtime::ExceptionDetails::ExceptionDetails(const dynamic &obj) { - assign(exceptionId, obj, "exceptionId"); - assign(text, obj, "text"); - assign(lineNumber, obj, "lineNumber"); - assign(columnNumber, obj, "columnNumber"); - assign(scriptId, obj, "scriptId"); - assign(url, obj, "url"); - assign(stackTrace, obj, "stackTrace"); - assign(exception, obj, "exception"); - assign(executionContextId, obj, "executionContextId"); -} - -dynamic runtime::ExceptionDetails::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "exceptionId", exceptionId); - put(obj, "text", text); - put(obj, "lineNumber", lineNumber); - put(obj, "columnNumber", columnNumber); - put(obj, "scriptId", scriptId); - put(obj, "url", url); - put(obj, "stackTrace", stackTrace); - put(obj, "exception", exception); - put(obj, "executionContextId", executionContextId); - return obj; -} - -debugger::Scope::Scope(const dynamic &obj) { - assign(type, obj, "type"); - assign(object, obj, "object"); - assign(name, obj, "name"); - assign(startLocation, obj, "startLocation"); - assign(endLocation, obj, "endLocation"); -} - -dynamic debugger::Scope::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "type", type); - put(obj, "object", object); - put(obj, "name", name); - put(obj, "startLocation", startLocation); - put(obj, "endLocation", endLocation); - return obj; -} - -debugger::CallFrame::CallFrame(const dynamic &obj) { - assign(callFrameId, obj, "callFrameId"); - assign(functionName, obj, "functionName"); - assign(functionLocation, obj, "functionLocation"); - assign(location, obj, "location"); - assign(url, obj, "url"); - assign(scopeChain, obj, "scopeChain"); - assign(thisObj, obj, "this"); - assign(returnValue, obj, "returnValue"); -} - -dynamic debugger::CallFrame::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "callFrameId", callFrameId); - put(obj, "functionName", functionName); - put(obj, "functionLocation", functionLocation); - put(obj, "location", location); - put(obj, "url", url); - put(obj, "scopeChain", scopeChain); - put(obj, "this", thisObj); - put(obj, "returnValue", returnValue); - return obj; -} - -heapProfiler::SamplingHeapProfileNode::SamplingHeapProfileNode( - const dynamic &obj) { - assign(callFrame, obj, "callFrame"); - assign(selfSize, obj, "selfSize"); - assign(id, obj, "id"); - assign(children, obj, "children"); -} - -dynamic heapProfiler::SamplingHeapProfileNode::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "callFrame", callFrame); - put(obj, "selfSize", selfSize); - put(obj, "id", id); - put(obj, "children", children); - return obj; -} - -heapProfiler::SamplingHeapProfileSample::SamplingHeapProfileSample( - const dynamic &obj) { - assign(size, obj, "size"); - assign(nodeId, obj, "nodeId"); - assign(ordinal, obj, "ordinal"); -} - -dynamic heapProfiler::SamplingHeapProfileSample::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "size", size); - put(obj, "nodeId", nodeId); - put(obj, "ordinal", ordinal); - return obj; -} - -heapProfiler::SamplingHeapProfile::SamplingHeapProfile(const dynamic &obj) { - assign(head, obj, "head"); - assign(samples, obj, "samples"); -} - -dynamic heapProfiler::SamplingHeapProfile::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "head", head); - put(obj, "samples", samples); - return obj; -} - -profiler::PositionTickInfo::PositionTickInfo(const dynamic &obj) { - assign(line, obj, "line"); - assign(ticks, obj, "ticks"); -} - -dynamic profiler::PositionTickInfo::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "line", line); - put(obj, "ticks", ticks); - return obj; -} - -profiler::ProfileNode::ProfileNode(const dynamic &obj) { - assign(id, obj, "id"); - assign(callFrame, obj, "callFrame"); - assign(hitCount, obj, "hitCount"); - assign(children, obj, "children"); - assign(deoptReason, obj, "deoptReason"); - assign(positionTicks, obj, "positionTicks"); -} - -dynamic profiler::ProfileNode::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "id", id); - put(obj, "callFrame", callFrame); - put(obj, "hitCount", hitCount); - put(obj, "children", children); - put(obj, "deoptReason", deoptReason); - put(obj, "positionTicks", positionTicks); - return obj; -} - -profiler::Profile::Profile(const dynamic &obj) { - assign(nodes, obj, "nodes"); - assign(startTime, obj, "startTime"); - assign(endTime, obj, "endTime"); - assign(samples, obj, "samples"); - assign(timeDeltas, obj, "timeDeltas"); -} - -dynamic profiler::Profile::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "nodes", nodes); - put(obj, "startTime", startTime); - put(obj, "endTime", endTime); - put(obj, "samples", samples); - put(obj, "timeDeltas", timeDeltas); - return obj; -} - -runtime::CallArgument::CallArgument(const dynamic &obj) { - assign(value, obj, "value"); - assign(unserializableValue, obj, "unserializableValue"); - assign(objectId, obj, "objectId"); -} - -dynamic runtime::CallArgument::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "value", value); - put(obj, "unserializableValue", unserializableValue); - put(obj, "objectId", objectId); - return obj; -} - -runtime::ExecutionContextDescription::ExecutionContextDescription( - const dynamic &obj) { - assign(id, obj, "id"); - assign(origin, obj, "origin"); - assign(name, obj, "name"); - assign(auxData, obj, "auxData"); -} - -dynamic runtime::ExecutionContextDescription::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "id", id); - put(obj, "origin", origin); - put(obj, "name", name); - put(obj, "auxData", auxData); - return obj; -} - -runtime::PropertyDescriptor::PropertyDescriptor(const dynamic &obj) { - assign(name, obj, "name"); - assign(value, obj, "value"); - assign(writable, obj, "writable"); - assign(get, obj, "get"); - assign(set, obj, "set"); - assign(configurable, obj, "configurable"); - assign(enumerable, obj, "enumerable"); - assign(wasThrown, obj, "wasThrown"); - assign(isOwn, obj, "isOwn"); - assign(symbol, obj, "symbol"); -} - -dynamic runtime::PropertyDescriptor::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "name", name); - put(obj, "value", value); - put(obj, "writable", writable); - put(obj, "get", get); - put(obj, "set", set); - put(obj, "configurable", configurable); - put(obj, "enumerable", enumerable); - put(obj, "wasThrown", wasThrown); - put(obj, "isOwn", isOwn); - put(obj, "symbol", symbol); - return obj; -} - -runtime::InternalPropertyDescriptor::InternalPropertyDescriptor( - const dynamic &obj) { - assign(name, obj, "name"); - assign(value, obj, "value"); -} - -dynamic runtime::InternalPropertyDescriptor::toDynamic() const { - dynamic obj = dynamic::object; - - put(obj, "name", name); - put(obj, "value", value); - return obj; -} - -/// Requests -UnknownRequest::UnknownRequest() {} - -UnknownRequest::UnknownRequest(const dynamic &obj) { - assign(id, obj, "id"); - assign(method, obj, "method"); - assign(params, obj, "params"); -} - -dynamic UnknownRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", params); - return obj; -} - -void UnknownRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::DisableRequest::DisableRequest() : Request("Debugger.disable") {} - -debugger::DisableRequest::DisableRequest(const dynamic &obj) - : Request("Debugger.disable") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic debugger::DisableRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void debugger::DisableRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::EnableRequest::EnableRequest() : Request("Debugger.enable") {} - -debugger::EnableRequest::EnableRequest(const dynamic &obj) - : Request("Debugger.enable") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic debugger::EnableRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void debugger::EnableRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::EvaluateOnCallFrameRequest::EvaluateOnCallFrameRequest() - : Request("Debugger.evaluateOnCallFrame") {} - -debugger::EvaluateOnCallFrameRequest::EvaluateOnCallFrameRequest( - const dynamic &obj) - : Request("Debugger.evaluateOnCallFrame") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(callFrameId, params, "callFrameId"); - assign(expression, params, "expression"); - assign(objectGroup, params, "objectGroup"); - assign(includeCommandLineAPI, params, "includeCommandLineAPI"); - assign(silent, params, "silent"); - assign(returnByValue, params, "returnByValue"); - assign(generatePreview, params, "generatePreview"); - assign(throwOnSideEffect, params, "throwOnSideEffect"); -} - -dynamic debugger::EvaluateOnCallFrameRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "callFrameId", callFrameId); - put(params, "expression", expression); - put(params, "objectGroup", objectGroup); - put(params, "includeCommandLineAPI", includeCommandLineAPI); - put(params, "silent", silent); - put(params, "returnByValue", returnByValue); - put(params, "generatePreview", generatePreview); - put(params, "throwOnSideEffect", throwOnSideEffect); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void debugger::EvaluateOnCallFrameRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::PauseRequest::PauseRequest() : Request("Debugger.pause") {} - -debugger::PauseRequest::PauseRequest(const dynamic &obj) - : Request("Debugger.pause") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic debugger::PauseRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void debugger::PauseRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::RemoveBreakpointRequest::RemoveBreakpointRequest() - : Request("Debugger.removeBreakpoint") {} - -debugger::RemoveBreakpointRequest::RemoveBreakpointRequest(const dynamic &obj) - : Request("Debugger.removeBreakpoint") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(breakpointId, params, "breakpointId"); -} - -dynamic debugger::RemoveBreakpointRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "breakpointId", breakpointId); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void debugger::RemoveBreakpointRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::ResumeRequest::ResumeRequest() : Request("Debugger.resume") {} - -debugger::ResumeRequest::ResumeRequest(const dynamic &obj) - : Request("Debugger.resume") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - auto it = obj.find("params"); - if (it != obj.items().end()) { - dynamic params = it->second; - assign(terminateOnResume, params, "terminateOnResume"); - } -} - -dynamic debugger::ResumeRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "terminateOnResume", terminateOnResume); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void debugger::ResumeRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::SetBreakpointRequest::SetBreakpointRequest() - : Request("Debugger.setBreakpoint") {} - -debugger::SetBreakpointRequest::SetBreakpointRequest(const dynamic &obj) - : Request("Debugger.setBreakpoint") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(location, params, "location"); - assign(condition, params, "condition"); -} - -dynamic debugger::SetBreakpointRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "location", location); - put(params, "condition", condition); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void debugger::SetBreakpointRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::SetBreakpointByUrlRequest::SetBreakpointByUrlRequest() - : Request("Debugger.setBreakpointByUrl") {} - -debugger::SetBreakpointByUrlRequest::SetBreakpointByUrlRequest( - const dynamic &obj) - : Request("Debugger.setBreakpointByUrl") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(lineNumber, params, "lineNumber"); - assign(url, params, "url"); - assign(urlRegex, params, "urlRegex"); - assign(scriptHash, params, "scriptHash"); - assign(columnNumber, params, "columnNumber"); - assign(condition, params, "condition"); -} - -dynamic debugger::SetBreakpointByUrlRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "lineNumber", lineNumber); - put(params, "url", url); - put(params, "urlRegex", urlRegex); - put(params, "scriptHash", scriptHash); - put(params, "columnNumber", columnNumber); - put(params, "condition", condition); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void debugger::SetBreakpointByUrlRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::SetBreakpointsActiveRequest::SetBreakpointsActiveRequest() - : Request("Debugger.setBreakpointsActive") {} - -debugger::SetBreakpointsActiveRequest::SetBreakpointsActiveRequest( - const dynamic &obj) - : Request("Debugger.setBreakpointsActive") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(active, params, "active"); -} - -dynamic debugger::SetBreakpointsActiveRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "active", active); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void debugger::SetBreakpointsActiveRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::SetInstrumentationBreakpointRequest:: - SetInstrumentationBreakpointRequest() - : Request("Debugger.setInstrumentationBreakpoint") {} - -debugger::SetInstrumentationBreakpointRequest:: - SetInstrumentationBreakpointRequest(const dynamic &obj) - : Request("Debugger.setInstrumentationBreakpoint") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(instrumentation, params, "instrumentation"); -} - -dynamic debugger::SetInstrumentationBreakpointRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "instrumentation", instrumentation); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void debugger::SetInstrumentationBreakpointRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::SetPauseOnExceptionsRequest::SetPauseOnExceptionsRequest() - : Request("Debugger.setPauseOnExceptions") {} - -debugger::SetPauseOnExceptionsRequest::SetPauseOnExceptionsRequest( - const dynamic &obj) - : Request("Debugger.setPauseOnExceptions") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(state, params, "state"); -} - -dynamic debugger::SetPauseOnExceptionsRequest::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 debugger::SetPauseOnExceptionsRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::StepIntoRequest::StepIntoRequest() : Request("Debugger.stepInto") {} - -debugger::StepIntoRequest::StepIntoRequest(const dynamic &obj) - : Request("Debugger.stepInto") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic debugger::StepIntoRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void debugger::StepIntoRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::StepOutRequest::StepOutRequest() : Request("Debugger.stepOut") {} - -debugger::StepOutRequest::StepOutRequest(const dynamic &obj) - : Request("Debugger.stepOut") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic debugger::StepOutRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void debugger::StepOutRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -debugger::StepOverRequest::StepOverRequest() : Request("Debugger.stepOver") {} - -debugger::StepOverRequest::StepOverRequest(const dynamic &obj) - : Request("Debugger.stepOver") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic debugger::StepOverRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void debugger::StepOverRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::CollectGarbageRequest::CollectGarbageRequest() - : Request("HeapProfiler.collectGarbage") {} - -heapProfiler::CollectGarbageRequest::CollectGarbageRequest(const dynamic &obj) - : Request("HeapProfiler.collectGarbage") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic heapProfiler::CollectGarbageRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void heapProfiler::CollectGarbageRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::GetHeapObjectIdRequest::GetHeapObjectIdRequest() - : Request("HeapProfiler.getHeapObjectId") {} - -heapProfiler::GetHeapObjectIdRequest::GetHeapObjectIdRequest(const dynamic &obj) - : Request("HeapProfiler.getHeapObjectId") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(objectId, params, "objectId"); -} - -dynamic heapProfiler::GetHeapObjectIdRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "objectId", objectId); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void heapProfiler::GetHeapObjectIdRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::GetObjectByHeapObjectIdRequest::GetObjectByHeapObjectIdRequest() - : Request("HeapProfiler.getObjectByHeapObjectId") {} - -heapProfiler::GetObjectByHeapObjectIdRequest::GetObjectByHeapObjectIdRequest( - const dynamic &obj) - : Request("HeapProfiler.getObjectByHeapObjectId") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(objectId, params, "objectId"); - assign(objectGroup, params, "objectGroup"); -} - -dynamic heapProfiler::GetObjectByHeapObjectIdRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "objectId", objectId); - put(params, "objectGroup", objectGroup); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void heapProfiler::GetObjectByHeapObjectIdRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::StartSamplingRequest::StartSamplingRequest() - : Request("HeapProfiler.startSampling") {} - -heapProfiler::StartSamplingRequest::StartSamplingRequest(const dynamic &obj) - : Request("HeapProfiler.startSampling") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - auto it = obj.find("params"); - if (it != obj.items().end()) { - dynamic params = it->second; - assign(samplingInterval, params, "samplingInterval"); - assign( - includeObjectsCollectedByMajorGC, - params, - "includeObjectsCollectedByMajorGC"); - assign( - includeObjectsCollectedByMinorGC, - params, - "includeObjectsCollectedByMinorGC"); - } -} - -dynamic heapProfiler::StartSamplingRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "samplingInterval", samplingInterval); - put(params, - "includeObjectsCollectedByMajorGC", - includeObjectsCollectedByMajorGC); - put(params, - "includeObjectsCollectedByMinorGC", - includeObjectsCollectedByMinorGC); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void heapProfiler::StartSamplingRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::StartTrackingHeapObjectsRequest::StartTrackingHeapObjectsRequest() - : Request("HeapProfiler.startTrackingHeapObjects") {} - -heapProfiler::StartTrackingHeapObjectsRequest::StartTrackingHeapObjectsRequest( - const dynamic &obj) - : Request("HeapProfiler.startTrackingHeapObjects") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - auto it = obj.find("params"); - if (it != obj.items().end()) { - dynamic params = it->second; - assign(trackAllocations, params, "trackAllocations"); - } -} - -dynamic heapProfiler::StartTrackingHeapObjectsRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "trackAllocations", trackAllocations); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void heapProfiler::StartTrackingHeapObjectsRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::StopSamplingRequest::StopSamplingRequest() - : Request("HeapProfiler.stopSampling") {} - -heapProfiler::StopSamplingRequest::StopSamplingRequest(const dynamic &obj) - : Request("HeapProfiler.stopSampling") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic heapProfiler::StopSamplingRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void heapProfiler::StopSamplingRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::StopTrackingHeapObjectsRequest::StopTrackingHeapObjectsRequest() - : Request("HeapProfiler.stopTrackingHeapObjects") {} - -heapProfiler::StopTrackingHeapObjectsRequest::StopTrackingHeapObjectsRequest( - const dynamic &obj) - : Request("HeapProfiler.stopTrackingHeapObjects") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - auto it = obj.find("params"); - if (it != obj.items().end()) { - dynamic params = it->second; - assign(reportProgress, params, "reportProgress"); - assign(treatGlobalObjectsAsRoots, params, "treatGlobalObjectsAsRoots"); - assign(captureNumericValue, params, "captureNumericValue"); - } -} - -dynamic heapProfiler::StopTrackingHeapObjectsRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "reportProgress", reportProgress); - put(params, "treatGlobalObjectsAsRoots", treatGlobalObjectsAsRoots); - put(params, "captureNumericValue", captureNumericValue); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void heapProfiler::StopTrackingHeapObjectsRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -heapProfiler::TakeHeapSnapshotRequest::TakeHeapSnapshotRequest() - : Request("HeapProfiler.takeHeapSnapshot") {} - -heapProfiler::TakeHeapSnapshotRequest::TakeHeapSnapshotRequest( - const dynamic &obj) - : Request("HeapProfiler.takeHeapSnapshot") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - auto it = obj.find("params"); - if (it != obj.items().end()) { - dynamic params = it->second; - assign(reportProgress, params, "reportProgress"); - assign(treatGlobalObjectsAsRoots, params, "treatGlobalObjectsAsRoots"); - assign(captureNumericValue, params, "captureNumericValue"); - } -} - -dynamic heapProfiler::TakeHeapSnapshotRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "reportProgress", reportProgress); - put(params, "treatGlobalObjectsAsRoots", treatGlobalObjectsAsRoots); - put(params, "captureNumericValue", captureNumericValue); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void heapProfiler::TakeHeapSnapshotRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -profiler::StartRequest::StartRequest() : Request("Profiler.start") {} - -profiler::StartRequest::StartRequest(const dynamic &obj) - : Request("Profiler.start") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic profiler::StartRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void profiler::StartRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -profiler::StopRequest::StopRequest() : Request("Profiler.stop") {} - -profiler::StopRequest::StopRequest(const dynamic &obj) - : Request("Profiler.stop") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic profiler::StopRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void profiler::StopRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -runtime::CallFunctionOnRequest::CallFunctionOnRequest() - : Request("Runtime.callFunctionOn") {} - -runtime::CallFunctionOnRequest::CallFunctionOnRequest(const dynamic &obj) - : Request("Runtime.callFunctionOn") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(functionDeclaration, params, "functionDeclaration"); - assign(objectId, params, "objectId"); - assign(arguments, params, "arguments"); - assign(silent, params, "silent"); - assign(returnByValue, params, "returnByValue"); - assign(generatePreview, params, "generatePreview"); - assign(userGesture, params, "userGesture"); - assign(awaitPromise, params, "awaitPromise"); - assign(executionContextId, params, "executionContextId"); - assign(objectGroup, params, "objectGroup"); -} - -dynamic runtime::CallFunctionOnRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "functionDeclaration", functionDeclaration); - put(params, "objectId", objectId); - put(params, "arguments", arguments); - put(params, "silent", silent); - put(params, "returnByValue", returnByValue); - put(params, "generatePreview", generatePreview); - put(params, "userGesture", userGesture); - put(params, "awaitPromise", awaitPromise); - put(params, "executionContextId", executionContextId); - put(params, "objectGroup", objectGroup); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void runtime::CallFunctionOnRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -runtime::CompileScriptRequest::CompileScriptRequest() - : Request("Runtime.compileScript") {} - -runtime::CompileScriptRequest::CompileScriptRequest(const dynamic &obj) - : Request("Runtime.compileScript") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(expression, params, "expression"); - assign(sourceURL, params, "sourceURL"); - assign(persistScript, params, "persistScript"); - assign(executionContextId, params, "executionContextId"); -} - -dynamic runtime::CompileScriptRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "expression", expression); - put(params, "sourceURL", sourceURL); - put(params, "persistScript", persistScript); - 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::CompileScriptRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -runtime::EvaluateRequest::EvaluateRequest() : Request("Runtime.evaluate") {} - -runtime::EvaluateRequest::EvaluateRequest(const dynamic &obj) - : Request("Runtime.evaluate") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(expression, params, "expression"); - assign(objectGroup, params, "objectGroup"); - assign(includeCommandLineAPI, params, "includeCommandLineAPI"); - assign(silent, params, "silent"); - assign(contextId, params, "contextId"); - assign(returnByValue, params, "returnByValue"); - assign(generatePreview, params, "generatePreview"); - assign(userGesture, params, "userGesture"); - assign(awaitPromise, params, "awaitPromise"); -} - -dynamic runtime::EvaluateRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "expression", expression); - put(params, "objectGroup", objectGroup); - put(params, "includeCommandLineAPI", includeCommandLineAPI); - put(params, "silent", silent); - put(params, "contextId", contextId); - put(params, "returnByValue", returnByValue); - put(params, "generatePreview", generatePreview); - put(params, "userGesture", userGesture); - put(params, "awaitPromise", awaitPromise); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -void runtime::EvaluateRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -runtime::GetHeapUsageRequest::GetHeapUsageRequest() - : Request("Runtime.getHeapUsage") {} - -runtime::GetHeapUsageRequest::GetHeapUsageRequest(const dynamic &obj) - : Request("Runtime.getHeapUsage") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic runtime::GetHeapUsageRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void runtime::GetHeapUsageRequest::accept(RequestHandler &handler) const { - handler.handle(*this); -} - -runtime::GetPropertiesRequest::GetPropertiesRequest() - : Request("Runtime.getProperties") {} - -runtime::GetPropertiesRequest::GetPropertiesRequest(const dynamic &obj) - : Request("Runtime.getProperties") { - assign(id, obj, "id"); - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(objectId, params, "objectId"); - assign(ownProperties, params, "ownProperties"); - assign(generatePreview, params, "generatePreview"); -} - -dynamic runtime::GetPropertiesRequest::toDynamic() const { - dynamic params = dynamic::object; - put(params, "objectId", objectId); - put(params, "ownProperties", ownProperties); - put(params, "generatePreview", generatePreview); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -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") {} - -runtime::RunIfWaitingForDebuggerRequest::RunIfWaitingForDebuggerRequest( - const dynamic &obj) - : Request("Runtime.runIfWaitingForDebugger") { - assign(id, obj, "id"); - assign(method, obj, "method"); -} - -dynamic runtime::RunIfWaitingForDebuggerRequest::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "method", method); - return obj; -} - -void runtime::RunIfWaitingForDebuggerRequest::accept( - RequestHandler &handler) const { - handler.handle(*this); -} - -/// Responses -ErrorResponse::ErrorResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic error = obj.at("error"); - assign(code, error, "code"); - assign(message, error, "message"); - assign(data, error, "data"); -} - -dynamic ErrorResponse::toDynamic() const { - dynamic error = dynamic::object; - put(error, "code", code); - put(error, "message", message); - put(error, "data", data); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "error", std::move(error)); - return obj; -} - -OkResponse::OkResponse(const dynamic &obj) { - assign(id, obj, "id"); -} - -dynamic OkResponse::toDynamic() const { - dynamic result = dynamic::object; - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(result)); - return obj; -} - -debugger::EvaluateOnCallFrameResponse::EvaluateOnCallFrameResponse( - const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(result, res, "result"); - assign(exceptionDetails, res, "exceptionDetails"); -} - -dynamic debugger::EvaluateOnCallFrameResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "result", result); - put(res, "exceptionDetails", exceptionDetails); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -debugger::SetBreakpointResponse::SetBreakpointResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(breakpointId, res, "breakpointId"); - assign(actualLocation, res, "actualLocation"); -} - -dynamic debugger::SetBreakpointResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "breakpointId", breakpointId); - put(res, "actualLocation", actualLocation); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -debugger::SetBreakpointByUrlResponse::SetBreakpointByUrlResponse( - const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(breakpointId, res, "breakpointId"); - assign(locations, res, "locations"); -} - -dynamic debugger::SetBreakpointByUrlResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "breakpointId", breakpointId); - put(res, "locations", locations); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -debugger::SetInstrumentationBreakpointResponse:: - SetInstrumentationBreakpointResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(breakpointId, res, "breakpointId"); -} - -dynamic debugger::SetInstrumentationBreakpointResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "breakpointId", breakpointId); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -heapProfiler::GetHeapObjectIdResponse::GetHeapObjectIdResponse( - const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(heapSnapshotObjectId, res, "heapSnapshotObjectId"); -} - -dynamic heapProfiler::GetHeapObjectIdResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "heapSnapshotObjectId", heapSnapshotObjectId); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -heapProfiler::GetObjectByHeapObjectIdResponse::GetObjectByHeapObjectIdResponse( - const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(result, res, "result"); -} - -dynamic heapProfiler::GetObjectByHeapObjectIdResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "result", result); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -heapProfiler::StopSamplingResponse::StopSamplingResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(profile, res, "profile"); -} - -dynamic heapProfiler::StopSamplingResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "profile", profile); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -profiler::StopResponse::StopResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(profile, res, "profile"); -} - -dynamic profiler::StopResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "profile", profile); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -runtime::CallFunctionOnResponse::CallFunctionOnResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(result, res, "result"); - assign(exceptionDetails, res, "exceptionDetails"); -} - -dynamic runtime::CallFunctionOnResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "result", result); - put(res, "exceptionDetails", exceptionDetails); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -runtime::CompileScriptResponse::CompileScriptResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(scriptId, res, "scriptId"); - assign(exceptionDetails, res, "exceptionDetails"); -} - -dynamic runtime::CompileScriptResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "scriptId", scriptId); - put(res, "exceptionDetails", exceptionDetails); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -runtime::EvaluateResponse::EvaluateResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(result, res, "result"); - assign(exceptionDetails, res, "exceptionDetails"); -} - -dynamic runtime::EvaluateResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "result", result); - put(res, "exceptionDetails", exceptionDetails); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -runtime::GetHeapUsageResponse::GetHeapUsageResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(usedSize, res, "usedSize"); - assign(totalSize, res, "totalSize"); -} - -dynamic runtime::GetHeapUsageResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "usedSize", usedSize); - put(res, "totalSize", totalSize); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - return obj; -} - -runtime::GetPropertiesResponse::GetPropertiesResponse(const dynamic &obj) { - assign(id, obj, "id"); - - dynamic res = obj.at("result"); - assign(result, res, "result"); - assign(internalProperties, res, "internalProperties"); - assign(exceptionDetails, res, "exceptionDetails"); -} - -dynamic runtime::GetPropertiesResponse::toDynamic() const { - dynamic res = dynamic::object; - put(res, "result", result); - put(res, "internalProperties", internalProperties); - put(res, "exceptionDetails", exceptionDetails); - - dynamic obj = dynamic::object; - put(obj, "id", id); - put(obj, "result", std::move(res)); - 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") {} - -debugger::BreakpointResolvedNotification::BreakpointResolvedNotification( - const dynamic &obj) - : Notification("Debugger.breakpointResolved") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(breakpointId, params, "breakpointId"); - assign(location, params, "location"); -} - -dynamic debugger::BreakpointResolvedNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "breakpointId", breakpointId); - put(params, "location", location); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -debugger::PausedNotification::PausedNotification() - : Notification("Debugger.paused") {} - -debugger::PausedNotification::PausedNotification(const dynamic &obj) - : Notification("Debugger.paused") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(callFrames, params, "callFrames"); - assign(reason, params, "reason"); - assign(data, params, "data"); - assign(hitBreakpoints, params, "hitBreakpoints"); - assign(asyncStackTrace, params, "asyncStackTrace"); -} - -dynamic debugger::PausedNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "callFrames", callFrames); - put(params, "reason", reason); - put(params, "data", data); - put(params, "hitBreakpoints", hitBreakpoints); - put(params, "asyncStackTrace", asyncStackTrace); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -debugger::ResumedNotification::ResumedNotification() - : Notification("Debugger.resumed") {} - -debugger::ResumedNotification::ResumedNotification(const dynamic &obj) - : Notification("Debugger.resumed") { - assign(method, obj, "method"); -} - -dynamic debugger::ResumedNotification::toDynamic() const { - dynamic obj = dynamic::object; - put(obj, "method", method); - return obj; -} - -debugger::ScriptParsedNotification::ScriptParsedNotification() - : Notification("Debugger.scriptParsed") {} - -debugger::ScriptParsedNotification::ScriptParsedNotification(const dynamic &obj) - : Notification("Debugger.scriptParsed") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(scriptId, params, "scriptId"); - assign(url, params, "url"); - assign(startLine, params, "startLine"); - assign(startColumn, params, "startColumn"); - assign(endLine, params, "endLine"); - assign(endColumn, params, "endColumn"); - assign(executionContextId, params, "executionContextId"); - assign(hash, params, "hash"); - assign(executionContextAuxData, params, "executionContextAuxData"); - assign(sourceMapURL, params, "sourceMapURL"); - assign(hasSourceURL, params, "hasSourceURL"); - assign(isModule, params, "isModule"); - assign(length, params, "length"); -} - -dynamic debugger::ScriptParsedNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "scriptId", scriptId); - put(params, "url", url); - put(params, "startLine", startLine); - put(params, "startColumn", startColumn); - put(params, "endLine", endLine); - put(params, "endColumn", endColumn); - put(params, "executionContextId", executionContextId); - put(params, "hash", hash); - put(params, "executionContextAuxData", executionContextAuxData); - put(params, "sourceMapURL", sourceMapURL); - put(params, "hasSourceURL", hasSourceURL); - put(params, "isModule", isModule); - put(params, "length", length); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -heapProfiler::AddHeapSnapshotChunkNotification:: - AddHeapSnapshotChunkNotification() - : Notification("HeapProfiler.addHeapSnapshotChunk") {} - -heapProfiler::AddHeapSnapshotChunkNotification:: - AddHeapSnapshotChunkNotification(const dynamic &obj) - : Notification("HeapProfiler.addHeapSnapshotChunk") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(chunk, params, "chunk"); -} - -dynamic heapProfiler::AddHeapSnapshotChunkNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "chunk", chunk); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -heapProfiler::HeapStatsUpdateNotification::HeapStatsUpdateNotification() - : Notification("HeapProfiler.heapStatsUpdate") {} - -heapProfiler::HeapStatsUpdateNotification::HeapStatsUpdateNotification( - const dynamic &obj) - : Notification("HeapProfiler.heapStatsUpdate") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(statsUpdate, params, "statsUpdate"); -} - -dynamic heapProfiler::HeapStatsUpdateNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "statsUpdate", statsUpdate); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -heapProfiler::LastSeenObjectIdNotification::LastSeenObjectIdNotification() - : Notification("HeapProfiler.lastSeenObjectId") {} - -heapProfiler::LastSeenObjectIdNotification::LastSeenObjectIdNotification( - const dynamic &obj) - : Notification("HeapProfiler.lastSeenObjectId") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(lastSeenObjectId, params, "lastSeenObjectId"); - assign(timestamp, params, "timestamp"); -} - -dynamic heapProfiler::LastSeenObjectIdNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "lastSeenObjectId", lastSeenObjectId); - put(params, "timestamp", timestamp); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -heapProfiler::ReportHeapSnapshotProgressNotification:: - ReportHeapSnapshotProgressNotification() - : Notification("HeapProfiler.reportHeapSnapshotProgress") {} - -heapProfiler::ReportHeapSnapshotProgressNotification:: - ReportHeapSnapshotProgressNotification(const dynamic &obj) - : Notification("HeapProfiler.reportHeapSnapshotProgress") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(done, params, "done"); - assign(total, params, "total"); - assign(finished, params, "finished"); -} - -dynamic heapProfiler::ReportHeapSnapshotProgressNotification::toDynamic() - const { - dynamic params = dynamic::object; - put(params, "done", done); - put(params, "total", total); - put(params, "finished", finished); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -runtime::ConsoleAPICalledNotification::ConsoleAPICalledNotification() - : Notification("Runtime.consoleAPICalled") {} - -runtime::ConsoleAPICalledNotification::ConsoleAPICalledNotification( - const dynamic &obj) - : Notification("Runtime.consoleAPICalled") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(type, params, "type"); - assign(args, params, "args"); - assign(executionContextId, params, "executionContextId"); - assign(timestamp, params, "timestamp"); - assign(stackTrace, params, "stackTrace"); -} - -dynamic runtime::ConsoleAPICalledNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "type", type); - put(params, "args", args); - put(params, "executionContextId", executionContextId); - put(params, "timestamp", timestamp); - put(params, "stackTrace", stackTrace); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -runtime::ExecutionContextCreatedNotification:: - ExecutionContextCreatedNotification() - : Notification("Runtime.executionContextCreated") {} - -runtime::ExecutionContextCreatedNotification:: - ExecutionContextCreatedNotification(const dynamic &obj) - : Notification("Runtime.executionContextCreated") { - assign(method, obj, "method"); - - dynamic params = obj.at("params"); - assign(context, params, "context"); -} - -dynamic runtime::ExecutionContextCreatedNotification::toDynamic() const { - dynamic params = dynamic::object; - put(params, "context", context); - - dynamic obj = dynamic::object; - put(obj, "method", method); - put(obj, "params", std::move(params)); - return obj; -} - -} // namespace message -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h deleted file mode 100644 index 26544df6e33..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ /dev/null @@ -1,1129 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. -// @generated SignedSource<<0c28a6bdf52b46bbad601080f35e98ee>> - -#pragma once - -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { -namespace message { - -template -void deleter(T *p); -struct UnknownRequest; - -namespace debugger { -using BreakpointId = std::string; -struct BreakpointResolvedNotification; -struct CallFrame; -using CallFrameId = std::string; -struct DisableRequest; -struct EnableRequest; -struct EvaluateOnCallFrameRequest; -struct EvaluateOnCallFrameResponse; -struct Location; -struct PauseRequest; -struct PausedNotification; -struct RemoveBreakpointRequest; -struct ResumeRequest; -struct ResumedNotification; -struct Scope; -struct ScriptParsedNotification; -struct SetBreakpointByUrlRequest; -struct SetBreakpointByUrlResponse; -struct SetBreakpointRequest; -struct SetBreakpointResponse; -struct SetBreakpointsActiveRequest; -struct SetInstrumentationBreakpointRequest; -struct SetInstrumentationBreakpointResponse; -struct SetPauseOnExceptionsRequest; -struct StepIntoRequest; -struct StepOutRequest; -struct StepOverRequest; -} // namespace debugger - -namespace runtime { -struct CallArgument; -struct CallFrame; -struct CallFunctionOnRequest; -struct CallFunctionOnResponse; -struct CompileScriptRequest; -struct CompileScriptResponse; -struct ConsoleAPICalledNotification; -struct CustomPreview; -struct EntryPreview; -struct EvaluateRequest; -struct EvaluateResponse; -struct ExceptionDetails; -struct ExecutionContextCreatedNotification; -struct ExecutionContextDescription; -using ExecutionContextId = int; -struct GetHeapUsageRequest; -struct GetHeapUsageResponse; -struct GetPropertiesRequest; -struct GetPropertiesResponse; -struct GlobalLexicalScopeNamesRequest; -struct GlobalLexicalScopeNamesResponse; -struct InternalPropertyDescriptor; -struct ObjectPreview; -struct PropertyDescriptor; -struct PropertyPreview; -struct RemoteObject; -using RemoteObjectId = std::string; -struct RunIfWaitingForDebuggerRequest; -using ScriptId = std::string; -struct StackTrace; -using Timestamp = double; -using UnserializableValue = std::string; -} // namespace runtime - -namespace heapProfiler { -struct AddHeapSnapshotChunkNotification; -struct CollectGarbageRequest; -struct GetHeapObjectIdRequest; -struct GetHeapObjectIdResponse; -struct GetObjectByHeapObjectIdRequest; -struct GetObjectByHeapObjectIdResponse; -using HeapSnapshotObjectId = std::string; -struct HeapStatsUpdateNotification; -struct LastSeenObjectIdNotification; -struct ReportHeapSnapshotProgressNotification; -struct SamplingHeapProfile; -struct SamplingHeapProfileNode; -struct SamplingHeapProfileSample; -struct StartSamplingRequest; -struct StartTrackingHeapObjectsRequest; -struct StopSamplingRequest; -struct StopSamplingResponse; -struct StopTrackingHeapObjectsRequest; -struct TakeHeapSnapshotRequest; -} // namespace heapProfiler - -namespace profiler { -struct PositionTickInfo; -struct Profile; -struct ProfileNode; -struct StartRequest; -struct StopRequest; -struct StopResponse; -} // namespace profiler - -/// RequestHandler handles requests via the visitor pattern. -struct RequestHandler { - virtual ~RequestHandler() = default; - - virtual void handle(const UnknownRequest &req) = 0; - virtual void handle(const debugger::DisableRequest &req) = 0; - virtual void handle(const debugger::EnableRequest &req) = 0; - virtual void handle(const debugger::EvaluateOnCallFrameRequest &req) = 0; - virtual void handle(const debugger::PauseRequest &req) = 0; - virtual void handle(const debugger::RemoveBreakpointRequest &req) = 0; - virtual void handle(const debugger::ResumeRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointByUrlRequest &req) = 0; - virtual void handle(const debugger::SetBreakpointsActiveRequest &req) = 0; - virtual void handle( - const debugger::SetInstrumentationBreakpointRequest &req) = 0; - virtual void handle(const debugger::SetPauseOnExceptionsRequest &req) = 0; - virtual void handle(const debugger::StepIntoRequest &req) = 0; - virtual void handle(const debugger::StepOutRequest &req) = 0; - virtual void handle(const debugger::StepOverRequest &req) = 0; - virtual void handle(const heapProfiler::CollectGarbageRequest &req) = 0; - virtual void handle(const heapProfiler::GetHeapObjectIdRequest &req) = 0; - virtual void handle( - const heapProfiler::GetObjectByHeapObjectIdRequest &req) = 0; - virtual void handle(const heapProfiler::StartSamplingRequest &req) = 0; - virtual void handle( - const heapProfiler::StartTrackingHeapObjectsRequest &req) = 0; - virtual void handle(const heapProfiler::StopSamplingRequest &req) = 0; - virtual void handle( - const heapProfiler::StopTrackingHeapObjectsRequest &req) = 0; - virtual void handle(const heapProfiler::TakeHeapSnapshotRequest &req) = 0; - virtual void handle(const profiler::StartRequest &req) = 0; - virtual void handle(const profiler::StopRequest &req) = 0; - virtual void handle(const runtime::CallFunctionOnRequest &req) = 0; - virtual void handle(const runtime::CompileScriptRequest &req) = 0; - 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; -}; - -/// NoopRequestHandler can be subclassed to only handle some requests. -struct NoopRequestHandler : public RequestHandler { - void handle(const UnknownRequest &req) override {} - void handle(const debugger::DisableRequest &req) override {} - void handle(const debugger::EnableRequest &req) override {} - void handle(const debugger::EvaluateOnCallFrameRequest &req) override {} - void handle(const debugger::PauseRequest &req) override {} - void handle(const debugger::RemoveBreakpointRequest &req) override {} - void handle(const debugger::ResumeRequest &req) override {} - void handle(const debugger::SetBreakpointRequest &req) override {} - void handle(const debugger::SetBreakpointByUrlRequest &req) override {} - void handle(const debugger::SetBreakpointsActiveRequest &req) override {} - void handle( - const debugger::SetInstrumentationBreakpointRequest &req) override {} - void handle(const debugger::SetPauseOnExceptionsRequest &req) override {} - void handle(const debugger::StepIntoRequest &req) override {} - void handle(const debugger::StepOutRequest &req) override {} - void handle(const debugger::StepOverRequest &req) override {} - void handle(const heapProfiler::CollectGarbageRequest &req) override {} - void handle(const heapProfiler::GetHeapObjectIdRequest &req) override {} - void handle( - const heapProfiler::GetObjectByHeapObjectIdRequest &req) override {} - void handle(const heapProfiler::StartSamplingRequest &req) override {} - void handle( - const heapProfiler::StartTrackingHeapObjectsRequest &req) override {} - void handle(const heapProfiler::StopSamplingRequest &req) override {} - void handle( - const heapProfiler::StopTrackingHeapObjectsRequest &req) override {} - void handle(const heapProfiler::TakeHeapSnapshotRequest &req) override {} - void handle(const profiler::StartRequest &req) override {} - void handle(const profiler::StopRequest &req) override {} - void handle(const runtime::CallFunctionOnRequest &req) override {} - void handle(const runtime::CompileScriptRequest &req) override {} - 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 {} -}; - -/// Types -struct debugger::Location : public Serializable { - Location() = default; - Location(Location &&) = default; - Location(const Location &) = delete; - explicit Location(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - Location &operator=(const Location &) = delete; - Location &operator=(Location &&) = default; - - runtime::ScriptId scriptId{}; - int lineNumber{}; - std::optional columnNumber; -}; - -struct runtime::PropertyPreview : public Serializable { - PropertyPreview() = default; - PropertyPreview(PropertyPreview &&) = default; - PropertyPreview(const PropertyPreview &) = delete; - explicit PropertyPreview(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - PropertyPreview &operator=(const PropertyPreview &) = delete; - PropertyPreview &operator=(PropertyPreview &&) = default; - - std::string name; - std::string type; - std::optional value; - std::unique_ptr< - runtime::ObjectPreview, - std::function> - valuePreview{nullptr, deleter}; - std::optional subtype; -}; - -struct runtime::EntryPreview : public Serializable { - EntryPreview() = default; - EntryPreview(EntryPreview &&) = default; - EntryPreview(const EntryPreview &) = delete; - explicit EntryPreview(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - EntryPreview &operator=(const EntryPreview &) = delete; - EntryPreview &operator=(EntryPreview &&) = default; - - std::unique_ptr< - runtime::ObjectPreview, - std::function> - key{nullptr, deleter}; - std::unique_ptr< - runtime::ObjectPreview, - std::function> - value{nullptr, deleter}; -}; - -struct runtime::ObjectPreview : public Serializable { - ObjectPreview() = default; - ObjectPreview(ObjectPreview &&) = default; - ObjectPreview(const ObjectPreview &) = delete; - explicit ObjectPreview(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - ObjectPreview &operator=(const ObjectPreview &) = delete; - ObjectPreview &operator=(ObjectPreview &&) = default; - - std::string type; - std::optional subtype; - std::optional description; - bool overflow{}; - std::vector properties; - std::optional> entries; -}; - -struct runtime::CustomPreview : public Serializable { - CustomPreview() = default; - CustomPreview(CustomPreview &&) = default; - CustomPreview(const CustomPreview &) = delete; - explicit CustomPreview(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - CustomPreview &operator=(const CustomPreview &) = delete; - CustomPreview &operator=(CustomPreview &&) = default; - - std::string header; - std::optional bodyGetterId; -}; - -struct runtime::RemoteObject : public Serializable { - RemoteObject() = default; - RemoteObject(RemoteObject &&) = default; - RemoteObject(const RemoteObject &) = delete; - explicit RemoteObject(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - RemoteObject &operator=(const RemoteObject &) = delete; - RemoteObject &operator=(RemoteObject &&) = default; - - std::string type; - std::optional subtype; - std::optional className; - std::optional value; - std::optional unserializableValue; - std::optional description; - std::optional objectId; - std::optional preview; - std::optional customPreview; -}; - -struct runtime::CallFrame : public Serializable { - CallFrame() = default; - CallFrame(CallFrame &&) = default; - CallFrame(const CallFrame &) = delete; - explicit CallFrame(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - CallFrame &operator=(const CallFrame &) = delete; - CallFrame &operator=(CallFrame &&) = default; - - std::string functionName; - runtime::ScriptId scriptId{}; - std::string url; - int lineNumber{}; - int columnNumber{}; -}; - -struct runtime::StackTrace : public Serializable { - StackTrace() = default; - StackTrace(StackTrace &&) = default; - StackTrace(const StackTrace &) = delete; - explicit StackTrace(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - StackTrace &operator=(const StackTrace &) = delete; - StackTrace &operator=(StackTrace &&) = default; - - std::optional description; - std::vector callFrames; - std::unique_ptr parent; -}; - -struct runtime::ExceptionDetails : public Serializable { - ExceptionDetails() = default; - ExceptionDetails(ExceptionDetails &&) = default; - ExceptionDetails(const ExceptionDetails &) = delete; - explicit ExceptionDetails(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - ExceptionDetails &operator=(const ExceptionDetails &) = delete; - ExceptionDetails &operator=(ExceptionDetails &&) = default; - - int exceptionId{}; - std::string text; - int lineNumber{}; - int columnNumber{}; - std::optional scriptId; - std::optional url; - std::optional stackTrace; - std::optional exception; - std::optional executionContextId; -}; - -struct debugger::Scope : public Serializable { - Scope() = default; - Scope(Scope &&) = default; - Scope(const Scope &) = delete; - explicit Scope(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - Scope &operator=(const Scope &) = delete; - Scope &operator=(Scope &&) = default; - - std::string type; - runtime::RemoteObject object{}; - std::optional name; - std::optional startLocation; - std::optional endLocation; -}; - -struct debugger::CallFrame : public Serializable { - CallFrame() = default; - CallFrame(CallFrame &&) = default; - CallFrame(const CallFrame &) = delete; - explicit CallFrame(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - CallFrame &operator=(const CallFrame &) = delete; - CallFrame &operator=(CallFrame &&) = default; - - debugger::CallFrameId callFrameId{}; - std::string functionName; - std::optional functionLocation; - debugger::Location location{}; - std::string url; - std::vector scopeChain; - runtime::RemoteObject thisObj{}; - std::optional returnValue; -}; - -struct heapProfiler::SamplingHeapProfileNode : public Serializable { - SamplingHeapProfileNode() = default; - SamplingHeapProfileNode(SamplingHeapProfileNode &&) = default; - SamplingHeapProfileNode(const SamplingHeapProfileNode &) = delete; - explicit SamplingHeapProfileNode(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - SamplingHeapProfileNode &operator=(const SamplingHeapProfileNode &) = delete; - SamplingHeapProfileNode &operator=(SamplingHeapProfileNode &&) = default; - - runtime::CallFrame callFrame{}; - double selfSize{}; - int id{}; - std::vector children; -}; - -struct heapProfiler::SamplingHeapProfileSample : public Serializable { - SamplingHeapProfileSample() = default; - SamplingHeapProfileSample(SamplingHeapProfileSample &&) = default; - SamplingHeapProfileSample(const SamplingHeapProfileSample &) = delete; - explicit SamplingHeapProfileSample(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - SamplingHeapProfileSample &operator=(const SamplingHeapProfileSample &) = - delete; - SamplingHeapProfileSample &operator=(SamplingHeapProfileSample &&) = default; - - double size{}; - int nodeId{}; - double ordinal{}; -}; - -struct heapProfiler::SamplingHeapProfile : public Serializable { - SamplingHeapProfile() = default; - SamplingHeapProfile(SamplingHeapProfile &&) = default; - SamplingHeapProfile(const SamplingHeapProfile &) = delete; - explicit SamplingHeapProfile(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - SamplingHeapProfile &operator=(const SamplingHeapProfile &) = delete; - SamplingHeapProfile &operator=(SamplingHeapProfile &&) = default; - - heapProfiler::SamplingHeapProfileNode head{}; - std::vector samples; -}; - -struct profiler::PositionTickInfo : public Serializable { - PositionTickInfo() = default; - PositionTickInfo(PositionTickInfo &&) = default; - PositionTickInfo(const PositionTickInfo &) = delete; - explicit PositionTickInfo(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - PositionTickInfo &operator=(const PositionTickInfo &) = delete; - PositionTickInfo &operator=(PositionTickInfo &&) = default; - - int line{}; - int ticks{}; -}; - -struct profiler::ProfileNode : public Serializable { - ProfileNode() = default; - ProfileNode(ProfileNode &&) = default; - ProfileNode(const ProfileNode &) = delete; - explicit ProfileNode(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - ProfileNode &operator=(const ProfileNode &) = delete; - ProfileNode &operator=(ProfileNode &&) = default; - - int id{}; - runtime::CallFrame callFrame{}; - std::optional hitCount; - std::optional> children; - std::optional deoptReason; - std::optional> positionTicks; -}; - -struct profiler::Profile : public Serializable { - Profile() = default; - Profile(Profile &&) = default; - Profile(const Profile &) = delete; - explicit Profile(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - Profile &operator=(const Profile &) = delete; - Profile &operator=(Profile &&) = default; - - std::vector nodes; - double startTime{}; - double endTime{}; - std::optional> samples; - std::optional> timeDeltas; -}; - -struct runtime::CallArgument : public Serializable { - CallArgument() = default; - CallArgument(CallArgument &&) = default; - CallArgument(const CallArgument &) = delete; - explicit CallArgument(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - CallArgument &operator=(const CallArgument &) = delete; - CallArgument &operator=(CallArgument &&) = default; - - std::optional value; - std::optional unserializableValue; - std::optional objectId; -}; - -struct runtime::ExecutionContextDescription : public Serializable { - ExecutionContextDescription() = default; - ExecutionContextDescription(ExecutionContextDescription &&) = default; - ExecutionContextDescription(const ExecutionContextDescription &) = delete; - explicit ExecutionContextDescription(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - ExecutionContextDescription &operator=(const ExecutionContextDescription &) = - delete; - ExecutionContextDescription &operator=(ExecutionContextDescription &&) = - default; - - runtime::ExecutionContextId id{}; - std::string origin; - std::string name; - std::optional auxData; -}; - -struct runtime::PropertyDescriptor : public Serializable { - PropertyDescriptor() = default; - PropertyDescriptor(PropertyDescriptor &&) = default; - PropertyDescriptor(const PropertyDescriptor &) = delete; - explicit PropertyDescriptor(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - PropertyDescriptor &operator=(const PropertyDescriptor &) = delete; - PropertyDescriptor &operator=(PropertyDescriptor &&) = default; - - std::string name; - std::optional value; - std::optional writable; - std::optional get; - std::optional set; - bool configurable{}; - bool enumerable{}; - std::optional wasThrown; - std::optional isOwn; - std::optional symbol; -}; - -struct runtime::InternalPropertyDescriptor : public Serializable { - InternalPropertyDescriptor() = default; - InternalPropertyDescriptor(InternalPropertyDescriptor &&) = default; - InternalPropertyDescriptor(const InternalPropertyDescriptor &) = delete; - explicit InternalPropertyDescriptor(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - InternalPropertyDescriptor &operator=(const InternalPropertyDescriptor &) = - delete; - InternalPropertyDescriptor &operator=(InternalPropertyDescriptor &&) = - default; - - std::string name; - std::optional value; -}; - -/// Requests -struct UnknownRequest : public Request { - UnknownRequest(); - explicit UnknownRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::optional params; -}; - -struct debugger::DisableRequest : public Request { - DisableRequest(); - explicit DisableRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::EnableRequest : public Request { - EnableRequest(); - explicit EnableRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::EvaluateOnCallFrameRequest : public Request { - EvaluateOnCallFrameRequest(); - explicit EvaluateOnCallFrameRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - debugger::CallFrameId callFrameId{}; - std::string expression; - std::optional objectGroup; - std::optional includeCommandLineAPI; - std::optional silent; - std::optional returnByValue; - std::optional generatePreview; - std::optional throwOnSideEffect; -}; - -struct debugger::PauseRequest : public Request { - PauseRequest(); - explicit PauseRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::RemoveBreakpointRequest : public Request { - RemoveBreakpointRequest(); - explicit RemoveBreakpointRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - debugger::BreakpointId breakpointId{}; -}; - -struct debugger::ResumeRequest : public Request { - ResumeRequest(); - explicit ResumeRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::optional terminateOnResume; -}; - -struct debugger::SetBreakpointRequest : public Request { - SetBreakpointRequest(); - explicit SetBreakpointRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - debugger::Location location{}; - std::optional condition; -}; - -struct debugger::SetBreakpointByUrlRequest : public Request { - SetBreakpointByUrlRequest(); - explicit SetBreakpointByUrlRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - int lineNumber{}; - std::optional url; - std::optional urlRegex; - std::optional scriptHash; - std::optional columnNumber; - std::optional condition; -}; - -struct debugger::SetBreakpointsActiveRequest : public Request { - SetBreakpointsActiveRequest(); - explicit SetBreakpointsActiveRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - bool active{}; -}; - -struct debugger::SetInstrumentationBreakpointRequest : public Request { - SetInstrumentationBreakpointRequest(); - explicit SetInstrumentationBreakpointRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::string instrumentation; -}; - -struct debugger::SetPauseOnExceptionsRequest : public Request { - SetPauseOnExceptionsRequest(); - explicit SetPauseOnExceptionsRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::string state; -}; - -struct debugger::StepIntoRequest : public Request { - StepIntoRequest(); - explicit StepIntoRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::StepOutRequest : public Request { - StepOutRequest(); - explicit StepOutRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct debugger::StepOverRequest : public Request { - StepOverRequest(); - explicit StepOverRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::CollectGarbageRequest : public Request { - CollectGarbageRequest(); - explicit CollectGarbageRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::GetHeapObjectIdRequest : public Request { - GetHeapObjectIdRequest(); - explicit GetHeapObjectIdRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - runtime::RemoteObjectId objectId{}; -}; - -struct heapProfiler::GetObjectByHeapObjectIdRequest : public Request { - GetObjectByHeapObjectIdRequest(); - explicit GetObjectByHeapObjectIdRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - heapProfiler::HeapSnapshotObjectId objectId{}; - std::optional objectGroup; -}; - -struct heapProfiler::StartSamplingRequest : public Request { - StartSamplingRequest(); - explicit StartSamplingRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::optional samplingInterval; - std::optional includeObjectsCollectedByMajorGC; - std::optional includeObjectsCollectedByMinorGC; -}; - -struct heapProfiler::StartTrackingHeapObjectsRequest : public Request { - StartTrackingHeapObjectsRequest(); - explicit StartTrackingHeapObjectsRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::optional trackAllocations; -}; - -struct heapProfiler::StopSamplingRequest : public Request { - StopSamplingRequest(); - explicit StopSamplingRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct heapProfiler::StopTrackingHeapObjectsRequest : public Request { - StopTrackingHeapObjectsRequest(); - explicit StopTrackingHeapObjectsRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::optional reportProgress; - std::optional treatGlobalObjectsAsRoots; - std::optional captureNumericValue; -}; - -struct heapProfiler::TakeHeapSnapshotRequest : public Request { - TakeHeapSnapshotRequest(); - explicit TakeHeapSnapshotRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::optional reportProgress; - std::optional treatGlobalObjectsAsRoots; - std::optional captureNumericValue; -}; - -struct profiler::StartRequest : public Request { - StartRequest(); - explicit StartRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct profiler::StopRequest : public Request { - StopRequest(); - explicit StopRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::CallFunctionOnRequest : public Request { - CallFunctionOnRequest(); - explicit CallFunctionOnRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::string functionDeclaration; - std::optional objectId; - std::optional> arguments; - std::optional silent; - std::optional returnByValue; - std::optional generatePreview; - std::optional userGesture; - std::optional awaitPromise; - std::optional executionContextId; - std::optional objectGroup; -}; - -struct runtime::CompileScriptRequest : public Request { - CompileScriptRequest(); - explicit CompileScriptRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::string expression; - std::string sourceURL; - bool persistScript{}; - std::optional executionContextId; -}; - -struct runtime::EvaluateRequest : public Request { - EvaluateRequest(); - explicit EvaluateRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::string expression; - std::optional objectGroup; - std::optional includeCommandLineAPI; - std::optional silent; - std::optional contextId; - std::optional returnByValue; - std::optional generatePreview; - std::optional userGesture; - std::optional awaitPromise; -}; - -struct runtime::GetHeapUsageRequest : public Request { - GetHeapUsageRequest(); - explicit GetHeapUsageRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -struct runtime::GetPropertiesRequest : public Request { - GetPropertiesRequest(); - explicit GetPropertiesRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - runtime::RemoteObjectId objectId{}; - std::optional ownProperties; - std::optional generatePreview; -}; - -struct runtime::GlobalLexicalScopeNamesRequest : public Request { - GlobalLexicalScopeNamesRequest(); - explicit GlobalLexicalScopeNamesRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; - - std::optional executionContextId; -}; - -struct runtime::RunIfWaitingForDebuggerRequest : public Request { - RunIfWaitingForDebuggerRequest(); - explicit RunIfWaitingForDebuggerRequest(const folly::dynamic &obj); - - folly::dynamic toDynamic() const override; - void accept(RequestHandler &handler) const override; -}; - -/// Responses -struct ErrorResponse : public Response { - ErrorResponse() = default; - explicit ErrorResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - int code; - std::string message; - std::optional data; -}; - -struct OkResponse : public Response { - OkResponse() = default; - explicit OkResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; -}; - -struct debugger::EvaluateOnCallFrameResponse : public Response { - EvaluateOnCallFrameResponse() = default; - explicit EvaluateOnCallFrameResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct debugger::SetBreakpointResponse : public Response { - SetBreakpointResponse() = default; - explicit SetBreakpointResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - debugger::BreakpointId breakpointId{}; - debugger::Location actualLocation{}; -}; - -struct debugger::SetBreakpointByUrlResponse : public Response { - SetBreakpointByUrlResponse() = default; - explicit SetBreakpointByUrlResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - debugger::BreakpointId breakpointId{}; - std::vector locations; -}; - -struct debugger::SetInstrumentationBreakpointResponse : public Response { - SetInstrumentationBreakpointResponse() = default; - explicit SetInstrumentationBreakpointResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - debugger::BreakpointId breakpointId{}; -}; - -struct heapProfiler::GetHeapObjectIdResponse : public Response { - GetHeapObjectIdResponse() = default; - explicit GetHeapObjectIdResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - heapProfiler::HeapSnapshotObjectId heapSnapshotObjectId{}; -}; - -struct heapProfiler::GetObjectByHeapObjectIdResponse : public Response { - GetObjectByHeapObjectIdResponse() = default; - explicit GetObjectByHeapObjectIdResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - runtime::RemoteObject result{}; -}; - -struct heapProfiler::StopSamplingResponse : public Response { - StopSamplingResponse() = default; - explicit StopSamplingResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - heapProfiler::SamplingHeapProfile profile{}; -}; - -struct profiler::StopResponse : public Response { - StopResponse() = default; - explicit StopResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - profiler::Profile profile{}; -}; - -struct runtime::CallFunctionOnResponse : public Response { - CallFunctionOnResponse() = default; - explicit CallFunctionOnResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct runtime::CompileScriptResponse : public Response { - CompileScriptResponse() = default; - explicit CompileScriptResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - std::optional scriptId; - std::optional exceptionDetails; -}; - -struct runtime::EvaluateResponse : public Response { - EvaluateResponse() = default; - explicit EvaluateResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - runtime::RemoteObject result{}; - std::optional exceptionDetails; -}; - -struct runtime::GetHeapUsageResponse : public Response { - GetHeapUsageResponse() = default; - explicit GetHeapUsageResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - double usedSize{}; - double totalSize{}; -}; - -struct runtime::GetPropertiesResponse : public Response { - GetPropertiesResponse() = default; - explicit GetPropertiesResponse(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - std::vector result; - std::optional> - internalProperties; - std::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(); - explicit BreakpointResolvedNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - debugger::BreakpointId breakpointId{}; - debugger::Location location{}; -}; - -struct debugger::PausedNotification : public Notification { - PausedNotification(); - explicit PausedNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - std::vector callFrames; - std::string reason; - std::optional data; - std::optional> hitBreakpoints; - std::optional asyncStackTrace; -}; - -struct debugger::ResumedNotification : public Notification { - ResumedNotification(); - explicit ResumedNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; -}; - -struct debugger::ScriptParsedNotification : public Notification { - ScriptParsedNotification(); - explicit ScriptParsedNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - runtime::ScriptId scriptId{}; - std::string url; - int startLine{}; - int startColumn{}; - int endLine{}; - int endColumn{}; - runtime::ExecutionContextId executionContextId{}; - std::string hash; - std::optional executionContextAuxData; - std::optional sourceMapURL; - std::optional hasSourceURL; - std::optional isModule; - std::optional length; -}; - -struct heapProfiler::AddHeapSnapshotChunkNotification : public Notification { - AddHeapSnapshotChunkNotification(); - explicit AddHeapSnapshotChunkNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - std::string chunk; -}; - -struct heapProfiler::HeapStatsUpdateNotification : public Notification { - HeapStatsUpdateNotification(); - explicit HeapStatsUpdateNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - std::vector statsUpdate; -}; - -struct heapProfiler::LastSeenObjectIdNotification : public Notification { - LastSeenObjectIdNotification(); - explicit LastSeenObjectIdNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - int lastSeenObjectId{}; - double timestamp{}; -}; - -struct heapProfiler::ReportHeapSnapshotProgressNotification - : public Notification { - ReportHeapSnapshotProgressNotification(); - explicit ReportHeapSnapshotProgressNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - int done{}; - int total{}; - std::optional finished; -}; - -struct runtime::ConsoleAPICalledNotification : public Notification { - ConsoleAPICalledNotification(); - explicit ConsoleAPICalledNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - std::string type; - std::vector args; - runtime::ExecutionContextId executionContextId{}; - runtime::Timestamp timestamp{}; - std::optional stackTrace; -}; - -struct runtime::ExecutionContextCreatedNotification : public Notification { - ExecutionContextCreatedNotification(); - explicit ExecutionContextCreatedNotification(const folly::dynamic &obj); - folly::dynamic toDynamic() const override; - - runtime::ExecutionContextDescription context{}; -}; - -} // namespace message -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h deleted file mode 100644 index 5f403fe1329..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { -namespace message { - -using dynamic = folly::dynamic; - -template -using optional = std::optional; - -template -struct is_vector : std::false_type {}; - -template -struct is_vector> : std::true_type {}; - -/// valueFromDynamic - -template -typename std::enable_if::value, T>::type -valueFromDynamic(const dynamic &obj) { - return T(obj); -} - -template -typename std::enable_if::value, T>::type valueFromDynamic( - const dynamic &obj) { - return obj.asInt(); -} - -template -typename std::enable_if::value, T>::type -valueFromDynamic(const dynamic &obj) { - return obj.asDouble(); -} - -template -typename std::enable_if::value, T>::type -valueFromDynamic(const dynamic &obj) { - return obj.asString(); -} - -template -typename std::enable_if::value, T>::type -valueFromDynamic(const dynamic &obj) { - return obj; -} - -template -typename std::enable_if::value, T>::type valueFromDynamic( - const dynamic &items) { - T result; - result.reserve(items.size()); - for (const auto &item : items) { - result.push_back(valueFromDynamic(item)); - } - return result; -} - -/// assign(lhs, obj, key) is a wrapper for: -/// -/// lhs = obj[key] -/// -/// It mainly exists so that we can choose the right version of valueFromDynamic -/// based on the type of lhs. - -template -void assign(T &lhs, const dynamic &obj, const U &key) { - lhs = valueFromDynamic(obj.at(key)); -} - -template -void assign(optional &lhs, const dynamic &obj, const U &key) { - auto it = obj.find(key); - if (it != obj.items().end()) { - lhs = valueFromDynamic(it->second); - } else { - lhs.reset(); - } -} - -template -void assign(std::unique_ptr &lhs, const dynamic &obj, const U &key) { - auto it = obj.find(key); - if (it != obj.items().end()) { - lhs = std::make_unique(valueFromDynamic(it->second)); - } else { - lhs.reset(); - } -} - -template -void assign( - std::unique_ptr> &lhs, - const dynamic &obj, - const U &key) { - auto it = obj.find(key); - if (it != obj.items().end()) { - lhs = std::make_unique(valueFromDynamic(it->second)); - } else { - lhs.reset(); - } -} - -/// valueToDynamic - -inline dynamic valueToDynamic(const Serializable &value) { - return value.toDynamic(); -} - -template -typename std::enable_if::value, dynamic>::type -valueToDynamic(const T &item) { - return dynamic(item); -} - -template -dynamic valueToDynamic(const std::vector &items) { - dynamic result = dynamic::array; - for (const auto &item : items) { - result.push_back(valueToDynamic(item)); - } - return result; -} - -/// put(obj, key, value) is a wrapper for: -/// -/// obj[key] = valueToDynamic(value); - -template -void put(dynamic &obj, const K &key, const V &value) { - obj[key] = valueToDynamic(value); -} - -template -void put(dynamic &obj, const K &key, const optional &optValue) { - if (optValue.has_value()) { - obj[key] = valueToDynamic(optValue.value()); - } else { - obj.erase(key); - } -} - -template -void put(dynamic &obj, const K &key, const std::unique_ptr &ptr) { - if (ptr.get()) { - obj[key] = valueToDynamic(*ptr); - } else { - obj.erase(key); - } -} - -template -void put( - dynamic &obj, - const K &key, - const std::unique_ptr> &ptr) { - if (ptr.get()) { - obj[key] = valueToDynamic(*ptr); - } else { - obj.erase(key); - } -} - -template -void deleter(T *p) { - delete p; -} - -} // namespace message -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.cpp deleted file mode 100644 index 4560b94950c..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "Registration.h" -#include "ConnectionDemux.h" - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -namespace { - -ConnectionDemux &demux() { - static ConnectionDemux instance{facebook::react::getInspectorInstance()}; - return instance; -} - -} // namespace - -DebugSessionToken enableDebugging( - std::unique_ptr adapter, - const std::string &title) { - return demux().enableDebugging(std::move(adapter), title); -} - -void disableDebugging(DebugSessionToken session) { - demux().disableDebugging(session); -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.h deleted file mode 100644 index d4a5ef57892..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/Registration.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -using DebugSessionToken = int; - -/* - * enableDebugging adds this runtime to the list of debuggable JS targets - * (called "pages" in the higher-level React Native API) in this process. It - * should be called before any JS runs in the runtime. The returned token - * can be used to disable debugging for this runtime. - */ -extern DebugSessionToken enableDebugging( - std::unique_ptr adapter, - const std::string &title); - -/* - * disableDebugging removes this runtime from the list of debuggable JS targets - * in this process. The runtime to remove is identified by the token returned - * from enableDebugging. - */ -extern void disableDebugging(DebugSessionToken session); - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.cpp deleted file mode 100644 index 28f9f79193e..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "RemoteObjectsTable.h" - -#include - -#include - -namespace { - -bool isScopeId(int64_t id) { - return id < 0; -} - -bool isValueId(int64_t id) { - return id > 0; -} - -std::string toObjId(int64_t id) { - return folly::to(id); -} - -int64_t toId(const std::string &objId) { - return atoll(objId.c_str()); -} - -} // namespace - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -const char *BacktraceObjectGroup = "backtrace"; - -const char *ConsoleObjectGroup = "console"; - -RemoteObjectsTable::RemoteObjectsTable() = default; - -RemoteObjectsTable::~RemoteObjectsTable() = default; - -std::string RemoteObjectsTable::addScope( - std::pair frameAndScopeIndex, - const std::string &objectGroup) { - int64_t id = scopeId_--; - scopes_[id] = frameAndScopeIndex; - - if (!objectGroup.empty()) { - idToGroup_[id] = objectGroup; - groupToIds_[objectGroup].push_back(id); - } - - return toObjId(id); -} - -std::string RemoteObjectsTable::addValue( - ::facebook::jsi::Value value, - const std::string &objectGroup) { - int64_t id = valueId_++; - values_[id] = std::move(value); - - if (!objectGroup.empty()) { - idToGroup_[id] = objectGroup; - groupToIds_[objectGroup].push_back(id); - } - - return toObjId(id); -} - -const std::pair *RemoteObjectsTable::getScope( - const std::string &objId) const { - int64_t id = toId(objId); - if (!isScopeId(id)) { - return nullptr; - } - - auto it = scopes_.find(id); - if (it == scopes_.end()) { - return nullptr; - } - - return &it->second; -} - -const ::facebook::jsi::Value *RemoteObjectsTable::getValue( - const std::string &objId) const { - int64_t id = toId(objId); - if (!isValueId(id)) { - return nullptr; - } - - auto it = values_.find(id); - if (it == values_.end()) { - return nullptr; - } - - return &it->second; -} - -std::string RemoteObjectsTable::getObjectGroup(const std::string &objId) const { - int64_t id = toId(objId); - - auto it = idToGroup_.find(id); - if (it == idToGroup_.end()) { - return ""; - } - - return it->second; -} - -void RemoteObjectsTable::releaseObject(int64_t id) { - if (isScopeId(id)) { - scopes_.erase(id); - } else if (isValueId(id)) { - values_.erase(id); - } -} - -void RemoteObjectsTable::releaseObject(const std::string &objId) { - int64_t id = toId(objId); - releaseObject(id); -} - -void RemoteObjectsTable::releaseObjectGroup(const std::string &objectGroup) { - auto it = groupToIds_.find(objectGroup); - if (it == groupToIds_.end()) { - return; - } - - const auto &ids = it->second; - for (int64_t id : ids) { - releaseObject(id); - } - - groupToIds_.erase(it); -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.h deleted file mode 100644 index 97c0c38bc39..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/RemoteObjectsTable.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -/// Well-known object group names - -/** - * Objects created as a result of the Debugger.paused notification (e.g. scope - * objects) are placed in the "backtrace" object group. This object group is - * cleared when the VM resumes. - */ -extern const char *BacktraceObjectGroup; - -/** - * Objects that are created as a result of a console evaluation are placed in - * the "console" object group. This object group is cleared when the client - * clears the console. - */ -extern const char *ConsoleObjectGroup; - -/** - * RemoteObjectsTable manages the mapping of string object ids to scope metadata - * or actual JSI objects. The debugger vends these ids to the client so that the - * client can perform operations on the ids (e.g. enumerate properties on the - * object backed by the id). See Runtime.RemoteObjectId in the CDT docs for - * more details. - * - * Note that object handles are not ref-counted. Suppose an object foo is mapped - * to object id "objId" and is also in object group "objGroup". Then *either* of - * `releaseObject("objId")` or `releaseObjectGroup("objGroup")` will remove foo - * from the table. This matches the behavior of object groups in CDT. - */ -class RemoteObjectsTable { - public: - RemoteObjectsTable(); - ~RemoteObjectsTable(); - - RemoteObjectsTable(const RemoteObjectsTable &) = delete; - RemoteObjectsTable &operator=(const RemoteObjectsTable &) = delete; - - /** - * addScope adds the provided (frameIndex, scopeIndex) mapping to the table. - * If objectGroup is non-empty, then the scope object is also added to that - * object group for releasing via releaseObjectGroup. Returns an object id. - */ - std::string addScope( - std::pair frameAndScopeIndex, - const std::string &objectGroup); - - /** - * addValue adds the JSI value to the table. If objectGroup is non-empty, then - * the scope object is also added to that object group for releasing via - * releaseObjectGroup. Returns an object id. - */ - std::string addValue( - ::facebook::jsi::Value value, - const std::string &objectGroup); - - /** - * Retrieves the (frameIndex, scopeIndex) associated with this object id, or - * nullptr if no mapping exists. The pointer stays valid as long as you only - * call const methods on this class. - */ - const std::pair *getScope(const std::string &objId) const; - - /** - * Retrieves the JSI value associated with this object id, or nullptr if no - * mapping exists. The pointer stays valid as long as you only call const - * methods on this class. - */ - const ::facebook::jsi::Value *getValue(const std::string &objId) const; - - /** - * Retrieves the object group that this object id is in, or empty string if it - * isn't in an object group. The returned pointer is only guaranteed to be - * valid until the next call to this class. - */ - std::string getObjectGroup(const std::string &objId) const; - - /** - * Removes the scope or JSI value backed by the provided object ID from the - * table. - */ - void releaseObject(const std::string &objId); - - /** - * Removes all objects that are part of the provided object group from the - * table. - */ - void releaseObjectGroup(const std::string &objectGroup); - - private: - void releaseObject(int64_t id); - - int64_t scopeId_ = -1; - int64_t valueId_ = 1; - - std::unordered_map> scopes_; - std::unordered_map values_; - std::unordered_map idToGroup_; - std::unordered_map> groupToIds_; -}; - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/cli/main.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/cli/main.cpp deleted file mode 100644 index 1dbeb999595..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/cli/main.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -using ::facebook::react::IRemoteConnection; - -namespace fbhermes = ::facebook::hermes; - -static const char *usageMessage = R"(hermes-chrome-debug-server script.js - -Uses Hermes to evaluate script.js within a debugging session. The process will -wait for Chrome DevTools Protocol requests on stdin and writes responses and -events to stdout. - -This can be used with a WebSocket bridge to host a Chrome DevTools Protocol -debug server. For instance, running this: - - websocketd --port=9999 hermes-chrome-debug-server script.js - -will run a WebSocket server on port 9999 that debugs script.js in Hermes. Chrome -can connect to this debugging session using a URL like this: - - devtools://devtools/bundled/inspector.html?experiments=false&v8only=true&ws=127.0.0.1:9999 - -Options: - - -l, --log: path to a file with pretty-printed protocol logs - -h, --help: this message -)"; - -static void usage() { - fputs(usageMessage, stderr); - exit(1); -} - -/// Truncate UTF8 string \p s to be at most \p len bytes long. If a multi-byte -/// sequence crosses the limit (len), it will be wholly omitted from the -/// output. If the output is truncated, it will be suffixed with "...". -/// -/// \pre len must be strictly greater than the length of the truncation suffix -/// (three characters), so there is something left after truncation. -static void truncate(std::string &s, size_t len) { - static constexpr char suffix[] = "..."; - static const size_t suflen = strlen(suffix); - assert(len > suflen); - - if (s.size() <= len) { - return; - } - - // Iterate back from the edge to pop off continuation bytes. - ssize_t last = len - suflen; - while (last > 0 && (s[last] & 0xC0) == 0x80) - --last; - - // Copy in the suffix. - strncpy(&s[last], suffix, suflen); - - // Trim the excess. - s.resize(last + suflen); -} - -/// Traverse the structure of \p d, truncating all the strings found, -/// (excluding object keys) to at most \p len bytes long using the definition -/// of truncate above. -static void truncateStringsIn(folly::dynamic &d, size_t len) { - switch (d.type()) { - case folly::dynamic::STRING: - truncate(d.getString(), len); - break; - - case folly::dynamic::ARRAY: - for (auto &child : d) { - truncateStringsIn(child, len); - } - break; - - case folly::dynamic::OBJECT: - for (auto &kvp : d.items()) { - truncateStringsIn(kvp.second, len); - } - break; - - default: - /* nop */ - break; - } -} - -/// Pretty print the JSON blob contained within \p str, by introducing -/// parsing it and pretty printing it. Large string values (larger than 512 -/// characters) are truncated. -/// -/// \pre str contains a valid JSON blob. -static std::string prettify(const std::string &str) { - constexpr size_t MAX_LINE_LEN = 512; - - try { - folly::dynamic obj = folly::parseJson(str); - truncateStringsIn(obj, MAX_LINE_LEN); - return folly::toPrettyJson(obj); - } catch (...) { - // pass - } - - if (str.size() > MAX_LINE_LEN) { - std::string cpy = str; - truncate(cpy, MAX_LINE_LEN); - return cpy; - } - - return str; -} - -static FILE *logFile = stderr; - -static void setLogFilePath(const char *path) { - logFile = fopen(path, "w"); - - if (logFile == nullptr) { - perror("fopen couldn't open log file"); - exit(1); - } -} - -static void log(const std::string &str, bool isReq) { - fprintf(logFile, "%s %s\n\n", isReq ? "=>" : "<=", prettify(str).c_str()); -} - -static void logRequest(const std::string &str) { - log(str, true); -} - -static void sendResponse(const std::string &str) { - log(str, false); - printf("%s\n", str.c_str()); -} - -static std::string readScriptSource(const char *path) { - std::ifstream stream(path); - return std::string{ - std::istreambuf_iterator(stream), std::istreambuf_iterator()}; -} - -static std::string getUrl(const char *path) { - char absPath[PATH_MAX] = {}; - realpath(path, absPath); - return std::string("file://") + absPath; -} - -static bool handleScriptSourceRequest( - const std::string &reqStr, - const std::string &scriptSource) { - auto req = folly::parseJson(reqStr); - - if (req.at("method") == "Debugger.getScriptSource") { - folly::dynamic result = folly::dynamic::object; - result["scriptSource"] = scriptSource; - - folly::dynamic resp = folly::dynamic::object; - resp["id"] = req.at("id"); - resp["result"] = std::move(result); - - sendResponse(folly::toJson(resp)); - - return true; - } - - return false; -} - -class RemoteConnection : public IRemoteConnection { - public: - void onMessage(std::string message) override { - sendResponse(message); - } - - void onDisconnect() override {} -}; - -static void runDebuggerLoop( - fbhermes::inspector::chrome::Connection &conn, - std::string scriptSource) { - conn.connect(std::make_unique()); - - std::string line; - while (std::getline(std::cin, line)) { - logRequest(line); - - if (!handleScriptSourceRequest(line, scriptSource)) { - conn.sendMessage(line); - } - } -} - -static void runScript(const std::string &scriptSource, const std::string &url) { - std::shared_ptr runtime( - fbhermes::makeHermesRuntime(::hermes::vm::RuntimeConfig::Builder() - .withEnableSampleProfiling(true) - .build())); - auto adapter = - std::make_unique(runtime); - fbhermes::inspector::chrome::Connection conn( - std::move(adapter), "hermes-chrome-debug-server"); - std::thread debuggerLoop(runDebuggerLoop, std::ref(conn), scriptSource); - - fbhermes::HermesRuntime::DebugFlags flags{}; - runtime->debugJavaScript(scriptSource, url, flags); - - debuggerLoop.join(); -} - -int main(int argc, char **argv) { - const char *shortOpts = "l:h"; - const option longOpts[] = { - {"log", 1, nullptr, 'l'}, - {"help", 0, nullptr, 'h'}, - {nullptr, 0, nullptr, 0}}; - - while (true) { - int opt = getopt_long(argc, argv, shortOpts, longOpts, nullptr); - if (opt == -1) { - break; - } - - switch (opt) { - case 'l': - setLogFilePath(optarg); - break; - case 'h': - usage(); - break; - default: - fprintf(stderr, "Unrecognized option: %c\n", opt); - usage(); - break; - } - } - - setbuf(logFile, nullptr); - setbuf(stdout, nullptr); - - if (optind + 1 != argc) { - usage(); - } - - const char *path = argv[optind]; - std::string scriptSource = readScriptSource(path); - std::string url = getUrl(path); - - runScript(scriptSource, url); - - fclose(logFile); - - return 0; -} diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.cpp deleted file mode 100644 index 9c630ad0121..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "AsyncHermesRuntime.h" - -#include -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -namespace detail = facebook::hermes::inspector::detail; - -AsyncHermesRuntime::AsyncHermesRuntime(bool veryLazy) - : executor_( - std::make_unique("async-hermes-runtime")) { - using namespace std::placeholders; - - auto builder = ::hermes::vm::RuntimeConfig::Builder(); - if (veryLazy) { - builder.withCompilationMode(::hermes::vm::ForceLazyCompilation); - } - runtime_ = facebook::hermes::makeHermesRuntime(builder.build()); - - runtime_->global().setProperty( - *runtime_, - "shouldStop", - jsi::Function::createFromHostFunction( - *runtime_, - jsi::PropNameID::forAscii(*runtime_, "shouldStop"), - 0, - std::bind(&AsyncHermesRuntime::shouldStop, this, _1, _2, _3, _4))); - - runtime_->global().setProperty( - *runtime_, - "storeValue", - jsi::Function::createFromHostFunction( - *runtime_, - jsi::PropNameID::forAscii(*runtime_, "storeValue"), - 0, - std::bind(&AsyncHermesRuntime::storeValue, this, _1, _2, _3, _4))); -} - -AsyncHermesRuntime::~AsyncHermesRuntime() { - stop(); - wait(); -} - -void AsyncHermesRuntime::executeScriptAsync( - const std::string &script, - const std::string &url, - HermesRuntime::DebugFlags flags) { - int scriptId = rand(); - LOG(INFO) << "AsyncHermesRuntime will execute script with id: " << scriptId - << ", contents: " << script; - - executor_->add([this, script, url, flags, scriptId] { - LOG(INFO) << "AsyncHermesRuntime executing script id " << scriptId - << " in background"; - - try { - runtime_->debugJavaScript(script, url, flags); - } catch (jsi::JSError &error) { - LOG(INFO) << "AsyncHermesRuntime JSError " << error.getMessage(); - thrownExceptions_.push_back(error.getMessage()); - } - - LOG(INFO) << "AsyncHermesRuntime finished executing script id " << scriptId; - }); -} - -void AsyncHermesRuntime::start() { - LOG(INFO) << "AsyncHermesRuntime: set stop flag false"; - stopFlag_.store(false); -} - -void AsyncHermesRuntime::stop() { - LOG(INFO) << "AsyncHermesRuntime: set stop flag true"; - stopFlag_.store(true); -} - -folly::Future AsyncHermesRuntime::getStoredValue() { - return storedValue_.getFuture(); -} - -bool AsyncHermesRuntime::hasStoredValue() { - return storedValue_.isFulfilled(); -} - -jsi::Value AsyncHermesRuntime::awaitStoredValue( - std::chrono::milliseconds timeout) { - return getStoredValue().get(timeout); -} - -void AsyncHermesRuntime::wait(std::chrono::milliseconds timeout) { - LOG(INFO) << "AsyncHermesRuntime wait requested"; - auto promise = std::make_shared>(); - auto future = promise->getFuture(); - executor_->add([promise] { - LOG(INFO) << "AsyncHermesRuntime wait resolved"; - promise->setValue(true); - }); - std::move(future).get(timeout); -} - -jsi::Value AsyncHermesRuntime::shouldStop( - jsi::Runtime &runtime, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count) { - return stopFlag_.load() ? jsi::Value(true) : jsi::Value(false); -} - -jsi::Value AsyncHermesRuntime::storeValue( - jsi::Runtime &runtime, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count) { - if (count > 0) { - storedValue_.setValue(jsi::Value(runtime, args[0])); - } - return jsi::Value(); -} - -size_t AsyncHermesRuntime::getNumberOfExceptions() { - return thrownExceptions_.size(); -} - -std::string AsyncHermesRuntime::getLastThrownExceptionMessage() { - return thrownExceptions_.back(); -} - -void AsyncHermesRuntime::registerForProfilingInExecutor() { - // Sampling profiler registration needs to happen in the thread where JS runs. - folly::via(executor_.get(), [runtime = runtime_]() { - runtime->registerForProfiling(); - }); - - // Wait until the executor is registered for profiling. - wait(); -} - -void AsyncHermesRuntime::unregisterForProfilingInExecutor() { - // Sampling profiler deregistration needs to happen in the thread where JS - // runs. - folly::via(executor_.get(), [runtime = runtime_]() { - runtime->unregisterForProfiling(); - }); - - // Wait until the executor is unregistered for profiling. - wait(); -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.h deleted file mode 100644 index 8772b14b62f..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/AsyncHermesRuntime.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -/** - * AsyncHermesRuntime is a helper class that runs JS scripts in a Hermes VM on - * a separate thread. This is useful for tests that want to test running JS - * in a multithreaded environment. - */ -class AsyncHermesRuntime { - public: - // Create a runtime. If veryLazy, configure the runtime to use completely - // lazy compilation. - AsyncHermesRuntime(bool veryLazy = false); - ~AsyncHermesRuntime(); - - std::shared_ptr runtime() { - return runtime_; - } - - /** - * stop sets the stop flag on this instance. JS scripts can get the current - * value of the stop flag by calling the global shouldStop() function. - */ - void stop(); - - /** - * start unsets the stop flag on this instance. JS scripts can get the current - * value of the stop flag by calling the global shouldStop() function. - */ - void start(); - - /** - * getStoredValue returns a future that is fulfilled with the value passed in - * to storeValue() by the JS script. - */ - folly::Future getStoredValue(); - - /** - * hasStoredValue returns whether or not a value has been stored yet - */ - bool hasStoredValue(); - - /** - * awaitStoredValue is a helper for getStoredValue that returns the value - * synchronously rather than in a future. - */ - jsi::Value awaitStoredValue( - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - /** - * executeScriptAsync evaluates JS in the underlying Hermes runtime on a - * separate thread. - * - * This method should be called at most once during the lifetime of an - * AsyncHermesRuntime instance. - */ - void executeScriptAsync( - const std::string &str, - const std::string &url = "url", - facebook::hermes::HermesRuntime::DebugFlags flags = - facebook::hermes::HermesRuntime::DebugFlags{}); - - /** - * wait blocks until all previous executeScriptAsync calls finish. - */ - void wait( - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - /** - * returns the number of thrown exceptions. - */ - size_t getNumberOfExceptions(); - - /** - * returns the message of the last thrown exception. - */ - std::string getLastThrownExceptionMessage(); - - /** - * registers the runtime for profiling in the executor thread. - */ - void registerForProfilingInExecutor(); - - /** - * unregisters the runtime for profiling in the executor thread. - */ - void unregisterForProfilingInExecutor(); - - private: - jsi::Value shouldStop( - jsi::Runtime &runtime, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count); - - jsi::Value storeValue( - jsi::Runtime &runtime, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count); - - std::shared_ptr runtime_; - std::unique_ptr executor_; - std::atomic stopFlag_{}; - folly::Promise storedValue_; - std::vector thrownExceptions_; -}; - -/// RAII-style class dealing with sampling profiler registration in tests. This -/// is especially important in tests -- if any test failure is caused by an -/// uncaught exception, stack unwinding will destroy a VM registered for -/// profiling in a thread that's not the one where registration happened, which -/// will lead to a hermes fatal error. Using this RAII class ensure that the -/// proper test failure cause is reported. -struct SamplingProfilerRAII { - explicit SamplingProfilerRAII(AsyncHermesRuntime &rt) : runtime_(rt) { - runtime_.registerForProfilingInExecutor(); - } - - ~SamplingProfilerRAII() { - runtime_.unregisterForProfilingInExecutor(); - } - - AsyncHermesRuntime &runtime_; -}; -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionDemuxTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionDemuxTests.cpp deleted file mode 100644 index 129e0658eec..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionDemuxTests.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -using ::facebook::react::IInspector; -using ::facebook::react::InspectorPage; -using ::facebook::react::IRemoteConnection; - -namespace { - -std::unordered_map makePageMap( - const std::vector &pages) { - std::unordered_map pageMap; - - for (auto &page : pages) { - pageMap[page.id] = page.title; - } - - return pageMap; -} - -void expectPages( - IInspector &inspector, - const std::unordered_map &expected) { - auto pages = makePageMap(inspector.getPages()); - EXPECT_EQ(pages, expected); -} - -class TestRemoteConnection : public IRemoteConnection { - public: - class Data { - public: - void expectDisconnected() { - std::unique_lock lock(mutex_); - cv_.wait_for( - lock, std::chrono::milliseconds(2500), [&] { return !connected_; }); - EXPECT_FALSE(connected_); - } - - void setDisconnected() { - std::scoped_lock lock(mutex_); - connected_ = false; - cv_.notify_one(); - } - - private: - std::mutex mutex_; - std::condition_variable cv_; - bool connected_{true}; - }; - - TestRemoteConnection() : data_(std::make_shared()) {} - ~TestRemoteConnection() {} - - void onMessage(std::string message) override {} - - void onDisconnect() override { - data_->setDisconnected(); - } - - std::shared_ptr getData() { - return data_; - } - - private: - std::shared_ptr data_; -}; - -}; // namespace - -TEST(ConnectionDemuxTests, TestEnableDisable) { - std::shared_ptr runtime1( - facebook::hermes::makeHermesRuntime()); - std::shared_ptr runtime2( - facebook::hermes::makeHermesRuntime()); - auto inspector = facebook::react::makeTestInspectorInstance(); - - ConnectionDemux demux{*inspector}; - - int id1 = demux.enableDebugging( - std::make_unique(runtime1), "page1"); - int id2 = demux.enableDebugging( - std::make_unique(runtime2), "page2"); - - expectPages(*inspector, {{id1, "page1"}, {id2, "page2"}}); - - auto remoteConn1 = std::make_unique(); - auto remoteData1 = remoteConn1->getData(); - auto localConn1 = inspector->connect(id1, std::move(remoteConn1)); - EXPECT_NE(localConn1.get(), nullptr); - - { - // If we connect to the same page id again without disconnecting, we should - // get null - auto remoteConn = std::make_unique(); - auto localConn = inspector->connect(id1, std::move(remoteConn)); - EXPECT_EQ(localConn.get(), nullptr); - } - - auto remoteConn2 = std::make_unique(); - auto remoteData2 = remoteConn2->getData(); - auto localConn2 = inspector->connect(id2, std::move(remoteConn2)); - EXPECT_NE(localConn2.get(), nullptr); - - // Disable debugging on runtime2. This should remove its page from the list - // and call onDisconnect on its remoteConn - demux.disableDebugging(id2); - expectPages(*inspector, {{id1, "page1"}}); - remoteData2->expectDisconnected(); - - // Disconnect conn1. Its page should still be in the page list and - // onDisconnect should be called. - localConn1->disconnect(); - remoteData1->expectDisconnected(); - - { - // Should still be able to reconnect after disconnecting - auto remoteConn = std::make_unique(); - auto localConn = inspector->connect(id1, std::move(remoteConn)); - EXPECT_NE(localConn.get(), nullptr); - } -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp deleted file mode 100644 index ce3c4ff070e..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp +++ /dev/null @@ -1,2955 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "AsyncHermesRuntime.h" -#include "SyncConnection.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -namespace m = ::facebook::hermes::inspector::chrome::message; - -using namespace std::chrono_literals; - -namespace { - -// This class mostly exists to call AsyncHermesRuntime::wait() before we -// destruct either AsyncHermesRuntime or SyncConnection. -// -// The reason for this is that we need to make sure the runtime is finished -// executing scripts before we destruct the debugger connection. Otherwise, if -// we destruct the connection while scripts are still executing, the script -// could perform an action (like hitting a breakpoint) that sends a message to -// the already-deallocated connection. -class TestContext { - public: - TestContext(bool waitForDebugger = false, bool veryLazy = false) - : runtime_(veryLazy), conn_(runtime_.runtime(), waitForDebugger) {} - ~TestContext() { - runtime_.wait(); - } - - AsyncHermesRuntime &runtime() { - return runtime_; - } - - SyncConnection &conn() { - return conn_; - } - - private: - AsyncHermesRuntime runtime_; - SyncConnection conn_; -}; - -template -ResponseType expectResponse(SyncConnection &conn, int id) { - ResponseType resp; - - conn.waitForResponse([id, &resp](const std::string &str) { - resp = ResponseType(folly::parseJson(str)); - EXPECT_EQ(resp.id, id); - }); - - return resp; -} - -template -NotificationType expectNotification(SyncConnection &conn) { - NotificationType note; - - conn.waitForNotification([¬e](const std::string &str) { - std::string parseError; - - try { - note = NotificationType(folly::parseJson(str)); - } catch (const std::exception &e) { - parseError = e.what(); - parseError += " (json: " + str + ")"; - } - - EXPECT_EQ(parseError, ""); - }); - - return note; -} - -class UnexpectedNotificationException : public std::runtime_error { - public: - UnexpectedNotificationException() - : std::runtime_error("unexpected notification") {} -}; - -void expectNothing(SyncConnection &conn) { - auto promise = std::make_shared>(); - auto future = promise->getFuture(); - try { - conn.waitForNotification([promise](const std::string &str) { - // if we receive a value fail the promise - promise->setException(UnexpectedNotificationException()); - }); - } catch (...) { - // if no values are received it times out with an exception - // so we can say that we've succeeded at seeing nothing - promise->setValue(); - } - // timeout is 2500 milliseconds in SyncConnection::waitForNotification - // so this value is mostly a redundant safety measure - std::move(future).get(3000ms); -} - -struct FrameInfo { - FrameInfo(const std::string &functionName, int lineNumber, int scopeCount) - : functionName(functionName), - lineNumberMin(lineNumber), - lineNumberMax(lineNumber), - scopeCount(scopeCount), - columnNumber(debugger::kInvalidLocation) {} - - FrameInfo &setLineNumberMax(int lineNumberMaxParam) { - lineNumberMax = lineNumberMaxParam; - return *this; - } - - FrameInfo &setScriptId(const std::string &scriptIdParam) { - scriptId = scriptIdParam; - return *this; - } - - FrameInfo &setColumnNumber(int columnNumberParam) { - columnNumber = columnNumberParam; - return *this; - } - - std::string functionName; - int lineNumberMin; - int lineNumberMax; - int scopeCount; - int columnNumber; - std::string scriptId; -}; - -void expectCallFrames( - const std::vector &frames, - const std::vector &infos) { - EXPECT_EQ(frames.size(), infos.size()); - - int i = 0; - for (const FrameInfo &info : infos) { - const m::debugger::CallFrame &frame = frames[i]; - - EXPECT_EQ(frame.callFrameId, folly::to(i)); - EXPECT_EQ(frame.functionName, info.functionName); - EXPECT_GE(frame.location.lineNumber, info.lineNumberMin); - EXPECT_LE(frame.location.lineNumber, info.lineNumberMax); - - if (info.columnNumber != debugger::kInvalidLocation) { - EXPECT_EQ(frame.location.columnNumber, info.columnNumber); - } - - if (info.scriptId.size() > 0) { - EXPECT_EQ(frame.location.scriptId, info.scriptId); - } - - // TODO: make expectation more specific once Hermes gives us something other - // than kInvalidBreakpoint for the file id - EXPECT_FALSE(frame.location.scriptId.empty()); - - if (info.scopeCount > 0) { - EXPECT_EQ(frame.scopeChain.size(), info.scopeCount); - - for (int j = 0; j < info.scopeCount; j++) { - EXPECT_TRUE(frame.scopeChain[j].object.objectId.has_value()); - } - } - - i++; - } -} - -// Helper to send a request with no params and wait for a response (defaults -// to empty) containing the req id. -template -ResponseType send(SyncConnection &conn, int id) { - RequestType req; - req.id = id; - conn.send(req.toJson()); - - return expectResponse(conn, id); -} - -template -ResponseType send(SyncConnection &conn, RequestType req) { - conn.send(req.toJson()); - return expectResponse(conn, req.id); -} - -void sendRuntimeEvalRequest( - SyncConnection &conn, - int id, - const std::string &expression) { - m::runtime::EvaluateRequest req; - req.id = id; - req.expression = expression; - req.generatePreview = true; - conn.send(req.toJson()); -} - -void sendEvalRequest( - SyncConnection &conn, - int id, - int callFrameId, - const std::string &expression) { - m::debugger::EvaluateOnCallFrameRequest req; - req.id = id; - req.callFrameId = folly::to(callFrameId); - req.expression = expression; - req.generatePreview = true; - conn.send(req.toJson()); -} - -m::runtime::ExecutionContextCreatedNotification expectExecutionContextCreated( - SyncConnection &conn) { - auto note = - expectNotification(conn); - - EXPECT_EQ(note.context.id, 1); - EXPECT_EQ(note.context.origin, ""); - EXPECT_EQ(note.context.name, "hermes"); - - return note; -} - -m::debugger::ScriptParsedNotification expectScriptParsed( - SyncConnection &conn, - const std::string &url, - const std::string &sourceMapURL) { - auto note = expectNotification(conn); - - EXPECT_EQ(note.url, url); - EXPECT_GT(note.scriptId.size(), 0); - - if (sourceMapURL.empty()) { - EXPECT_FALSE(note.sourceMapURL.has_value()); - } else { - EXPECT_EQ(note.sourceMapURL.value(), sourceMapURL); - } - - return note; -} - -m::debugger::PausedNotification expectPaused( - SyncConnection &conn, - const std::string &reason, - const std::vector &infos) { - auto note = expectNotification(conn); - - EXPECT_EQ(note.reason, reason); - expectCallFrames(note.callFrames, infos); - // TODO: check breakpoint location for pause once hermes gives that to us - - return note; -} - -m::debugger::BreakpointId expectBreakpointResponse( - SyncConnection &conn, - int id, - int line, - int resolvedLine) { - auto resp = expectResponse(conn, id); - - EXPECT_EQ(resp.id, id); - EXPECT_FALSE(resp.breakpointId.empty()); - EXPECT_NE( - resp.breakpointId, - folly::to(facebook::hermes::debugger::kInvalidBreakpoint)); - - if (line == -1) { - EXPECT_EQ(resp.locations.size(), 0); - } else { - EXPECT_EQ(resp.locations.size(), 1); - EXPECT_EQ(resp.locations[0].lineNumber, resolvedLine); - } - - return resp.breakpointId; -} - -void expectEvalResponse( - SyncConnection &conn, - int id, - const char *expectedValue) { - auto resp = - expectResponse(conn, id); - - EXPECT_EQ(resp.id, id); - EXPECT_EQ(resp.result.type, "string"); - EXPECT_EQ(resp.result.value, expectedValue); - EXPECT_FALSE(resp.exceptionDetails.has_value()); -} - -void expectEvalResponse(SyncConnection &conn, int id, bool expectedValue) { - auto resp = - expectResponse(conn, id); - - EXPECT_EQ(resp.id, id); - EXPECT_EQ(resp.result.type, "boolean"); - EXPECT_EQ(resp.result.value, expectedValue); - EXPECT_FALSE(resp.exceptionDetails.has_value()); -} - -void expectEvalResponse(SyncConnection &conn, int id, int expectedValue) { - auto resp = - expectResponse(conn, id); - - EXPECT_EQ(resp.id, id); - EXPECT_EQ(resp.result.type, "number"); - EXPECT_EQ(resp.result.value, expectedValue); - EXPECT_FALSE(resp.exceptionDetails.has_value()); -} - -void expectEvalException( - SyncConnection &conn, - int id, - const std::string &exceptionText, - const std::vector infos) { - auto resp = - expectResponse(conn, id); - - EXPECT_EQ(resp.id, id); - EXPECT_TRUE(resp.exceptionDetails.has_value()); - - m::runtime::ExceptionDetails &details = resp.exceptionDetails.value(); - EXPECT_EQ(details.text, exceptionText); - - // TODO: Hermes doesn't seem to populate the line number for the exception? - EXPECT_EQ(details.lineNumber, 0); - - EXPECT_TRUE(details.stackTrace.has_value()); - - m::runtime::StackTrace &stackTrace = details.stackTrace.value(); - EXPECT_EQ(stackTrace.callFrames.size(), infos.size()); - - int i = 0; - for (const FrameInfo &info : infos) { - const m::runtime::CallFrame &callFrame = stackTrace.callFrames[i]; - - EXPECT_GE(callFrame.lineNumber, info.lineNumberMin); - EXPECT_LE(callFrame.lineNumber, info.lineNumberMax); - EXPECT_EQ(callFrame.functionName, info.functionName); - - i++; - } -} - -struct PropInfo { - PropInfo(const std::string &type) : type(type) {} - - PropInfo &setSubtype(const std::string &subtypeParam) { - subtype = subtypeParam; - return *this; - } - - PropInfo &setValue(const folly::dynamic &valueParam) { - value = valueParam; - return *this; - } - - PropInfo &setUnserializableValue( - const std::string &unserializableValueParam) { - unserializableValue = unserializableValueParam; - return *this; - } - - std::string type; - std::optional subtype; - std::optional value; - std::optional unserializableValue; -}; - -std::unordered_map expectProps( - SyncConnection &conn, - int msgId, - const std::string &objectId, - const std::unordered_map &infos, - bool ownProperties = true) { - m::runtime::GetPropertiesRequest req; - req.id = msgId; - req.objectId = objectId; - req.ownProperties = ownProperties; - conn.send(req.toJson()); - - std::unordered_map objectIds; - auto resp = expectResponse(conn, msgId); - - EXPECT_EQ(resp.result.size(), infos.size()); - - for (int i = 0; i < resp.result.size(); i++) { - m::runtime::PropertyDescriptor &desc = resp.result[i]; - - auto infoIt = infos.find(desc.name); - EXPECT_FALSE(infoIt == infos.end()) << desc.name; - - if (infoIt != infos.end()) { - const PropInfo &info = infoIt->second; - - EXPECT_TRUE(desc.value.has_value()); - - m::runtime::RemoteObject &remoteObj = desc.value.value(); - EXPECT_EQ(remoteObj.type, info.type); - - if (info.subtype.has_value()) { - EXPECT_TRUE(remoteObj.subtype.has_value()); - EXPECT_EQ(remoteObj.subtype.value(), info.subtype.value()); - } - - if (info.value.has_value()) { - EXPECT_TRUE(remoteObj.value.has_value()); - EXPECT_EQ(remoteObj.value.value(), info.value.value()); - } - - if (info.unserializableValue.has_value()) { - EXPECT_TRUE(remoteObj.unserializableValue.has_value()); - EXPECT_EQ( - remoteObj.unserializableValue.value(), - info.unserializableValue.value()); - } - - if ((info.type == "object" && info.subtype != "null") || - info.type == "function") { - EXPECT_TRUE(remoteObj.objectId.has_value()); - objectIds[desc.name] = remoteObj.objectId.value(); - } - } - } - - return objectIds; -} - -void expectEvalResponse( - SyncConnection &conn, - int id, - const std::unordered_map &infos) { - auto resp = - expectResponse(conn, id); - - EXPECT_EQ(resp.id, id); - EXPECT_EQ(resp.result.type, "object"); - EXPECT_FALSE(resp.exceptionDetails.has_value()); - - EXPECT_TRUE(resp.result.objectId.has_value()); - EXPECT_TRUE(resp.result.preview.has_value()); - EXPECT_EQ(resp.result.preview->type, "object"); - - expectProps(conn, id + 1, resp.result.objectId.value(), infos); -} - -m::runtime::CallArgument makeValueCallArgument(folly::dynamic val) { - m::runtime::CallArgument ret; - ret.value = val; - return ret; -} - -m::runtime::CallArgument makeUnserializableCallArgument(std::string val) { - m::runtime::CallArgument ret; - ret.unserializableValue = std::move(val); - return ret; -} - -m::runtime::CallArgument makeObjectIdCallArgument( - m::runtime::RemoteObjectId objectId) { - m::runtime::CallArgument ret; - ret.objectId = std::move(objectId); - return ret; -} - -} // namespace - -TEST(ConnectionTests, testRespondsOkToUnknownRequests) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - - asyncRuntime.executeScriptAsync(R"( - var a = 1 + 2; - var b = a / 2; - )"); - - send(conn, 1); - expectExecutionContextCreated(conn); - expectNotification(conn); - - conn.send(R"({"id": 2, "method": "Debugger.foo"})"); - conn.send(R"({"id": 3, "method": "Debugger.bar", "params": {"a": "b"}})"); - - expectResponse(conn, 2); - expectResponse(conn, 3); -} - -TEST(ConnectionTests, testDebuggerStatement) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var a = 1 + 2; - debugger; // [1] (line 2) hit debugger statement, resume - var b = a / 2; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2) hit debugger statement, resume - expectPaused(conn, "other", {{"global", 2, 1}}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testDebuggerStatementFromPausedWaitEnable) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var a = 1 + 2; - debugger; // [1] (line 2) hit debugger statement, resume - var b = a / 2; - )"); - - // TODO: hack that gives JS the chance to run so that we end up in the - // PausedWaitEnable state. Will move the entire test to InspectorTests once - // I get around to refactoring InspectorTests. - std::this_thread::sleep_for(250ms); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2) hit debugger statement, resume - expectPaused(conn, "other", {{"global", 2, 1}}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testIsDebuggerAttached) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var a = 1 + 2; - debugger; // [1] (line 2) hit debugger statement - // [2] evaluate DebuggerInternal.isDebuggerAttached to true - var b = a / 2; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2) hit debugger statement - expectPaused(conn, "other", {{"global", 2, 1}}); - - // [2] evaluate DebuggerInternal.IsDebuggerAttached to true - sendEvalRequest(conn, 0, 0, R"("-> " + DebuggerInternal.isDebuggerAttached)"); - expectEvalResponse(conn, 0, "-> true"); - - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testStepOver) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var a = 1 + 2; - debugger; // [1] (line 2) hit debugger statement, step over - var b = a / 2; // [2] (line 3) step over - var c = a + b; // [3] (line 4) resume - var d = b - c; - var e = c * d; - var f = 10; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2): hit debugger statement, step over - expectPaused(conn, "other", {{"global", 2, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 3): step over - expectPaused(conn, "other", {{"global", 3, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [3] (line 4): resume - expectPaused(conn, "other", {{"global", 4, 1}}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testStepIn) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - function addOne(val) { - return val + 1; // [3]: resume - } - - var a = 1 + 2; - debugger; // [1] (line 6) hit debugger statement, step over - var b = addOne(a); // [2] (line 7) step in - var c = a + b; - var d = b - c; - var e = c * d; - var f = 10; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 6): hit debugger statement, step over - expectPaused(conn, "other", {{"global", 6, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 7): step in - expectPaused(conn, "other", {{"global", 7, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [3] (line 2): resume - expectPaused(conn, "other", {{"addOne", 2, 2}, {"global", 7, 1}}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testStepOut) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - function addSquares(a, b) { - var a2 = a * a; - debugger; // [1] (line 3) hit debugger statement, step over - var b2 = b * b; // [2] (line 4) step out - return a2 + b2; - } - - var c = addSquares(1, 2); // [3] (line 8) resume - var d = c * c; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 3) hit debugger statement, step over - expectPaused(conn, "other", {{"addSquares", 3, 2}, {"global", 8, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 4) step out - expectPaused(conn, "other", {{"addSquares", 4, 2}, {"global", 8, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [3] (line 8): resume - expectPaused(conn, "other", {{"global", 8, 1}}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetBreakpoint) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var a = 1 + 2; - debugger; // [1] (line 2) hit debugger statement, - // set breakpoint on line 5 - var b = a / 2; - var c = a + b; // [2] (line 5) hit breakpoint, step over - var d = b - c; // [3] (line 6) resume - var e = c * d; - var f = 10; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2) hit debugger statement, set breakpoint on line 6 - expectPaused(conn, "other", {{"global", 2, 1}}); - - m::debugger::SetBreakpointByUrlRequest req; - req.id = msgId++; - req.lineNumber = 5; - req.columnNumber = 0; - conn.send(req.toJson()); - - expectBreakpointResponse(conn, req.id, 5, 5); - - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 5) hit breakpoint, step over - expectPaused(conn, "other", {{"global", 5, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [3] (line 6) resume - expectPaused(conn, "other", {{"global", 6, 1}}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetBreakpointById) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // line 1 - Math.random(); // 2 - )"); - - send(conn, ++msgId); - expectExecutionContextCreated(conn); - auto script = expectNotification(conn); - - expectPaused(conn, "other", {{"global", 1, 1}}); - - m::debugger::SetBreakpointRequest req; - req.id = ++msgId; - req.location.scriptId = script.scriptId; - req.location.lineNumber = 2; - - conn.send(req.toJson()); - auto resp = expectResponse(conn, req.id); - EXPECT_EQ(resp.actualLocation.scriptId, script.scriptId); - EXPECT_EQ(resp.actualLocation.lineNumber, 2); - EXPECT_EQ(resp.actualLocation.columnNumber.value(), 4); - - send(conn, ++msgId); - expectNotification(conn); - - expectPaused(conn, "other", {{"global", 2, 1}}); - - send(conn, ++msgId); - expectNotification(conn); -} - -TEST(ConnectionTests, testActivateBreakpoints) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // line 1 - x=100 // 2 - debugger; // 3 - x=101; // 4 - )"); - - send(conn, ++msgId); - expectExecutionContextCreated(conn); - auto script = expectNotification(conn); - - expectPaused(conn, "other", {{"global", 1, 1}}); - - // Set breakpoint #1 - m::debugger::SetBreakpointRequest req; - req.id = ++msgId; - req.location.scriptId = script.scriptId; - req.location.lineNumber = 2; - conn.send(req.toJson()); - expectResponse(conn, req.id); - - // Set breakpoint #2 - req.id = ++msgId; - req.location.scriptId = script.scriptId; - req.location.lineNumber = 4; - conn.send(req.toJson()); - expectResponse(conn, req.id); - - // Disable breakpoints - m::debugger::SetBreakpointsActiveRequest activeReq; - activeReq.id = ++msgId; - activeReq.active = false; - conn.send(activeReq.toJson()); - expectResponse(conn, activeReq.id); - - // Resume - send(conn, ++msgId); - expectNotification(conn); - - // Expect first breakpoint to be skipped, now hitting line #3 - expectPaused(conn, "other", {{"global", 3, 1}}); - - // Re-enable breakpoints - activeReq.id = ++msgId; - activeReq.active = true; - conn.send(activeReq.toJson()); - expectResponse(conn, activeReq.id); - - // Resume and expect breakpoints to trigger again - send(conn, ++msgId); - expectNotification(conn); - expectPaused(conn, "other", {{"global", 4, 1}}); - - // Continue and exit - send(conn, ++msgId); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetBreakpointByIdWithColumnInIndenting) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // line 1 - Math.random(); // 2 - )"); - - send(conn, ++msgId); - expectExecutionContextCreated(conn); - auto script = expectNotification(conn); - - expectPaused(conn, "other", {{"global", 1, 1}}); - - m::debugger::SetBreakpointRequest req; - req.id = ++msgId; - req.location.scriptId = script.scriptId; - req.location.lineNumber = 2; - // Specify a column location *before* rather than *on* the actual location - req.location.columnNumber = 0; - - conn.send(req.toJson()); - auto resp = expectResponse(conn, req.id); - EXPECT_EQ(resp.actualLocation.scriptId, script.scriptId); - EXPECT_EQ(resp.actualLocation.lineNumber, 2); - // Check that we resolved the column to the first available location - EXPECT_EQ(resp.actualLocation.columnNumber.value(), 4); - - send(conn, ++msgId); - expectNotification(conn); - - expectPaused(conn, "other", {{"global", 2, 1}}); - - send(conn, ++msgId); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetLazyBreakpoint) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync( - R"( - var a = 1 + 2; - debugger; // [1] (line 2) hit debugger statement, - // set breakpoint on line 5 - - function foo() { - var b = a / 2; - var c = a + b; // [2] (line 7) hit breakpoint, step over - var d = b - c; // [3] (line 8) resume - var e = c * d; - var f = 10; - } - - foo(); - )", - "url"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2) hit debugger statement, set breakpoint on line 6 - expectPaused(conn, "other", {{"global", 2, 1}}); - - m::debugger::SetBreakpointByUrlRequest req; - req.id = msgId++; - req.lineNumber = 7; - req.columnNumber = 0; - conn.send(req.toJson()); - - auto breakpointId = expectBreakpointResponse(conn, req.id, 7, 7); - - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 7) hit breakpoint, step over - expectPaused(conn, "other", {{"foo", 7, 2}, {"global", 13, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [3] (line 8) resume - expectPaused(conn, "other", {{"foo", 8, 2}, {"global", 13, 1}}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetBreakpointWhileRunning) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - while (!shouldStop()) { - var a = 1; - var b = 2; - var c = a + b; // [1] (line 4) first time: step over - // [3] second time: set stop flag, resume - var d = 10; // [2] (line 6) resume - } - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // set breakpoint on line 4: "var c = ..." - m::debugger::SetBreakpointByUrlRequest req; - req.id = msgId++; - req.lineNumber = 4; - req.columnNumber = 0; - conn.send(req.toJson()); - - expectBreakpointResponse(conn, req.id, 4, 4); - - // [1] (line 4) hit breakpoint, step over - expectPaused(conn, "other", {{"global", 4, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 6) resume - expectPaused(conn, "other", {{"global", 6, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [3] (line 4) hit breakpoint again, set stop flag, resume - expectPaused(conn, "other", {{"global", 4, 1}}); - asyncRuntime.stop(); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetBreakpointConditional) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var a = 3; - debugger; // [1] (line 2) hit debugger statement, - // set conditional breakpoint on lines 4, 5 and 6 - var b = a + 5; // [2] (line 4) skip breakpoint, condition throws - var c = b - a; // [3] (line 5) skip breakpoint, condition false - var d = b - c; // [4] (line 6) hit breakpoint, condition true - var e = c * d; - var f = 10; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2) hit debugger statement, - // set conditional breakpoint on lines 4, 5 and 6 - expectPaused(conn, "other", {{"global", 2, 1}}); - - m::debugger::SetBreakpointByUrlRequest req0; - req0.id = msgId++; - req0.lineNumber = 4; - req0.condition = std::optional("throw Error('Boom!')"); - conn.send(req0.toJson()); - - expectBreakpointResponse(conn, req0.id, 4, 4); - - m::debugger::SetBreakpointByUrlRequest req1; - req1.id = msgId++; - req1.lineNumber = 5; - req1.condition = std::optional("b === a"); - conn.send(req1.toJson()); - - expectBreakpointResponse(conn, req1.id, 5, 5); - - m::debugger::SetBreakpointByUrlRequest req2; - req2.id = msgId++; - req2.lineNumber = 6; - req2.condition = std::optional("c === 5"); - conn.send(req2.toJson()); - - expectBreakpointResponse(conn, req2.id, 6, 6); - - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 4) skip breakpoint, condition throws - - // [3] (line 5) skip breakpoint, condition false - - // [4] (line 6) hit breakpoint, condition true - expectPaused(conn, "other", {{"global", 6, 1}}); - - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testRemoveBreakpoint) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - // [1] (line 2) hit debugger statement, set breakpoint on line 7 - debugger; - var a = 1; - - for (var i = 1; i <= 2; i++) { - // [1] (line 7) hit breakpoint and then remove it - a += i; - } - - storeValue(a); - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 2) hit debugger statement, set breakpoint on line 7 - expectPaused(conn, "other", {{"global", 2, 1}}); - - m::debugger::SetBreakpointByUrlRequest req; - req.id = msgId++; - req.lineNumber = 7; - conn.send(req.toJson()); - - auto breakpointId = expectBreakpointResponse(conn, req.id, 7, 7); - - send(conn, msgId++); - expectNotification(conn); - - // [1] (line 7) hit breakpoint and then remove it - expectPaused(conn, "other", {{"global", 7, 1}}); - - m::debugger::RemoveBreakpointRequest removeReq; - removeReq.id = msgId++; - removeReq.breakpointId = breakpointId; - conn.send(removeReq.toJson()); - expectResponse(conn, removeReq.id); - - send(conn, msgId++); - expectNotification(conn); - - // check final value - jsi::Value finalValue = asyncRuntime.awaitStoredValue(); - EXPECT_EQ(finalValue.asNumber(), 4); -} - -TEST(ConnectionTests, testAsyncPauseWhileRunning) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var accum = 10; - - while (!shouldStop()) { - var a = 1; - var b = 2; - var c = a + b; - - accum += c; - } // (line 9) - - var d = -accum; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // send some number of async pauses, make sure that we always stop before - // the end of the loop on line 9 - for (int i = 0; i < 10; i++) { - send(conn, msgId++); - expectPaused( - conn, "other", {FrameInfo("global", 0, 1).setLineNumberMax(9)}); - - send(conn, msgId++); - expectNotification(conn); - } - - // break out of loop - asyncRuntime.stop(); -} - -TEST(ConnectionTests, testEvalOnCallFrame) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var globalVar = "omega"; - var booleanVar = true; - var numberVar = 42; - var objectVar = {number: 1, bool: false, str: "string"}; - - function func1(closure, f1param) { // frame 4 - var f1v1 = "alpha"; - var f1v2 = "beta"; - function func1b() { // frame 3 - var f1bv1 = "gamma"; - function func1c() { // frame 2 - var f1cv1 = 19; - closure(); - } - func1c(); - } - func1b(); - } - - function func2() { // frame 1 - var f2v1 = "baker"; - var f2v2 = "charlie"; - function func2b() { // frame 0 - var f2bv1 = "dog"; - debugger; // [1] (line 25) hit debugger statement - // [2] run evals - // [3] resume - print(globalVar); - print(f2bv1); - } - func2b(); - } - - func1(func2, "tau"); - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 25) hit debugger statement - expectPaused( - conn, - "other", - {{"func2b", 25, 3}, - {"func2", 31, 2}, - {"func1c", 13, 4}, - {"func1b", 15, 3}, - {"func1", 17, 2}, - {"global", 34, 1}}); - - // [2] run eval statements - int frame = 0; - sendEvalRequest(conn, msgId + 0, frame, R"("0: " + globalVar)"); - sendEvalRequest(conn, msgId + 1, frame, R"("1: " + f2bv1)"); - sendEvalRequest(conn, msgId + 2, frame, R"("2: " + f2v2)"); - sendEvalRequest(conn, msgId + 3, frame, R"("3: " + f2bv1 + " && " + f2v2)"); - expectEvalResponse(conn, msgId + 0, "0: omega"); - expectEvalResponse(conn, msgId + 1, "1: dog"); - expectEvalResponse(conn, msgId + 2, "2: charlie"); - expectEvalResponse(conn, msgId + 3, "3: dog && charlie"); - msgId += 4; - - frame = 1; - sendEvalRequest(conn, msgId + 0, frame, R"("4: " + f2v1)"); - sendEvalRequest(conn, msgId + 1, frame, R"("5: " + f2v2)"); - sendEvalRequest(conn, msgId + 2, frame, R"(globalVar = "mod by debugger")"); - expectEvalResponse(conn, msgId + 0, "4: baker"); - expectEvalResponse(conn, msgId + 1, "5: charlie"); - expectEvalResponse(conn, msgId + 2, "mod by debugger"); - msgId += 3; - - frame = 2; - sendEvalRequest(conn, msgId + 0, frame, R"("6: " + f1cv1 + f1bv1 + f1v1)"); - sendEvalRequest(conn, msgId + 1, frame, R"("7: " + globalVar)"); - expectEvalResponse(conn, msgId + 0, "6: 19gammaalpha"); - expectEvalResponse(conn, msgId + 1, "7: mod by debugger"); - msgId += 2; - - // [2.1] run eval statements that return non-string primitive values - frame = 0; - sendEvalRequest(conn, msgId + 0, frame, "booleanVar"); - sendEvalRequest(conn, msgId + 1, frame, "numberVar"); - expectEvalResponse(conn, msgId + 0, true); - expectEvalResponse(conn, msgId + 1, 42); - msgId += 2; - - // [2.2] run eval statement that returns object - frame = 0; - sendEvalRequest(conn, msgId + 0, frame, "objectVar"); - expectEvalResponse( - conn, - msgId + 0, - {{"number", PropInfo("number").setValue(1)}, - {"bool", PropInfo("boolean").setValue(false)}, - {"str", PropInfo("string").setValue("string")}, - {"__proto__", PropInfo("object")}}); - - // msgId is increased by 2 because expectEvalResponse will make additional - // request with expectProps. - msgId += 2; - - // [3] resume - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testRuntimeEvaluate) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var globalVar = "omega"; - var booleanVar = true; - var numberVar = 42; - var objectVar = {number: 1, bool: false, str: "string"}; - - while(!shouldStop()) { // [1] (line 6) hit infinite loop - var a = 1; // [2] run evals - a++; // [3] exit run loop - } - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 6) hit infinite loop - - // [2] run eval statements - sendRuntimeEvalRequest(conn, msgId + 0, R"("0: " + globalVar)"); - expectEvalResponse(conn, msgId + 0, "0: omega"); - - // [2.1] run eval statements that return non-string primitive values - sendRuntimeEvalRequest(conn, msgId + 1, "booleanVar"); - sendRuntimeEvalRequest(conn, msgId + 2, "numberVar"); - expectEvalResponse(conn, msgId + 1, true); - expectEvalResponse(conn, msgId + 2, 42); - - // [2.2] run eval statement that returns object - sendRuntimeEvalRequest(conn, msgId + 3, "objectVar"); - expectEvalResponse( - conn, - msgId + 3, - {{"number", PropInfo("number").setValue(1)}, - {"bool", PropInfo("boolean").setValue(false)}, - {"str", PropInfo("string").setValue("string")}, - {"__proto__", PropInfo("object")}}); - - // [3] exit run loop - asyncRuntime.stop(); -} - -TEST(ConnectionTests, testRuntimeEvaluateReturnByValue) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync("while(!shouldStop());"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // We expect this JSON object to be evaluated and return by value, so - // that JSON encoding the result will give the same string. - auto object = "{\"key\":[1,\"two\"]}"; - auto preview = - "{\"properties\":[{\"subtype\":\"array\",\"value\":\"Array(2)\",\"type\":\"object\",\"name\":\"key\"}],\"overflow\":false,\"description\":\"Object\",\"type\":\"object\"}"; - - m::runtime::EvaluateRequest req; - req.id = msgId; - req.expression = std::string("(") + object + ")"; - req.returnByValue = true; - req.generatePreview = true; - conn.send(req.toJson()); - - auto resp = - expectResponse(conn, msgId); - EXPECT_EQ(resp.result.type, "object"); - ASSERT_TRUE(resp.result.value.has_value()); - ASSERT_TRUE(resp.result.preview.has_value()); - EXPECT_EQ(resp.result.preview->type, "object"); - EXPECT_EQ(folly::toJson(resp.result.preview->toDynamic()), preview); - EXPECT_EQ(folly::toJson(resp.result.value.value()), object); - - // [3] exit run loop - asyncRuntime.stop(); -} - -TEST(ConnectionTests, testEvalOnCallFrameException) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var count = 0; - - function eventuallyThrows(x) { - if (x <= 0) - throw new Error("I frew up."); - count++; - eventuallyThrows(x-1); - } - - function callme() { - print("Hello"); - debugger; // [1] (line 12) hit debugger statement - // [2] run evals - // [3] resume - print("Goodbye"); - } - - callme(); - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 12) hit debugger statement - expectPaused(conn, "other", {{"callme", 12, 2}, {"global", 18, 1}}); - - // [2] run evals - int frame = 0; - sendEvalRequest(conn, msgId + 0, frame, "this is not valid javascript"); - sendEvalRequest(conn, msgId + 1, frame, "eventuallyThrows(5)"); - sendEvalRequest(conn, msgId + 2, frame, "count"); - - expectEvalException(conn, msgId + 0, "SyntaxError: 1:6:';' expected", {}); - expectEvalException( - conn, - msgId + 1, - "Error: I frew up.", - {{"eventuallyThrows", 5, 0}, - {"eventuallyThrows", 7, 0}, - {"eventuallyThrows", 7, 0}, - {"eventuallyThrows", 7, 0}, - {"eventuallyThrows", 7, 0}, - {"eventuallyThrows", 7, 0}, - - // TODO: unsure why these frames are here, but they're in hdb tests - // too. Ask Hermes about if they really should be there. - FrameInfo("eval", 0, 0).setLineNumberMax(19), - FrameInfo("callme", 12, 2), - FrameInfo("global", 0, 0).setLineNumberMax(19)}); - expectEvalResponse(conn, msgId + 2, 5); - msgId += 3; - - // [3] resume - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testLoadMultipleScripts) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync( - R"( - function foo(x) { - debugger; - print(x); - } - - var a = 1 + 1; - - //# sourceMappingURL=/foo/bar/url1.js.map - )", - "url1"); - - send(conn, msgId++); - - expectExecutionContextCreated(conn); - - m::debugger::ScriptParsedNotification script1 = - expectScriptParsed(conn, "url1", "/foo/bar/url1.js.map"); - - asyncRuntime.executeScriptAsync( - R"( - var b = a + 2; - var c = b - 1; - foo(c); - - //# sourceMappingURL=/foo/bar/url2.js.map - )", - "url2"); - - m::debugger::ScriptParsedNotification script2 = - expectScriptParsed(conn, "url2", "/foo/bar/url2.js.map"); - - // [1] (line 2) hit debugger statement, resume - expectPaused( - conn, - "other", - {FrameInfo("foo", 2, 2).setScriptId(script1.scriptId), - FrameInfo("global", 3, 1).setScriptId(script2.scriptId)}); - - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testGetProperties) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - std::vector objIds; - - asyncRuntime.executeScriptAsync(R"( - function foo() { - var num = 123; - var obj = { - "depth": 0, - "value": { - "a": -1/0, - "b": 1/0, - "c": Math.sqrt(-2), - "d": -0, - "e": "e_string" - } - }; - var arr = [1, 2, 3]; - function bar() { - var num = 456; - var obj = {"depth": 1, "value": {"c": 5, "d": "d_string"}}; - debugger; - }; - bar(); - debugger; - } - - foo(); - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - auto pausedNote = expectPaused( - conn, "other", {{"bar", 17, 3}, {"foo", 19, 2}, {"global", 23, 1}}); - - auto &scopeObj = pausedNote.callFrames.at(1).scopeChain.at(0).object; - EXPECT_TRUE(scopeObj.objectId.has_value()); - std::string scopeObjId = scopeObj.objectId.value(); - objIds.push_back(scopeObjId); - - auto scopeChildren = expectProps( - conn, - msgId++, - scopeObjId, - {{"this", PropInfo("undefined")}, - {"num", PropInfo("number").setValue(123)}, - {"obj", PropInfo("object")}, - {"arr", PropInfo("object").setSubtype("array")}, - {"bar", PropInfo("function")}}); - EXPECT_EQ(scopeChildren.size(), 3); - - EXPECT_EQ(scopeChildren.count("obj"), 1); - std::string objId = scopeChildren.at("obj"); - objIds.push_back(objId); - - auto objChildren = expectProps( - conn, - msgId++, - objId, - {{"depth", PropInfo("number").setValue(0)}, - {"value", PropInfo("object")}, - {"__proto__", PropInfo("object")}}); - EXPECT_EQ(objChildren.size(), 2); - - EXPECT_EQ(objChildren.count("value"), 1); - std::string valueId = objChildren.at("value"); - objIds.push_back(valueId); - - auto valueChildren = expectProps( - conn, - msgId++, - valueId, - {{"a", PropInfo("number").setUnserializableValue("-Infinity")}, - {"b", PropInfo("number").setUnserializableValue("Infinity")}, - {"c", PropInfo("number").setUnserializableValue("NaN")}, - {"d", PropInfo("number").setUnserializableValue("-0")}, - {"e", PropInfo("string").setValue("e_string")}, - {"__proto__", PropInfo("object")}}); - EXPECT_EQ(valueChildren.size(), 1); - - send(conn, msgId++); - expectNotification(conn); - - expectPaused(conn, "other", {{"foo", 20, 2}, {"global", 23, 1}}); - - // all old object ids should be invalid after resuming - for (std::string oldObjId : objIds) { - expectProps( - conn, msgId++, oldObjId, std::unordered_map{}); - } - - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testGetPropertiesOnlyOwnProperties) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - function foo() { - var protoObject = { - "protoNum": 77 - }; - var obj = Object.create(protoObject); - obj.num = 42; - debugger; - } - foo(); - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // wait for a pause on debugger statement and get object ID from the local - // scope. - auto pausedNote = - expectPaused(conn, "other", {{"foo", 7, 2}, {"global", 9, 1}}); - const auto &scopeObject = pausedNote.callFrames.at(0).scopeChain.at(0).object; - auto scopeChildren = expectProps( - conn, - msgId++, - scopeObject.objectId.value(), - {{"this", PropInfo("undefined")}, - {"obj", PropInfo("object")}, - {"protoObject", PropInfo("object")}}); - EXPECT_EQ(scopeChildren.count("obj"), 1); - std::string objId = scopeChildren.at("obj"); - - // Check that GetProperties request for obj object only have own properties - // when onlyOwnProperties = true. - expectProps( - conn, - msgId++, - objId, - {{"num", PropInfo("number").setValue(42)}, - {"__proto__", PropInfo("object")}}, - true); - - // Check that GetProperties request for obj object only have all properties - // when onlyOwnProperties = false. - // __proto__ is not returned here because all properties from proto chain - // are already included in the result. - expectProps( - conn, - msgId++, - objId, - {{"num", PropInfo("number").setValue(42)}, - {"protoNum", PropInfo("number").setValue(77)}}, - false); - - // resume - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testDisable) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - while (!shouldStop()) { - var a = 1; - var b = 2; - var c = a + b; // [1] (line 4) disable to remove breakpoints and resume - // [2] (line 4) the breakpoint should not hit anymore - var d = 10; - } - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // set breakpoint on line 4: "var c = ..." - m::debugger::SetBreakpointByUrlRequest req; - req.id = msgId++; - req.lineNumber = 4; - conn.send(req.toJson()); - - expectBreakpointResponse(conn, req.id, 4, 4); - - // [1] (line 4) disable to remove breakpoints and resume - expectPaused(conn, "other", {{"global", 4, 1}}); - - send(conn, msgId++); - expectNotification(conn); - - // [2] (line 4) the breakpoint should not hit anymore - expectNothing(conn); - asyncRuntime.stop(); -} - -TEST(ConnectionTests, testDisableWhileRunning) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // [1] initial pause to set the breakpoint on line 6 - while (!shouldStop()) { // [2] loop running until we receive a detach request - var a = 1; - } - while (shouldStop()) { - var c = a + 1; // [3] (line 6) the breakpoint should not hit after detach - } - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] initial pause to set the breakpoint on line 6 - expectPaused(conn, "other", {{"global", 1, 1}}); - - // set breakpoint on line 6: "var c = ..." - m::debugger::SetBreakpointByUrlRequest req; - req.id = msgId++; - req.lineNumber = 6; - conn.send(req.toJson()); - - expectBreakpointResponse(conn, req.id, 6, 6); - - // [2] loop running until we receive a detach request - send(conn, msgId++); - expectNotification(conn); - - send(conn, msgId++); - asyncRuntime.stop(); - - // [3] (line 6) the breakpoint should not hit after detach - expectNothing(conn); - asyncRuntime.start(); -} - -TEST(ConnectionTests, testSetPauseOnExceptionsAll) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // [1] (line 1) initial pause, set throw on exceptions to 'All' - - try { - var a = 123; - throw new Error('Caught error'); // [2] line 5, pause on exception - } catch (err) { - // Do nothing. - } - - throw new Error('Uncaught exception'); // [3] line 10, pause on exception - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 1) initial pause, set throw on exceptions to 'All' - expectPaused(conn, "other", {{"global", 1, 1}}); - m::debugger::SetPauseOnExceptionsRequest allExceptionsReq; - allExceptionsReq.id = msgId++; - allExceptionsReq.state = "all"; - conn.send(allExceptionsReq.toJson()); - expectResponse(conn, allExceptionsReq.id); - - // Resume - send(conn, msgId++); - expectNotification(conn); - - // [2] line 5, pause on exception - expectPaused(conn, "exception", {{"global", 5, 1}}); - send(conn, msgId++); - expectNotification(conn); - - // [3] line 10, pause on exception - expectPaused(conn, "exception", {{"global", 10, 1}}); - - // Send resume event and check that Hermes has thrown an exception. - send(conn, msgId++); - expectNotification(conn); - asyncRuntime.wait(); - EXPECT_EQ(asyncRuntime.getNumberOfExceptions(), 1); - EXPECT_EQ(asyncRuntime.getLastThrownExceptionMessage(), "Uncaught exception"); -} - -TEST(ConnectionTests, testSetPauseOnExceptionsNone) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // [1] (line 1) initial pause, set throw on exceptions to 'None' - - try { - var a = 123; - throw new Error('Caught error'); - } catch (err) { - // Do nothing. - } - - throw new Error('Uncaught exception'); - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 1) initial pause, set throw on exceptions to 'None' - expectPaused(conn, "other", {{"global", 1, 1}}); - m::debugger::SetPauseOnExceptionsRequest allExceptionsReq; - allExceptionsReq.id = msgId++; - allExceptionsReq.state = "none"; - conn.send(allExceptionsReq.toJson()); - expectResponse(conn, allExceptionsReq.id); - - // Resume - send(conn, msgId++); - expectNotification(conn); - - // Check that Hermes has thrown an exception (without reporting it). - expectNothing(conn); - asyncRuntime.wait(); - EXPECT_EQ(asyncRuntime.getNumberOfExceptions(), 1); - EXPECT_EQ(asyncRuntime.getLastThrownExceptionMessage(), "Uncaught exception"); -} - -TEST(ConnectionTests, testSetPauseOnExceptionsUncaught) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // [1] (line 1) initial pause, set throw on exceptions to 'Uncaught' - - try { - var a = 123; - throw new Error('Caught error'); - } catch (err) { - // Do nothing. - } - - throw new Error('Uncaught exception'); // [3] line 10, pause on exception - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 1) initial pause, set throw on exceptions to 'Uncaught' - expectPaused(conn, "other", {{"global", 1, 1}}); - m::debugger::SetPauseOnExceptionsRequest allExceptionsReq; - allExceptionsReq.id = msgId++; - allExceptionsReq.state = "uncaught"; - conn.send(allExceptionsReq.toJson()); - expectResponse(conn, allExceptionsReq.id); - - // Resume - send(conn, msgId++); - expectNotification(conn); - - // [3] line 10, pause on exception - expectPaused(conn, "exception", {{"global", 10, 1}}); - - // Send resume event and check that Hermes has thrown an exception. - send(conn, msgId++); - expectNotification(conn); - asyncRuntime.wait(); - EXPECT_EQ(asyncRuntime.getNumberOfExceptions(), 1); - EXPECT_EQ(asyncRuntime.getLastThrownExceptionMessage(), "Uncaught exception"); -} - -TEST(ConnectionTests, invalidPauseModeGivesError) { - TestContext context; - SyncConnection &conn = context.conn(); - - m::debugger::SetPauseOnExceptionsRequest req; - req.id = 1; - req.state = "badgers"; - conn.send(req.toJson()); - expectResponse(conn, req.id); -} - -TEST(ConnectionTests, testShouldPauseOnThrow) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; // [1] (line 1) initial pause, check shouldPauseOnThrow is false - // [2] set throw to 'All', check shouldPauseOnThrow is true - // [3] set throw to 'None', check shouldPauseOnThrow is false - // [4] set throw to 'Uncaught', check shouldPauseOnThrow is true - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - auto shouldPauseOnThrowEvalMsg = - R"("-> " + DebuggerInternal.shouldPauseOnThrow)"; - auto responseTrue = "-> true"; - auto responseFalse = "-> false"; - - // [1] (line 1) initial pause, check shouldPauseOnThrow is false - expectPaused(conn, "other", {{"global", 1, 1}}); - sendEvalRequest(conn, msgId + 1, 0, shouldPauseOnThrowEvalMsg); - expectEvalResponse(conn, msgId + 1, responseFalse); - - // [2] set throw to 'All', check shouldPauseOnThrow is true - m::debugger::SetPauseOnExceptionsRequest allExceptionsReq; - allExceptionsReq.id = msgId++; - allExceptionsReq.state = "all"; - conn.send(allExceptionsReq.toJson()); - expectResponse(conn, allExceptionsReq.id); - - sendEvalRequest(conn, msgId + 1, 0, shouldPauseOnThrowEvalMsg); - expectEvalResponse(conn, msgId + 1, responseTrue); - - // [3] set throw to 'None', check shouldPauseOnThrow is false - m::debugger::SetPauseOnExceptionsRequest noExceptionsReq; - noExceptionsReq.id = msgId++; - noExceptionsReq.state = "none"; - conn.send(noExceptionsReq.toJson()); - expectResponse(conn, noExceptionsReq.id); - - sendEvalRequest(conn, msgId + 1, 0, shouldPauseOnThrowEvalMsg); - expectEvalResponse(conn, msgId + 1, responseFalse); - - // [4] set throw to 'Uncaught', check shouldPauseOnThrow is true - m::debugger::SetPauseOnExceptionsRequest uncaughtExceptionsReq; - uncaughtExceptionsReq.id = msgId++; - uncaughtExceptionsReq.state = "uncaught"; - conn.send(uncaughtExceptionsReq.toJson()); - expectResponse(conn, uncaughtExceptionsReq.id); - - sendEvalRequest(conn, msgId + 1, 0, shouldPauseOnThrowEvalMsg); - expectEvalResponse(conn, msgId + 1, responseTrue); - - // Resume - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testScopeVariables) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var globalString = "global-string"; - var globalObject = {number: 1, bool: false}; - - function func() { - var localString = "local-string"; - var localObject = {number: 2, bool: true}; - debugger; // [1] (line 7) hit debugger statement - // two local vars - localString and localObject - // two global vars - globalString and globalObject - } - - func(); // line 12 - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 7) hit debugger statement - auto pausedNote = - expectPaused(conn, "other", {{"func", 7, 2}, {"global", 12, 1}}); - const auto &scopeChain = pausedNote.callFrames.at(0).scopeChain; - EXPECT_EQ(scopeChain.size(), 2); - - // [2] inspect local scope - EXPECT_EQ(scopeChain.at(0).type, "local"); - const auto &localScopeObject = scopeChain.at(0).object; - auto localScopeObjectChildren = expectProps( - conn, - msgId++, - localScopeObject.objectId.value(), - {{"this", PropInfo("undefined")}, - {"localString", PropInfo("string").setValue("local-string")}, - {"localObject", PropInfo("object")}}); - auto localObjectId = localScopeObjectChildren.at("localObject"); - expectProps( - conn, - msgId++, - localObjectId, - {{"number", PropInfo("number").setValue(2)}, - {"bool", PropInfo("boolean").setValue(true)}, - {"__proto__", PropInfo("object")}}); - - // [3] inspect global scope - // Global scope can contain more properties than we have defined - // in our test code and we can't use expectProps() method here. - // As a workaround we create a Map of properties and check that - // those global properties that we have defined are in the map. - EXPECT_EQ(scopeChain.at(1).type, "global"); - const auto &globalScopeObject = scopeChain.at(1).object; - m::runtime::GetPropertiesRequest req; - req.id = msgId++; - req.objectId = globalScopeObject.objectId.value(); - conn.send(req.toJson()); - auto resp = expectResponse(conn, req.id); - std::unordered_map> - globalProperties; - for (auto &propertyDescriptor : resp.result) { - globalProperties[propertyDescriptor.name] = - std::move(propertyDescriptor.value); - } - EXPECT_GE(globalProperties.size(), 3); - - // globalString should be of type "string" and have value "global-string". - EXPECT_EQ(globalProperties.count("globalString"), 1); - EXPECT_TRUE(globalProperties["globalString"].has_value()); - EXPECT_EQ(globalProperties["globalString"].value().type, "string"); - EXPECT_EQ( - globalProperties["globalString"].value().value.value(), "global-string"); - - // func should be of type "function". - EXPECT_EQ(globalProperties.count("func"), 1); - EXPECT_TRUE(globalProperties["func"].has_value()); - EXPECT_EQ(globalProperties["func"].value().type, "function"); - - // globalObject should be of type "object" with "number" and "bool" - // properties. - EXPECT_EQ(globalProperties.count("globalObject"), 1); - EXPECT_TRUE(globalProperties["globalObject"].has_value()); - EXPECT_EQ(globalProperties["globalObject"].value().type, "object"); - expectProps( - conn, - msgId++, - globalProperties["globalObject"].value().objectId.value(), - {{"number", PropInfo("number").setValue(1)}, - {"bool", PropInfo("boolean").setValue(false)}, - {"__proto__", PropInfo("object")}}); - - // [4] resume - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testRuntimeCallFunctionOnObject) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // create a new Object() that will be used as "this" below. - m::runtime::RemoteObjectId thisId; - { - sendRuntimeEvalRequest(conn, msgId, "new Object()"); - auto resp = expectResponse(conn, msgId++); - ASSERT_TRUE(resp.result.objectId) << resp.toDynamic(); - thisId = *resp.result.objectId; - } - - // expectedPropInfos are properties that are expected to exist in thisId. It - // is modified by addMember (below). - std::unordered_map expectedPropInfos; - - // Add __proto__ as it always exists. - expectedPropInfos.emplace("__proto__", PropInfo("object")); - - /// addMember sends Runtime.callFunctionOn() requests with a function - /// declaration that simply adds a new property called \p propName with type - /// \p type to the remote object \p id. \p ca is the property's value. - /// The new property must not exist in \p id unless \p allowRedefinition is - /// true. - auto addMember = [&](const m::runtime::RemoteObjectId id, - const char *type, - const char *propName, - m::runtime::CallArgument ca, - bool allowRedefinition = false) { - auto it = expectedPropInfos.emplace(propName, PropInfo(type)); - - EXPECT_TRUE(allowRedefinition || it.second) - << "property \"" << propName << "\" redefined."; - - if (ca.value) { - it.first->second.setValue(*ca.value); - } - - if (ca.unserializableValue) { - it.first->second.setUnserializableValue(*ca.unserializableValue); - } - - m::runtime::CallFunctionOnRequest req; - req.id = msgId++; - req.functionDeclaration = - std::string("function(e){const r=\"") + propName + "\"; this[r]=e,r}"; - req.arguments = std::vector{}; - req.arguments->push_back(std::move(ca)); - req.objectId = thisId; - conn.send(req.toJson()); - expectResponse(conn, req.id); - }; - - addMember(thisId, "boolean", "b", makeValueCallArgument(true)); - addMember(thisId, "number", "num", makeValueCallArgument(12)); - addMember(thisId, "string", "str", makeValueCallArgument("string value")); - addMember(thisId, "object", "self_ref", makeObjectIdCallArgument(thisId)); - addMember( - thisId, "number", "inf", makeUnserializableCallArgument("Infinity")); - addMember( - thisId, "number", "ni", makeUnserializableCallArgument("-Infinity")); - addMember(thisId, "number", "nan", makeUnserializableCallArgument("NaN")); - - /// ensures that \p objId has all of the expected properties; Returns the - /// runtime::RemoteObjectId for the "self_ref" property (which must exist). - auto verifyObjShape = [&](const m::runtime::RemoteObjectId &objId) - -> std::optional { - auto objProps = expectProps(conn, msgId++, objId, expectedPropInfos); - EXPECT_TRUE(objProps.count("__proto__")); - auto objPropIt = objProps.find("self_ref"); - if (objPropIt == objProps.end()) { - EXPECT_TRUE(false) << "missing \"self_ref\" property."; - return {}; - } - return objPropIt->second; - }; - - // Verify that thisId has the correct shape. - auto selfRefId = verifyObjShape(thisId); - ASSERT_TRUE(selfRefId); - // Then verify that the self reference has the correct shape. If thisId does - // not have the "self_ref" property the call to verifyObjShape will return an - // empty Optional, as well as report an error. - selfRefId = verifyObjShape(*selfRefId); - ASSERT_TRUE(selfRefId); - - // Now we modify the self reference, which should cause thisId to change - // as well. - const bool kAllowRedefinition = true; - - addMember( - *selfRefId, - "number", - "num", - makeValueCallArgument(42), - kAllowRedefinition); - - addMember( - *selfRefId, "number", "neg_zero", makeUnserializableCallArgument("-0")); - - verifyObjShape(thisId); - - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testRuntimeCallFunctionOnExecutionContext) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - debugger; - )"); - - /// helper that returns a map with all of \p objId 's members. - auto getProps = [&msgId, &conn](const m::runtime::RemoteObjectId &objId) { - m::runtime::GetPropertiesRequest req; - req.id = msgId++; - req.objectId = objId; - conn.send(req.toJson()); - auto resp = expectResponse(conn, req.id); - std::unordered_map> - properties; - for (auto &propertyDescriptor : resp.result) { - properties[propertyDescriptor.name] = std::move(propertyDescriptor.value); - } - return properties; - }; - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // globalThisId is the inspector's object Id for globalThis. - m::runtime::RemoteObjectId globalThisId; - { - sendRuntimeEvalRequest(conn, msgId, "globalThis"); - auto resp = expectResponse(conn, msgId++); - ASSERT_TRUE(resp.result.objectId) << resp.toDynamic(); - globalThisId = *resp.result.objectId; - } - - // This test table has all of the new fields we want to add to globalThis, - // plus the Runtime.CallArgument to be sent to the inspector. - struct { - const char *propName; - m::runtime::CallArgument callArg; - } tests[] = { - {"callFunctionOnTestMember1", makeValueCallArgument(10)}, - {"callFunctionOnTestMember2", makeValueCallArgument("string")}, - {"callFunctionOnTestMember3", makeUnserializableCallArgument("NaN")}, - {"callFunctionOnTestMember4", makeUnserializableCallArgument("-0")}, - }; - - // sanity-check that our test fields don't exist in global this. - { - auto currProps = getProps(globalThisId); - for (const auto &test : tests) { - EXPECT_EQ(currProps.count(test.propName), 0) << test.propName; - } - } - - auto addMember = [&msgId, &conn]( - const char *propName, m::runtime::CallArgument &ca) { - m::runtime::CallFunctionOnRequest req; - req.id = msgId++; - req.functionDeclaration = - std::string("function(e){const r=\"") + propName + "\"; this[r]=e,r}"; - // Don't have an easy way to copy these, so... - req.arguments = std::vector{}; - req.arguments->push_back(std::move(ca)); - req.executionContextId = 1; - conn.send(req.toJson()); - expectResponse(conn, req.id); - // n.b. we're only borrowing the CallArgument, so give it back... - ca = std::move(req.arguments->at(0)); - }; - - for (auto &test : tests) { - addMember(test.propName, test.callArg); - } - - { - auto currProps = getProps(globalThisId); - for (const auto &test : tests) { - auto it = currProps.find(test.propName); - - // there should be a property named test.propName in globalThis. - ASSERT_TRUE(it != currProps.end()) << test.propName; - - // and it should have a value. - ASSERT_TRUE(it->second) << test.propName; - - if (it->second->value.has_value()) { - // the property has a value, so make sure that's what's being expected. - auto actual = it->second->value; - auto expected = test.callArg.value; - ASSERT_TRUE(expected.has_value()) << test.propName; - EXPECT_EQ(*actual, *expected) << test.propName; - } else if (it->second->unserializableValue.has_value()) { - // the property has an unserializable value, so make sure that's what's - // being expected. - auto actual = it->second->unserializableValue; - auto expected = test.callArg.unserializableValue; - ASSERT_TRUE(expected.has_value()) << test.propName; - EXPECT_EQ(*actual, *expected) << test.propName; - } else { - FAIL() << "No value or unserializable value in " << test.propName; - } - } - } - - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testConsoleLog) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var object1 = {number1: 1, bool1: false}; - var object2 = {number2: 2, bool2: true}; - console.warn('string value', object1, object2); - - debugger; // Hit debugger statement so that we receive console - // api notification before VM gets destroyed. - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // Two notifications (hitting debugger and console API call) can appear - // in any order. We wait for two notifications here and later check - // that both of them were hit. - bool receivedConsoleNotification = false; - bool receivedPausedNotification = false; - for (size_t i = 0; i < 2; ++i) { - conn.waitForNotification([&receivedConsoleNotification, - &receivedPausedNotification, - &conn, - &msgId](const std::string &str) { - auto parsedNote = folly::parseJson(str); - auto method = parsedNote.at("method").asString(); - if (method == "Runtime.consoleAPICalled") { - receivedConsoleNotification = true; - auto note = m::runtime::ConsoleAPICalledNotification(parsedNote); - EXPECT_EQ(note.type, "warning"); - EXPECT_EQ(note.args.size(), 3); - - EXPECT_EQ(note.args[0].type, "string"); - EXPECT_EQ(note.args[0].value, "string value"); - - EXPECT_EQ(note.args[1].type, "object"); - expectProps( - conn, - msgId++, - note.args[1].objectId.value(), - {{"number1", PropInfo("number").setValue(1)}, - {"bool1", PropInfo("boolean").setValue(false)}, - {"__proto__", PropInfo("object")}}); - - EXPECT_EQ(note.args[2].type, "object"); - expectProps( - conn, - msgId++, - note.args[2].objectId.value(), - {{"number2", PropInfo("number").setValue(2)}, - {"bool2", PropInfo("boolean").setValue(true)}, - {"__proto__", PropInfo("object")}}); - } else if (method == "Debugger.paused") { - receivedPausedNotification = true; - auto note = m::debugger::PausedNotification(parsedNote); - EXPECT_EQ(note.reason, "other"); - EXPECT_EQ(note.callFrames.size(), 1); - EXPECT_EQ(note.callFrames[0].functionName, "global"); - EXPECT_EQ(note.callFrames[0].location.lineNumber, 5); - } else { - throw UnexpectedNotificationException(); - } - }); - } - - EXPECT_TRUE(receivedConsoleNotification); - EXPECT_TRUE(receivedPausedNotification); - - // Resume and expect no further notifications - send(conn, msgId++); - expectNotification(conn); - expectNothing(conn); -} - -TEST(ConnectionTests, testThisObject) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - var globalString = "global-string"; - - var object = { - someVar: "object var", - foo: function() { - var localString = "local-string"; - debugger; // [1] (line 7) hit debugger statement. - } - } - - object.foo(); // (line 11) - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // [1] (line 1) hit debugger statement - auto pausedNote = - expectPaused(conn, "other", {{"foo", 7, 2}, {"global", 11, 1}}); - - // [2] inspect first call frame (foo) - const auto &localThisObj = pausedNote.callFrames.at(0).thisObj; - expectProps( - conn, - msgId++, - localThisObj.objectId.value(), - {{"someVar", PropInfo("string").setValue("object var")}, - {"foo", PropInfo("function")}, - {"__proto__", PropInfo("object")}}); - - // [3] inspect second call frame (global) - // Global scope can contain more properties than we have defined - // in our test code and we can't use expectProps() method here. - // As a workaround we create a Map of properties and check that - // those global properties that we have defined are in the map. - const auto &globalThisObj = pausedNote.callFrames.at(1).thisObj; - m::runtime::GetPropertiesRequest req; - req.id = msgId++; - req.objectId = globalThisObj.objectId.value(); - conn.send(req.toJson()); - auto resp = expectResponse(conn, req.id); - std::unordered_map> - properties; - for (auto &propertyDescriptor : resp.result) { - properties[propertyDescriptor.name] = std::move(propertyDescriptor.value); - } - - // globalString should be of type "string" and have value "global-string". - EXPECT_EQ(properties.count("globalString"), 1); - EXPECT_TRUE(properties["globalString"].has_value()); - EXPECT_EQ(properties["globalString"].value().type, "string"); - EXPECT_EQ(properties["globalString"].value().value.value(), "global-string"); - - // [4] resume - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetBreakpointsMultipleScripts) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - std::string url1 = "first-url"; - asyncRuntime.executeScriptAsync( - R"( - function foo1() { - var somevar1 = 111; // (line 2) hit breakpoint - var somevar2 = 222; - } - )", - url1); - send(conn, msgId++); - expectExecutionContextCreated(conn); - auto scriptParsed1 = - expectNotification(conn); - - std::string url2 = "second-url"; - asyncRuntime.executeScriptAsync( - R"( - function foo2() { - var somevar3 = 333; - var somevar4 = 444; // (line 3) hit breakpoint - } - )", - url2); - auto scriptParsed2 = - expectNotification(conn); - - // In the third script we will set breakpoint (on debugger statement) - // and call both functions (script url doesn't matter). - asyncRuntime.executeScriptAsync(R"( - debugger; // [1] (line 1) set breakpoints in both files - foo1(); - foo2(); - )"); - auto scriptParsed3 = - expectNotification(conn); - - // [1] hit debugger statement - expectPaused(conn, "other", {{"global", 1, 1}}); - - // Set breakpoint on line 2 in the first script and line 3 in the second - // script. - m::debugger::SetBreakpointByUrlRequest req1; - req1.id = msgId++; - req1.lineNumber = 2; - req1.url = url1; - conn.send(req1.toJson()); - - expectBreakpointResponse(conn, req1.id, 2, 2); - - m::debugger::SetBreakpointByUrlRequest req2; - req2.id = msgId++; - req2.lineNumber = 3; - req2.url = url2; - conn.send(req2.toJson()); - - expectBreakpointResponse(conn, req2.id, 3, 3); - - // Resume and check that we hit correct breakpoints. - send(conn, msgId++); - expectNotification(conn); - - // First we should stop on line 2 of the first script. - expectPaused( - conn, - "other", - {FrameInfo("foo1", 2, 2).setScriptId(scriptParsed1.scriptId), - FrameInfo("global", 2, 1).setScriptId(scriptParsed3.scriptId)}); - send(conn, msgId++); - expectNotification(conn); - - // Next we should stop on line 3 of the second script. - expectPaused( - conn, - "other", - {FrameInfo("foo2", 3, 2).setScriptId(scriptParsed2.scriptId), - FrameInfo("global", 3, 1).setScriptId(scriptParsed3.scriptId)}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testSetBreakpointByUrlRegex) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - std::string url1 = "https://www.example.com/123456"; - asyncRuntime.executeScriptAsync( - R"( - function foo1() { - var somevar1 = 111; // (line 2) hit breakpoint - } - )", - url1); - send(conn, msgId++); - expectExecutionContextCreated(conn); - auto scriptParsed1 = - expectNotification(conn); - - std::string url2 = "https://www.example.com/abcdefg"; - asyncRuntime.executeScriptAsync( - R"( - function foo2() { - var aaa = 'bbb'; - var somevar2 = 222; // (line 3) hit breakpoint - } - )", - url2); - auto scriptParsed2 = - expectNotification(conn); - - // In the third script we will set breakpoint (on debugger statement) - // and call both functions (script url doesn't matter). - asyncRuntime.executeScriptAsync(R"( - debugger; // [1] (line 1) set breakpoints in both files - foo1(); - foo2(); - )"); - auto scriptParsed3 = - expectNotification(conn); - - // [1] hit debugger statement - expectPaused(conn, "other", {{"global", 1, 1}}); - - // Set breakpoint on line 2 of URL matching "www\.example\.com\/[\d]+" - // (should match url1). - m::debugger::SetBreakpointByUrlRequest req1; - req1.id = msgId++; - req1.lineNumber = 2; - req1.urlRegex = R"(https://www\.example\.com\/[\d]+)"; - conn.send(req1.toJson()); - - expectBreakpointResponse(conn, req1.id, 2, 2); - - // Set breakpoint on line 3 of URL matching "www\.example\.com\/[a-zA-z]+" - // (should match url2). - m::debugger::SetBreakpointByUrlRequest req2; - req2.id = msgId++; - req2.lineNumber = 3; - req2.urlRegex = R"(https://www\.example\.com\/[a-zA-z]+)"; - conn.send(req2.toJson()); - - expectBreakpointResponse(conn, req2.id, 3, 3); - - // Resume and check that we hit correct breakpoints. - send(conn, msgId++); - expectNotification(conn); - - // First we should stop on line 2 of the first script. - expectPaused( - conn, - "other", - {FrameInfo("foo1", 2, 2).setScriptId(scriptParsed1.scriptId), - FrameInfo("global", 2, 1).setScriptId(scriptParsed3.scriptId)}); - send(conn, msgId++); - expectNotification(conn); - - // Next we should stop on line 3 of the second script. - expectPaused( - conn, - "other", - {FrameInfo("foo2", 3, 2).setScriptId(scriptParsed2.scriptId), - FrameInfo("global", 3, 1).setScriptId(scriptParsed3.scriptId)}); - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testColumnBreakpoint) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync( - R"( -function foo(){x=1}debugger;foo(); -)", - "url"); - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // Hit debugger statement. - expectPaused(conn, "other", {{"global", 1, 1}}); - - // Set breakpoint on position 1:16 (x=1). - m::debugger::SetBreakpointByUrlRequest req; - req.id = msgId++; - req.lineNumber = 1; - req.columnNumber = 16; - req.url = "url"; - conn.send(req.toJson()); - - expectBreakpointResponse(conn, req.id, 1, 1); - - // Resume and except to pause on a breakpoint. - send(conn, msgId++); - expectNotification(conn); - expectPaused( - conn, - "other", - {FrameInfo("foo", 1, 2).setColumnNumber(16), FrameInfo("global", 1, 1)}); - - // Resume execution - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, canBreakOnScriptsWithSourceMap) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - send(conn, msgId++); - expectExecutionContextCreated(conn); - - m::debugger::SetInstrumentationBreakpointRequest req; - req.id = msgId++; - req.instrumentation = "beforeScriptWithSourceMapExecution"; - - conn.send(req.toJson()); - auto bpId = expectResponse( - conn, req.id) - .breakpointId; - - asyncRuntime.executeScriptAsync(R"( - storeValue(42); debugger; - //# sourceURL=http://example.com/source.js - //# sourceMappingURL=http://example.com/source.map - )"); - expectNotification(conn); - - // We should get a pause before the first statement - auto note = expectNotification(conn); - ASSERT_FALSE(asyncRuntime.hasStoredValue()); - EXPECT_EQ(note.reason, "other"); - ASSERT_TRUE(note.hitBreakpoints.has_value()); - ASSERT_EQ(note.hitBreakpoints->size(), 1); - EXPECT_EQ(note.hitBreakpoints->at(0), bpId); - - // Continue and verify that the JS code has now executed - send(conn, msgId++); - expectNotification(conn); - expectNotification(conn); - EXPECT_EQ(asyncRuntime.awaitStoredValue().asNumber(), 42); - - // Resume and exit - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, wontStopOnFilesWithoutSourceMaps) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - send(conn, msgId++); - expectExecutionContextCreated(conn); - - m::debugger::SetInstrumentationBreakpointRequest req; - req.id = msgId++; - req.instrumentation = "beforeScriptWithSourceMapExecution"; - - conn.send(req.toJson()); - expectResponse( - conn, req.id); - - // This script has no source map, so it should not trigger a break - asyncRuntime.executeScriptAsync(R"( - storeValue(42); debugger; - //# sourceURL=http://example.com/source.js - )"); - expectNotification(conn); - - // Continue and verify that the JS code has now executed without first - // pausing on the script load. - expectNotification(conn); - EXPECT_EQ(asyncRuntime.awaitStoredValue().asNumber(), 42); - - // Resume and exit - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, runIfWaitingForDebugger) { - TestContext context(true); - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 0; - - asyncRuntime.executeScriptAsync(R"( - storeValue(1); debugger; - )"); - - send(conn, ++msgId); - expectExecutionContextCreated(conn); - expectNotification(conn); - expectNotification(conn); - - // We should now be paused on load. Verify that we didn't run code. - ASSERT_FALSE(asyncRuntime.hasStoredValue()); - - // RunIfWaitingForDebugger should cause us to resume - send(conn, ++msgId); - expectNotification(conn); - - // We should immediately hit the 'debugger;' statement - expectNotification(conn); - EXPECT_EQ(1, asyncRuntime.awaitStoredValue().asNumber()); - - // RunIfWaitingForDebuggerResponse should be accepted but have no effect - send(conn, ++msgId); - - // Do a dummy call so we can expect something other than a ResumeRequest - sendRuntimeEvalRequest(conn, ++msgId, "true"); - expectEvalResponse(conn, msgId, true); - - // Finally explicitly continue and exit - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, heapProfilerSampling) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - send(conn, msgId++); - expectExecutionContextCreated(conn); - - asyncRuntime.executeScriptAsync(R"( - debugger; - function allocator() { - // Do some allocation. - return new Object; - } - (function main() { - var a = []; - for (var i = 0; i < 100; i++) { - a[i] = allocator(); - } - })(); - debugger; - )"); - expectNotification(conn); - - // We should get a pause before the first statement. - expectNotification(conn); - - { - m::heapProfiler::StartSamplingRequest req; - req.id = msgId++; - // Sample every 256 bytes to ensure there are some samples. The default is - // 32768, which is too high for a small example. Note that sampling is a - // random process, so there's no guarantee there will be any samples in - // any finite number of allocations. In practice the likelihood is so high - // that there shouldn't be any issues. - req.samplingInterval = 256; - send(conn, req); - } - // Resume, run the allocations, and once it's paused again, stop them. - send(conn, msgId++); - expectNotification(conn); - expectNotification(conn); - // Send the stop sampling request, expect the value coming back to be JSON. - auto resp = send< - m::heapProfiler::StopSamplingRequest, - m::heapProfiler::StopSamplingResponse>(conn, msgId++); - // Make sure there were some samples. - EXPECT_NE(resp.profile.samples.size(), 0); - // Don't test the content of the JSON, that is tested via the - // SamplingHeapProfilerTest. - - // Resume and exit - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, heapSnapshotRemoteObject) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - std::shared_ptr runtime = asyncRuntime.runtime(); - SyncConnection &conn = context.conn(); - int msgId = 1; - - send(conn, msgId++); - expectExecutionContextCreated(conn); - - asyncRuntime.executeScriptAsync(R"( - storeValue([1, 2, 3]); - debugger; - )"); - expectNotification(conn); - - // We should get a pause before the first statement. - expectNotification(conn); - - { - // Take a heap snapshot first to assign IDs. - m::heapProfiler::TakeHeapSnapshotRequest req; - req.id = msgId++; - req.reportProgress = false; - // We don't need the response because we can directly query for object IDs - // from the runtime. - send(conn, req); - } - - const uint64_t globalObjID = runtime->getUniqueID(runtime->global()); - jsi::Value storedValue = asyncRuntime.awaitStoredValue(); - const uint64_t storedObjID = - runtime->getUniqueID(storedValue.asObject(*runtime)); - - auto testObject = [&msgId, &conn]( - uint64_t objID, - const char *type, - const char *className, - const char *description, - const char *subtype) { - // Get the object by its snapshot ID. - m::heapProfiler::GetObjectByHeapObjectIdRequest req; - req.id = msgId++; - req.objectId = std::to_string(objID); - auto resp = send< - m::heapProfiler::GetObjectByHeapObjectIdRequest, - m::heapProfiler::GetObjectByHeapObjectIdResponse>(conn, req); - EXPECT_EQ(resp.result.type, type); - EXPECT_EQ(resp.result.className, className); - EXPECT_EQ(resp.result.description, description); - if (subtype) { - EXPECT_EQ(resp.result.subtype, subtype); - } - - // Check that fetching the object by heap snapshot ID works. - m::heapProfiler::GetHeapObjectIdRequest idReq; - idReq.id = msgId++; - idReq.objectId = resp.result.objectId.value(); - auto idResp = send< - m::heapProfiler::GetHeapObjectIdRequest, - m::heapProfiler::GetHeapObjectIdResponse>(conn, idReq); - EXPECT_EQ(atoi(idResp.heapSnapshotObjectId.c_str()), objID); - }; - - // Test once before a collection. - testObject(globalObjID, "object", "Object", "Object", nullptr); - testObject(storedObjID, "object", "Array", "Array(3)", "array"); - // Force a collection to move the heap. - runtime->instrumentation().collectGarbage("test"); - // A collection should not disturb the unique ID lookup, and it should be - // the same object as before. Note that it won't have the same remote ID, - // because Hermes doesn't do uniquing. - testObject(globalObjID, "object", "Object", "Object", nullptr); - testObject(storedObjID, "object", "Array", "Array(3)", "array"); - - // Resume and exit. - send(conn, msgId++); - expectNotification(conn); -} - -TEST(ConnectionTests, testBasicProfilerOperation) { - TestContext context; - AsyncHermesRuntime &asyncRuntime = context.runtime(); - SamplingProfilerRAII spRegistration(asyncRuntime); - SyncConnection &conn = context.conn(); - int msgId = 1; - - asyncRuntime.executeScriptAsync(R"( - while(!shouldStop()); - )"); - - send(conn, msgId++); - expectExecutionContextCreated(conn); - expectNotification(conn); - - // Start the sampling profiler. At this point it is not safe to manipulate the - // VM, so... - send(conn, msgId++); - - // Disable the debugger. - send(conn, msgId++); - - // Keep the profiler running for a small amount of time to allow for some - // samples to be collected. - std::this_thread::sleep_for(500ms); - - // Finally, re-enable the debugger in order to stop profiling. - send(conn, msgId++); - expectExecutionContextCreated(conn); - - // Being re-attached to the VM, send the stop sampling profile request. - { - auto resp = send( - conn, msgId++); - - const m::profiler::Profile &profile = resp.profile; - EXPECT_GT(profile.nodes.size(), 0); - EXPECT_LT(profile.startTime, profile.endTime); - ASSERT_TRUE(profile.samples); - EXPECT_FALSE(profile.samples->empty()); - ASSERT_TRUE(profile.timeDeltas); - EXPECT_EQ(profile.samples->size(), profile.timeDeltas->size()); - } - - 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 -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp deleted file mode 100644 index b4f5fc58d5e..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp +++ /dev/null @@ -1,1549 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { -namespace message { - -using folly::dynamic; - -TEST(MessageTests, testSerializeSomeFieldsInRequest) { - debugger::SetBreakpointByUrlRequest req; - // req.id should default to 0 - req.lineNumber = 2; - req.url = "http://example.com/example.js"; - - dynamic result = req.toDynamic(); - dynamic expected = folly::parseJson(R"({ - "id": 0, - "method": "Debugger.setBreakpointByUrl", - "params": { - "lineNumber": 2, - "url": "http://example.com/example.js" - } - })"); - EXPECT_EQ(result, expected); -} - -TEST(MessageTests, testDeserializeSomeFieldsInRequest) { - dynamic message = folly::parseJson(R"( - { - "id": 10, - "method": "Debugger.setBreakpointByUrl", - "params": { - "lineNumber": 42, - "url": "http://example.com" - } - } - )"); - debugger::SetBreakpointByUrlRequest req(message); - - EXPECT_EQ(req.toDynamic(), message); - EXPECT_EQ(req.id, 10); - EXPECT_EQ(req.method, "Debugger.setBreakpointByUrl"); - EXPECT_EQ(req.lineNumber, 42); - EXPECT_FALSE(req.columnNumber.has_value()); - EXPECT_FALSE(req.condition.has_value()); - EXPECT_EQ(req.url, "http://example.com"); - EXPECT_FALSE(req.urlRegex.has_value()); -} - -TEST(MessageTests, testSerializeAllFieldsInRequest) { - debugger::SetBreakpointByUrlRequest req; - req.id = 1; - req.lineNumber = 2; - req.columnNumber = 3; - req.condition = "foo == 42"; - req.url = "http://example.com/example.js"; - req.urlRegex = "http://example.com/.*"; - - dynamic result = req.toDynamic(); - dynamic expected = folly::parseJson(R"({ - "id": 1, - "method": "Debugger.setBreakpointByUrl", - "params": { - "lineNumber": 2, - "columnNumber": 3, - "condition": "foo == 42", - "url": "http://example.com/example.js", - "urlRegex": "http://example.com/.*" - } - })"); - EXPECT_EQ(result, expected); -} - -TEST(MessageTests, testDeserializeAllFieldsInRequest) { - dynamic message = folly::parseJson(R"({ - "id": 1, - "method": "Debugger.setBreakpointByUrl", - "params": { - "lineNumber": 2, - "columnNumber": 3, - "condition": "foo == 42", - "url": "http://example.com/example.js", - "urlRegex": "http://example.com/.*" - } - })"); - debugger::SetBreakpointByUrlRequest req(message); - - EXPECT_EQ(req.id, 1); - EXPECT_EQ(req.method, "Debugger.setBreakpointByUrl"); - EXPECT_EQ(req.lineNumber, 2); - EXPECT_EQ(req.columnNumber, 3); - EXPECT_EQ(req.condition, "foo == 42"); - EXPECT_EQ(req.url, "http://example.com/example.js"); - EXPECT_EQ(req.urlRegex, "http://example.com/.*"); -} - -TEST(MessageTests, testSerializeResponse) { - debugger::Location location; - location.scriptId = "myScriptId"; - location.lineNumber = 2; - location.columnNumber = 3; - - debugger::SetBreakpointByUrlResponse resp; - resp.id = 1; - resp.breakpointId = "myBreakpointId"; - resp.locations.push_back(std::move(location)); - - dynamic result = resp.toDynamic(); - dynamic expected = folly::parseJson(R"({ - "id": 1, - "result": { - "breakpointId": "myBreakpointId", - "locations": [ - { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - } - ] - } - })"); - EXPECT_EQ(result, expected); -} - -TEST(MessageTests, testDeserializeResponse) { - dynamic message = folly::parseJson(R"({ - "id": 1, - "result": { - "breakpointId": "myBreakpointId", - "locations": [ - { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - } - ] - } - })"); - debugger::SetBreakpointByUrlResponse resp(message); - EXPECT_EQ(resp.toDynamic(), message); - EXPECT_EQ(resp.id, 1); - EXPECT_EQ(resp.breakpointId, "myBreakpointId"); - EXPECT_EQ(resp.locations.size(), 1); - EXPECT_EQ(resp.locations[0].lineNumber, 2); - EXPECT_EQ(resp.locations[0].columnNumber, 3); - EXPECT_EQ(resp.locations[0].scriptId, "myScriptId"); -} - -TEST(MessageTests, testSerializeNotification) { - debugger::Location startLocation; - startLocation.lineNumber = 1; - startLocation.scriptId = "script1"; - - debugger::Location endLocation; - endLocation.lineNumber = 2; - endLocation.scriptId = "script2"; - - debugger::Scope scope; - scope.type = "closure"; - scope.object.type = "object"; - scope.object.subtype = "regexp"; - scope.object.className = "RegExp"; - scope.object.value = dynamic::object("foo", "bar"); - scope.object.unserializableValue = "nope"; - scope.object.description = "myDesc"; - scope.object.objectId = "id1"; - scope.name = "myScope"; - scope.startLocation = std::move(startLocation); - scope.endLocation = std::move(endLocation); - - debugger::CallFrame frame; - frame.callFrameId = "callFrame1"; - frame.functionName = "foo1"; - frame.location.scriptId = "script1"; - frame.location.lineNumber = 3; - frame.location.columnNumber = 4; - frame.url = "foo.js"; - frame.scopeChain.push_back(std::move(scope)); - frame.thisObj.type = "function"; - - debugger::PausedNotification note; - note.callFrames.push_back(std::move(frame)); - note.reason = "debugCommand"; - note.data = dynamic::object("foo", "bar"); - note.hitBreakpoints = std::vector{"a", "b"}; - - dynamic result = note.toDynamic(); - dynamic expected = folly::parseJson(R"({ - "method": "Debugger.paused", - "params": { - "callFrames": [ - { - "callFrameId": "callFrame1", - "functionName": "foo1", - "location": { - "scriptId": "script1", - "lineNumber": 3, - "columnNumber": 4 - }, - "url": "foo.js", - "scopeChain": [ - { - "type": "closure", - "object": { - "type": "object", - "subtype": "regexp", - "className": "RegExp", - "value": { "foo": "bar" }, - "unserializableValue": "nope", - "description": "myDesc", - "objectId": "id1" - }, - "name": "myScope", - "startLocation": { - "lineNumber": 1, - "scriptId": "script1" - }, - "endLocation": { - "lineNumber": 2, - "scriptId": "script2" - } - } - ], - "this": { "type": "function" } - } - ], - "reason": "debugCommand", - "data": { - "foo": "bar" - }, - "hitBreakpoints": [ "a", "b" ] - } - })"); - EXPECT_EQ(result, expected); -} - -TEST(MessageTests, testDeserializeNotification) { - dynamic message = folly::parseJson(R"({ - "method": "Debugger.paused", - "params": { - "callFrames": [ - { - "callFrameId": "callFrame1", - "functionName": "foo1", - "location": { - "scriptId": "script1", - "lineNumber": 3, - "columnNumber": 4 - }, - "url": "foo.js", - "scopeChain": [ - { - "type": "closure", - "object": { - "type": "object", - "subtype": "regexp", - "className": "RegExp", - "value": { "foo": "bar" }, - "unserializableValue": "nope", - "description": "myDesc", - "objectId": "id1" - }, - "name": "myScope", - "startLocation": { - "lineNumber": 1, - "scriptId": "script1" - }, - "endLocation": { - "lineNumber": 2, - "scriptId": "script2" - } - } - ], - "this": { "type": "function" } - } - ], - "reason": "debugCommand", - "data": { - "foo": "bar" - }, - "hitBreakpoints": [ "a", "b" ] - } - })"); - debugger::PausedNotification note(message); - - EXPECT_EQ(note.method, "Debugger.paused"); - EXPECT_EQ(note.callFrames.size(), 1); - EXPECT_EQ(note.reason, "debugCommand"); - EXPECT_EQ(note.data, static_cast(dynamic::object("foo", "bar"))); - auto expectedHitBreakpoints = std::vector{"a", "b"}; - EXPECT_EQ(note.hitBreakpoints, expectedHitBreakpoints); - - debugger::CallFrame &callFrame = note.callFrames[0]; - EXPECT_EQ(callFrame.callFrameId, "callFrame1"); - EXPECT_EQ(callFrame.functionName, "foo1"); - EXPECT_EQ(callFrame.location.scriptId, "script1"); - EXPECT_EQ(callFrame.location.lineNumber, 3); - EXPECT_EQ(callFrame.location.columnNumber, 4); - EXPECT_EQ(callFrame.url, "foo.js"); - EXPECT_EQ(callFrame.scopeChain.size(), 1); - EXPECT_EQ(callFrame.thisObj.type, "function"); - - debugger::Scope &scope = callFrame.scopeChain[0]; - EXPECT_EQ(scope.type, "closure"); - EXPECT_EQ(scope.object.type, "object"); - EXPECT_EQ(scope.object.subtype, "regexp"); - EXPECT_EQ(scope.object.className, "RegExp"); - EXPECT_EQ( - scope.object.value, static_cast(dynamic::object("foo", "bar"))); - EXPECT_EQ(scope.object.unserializableValue, "nope"); - EXPECT_EQ(scope.object.description, "myDesc"); - EXPECT_EQ(scope.object.objectId, "id1"); - EXPECT_EQ(scope.name, "myScope"); - - debugger::Location &startLocation = scope.startLocation.value(); - EXPECT_EQ(startLocation.lineNumber, 1); - EXPECT_FALSE(startLocation.columnNumber.has_value()); - EXPECT_EQ(startLocation.scriptId, "script1"); - - debugger::Location &endLocation = scope.endLocation.value(); - EXPECT_EQ(endLocation.lineNumber, 2); - EXPECT_FALSE(endLocation.columnNumber.has_value()); - EXPECT_EQ(endLocation.scriptId, "script2"); -} - -TEST(MessageTests, TestSerializeAsyncStackTrace) { - runtime::StackTrace stack; - stack.description = "childStack"; - stack.parent = std::make_unique(); - stack.parent->description = "parentStack"; - - dynamic result = stack.toDynamic(); - dynamic expected = folly::parseJson(R"({ - "description": "childStack", - "callFrames": [], - "parent": { - "description": "parentStack", - "callFrames": [] - } - })"); - EXPECT_EQ(result, expected); -} - -TEST(MessageTests, TestDeserializeAsyncStackTrace) { - dynamic message = folly::parseJson(R"({ - "description": "childStack", - "callFrames": [], - "parent": { - "description": "parentStack", - "callFrames": [] - } - })"); - runtime::StackTrace stack(message); - - EXPECT_EQ(stack.description, "childStack"); - EXPECT_EQ(stack.callFrames.size(), 0); - EXPECT_EQ(stack.parent->description, "parentStack"); - EXPECT_EQ(stack.parent->callFrames.size(), 0); -} - -TEST(MessageTests, TestRequestFromJson) { - std::unique_ptr baseReq1 = Request::fromJsonThrowOnError(R"({ - "id": 1, - "method": "Debugger.enable" - })"); - auto req1 = static_cast(baseReq1.get()); - EXPECT_EQ(req1->id, 1); - EXPECT_EQ(req1->method, "Debugger.enable"); - - std::unique_ptr baseReq2 = Request::fromJsonThrowOnError(R"({ - "id": 2, - "method": "Debugger.removeBreakpoint", - "params": { - "breakpointId": "foobar" - } - })"); - auto req2 = static_cast(baseReq2.get()); - EXPECT_EQ(req2->id, 2); - EXPECT_EQ(req2->method, "Debugger.removeBreakpoint"); - EXPECT_EQ(req2->breakpointId, "foobar"); - - folly::Try> invalidReq = - Request::fromJson("invalid"); - EXPECT_TRUE(invalidReq.hasException()); -} - -TEST(MessageTests, TestBreakpointRequestFromJSON) { - std::unique_ptr baseReq = Request::fromJsonThrowOnError(R"({ - "id": 1, - "method": "Debugger.setBreakpoint", - "params": { - "location": { - "scriptId": "23", - "lineNumber": 45, - "columnNumber": 67 - } - } - })"); - auto req = static_cast(baseReq.get()); - EXPECT_EQ(req->location.scriptId, "23"); - EXPECT_EQ(req->location.lineNumber, 45); - EXPECT_EQ(req->location.columnNumber.value(), 67); -} - -struct MyHandler : public NoopRequestHandler { - void handle(const debugger::EnableRequest &req) override { - enableReq = req; - } - - void handle(const debugger::RemoveBreakpointRequest &req) override { - removeReq = req; - } - - debugger::EnableRequest enableReq; - debugger::RemoveBreakpointRequest removeReq; -}; - -TEST(MessageTests, TestRequestHandler) { - MyHandler handler; - - std::unique_ptr enableReq = Request::fromJsonThrowOnError(R"({ - "id": 1, - "method": "Debugger.enable" - })"); - enableReq->accept(handler); - - EXPECT_EQ(handler.enableReq.id, 1); - EXPECT_EQ(handler.enableReq.method, "Debugger.enable"); - - std::unique_ptr removeReq = Request::fromJsonThrowOnError(R"({ - "id": 2, - "method": "Debugger.removeBreakpoint", - "params": { - "breakpointId": "foobar" - } - })"); - removeReq->accept(handler); - - EXPECT_EQ(handler.removeReq.id, 2); - EXPECT_EQ(handler.removeReq.method, "Debugger.removeBreakpoint"); - EXPECT_EQ(handler.removeReq.breakpointId, "foobar"); -} - -TEST(MessageTests, testEnableRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.enable" - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::EnableRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::EnableRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.enable"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); -} - -TEST(MessageTests, testDisableRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.disable" - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::DisableRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::DisableRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.disable"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); -} - -TEST(MessageTests, testEvaluateOnCallFrameRequestMinimal) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.evaluateOnCallFrame", - "params":{ - "callFrameId" : "42", - "expression": "Foo Bar" - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::EvaluateOnCallFrameRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::EvaluateOnCallFrameRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.evaluateOnCallFrame"); - EXPECT_EQ(resolvedReq->callFrameId, "42"); - EXPECT_EQ(resolvedReq->expression, "Foo Bar"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->callFrameId, deserializedReq.callFrameId); - EXPECT_EQ(resolvedReq->expression, deserializedReq.expression); - - EXPECT_FALSE(resolvedReq->generatePreview.has_value()); - EXPECT_FALSE(resolvedReq->objectGroup.has_value()); - EXPECT_FALSE(resolvedReq->includeCommandLineAPI.has_value()); - EXPECT_FALSE(resolvedReq->silent.has_value()); - EXPECT_FALSE(resolvedReq->returnByValue.has_value()); - EXPECT_FALSE(resolvedReq->throwOnSideEffect.has_value()); -} - -TEST(MessageTests, testEvaluateOnCallFrameRequestFull) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.evaluateOnCallFrame", - "params":{ - "callFrameId" : "42", - "expression": "Foo Bar", - "objectGroup" : "FooBarGroup", - "includeCommandLineAPI" : false, - "silent" : true, - "returnByValue" : false, - "throwOnSideEffect" : true, - "generatePreview": true - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::EvaluateOnCallFrameRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::EvaluateOnCallFrameRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics, resolvedReq is correct - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.evaluateOnCallFrame"); - EXPECT_EQ(resolvedReq->callFrameId, "42"); - EXPECT_EQ(resolvedReq->expression, "Foo Bar"); - - EXPECT_TRUE(resolvedReq->objectGroup.has_value()); - EXPECT_TRUE(resolvedReq->includeCommandLineAPI.has_value()); - EXPECT_TRUE(resolvedReq->silent.has_value()); - EXPECT_TRUE(resolvedReq->returnByValue.has_value()); - EXPECT_TRUE(resolvedReq->throwOnSideEffect.has_value()); - EXPECT_TRUE(resolvedReq->generatePreview.has_value()); - - EXPECT_TRUE(resolvedReq->objectGroup.value() == "FooBarGroup"); - EXPECT_TRUE(resolvedReq->includeCommandLineAPI.value() == false); - EXPECT_TRUE(resolvedReq->silent.value() == true); - EXPECT_TRUE(resolvedReq->returnByValue.value() == false); - EXPECT_TRUE(resolvedReq->throwOnSideEffect.value() == true); - EXPECT_TRUE(resolvedReq->generatePreview.value() == true); - - // Specifics, resolvedReq and deserialized match - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->callFrameId, deserializedReq.callFrameId); - EXPECT_EQ(resolvedReq->expression, deserializedReq.expression); - EXPECT_EQ( - resolvedReq->objectGroup.value(), deserializedReq.objectGroup.value()); - EXPECT_EQ( - resolvedReq->includeCommandLineAPI.value(), - deserializedReq.includeCommandLineAPI.value()); - EXPECT_EQ(resolvedReq->silent.value(), deserializedReq.silent.value()); - EXPECT_EQ( - resolvedReq->returnByValue.value(), - deserializedReq.returnByValue.value()); - EXPECT_EQ( - resolvedReq->throwOnSideEffect.value(), - deserializedReq.throwOnSideEffect.value()); -} - -TEST(MessageTests, testPauseRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.pause" - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::PauseRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::PauseRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.pause"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); -} - -TEST(MessageTests, testRemoveBreakpointRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.removeBreakpoint", - "params":{ - "breakpointId" : "42" - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::RemoveBreakpointRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::RemoveBreakpointRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.removeBreakpoint"); - EXPECT_TRUE(resolvedReq->breakpointId == "42"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->breakpointId, deserializedReq.breakpointId); -} - -TEST(MessageTests, testResumeRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.resume", - "params": { - "terminateOnResume": false - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::ResumeRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::ResumeRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.resume"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); -} - -TEST(MessageTests, testSetBreakpointRequestMinimal) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.setBreakpoint", - "params":{ - "location" : - { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - } - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::SetBreakpointRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetBreakpointRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - debugger::Location location; - location.scriptId = "myScriptId"; - location.lineNumber = 2; - location.columnNumber = 3; - - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.setBreakpoint"); - EXPECT_EQ(resolvedReq->location.scriptId, "myScriptId"); - EXPECT_EQ(resolvedReq->location.lineNumber, 2); - EXPECT_EQ(resolvedReq->location.columnNumber, 3); - - EXPECT_FALSE(resolvedReq->condition.has_value()); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->location.scriptId, deserializedReq.location.scriptId); - EXPECT_EQ( - resolvedReq->location.lineNumber, deserializedReq.location.lineNumber); - EXPECT_EQ( - resolvedReq->location.columnNumber, - deserializedReq.location.columnNumber); -} - -TEST(MessageTests, testSetBreakpointRequestFull) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.setBreakpoint", - "params":{ - "location" : - { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - }, - "condition": "FooBarCondition" - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::SetBreakpointRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetBreakpointRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.setBreakpoint"); - EXPECT_EQ(resolvedReq->location.scriptId, "myScriptId"); - EXPECT_EQ(resolvedReq->location.lineNumber, 2); - EXPECT_EQ(resolvedReq->location.columnNumber, 3); - - EXPECT_TRUE(resolvedReq->condition.has_value()); - EXPECT_EQ(resolvedReq->condition.value(), "FooBarCondition"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->location.scriptId, deserializedReq.location.scriptId); - EXPECT_EQ( - resolvedReq->location.lineNumber, deserializedReq.location.lineNumber); - EXPECT_EQ( - resolvedReq->location.columnNumber, - deserializedReq.location.columnNumber); - EXPECT_EQ(resolvedReq->condition.value(), deserializedReq.condition.value()); -} - -TEST(MessageTests, testSetBreakpointByUrlRequestMinimal) { - std::string message = R"( - { - "id": 1, - "method": "Debugger.setBreakpointByUrl", - "params": { - "lineNumber": 2 - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::SetBreakpointByUrlRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetBreakpointByUrlRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 1); - EXPECT_EQ(resolvedReq->method, "Debugger.setBreakpointByUrl"); - EXPECT_EQ(resolvedReq->lineNumber, 2); - - EXPECT_FALSE(resolvedReq->condition.has_value()); - EXPECT_FALSE(resolvedReq->columnNumber.has_value()); - EXPECT_FALSE(resolvedReq->url.has_value()); - EXPECT_FALSE(resolvedReq->urlRegex.has_value()); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->lineNumber, deserializedReq.lineNumber); -} - -TEST(MessageTests, testSetBreakpointByUrlRequestFull) { - std::string message = R"( - { - "id": 1, - "method": "Debugger.setBreakpointByUrl", - "params": { - "lineNumber": 2, - "columnNumber": 3, - "condition": "foo == 42", - "url": "http://example.com/example.js", - "urlRegex": "http://example.com/.*" - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::SetBreakpointByUrlRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetBreakpointByUrlRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 1); - EXPECT_EQ(resolvedReq->method, "Debugger.setBreakpointByUrl"); - EXPECT_EQ(resolvedReq->lineNumber, 2); - - EXPECT_TRUE(resolvedReq->condition.has_value()); - EXPECT_EQ(resolvedReq->condition.value(), "foo == 42"); - EXPECT_TRUE(resolvedReq->columnNumber.has_value()); - EXPECT_EQ(resolvedReq->columnNumber.value(), 3); - EXPECT_TRUE(resolvedReq->url.has_value()); - EXPECT_EQ(resolvedReq->url.value(), "http://example.com/example.js"); - EXPECT_TRUE(resolvedReq->urlRegex.has_value()); - EXPECT_EQ(resolvedReq->urlRegex.value(), "http://example.com/.*"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->lineNumber, deserializedReq.lineNumber); - EXPECT_EQ(resolvedReq->condition.value(), deserializedReq.condition.value()); - EXPECT_EQ( - resolvedReq->columnNumber.value(), deserializedReq.columnNumber.value()); - EXPECT_EQ(resolvedReq->url.value(), deserializedReq.url.value()); - EXPECT_EQ(resolvedReq->urlRegex.value(), deserializedReq.urlRegex.value()); -} - -TEST(MessageTests, testSetBreakpointsActiveRequest) { - std::string message = R"( - { - "id": 1, - "method": "Debugger.setBreakpointsActive", - "params": { - "active": true - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::SetBreakpointsActiveRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetBreakpointsActiveRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 1); - EXPECT_EQ(resolvedReq->method, "Debugger.setBreakpointsActive"); - EXPECT_EQ(resolvedReq->active, true); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->active, deserializedReq.active); -} - -TEST(MessageTests, testSetInstrumentationBreakpointRequest) { - std::string message = R"( - { - "id": 1, - "method": "Debugger.setInstrumentationBreakpoint", - "params": { - "instrumentation": "TODO: THIS SHOULD NOT BE ACCEPTED BY ENUM" - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::SetInstrumentationBreakpointRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetInstrumentationBreakpointRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 1); - EXPECT_EQ(resolvedReq->method, "Debugger.setInstrumentationBreakpoint"); - EXPECT_EQ( - resolvedReq->instrumentation, - "TODO: THIS SHOULD NOT BE ACCEPTED BY ENUM"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->instrumentation, deserializedReq.instrumentation); -} - -TEST(MessageTests, testSetPauseOnExceptionsRequest) { - std::string message = R"( - { - "id": 1, - "method": "Debugger.setPauseOnExceptions", - "params": { - "state": "TODO: THIS SHOULD NOT BE ACCEPTED BY ENUM" - } - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::SetPauseOnExceptionsRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetPauseOnExceptionsRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 1); - EXPECT_EQ(resolvedReq->method, "Debugger.setPauseOnExceptions"); - EXPECT_EQ(resolvedReq->state, "TODO: THIS SHOULD NOT BE ACCEPTED BY ENUM"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); - EXPECT_EQ(resolvedReq->state, deserializedReq.state); -} - -TEST(MessageTests, testStepIntoRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.stepInto" - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::StepIntoRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::StepIntoRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.stepInto"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); -} - -TEST(MessageTests, testStepOutRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.stepOut" - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::StepOutRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::StepOutRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.stepOut"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); -} - -TEST(MessageTests, testStepOverRequest) { - std::string message = R"( - { - "id": 10, - "method": "Debugger.stepOver" - } - )"; - - // Builder does not throw - auto req = Request::fromJsonThrowOnError(message); - debugger::StepOverRequest *resolvedReq = - dynamic_cast(req.get()); - - // Builder returns correct type - EXPECT_FALSE(resolvedReq == nullptr); - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::StepOverRequest deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(resolvedReq->id, 10); - EXPECT_EQ(resolvedReq->method, "Debugger.stepOver"); - - EXPECT_EQ(resolvedReq->id, deserializedReq.id); - EXPECT_EQ(resolvedReq->method, deserializedReq.method); -} - -TEST(MessageTests, testEvaluateOnCallFrameResponseMinimal) { - std::string message = R"( - { - "result": - { - "result":{ - "type": "string" - } - }, - "id":2 - } - )"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::EvaluateOnCallFrameResponse deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - EXPECT_FALSE(deserializedReq.result.subtype.has_value()); - EXPECT_FALSE(deserializedReq.result.value.has_value()); - EXPECT_FALSE(deserializedReq.result.unserializableValue.has_value()); - EXPECT_FALSE(deserializedReq.result.description.has_value()); - EXPECT_FALSE(deserializedReq.result.objectId.has_value()); - EXPECT_FALSE(deserializedReq.result.preview.has_value()); - - // Specifics - EXPECT_EQ(deserializedReq.id, 2); - EXPECT_EQ(deserializedReq.result.type, "string"); -} - -TEST(MessageTests, testEvaluateOnCallFrameResponseFull) { - std::string message = R"( - { - "result": - { - "result":{ - "type": "string", - "subtype": "SuperString", - "value": {"foobarkey": "foobarval"}, - "unserializableValue": "unserializableValueVal", - "description": "A Wonderful desc", - "objectId": "AnObjectID", - "preview": { - "type": "object", - "overflow": false, - "properties": [] - } - } - }, - "id":2 - } - )"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::EvaluateOnCallFrameResponse deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - EXPECT_TRUE(deserializedReq.result.subtype.has_value()); - EXPECT_TRUE(deserializedReq.result.value.has_value()); - EXPECT_TRUE(deserializedReq.result.unserializableValue.has_value()); - EXPECT_TRUE(deserializedReq.result.description.has_value()); - EXPECT_TRUE(deserializedReq.result.objectId.has_value()); - EXPECT_TRUE(deserializedReq.result.preview.has_value()); - - EXPECT_EQ(deserializedReq.result.subtype.value(), "SuperString"); - EXPECT_EQ( - deserializedReq.result.value.value(), - folly::parseJson(R"({"foobarkey": "foobarval"})")); - EXPECT_EQ( - deserializedReq.result.unserializableValue.value(), - "unserializableValueVal"); - EXPECT_EQ(deserializedReq.result.description.value(), "A Wonderful desc"); - EXPECT_EQ(deserializedReq.result.objectId.value(), "AnObjectID"); - - // Specifics - EXPECT_EQ(deserializedReq.id, 2); - EXPECT_EQ(deserializedReq.result.type, "string"); - EXPECT_EQ(deserializedReq.result.preview->type, "object"); - EXPECT_EQ(deserializedReq.result.preview->overflow, false); - EXPECT_EQ(deserializedReq.result.preview->properties.size(), 0); -} - -TEST(MessageTests, testSetBreakpointByUrlResponse) { - std::string message = R"({ - "id": 1, - "result":{ - "breakpointId": "myBreakpointId", - "locations": [ - { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - } - ] - } - })"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetBreakpointByUrlResponse deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(deserializedReq.id, 1); - EXPECT_EQ(deserializedReq.breakpointId, "myBreakpointId"); - EXPECT_EQ(deserializedReq.locations.size(), 1); - EXPECT_EQ(deserializedReq.locations[0].lineNumber, 2); - EXPECT_EQ(deserializedReq.locations[0].columnNumber, 3); - EXPECT_EQ(deserializedReq.locations[0].scriptId, "myScriptId"); -} - -TEST(MessageTests, testSetBreakpointResponse) { - std::string message = R"({ - "id": 1, - "result":{ - "breakpointId": "myBreakpointId", - "actualLocation": - { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - } - } - })"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetBreakpointResponse deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(deserializedReq.breakpointId, "myBreakpointId"); - EXPECT_EQ(deserializedReq.actualLocation.lineNumber, 2); - EXPECT_EQ(deserializedReq.actualLocation.columnNumber, 3); - EXPECT_EQ(deserializedReq.actualLocation.scriptId, "myScriptId"); - EXPECT_EQ(deserializedReq.id, 1); -} - -TEST(MessageTests, testSetInstrumentationBreakpointResponse) { - std::string message = R"({ - "id": 1, - "result":{ - "breakpointId": "myBreakpointId" - } - })"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::SetInstrumentationBreakpointResponse deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(deserializedReq.breakpointId, "myBreakpointId"); - EXPECT_EQ(deserializedReq.id, 1); -} - -TEST(MessageTests, testBreakpointResolvedNotification) { - std::string message = R"( - { - "method": "Debugger.breakpointResolved", - "params":{ - "breakpointId" : "42", - "location": - { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - } - } - } - )"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::BreakpointResolvedNotification deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(deserializedReq.method, "Debugger.breakpointResolved"); - EXPECT_EQ(deserializedReq.breakpointId, "42"); - EXPECT_EQ(deserializedReq.location.lineNumber, 2); - EXPECT_EQ(deserializedReq.location.columnNumber, 3); - EXPECT_EQ(deserializedReq.location.scriptId, "myScriptId"); -} - -TEST(MessageTests, testPauseNotificationMinimal) { - std::string message = R"( - { - "method": "Debugger.paused", - "params":{ - "reason": "Some Valid Reason", - "callFrames":[ - { - "callFrameId": "aCallFrameId", - "functionName": "aFunctionName", - "location":{ - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - }, - "url": "aURL", - "scopeChain": [ - { - "type": "aType", - "object": { - "type": "aRemoteObjectType" - } - } - ], - "this": { - "type": "aType" - } - } - ] - } - } - )"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::PausedNotification deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - EXPECT_FALSE(deserializedReq.callFrames[0].functionLocation.has_value()); - EXPECT_FALSE(deserializedReq.callFrames[0].returnValue.has_value()); - EXPECT_FALSE(deserializedReq.asyncStackTrace.has_value()); - EXPECT_FALSE(deserializedReq.hitBreakpoints.has_value()); - EXPECT_FALSE(deserializedReq.data.has_value()); - - // Specifics - EXPECT_EQ(deserializedReq.method, "Debugger.paused"); - EXPECT_EQ(deserializedReq.reason, "Some Valid Reason"); - EXPECT_EQ(deserializedReq.callFrames[0].functionName, "aFunctionName"); - EXPECT_EQ(deserializedReq.callFrames[0].callFrameId, "aCallFrameId"); - EXPECT_EQ(deserializedReq.callFrames[0].url, "aURL"); - EXPECT_EQ(deserializedReq.callFrames[0].location.lineNumber, 2); - EXPECT_EQ(deserializedReq.callFrames[0].location.columnNumber, 3); - EXPECT_EQ(deserializedReq.callFrames[0].location.scriptId, "myScriptId"); - EXPECT_EQ(deserializedReq.callFrames[0].scopeChain[0].type, "aType"); - EXPECT_EQ( - deserializedReq.callFrames[0].scopeChain[0].object.type, - "aRemoteObjectType"); - EXPECT_EQ(deserializedReq.callFrames[0].thisObj.type, "aType"); -} - -TEST(MessageTests, testPauseNotificationFull) { - std::string message = R"( - { - "method": "Debugger.paused", - "params":{ - "reason": "Some Valid Reason", - "callFrames":[ - { - "functionLocation": { - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - }, - "returnValue" : { - "type": "aRemoteObjectType", - "subtype": "subtype", - "className":"className", - "value": "value", - "unserializableValue": "unserializableValue", - "description": "description", - "objectId": "objectId" - }, - "callFrameId": "aCallFrameId", - "functionName": "aFunctionName", - "location":{ - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId" - }, - "url": "aURL", - "scopeChain": [ - { - "type": "aType", - "object": { - "type": "aRemoteObjectType" - } - } - ], - "this": { - "type": "aType" - } - } - ], - "data": {"dataKey": "dataVal"}, - "hitBreakpoints": [ - "foo","bar" - ], - "asyncStackTrace":{ - "description": "an asyncStackTrace Desc", - "callFrames":[ - { - "functionName": "aFunctionName", - "lineNumber": 2, - "columnNumber": 3, - "scriptId": "myScriptId", - "url": "aURL" - } - ] - } - } - } - )"; - - std::optional functionLocation; - std::optional returnValue; - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::PausedNotification deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Check optionnals - // ---------------- - EXPECT_TRUE(deserializedReq.callFrames[0].functionLocation.has_value()); - EXPECT_TRUE(deserializedReq.callFrames[0].returnValue.has_value()); - EXPECT_TRUE(deserializedReq.asyncStackTrace.has_value()); - EXPECT_TRUE(deserializedReq.hitBreakpoints.has_value()); - EXPECT_TRUE(deserializedReq.data.has_value()); - - EXPECT_TRUE( - deserializedReq.callFrames[0].returnValue.value().subtype.has_value()); - EXPECT_TRUE( - deserializedReq.callFrames[0].returnValue.value().className.has_value()); - EXPECT_TRUE(deserializedReq.callFrames[0] - .returnValue.value() - .unserializableValue.has_value()); - EXPECT_TRUE( - deserializedReq.callFrames[0].returnValue.value().value.has_value()); - EXPECT_TRUE(deserializedReq.callFrames[0] - .returnValue.value() - .description.has_value()); - EXPECT_TRUE( - deserializedReq.callFrames[0].returnValue.value().objectId.has_value()); - - // Check optionnals Values - // ----------------------- - EXPECT_EQ( - deserializedReq.callFrames[0].functionLocation.value().lineNumber, 2); - EXPECT_EQ( - deserializedReq.callFrames[0].functionLocation.value().columnNumber, 3); - EXPECT_EQ( - deserializedReq.callFrames[0].functionLocation.value().scriptId, - "myScriptId"); - - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().type, - "aRemoteObjectType"); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().subtype.has_value(), - true); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().subtype.value(), - "subtype"); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().className.has_value(), - true); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().className.value(), - "className"); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().value.has_value(), - true); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().value.value(), "value"); - EXPECT_EQ( - deserializedReq.callFrames[0] - .returnValue.value() - .unserializableValue.has_value(), - true); - EXPECT_EQ( - deserializedReq.callFrames[0] - .returnValue.value() - .unserializableValue.value(), - "unserializableValue"); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().description.has_value(), - true); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().description.value(), - "description"); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().objectId.has_value(), - true); - EXPECT_EQ( - deserializedReq.callFrames[0].returnValue.value().objectId.value(), - "objectId"); - - EXPECT_EQ(deserializedReq.hitBreakpoints.value()[0], "foo"); - EXPECT_EQ(deserializedReq.hitBreakpoints.value()[1], "bar"); - - EXPECT_EQ( - deserializedReq.data.value(), - folly::parseJson(R"({"dataKey": "dataVal"})")); - - // Check Compulsory - // ---------------- - EXPECT_EQ(deserializedReq.method, "Debugger.paused"); - EXPECT_EQ(deserializedReq.reason, "Some Valid Reason"); - EXPECT_EQ(deserializedReq.callFrames[0].functionName, "aFunctionName"); - EXPECT_EQ(deserializedReq.callFrames[0].callFrameId, "aCallFrameId"); - EXPECT_EQ(deserializedReq.callFrames[0].url, "aURL"); - EXPECT_EQ(deserializedReq.callFrames[0].location.lineNumber, 2); - EXPECT_EQ(deserializedReq.callFrames[0].location.columnNumber, 3); - EXPECT_EQ(deserializedReq.callFrames[0].location.scriptId, "myScriptId"); - EXPECT_EQ(deserializedReq.callFrames[0].scopeChain[0].type, "aType"); - EXPECT_EQ( - deserializedReq.callFrames[0].scopeChain[0].object.type, - "aRemoteObjectType"); - EXPECT_EQ(deserializedReq.callFrames[0].thisObj.type, "aType"); -} - -TEST(MessageTests, testResumedNotification) { - std::string message = R"( - { - "method": "Debugger.resumed" - } - )"; - - // Serialize and Deserialize are inverse functions - dynamic messageJSON = folly::parseJson(message); - debugger::ResumedNotification deserializedReq(messageJSON); - EXPECT_EQ(deserializedReq.toDynamic(), messageJSON); - - // Specifics - EXPECT_EQ(deserializedReq.method, "Debugger.resumed"); -} - -} // namespace message -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/RemoteObjectsTableTest.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/RemoteObjectsTableTest.cpp deleted file mode 100644 index 834a8e72706..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/RemoteObjectsTableTest.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -namespace { - -struct TestContext { - TestContext() { - scope1 = table.addScope(std::make_pair(1, 1), BacktraceObjectGroup); - scope2 = table.addScope(std::make_pair(2, 1), ConsoleObjectGroup); - scope3 = table.addScope(std::make_pair(3, 1), ""); - value1 = table.addValue(jsi::Value(1.5), BacktraceObjectGroup); - value2 = table.addValue(jsi::Value(2.5), BacktraceObjectGroup); - value3 = table.addValue(jsi::Value(3.5), ""); - } - - RemoteObjectsTable table; - std::string scope1; - std::string scope2; - std::string scope3; - std::string value1; - std::string value2; - std::string value3; -}; - -} // namespace - -TEST(RemoteObjectsTableTest, TestGetScope) { - TestContext ctx; - - EXPECT_EQ(ctx.table.getScope(ctx.scope1)->first, 1); - EXPECT_EQ(ctx.table.getScope(ctx.scope2)->first, 2); - EXPECT_EQ(ctx.table.getScope(ctx.scope3)->first, 3); - EXPECT_TRUE(ctx.table.getScope(ctx.value1) == nullptr); - EXPECT_TRUE(ctx.table.getScope(ctx.value2) == nullptr); - EXPECT_TRUE(ctx.table.getScope(ctx.value3) == nullptr); -} - -TEST(RemoteObjectsTableTest, TestGetValue) { - TestContext ctx; - - EXPECT_TRUE(ctx.table.getValue(ctx.scope1) == nullptr); - EXPECT_TRUE(ctx.table.getValue(ctx.scope2) == nullptr); - EXPECT_TRUE(ctx.table.getValue(ctx.scope3) == nullptr); - EXPECT_EQ(ctx.table.getValue(ctx.value1)->asNumber(), 1.5); - EXPECT_EQ(ctx.table.getValue(ctx.value2)->asNumber(), 2.5); - EXPECT_EQ(ctx.table.getValue(ctx.value3)->asNumber(), 3.5); -} - -TEST(RemoteObjectsTableTest, TestGetObjectGroup) { - TestContext ctx; - - EXPECT_EQ(ctx.table.getObjectGroup(ctx.scope1), BacktraceObjectGroup); - EXPECT_EQ(ctx.table.getObjectGroup(ctx.scope2), ConsoleObjectGroup); - EXPECT_EQ(ctx.table.getObjectGroup(ctx.scope3), ""); - EXPECT_EQ(ctx.table.getObjectGroup(ctx.value1), BacktraceObjectGroup); - EXPECT_EQ(ctx.table.getObjectGroup(ctx.value2), BacktraceObjectGroup); - EXPECT_EQ(ctx.table.getObjectGroup(ctx.value3), ""); -} - -TEST(RemoteObjectsTableTest, TestReleaseObject) { - TestContext ctx; - - ctx.table.releaseObject(ctx.scope1); - ctx.table.releaseObject(ctx.value3); - std::string scope4 = ctx.table.addScope(std::make_pair(4, 1), ""); - std::string value4 = ctx.table.addValue(jsi::Value(4.5), ""); - - EXPECT_EQ(ctx.table.getScope(ctx.scope1), nullptr); - EXPECT_EQ(ctx.table.getScope(ctx.scope2)->first, 2); - EXPECT_EQ(ctx.table.getScope(ctx.scope3)->first, 3); - EXPECT_EQ(ctx.table.getScope(scope4)->first, 4); - EXPECT_EQ(ctx.table.getValue(ctx.value1)->asNumber(), 1.5); - EXPECT_EQ(ctx.table.getValue(ctx.value2)->asNumber(), 2.5); - EXPECT_EQ(ctx.table.getValue(ctx.value3), nullptr); - EXPECT_EQ(ctx.table.getValue(value4)->asNumber(), 4.5); -} - -TEST(RemoteObjectsTableTest, TestReleaseObjectGroup) { - TestContext ctx; - - ctx.table.releaseObjectGroup(BacktraceObjectGroup); - std::string scope4 = ctx.table.addScope(std::make_pair(4, 1), ""); - std::string value4 = ctx.table.addValue(jsi::Value(4.5), ""); - - EXPECT_EQ(ctx.table.getScope(ctx.scope1), nullptr); - EXPECT_EQ(ctx.table.getScope(ctx.scope2)->first, 2); - EXPECT_EQ(ctx.table.getScope(ctx.scope3)->first, 3); - EXPECT_EQ(ctx.table.getScope(scope4)->first, 4); - EXPECT_EQ(ctx.table.getValue(ctx.value1), nullptr); - EXPECT_EQ(ctx.table.getValue(ctx.value2), nullptr); - EXPECT_EQ(ctx.table.getValue(ctx.value3)->asNumber(), 3.5); - EXPECT_EQ(ctx.table.getValue(value4)->asNumber(), 4.5); -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp deleted file mode 100644 index 4218afd0c51..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "SyncConnection.h" - -#include -#include - -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -using namespace std::placeholders; -using ::facebook::react::IRemoteConnection; - -namespace { - -std::string prettify(const std::string &str) { - try { - folly::dynamic obj = folly::parseJson(str); - return folly::toPrettyJson(obj); - } catch (...) { - // pass - } - - return str; -} - -} // namespace - -class SyncConnection::RemoteConnection : public IRemoteConnection { - public: - RemoteConnection(SyncConnection &conn) : conn_(conn) {} - - void onMessage(std::string message) override { - conn_.onReply(message); - } - - void onDisconnect() override {} - - private: - SyncConnection &conn_; -}; - -SyncConnection::SyncConnection( - std::shared_ptr runtime, - bool waitForDebugger) - : connection_( - std::make_unique(runtime), - "testConn", - waitForDebugger) { - connection_.connect(std::make_unique(*this)); -} - -void SyncConnection::send(const std::string &str) { - LOG(INFO) << "SyncConnection::send sending " << str; - - connection_.sendMessage(str); -} - -void SyncConnection::waitForResponse( - folly::Function handler, - std::chrono::milliseconds timeout) { - std::string reply; - - { - std::unique_lock lock(mutex_); - - bool success = hasReply_.wait_for( - lock, timeout, [this]() -> bool { return !replies_.empty(); }); - - if (!success) { - throw std::runtime_error("timed out waiting for reply"); - } - - reply = std::move(replies_.front()); - replies_.pop(); - } - - handler(reply); -} - -void SyncConnection::waitForNotification( - folly::Function handler, - std::chrono::milliseconds timeout) { - std::string notification; - - { - std::unique_lock lock(mutex_); - - bool success = hasNotification_.wait_for( - lock, timeout, [this]() -> bool { return !notifications_.empty(); }); - - if (!success) { - throw std::runtime_error("timed out waiting for notification"); - } - - notification = std::move(notifications_.front()); - notifications_.pop(); - } - - handler(notification); -} - -void SyncConnection::onReply(const std::string &message) { - LOG(INFO) << "SyncConnection::onReply got message: " << prettify(message); - - std::scoped_lock lock(mutex_); - - folly::dynamic obj = folly::parseJson(message); - if (obj.count("id")) { - replies_.push(message); - hasReply_.notify_one(); - } else { - notifications_.push(message); - hasNotification_.notify_one(); - } -} - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h deleted file mode 100644 index e8864f0fea5..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/SyncConnection.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace chrome { - -/** - * SyncConnection provides a synchronous interface over Connection that is - * useful in tests. - */ -class SyncConnection { - public: - SyncConnection( - std::shared_ptr runtime, - bool waitForDebugger = false); - ~SyncConnection() = default; - - /// sends a message to the debugger - void send(const std::string &str); - - /// waits for the next response from the debugger. handler is called with the - /// response. throws on timeout. - void waitForResponse( - folly::Function handler, - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - /// waits for the next notification from the debugger. handler is called with - /// the notification. throws on timeout. - void waitForNotification( - folly::Function handler, - std::chrono::milliseconds timeout = std::chrono::milliseconds(2500)); - - private: - class RemoteConnection; - friend class RemoteConnection; - - void onReply(const std::string &message); - - Connection connection_; - - std::mutex mutex_; - std::condition_variable hasReply_; - std::queue replies_; - std::condition_variable hasNotification_; - std::queue notifications_; -}; - -} // namespace chrome -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.cpp b/packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.cpp deleted file mode 100644 index a04524e7b14..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "CallbackOStream.h" - -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace detail { - -CallbackOStream::CallbackOStream(size_t sz, Fn cb) - : std::ostream(&sbuf_), sbuf_(sz, std::move(cb)) {} - -CallbackOStream::StreamBuf::StreamBuf(size_t sz, Fn cb) - : sz_(sz), buf_(std::make_unique(sz)), cb_(std::move(cb)) { - reset(); -} - -CallbackOStream::StreamBuf::~StreamBuf() { - sync(); -} - -std::streambuf::int_type CallbackOStream::StreamBuf::overflow( - std::streambuf::int_type ch) { - assert(pptr() <= epptr() && "overflow expects the buffer not to be overfull"); - - if (!pptr()) { - return traits_type::eof(); - } - - *pptr() = ch; - pbump(1); - - if (sync() == 0) { - return traits_type::not_eof(ch); - } - - // Set to nullptr on failure. - setp(nullptr, nullptr); - return traits_type::eof(); -} - -int CallbackOStream::StreamBuf::sync() { - try { - return pbase() == pptr() || cb_(take()) ? 0 : -1; - } catch (...) { - return -1; - } -} - -void CallbackOStream::StreamBuf::reset() { - assert(sz_ > 0 && "Buffer cannot be empty."); - // std::streambuf::overflow accepts the character that caused the overflow as - // a parameter. Part of handling the overflow is adding this character to the - // stream. We choose to do this by stealing a byte at the end of the "put" - // area where the character can be written, even if the area is otherwise - // full, immediately prior to being flushed. - setp(&buf_[0], &buf_[0] + sz_ - 1); -} - -std::string CallbackOStream::StreamBuf::take() { - const size_t strsz = pptr() - pbase(); - reset(); - return std::string(pbase(), strsz); -} - -} // namespace detail -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.h b/packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.h deleted file mode 100644 index 44a1ab7cdf6..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/CallbackOStream.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace detail { - -/// Subclass of \c std::ostream where flushing is implemented through a -/// callback. Writes are collected in a buffer. When filled, the buffer's -/// contents are emptied out and sent to a callback. -struct CallbackOStream : public std::ostream { - /// Signature of callback called to flush buffer contents. Accepts the buffer - /// as a string. Returns a boolean indicating whether flushing succeeded. - /// Callback failure will be translated to stream failure. If the callback - /// throws an exception it will be swallowed and translated into stream - /// failure. - using Fn = std::function; - - /// Construct a new stream. - /// - /// \p sz The size of the buffer -- how large it can get before it must be - /// flushed. Must be non-zero. - /// \p cb The callback function. - CallbackOStream(size_t sz, Fn cb); - - /// This class is neither movable nor copyable. - CallbackOStream(CallbackOStream &&that) = delete; - CallbackOStream &operator=(CallbackOStream &&that) = delete; - CallbackOStream(const CallbackOStream &that) = delete; - CallbackOStream &operator=(const CallbackOStream &that) = delete; - - private: - /// \c std::streambuf sub-class backed by a std::string buffer and - /// implementing overflow by calling a callback. - struct StreamBuf : public std::streambuf { - /// Construct a new streambuf. Parameters are the same as those of - /// \c CallbackOStream . - StreamBuf(size_t sz, Fn cb); - - /// Destruction will flush any remaining buffer contents. - ~StreamBuf(); - - /// StreamBufs are not copyable, to avoid the flush callback receiving - /// the contents of multiple streams. - StreamBuf(const StreamBuf &) = delete; - StreamBuf &operator=(const StreamBuf &) = delete; - - protected: - /// std::streambuf overrides - int_type overflow(int_type ch) override; - int sync() override; - - private: - /// The size of the backing buffer. Fixed for an instance of the streambuf. - size_t sz_; - - /// The backing buffer that writes will go to until full. - std::unique_ptr buf_; - - /// The function called when buf_ has been filled. - Fn cb_; - - /// Clears the backing buffer. - void reset(); - - /// Clears the backing buffer and returns it contents in a string. - std::string take(); - }; - - StreamBuf sbuf_; -}; - -} // namespace detail -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.cpp b/packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.cpp deleted file mode 100644 index 55e8278ecc1..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "SerialExecutor.h" - -namespace facebook { -namespace hermes { -namespace inspector { -namespace detail { - -SerialExecutor::SerialExecutor(const std::string &name) - : finish_(false), thread_(name, [this]() { runLoop(); }) {} - -SerialExecutor::~SerialExecutor() { - { - std::scoped_lock lock(mutex_); - finish_ = true; - wakeup_.notify_one(); - } - - thread_.join(); -} - -void SerialExecutor::add(folly::Func func) { - std::scoped_lock lock(mutex_); - funcs_.push(std::move(func)); - wakeup_.notify_one(); -} - -void SerialExecutor::runLoop() { - bool shouldExit = false; - while (!shouldExit) { - folly::Func func; - - { - std::unique_lock lock(mutex_); - wakeup_.wait(lock, [this] { return finish_ || !funcs_.empty(); }); - - if (!funcs_.empty()) { - func = std::move(funcs_.front()); - funcs_.pop(); - } - - shouldExit = funcs_.empty() && finish_; - } - - if (func) { - func(); - } - } -} - -} // namespace detail -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.h b/packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.h deleted file mode 100644 index 919a15b0913..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/SerialExecutor.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace detail { - -/// SerialExecutor is a simple implementation of folly::Executor that processes -/// work items serially on a worker thread. It exists for two reasons: -/// -/// 1. Currently Hermes builds for the host as well as in fbandroid and -/// fbobjc, so we need an implementation of a serial executor that doesn't -/// use the SerialAsyncExecutorFactory from fbandroid or fbobjc. -/// 2. None of folly's Executor factories are included in the stripped-down -/// version of folly in xplat. -/// -/// TODO: create a factory that uses SerialAsyncExecutorFactory if we're -/// building for fbandroid or fbobjc, and otherwise creates an instance of this -/// class. -class SerialExecutor : public folly::Executor { - public: - SerialExecutor(const std::string &name); - ~SerialExecutor() override; - - void add(folly::Func) override; - - private: - void runLoop(); - - std::mutex mutex_; - std::queue funcs_; - std::condition_variable wakeup_; - bool finish_; - - Thread thread_; -}; - -} // namespace detail -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/Thread.cpp b/packages/react-native/ReactCommon/hermes/inspector/detail/Thread.cpp deleted file mode 100644 index adfb6330efb..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/Thread.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#ifdef __ANDROID__ -#include "Thread.h" - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace detail { - -struct Thread::Impl { - facebook::jni::global_ref thread_; -}; - -Thread::Thread(std::string, std::function runnable) - : impl_(std::make_unique(Impl{facebook::jni::make_global( - facebook::jni::JThread::create(std::move(runnable)))})) { - impl_->thread_->start(); -} - -Thread::~Thread() {} - -void Thread::join() { - impl_->thread_->join(); -} - -} // namespace detail -} // namespace inspector -} // namespace hermes -} // namespace facebook - -#endif diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/Thread.h b/packages/react-native/ReactCommon/hermes/inspector/detail/Thread.h deleted file mode 100644 index e4bbc03e6e2..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/Thread.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -#ifdef _WINDOWS -#include -#elif !defined(__ANDROID__) -#include -#include -#endif - -namespace facebook { -namespace hermes { -namespace inspector { -namespace detail { - -#ifdef __ANDROID__ - -/// Android version of Thread that uses JThread, which is a java.lang.Thread. -/// This is desirable because real Java threads have access to the app's -/// classloader, which allows us to call in to Java from C++. -/// -/// The implementation is private to the .cpp file to avoid leaking -/// the fbjni dependencies into code which creates Threads. - -class Thread { - public: - Thread(std::string name, std::function runnable); - ~Thread(); - - void detach() { - // Java threads don't need to be explicitly detached - } - - void join(); - - private: - struct Impl; - std::unique_ptr impl_; -}; - -#else - -class Thread { - public: - Thread(std::string name, std::function runnable) - : thread_(run, name, runnable) {} - - void detach() { - thread_.detach(); - } - - void join() { - thread_.join(); - } - - private: - static void run(std::string name, std::function runnable) { -#if defined(_GNU_SOURCE) - pthread_setname_np(pthread_self(), name.c_str()); -#elif defined(__APPLE__) - pthread_setname_np(name.c_str()); -#endif - - runnable(); - } - - std::thread thread_; -}; - -#endif - -}; // namespace detail - -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/tests/CallbackOStreamTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/detail/tests/CallbackOStreamTests.cpp deleted file mode 100644 index 3b37d4465b8..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/tests/CallbackOStreamTests.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include -#include -#include -#include - -#include - -namespace { -using namespace ::testing; -using namespace facebook::hermes::inspector::detail; - -TEST(CallbackOStreamTests, Chunking) { - std::vector recvd; - CallbackOStream cos(/* sz */ 4, [&recvd](std::string s) { - recvd.emplace_back(std::move(s)); - return true; - }); - - cos << "123412341234"; - - EXPECT_THAT(recvd, ElementsAre("1234", "1234", "1234")); -} - -TEST(CallbackOStreamTests, SyncOnDestruction) { - std::vector recvd; - - { - CallbackOStream cos(/* sz */ 4, [&recvd](std::string s) { - recvd.emplace_back(std::move(s)); - return true; - }); - - cos << "123412341234123"; - ASSERT_THAT(recvd, ElementsAre("1234", "1234", "1234")); - } - - EXPECT_THAT(recvd, ElementsAre("1234", "1234", "1234", "123")); -} - -TEST(CallbackOStreamTests, ExplicitFlush) { - std::vector recvd; - CallbackOStream cos(/* sz */ 4, [&recvd](std::string s) { - recvd.emplace_back(std::move(s)); - return true; - }); - - cos << "123412341234123"; - EXPECT_THAT(recvd, ElementsAre("1234", "1234", "1234")); - - cos << std::flush; - EXPECT_THAT(recvd, ElementsAre("1234", "1234", "1234", "123")); -} - -TEST(CallbackOStreamTests, FlushEmpty) { - size_t i = 0; - CallbackOStream cos(/* sz */ 4, [&i](std::string) { return ++i; }); - - cos << "12341234"; - ASSERT_THAT(i, Eq(2)); - - // If the put area is empty, we will not flush. - cos << std::flush; - EXPECT_THAT(i, Eq(2)); -} - -TEST(CallbackOStreamTests, FailingCallback) { - size_t i = 0; - std::vector recvd; - CallbackOStream cos(/* sz */ 4, [&i, &recvd](std::string s) { - recvd.emplace_back(std::move(s)); - return ++i < 2; - }); - - cos << "123412341234"; - EXPECT_THAT(recvd, ElementsAre("1234", "1234")); - EXPECT_THAT(!cos, Eq(true)); -} - -TEST(CallbackOStreamTests, ThrowingCallback) { - size_t i = 0; - std::vector recvd; - CallbackOStream cos(/* sz */ 4, [&i, &recvd](std::string s) { - if (i++ >= 2) { - throw "too big"; - } - recvd.emplace_back(std::move(s)); - return true; - }); - - cos << "123412341234"; - EXPECT_THAT(recvd, ElementsAre("1234", "1234")); - EXPECT_THAT(!cos, Eq(true)); -} - -} // namespace diff --git a/packages/react-native/ReactCommon/hermes/inspector/detail/tests/SerialExecutorTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/detail/tests/SerialExecutorTests.cpp deleted file mode 100644 index 4a74de467b6..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/detail/tests/SerialExecutorTests.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include -#include - -#include - -namespace facebook { -namespace hermes { -namespace inspector { -namespace detail { - -TEST(SerialExecutorTests, testProcessesItems) { - std::array values{}; - - { - SerialExecutor executor("TestExecutor"); - - for (int i = 0; i < values.size(); i++) { - executor.add([=, &values]() { values[i] = i; }); - } - } - - // By this time the serial executor destructor should have exited and waited - // for all work items to complete. - for (int i = 0; i < values.size(); i++) { - EXPECT_EQ(values[i], i); - } -} - -} // namespace detail -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/docs/GenerateInspectorFSM.sh b/packages/react-native/ReactCommon/hermes/inspector/docs/GenerateInspectorFSM.sh deleted file mode 100755 index 6218d33d6ea..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/docs/GenerateInspectorFSM.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -dot -Tpdf InspectorFSM.gv -o InspectorFSM.pdf diff --git a/packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.gv b/packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.gv deleted file mode 100644 index 7b537f8638a..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.gv +++ /dev/null @@ -1,39 +0,0 @@ -digraph InspectorFSM { - node [shape = point]; - init1; - init2; - - node [shape = circle]; - init1 -> RWE [ label = " pauseOnFirstStmt " ] - init2 -> RD [ label = " !pauseOnFirstStmt " ] - RD -> RD [ label = " didPause" ]; - RD -> PWE [ label = " debuggerStmt " ]; - RWE -> PWE [ label = " didPause " ]; - RWP -> P [label = " didPause " ]; - RD -> R [ label = " enable " ]; - PWE -> P [label = " enable " ]; - RWE -> RWP [ label = " enable" ]; - R -> P [ label = " !implicitPause " ]; - R -> R [ label = " implicitPause "]; - P -> R [ label = " receivedCommand " ]; - P -> RD [ label = " disable "]; - R -> RD [ label = " disable "]; - - { rank = same; RD RWE } - { rank = same; R P } - - label = < - - - - -
- - - - - - -
AbbrevState
RWERunningWaitEnable
RWPRunningWaitPause
RDRunningDetached
PWEPausedWaitEnable
PPaused
RRunning
>; - labelloc = "b"; -} diff --git a/packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.pdf b/packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.pdf deleted file mode 100644 index f2daf5a4fd049ae07552396040fbdbefb0f3ed0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18808 zcmbrkb97}*zcm`$c6QRSZQHiBV|9#t^y)t(mA#c`H7ij&qOh|51+8EhM{Ql-SZN@2$$epNCt~OS^iYtd6Bhd|F;Qy@e zQ{$_-+3{>P|CBU)*R5zFR^rPxX7lv%D(vI^BRVhGK>I=hT}FlCi227F@=4a3E-Q}f z3$m*U=gEEE$(^0LkAB>h7aau@@V87H3%HlBN~z#_SHh-KL>oX3v-}2++$F`yhg^ zBd{V$rAvysogPbjd^|y__pZL49*?eWL;cc&CLlnsKk|b0?aT4Cc2KVc7~6L()=mcO7}FUuyB|X`1qMF3s&Mm9z^0n=z%Ma2F z^~T3d`e!1Q@xX=5K@7gC!~yG%z{BKJ5ly!Ii|TZ8a3Du4Noc(050vGjuP$@Jij>lu z$c8T39Hyitq16%JA_mfc-3;7}8m=o&nI%>UjW-dj8)rc=N-K=6fE95Xf7<29q-O11 za!lVV-N>F_?Vv}lh7_XG`aa0MY@_KZVu52Ama7}Mdm{P-F+*xoGNvXWZc7dpE1mVg zhF`>kTjHcT(Zx>l%Pa9z-wJ$f#818^5-a%GxSnq_o~xSD5PQ4YH~o>a!4G8}%A;Rq`wFO|w?B(ZI%3FwlV95a8Qnzn0Zub$apcOA{v-22Ipwd*`H zZ{o4ykcuJ~r9NKan^|2h`|R7E(Fi6A-ujE#dk-{~;$|M)(hd|GHP9B;lP1IsO4@yh z4s|z?(2his{zoBUP72>LJG=B|++myCH5a$XQKp^_q`l%X1gAFRh5MOo#@D<3D6(`eK1^(JMLKvYNoxA>W*PxZU?NmK2 z(icHEel`c|X~rDG zj8(BBm0Usqg*`Ikp^(8$=0Hw9O$=Wle7FRIs1#Jj+#*7~j&WR=50#IN6Q;?uZ6qs_ zL_)y&a$cz8#H&ec``xbaTWoLdy_C5*ov;V1VE~NahI4>)e>Eg*vLD(SY9-^4%N(LL z5_A7^U{?i)xe?!O{UToea~&D{G3D6+WBSy*H9}@?s9U^2Np6ffWLEi;X{3Tzx%TW4 zXsgcX*-^t5?xb=!3Ust>B-pUf22-}U3~B$c&#*>czY51;=J#gdrono1df|{2ZMg5O zS|*Uta(Se7rwOKv*Mnml<&2M0H-ig&B#qQ~!X!^~vJNs&VdJ{eS>K<)_Kq^BpkXoE zwT9x$V_8z>l{1?lJD89kVGiBYL`lKU;HZwA$Q^93%adZGf9Y8|CQCYv2uon4<1*r= zT9x%@?oghM?Ha(raM7Fj*2>0@@E`$BD|u}>8S2Jy$=3CI|LB8Xv@>38woDP%upy)O zElfBr95dBhSpXoQ>Tn;~GKw3`7}FDCe-CqMMXKtn+vI5dCI`uSMgQ{VoF=VPeW=4W zkyvms>};1(5M_kXOV2J3HQTmcBkNJEy#QRl7FXpRx9+CRxvIScQnQ%xT5~c>9pCFN z=T%P@S;KgJ|qz)5cHu)PUHvrcjUk8&ST;fHheL`_%%)ZqEwdDN! zP3((5CQ`7l<2MqjOuU%rd)=f%sBug$WFYB`)Y(kC?x-;ZpI`v5<(o@r##|>;z*6~f zlesoamQb<%>{V@|YcdOGm4{*cFU-lI2~;7{v|kN_?PMv(F!WAs-~UFfE=paN#qLV> z@YW_aa%ZcaMqiro#X^gWJ{Os46o|9~DU?{bcG>0nfi7w~OC(AVH~qR zd5=e))qo*K5l9&IKf~&5T=}j)TGk$mHLZ;`?Vc1{z=WX=i`gc@!b1>hj^>SifkX!w zQO;EC6YvT@cMkZKZYI!cOnQ!JKrH!3d52K!W>wc9yJjOrc4!+2zh#3Pwrh!7ZtDHH zQvr0EKdjlojcq6#QC{jXmLhp@A<^cNiORW>Y$+HXnuHWsYBiSlmw+7+`MppJC31uB zEcY+6PxmVIf>XEt zp5&C|#7v~nejqA5kmK+tVql;QeLwvVm_Z82*s}8J`>Mf$eC-hulfa+aCy5}pS@_d5 zP!+&Ha7_pla2Qz1izF0CZo5J9lTxE4B920TP4lU_#6N85j8I9sFcA#Jw$+Bipd$@L zSRr=6C(C)kZ@Uk0zspC*Q8z)<1x57jNGgtMlLZQ+lT$NHT~!BG$9KaftWfbItX)Hu zBl&tf9Ka41m9a|))MIm8H8zdnMt3t)i zbP)zs$JR>Pk)!)9D6Pl8>UzV!oxYZ0(j%<)-9PWl3`ptc;2>UOb7NDBQy?M0leyjm zkWq2Mi8e#M>?=NY21~podlFOv zih;|`2_J!3p>>F(VL91~sAO%WHfl$;z)Re>%5-A`j;5&%Xupoa@UAHXQ;VC&$d z>S$~V1pJjXqRz|!&VNlWAn++?J}v$WR#RgJaQ%fpXa6f~{%wH!zZv+qAY%UO%cpY+ zpJ6Zx|5ZiypOOf`C_|YN+A<4}-s1`x;%h5{2{PhhG9IrzjZ|2%Pu=2cItm_C8p$YQrEd%Ll~R zZ3;C@C^HLI0c>F<>Fz*{?S1J1ZTo0?8pU1#p7tbe2U~7*yV6D``F2q>M1fv9`Ibe| zH)vp|nsg?}{sYzA++(&J2opwz;jcrA0^0}{aSYr)5!YZKFv$bl9ZpD5Pu!NdARZ+a zdyx(!*#0#Rsp6pI+IAV13FlP= z=jYms2bNFWE1N{Vs&D)wUN7ob6lYVp!qK_fo3>|_jGb5@_hdR*gbiIO>L87FP(u6L zm5LVtkZVYg#*OS7qEK6d#DN_IO8BU=Gh1iSJR1huBhHt+K!>sJQ0|9}@jovbyd~Iz z0bissS%KpFC8Z^@V!wVNAf-*2y*;3O?DlY5O^o)_W#{!^Uxgg0D<37p21i)7hwuex z#WR?}1^X>*`{8gFTho zN~m#;WAS=Fd?tq1i?rXC6DCzXxJQUHb0ROK!!6njw(SK&i-&f}hhqz1U;?8vLL~5K zbp+o4fCT$Xp@64?Gf0712|?5g3oL`c2{SK)x&>J~g3JeaO+zRIayUZiL1^|MI>ICP zOA5h-_R2DWZUk7t1YV+G77ACyV|_=?5LraQ*MadA6;|TH03Q^Mh(}2jdYYyv!=VYP zC=gZRzy0EZ*9M~>^qdbp4Pb+M1N8|;O$VDZMqCDI_Ft*xC4iCa8{ZQ1z|4lm?pfQG zX~hyiYU#t?#=iui3o-0Vf)N^l0iTi3g2BHM6N%R%14SjVNg(|WRxX|ykMte?XN*+B z*FA8kuma=(&4LR%s4s#UNfTzG>lZCnxEy_wkH&_nT ztQh(M4Wl?C-P*J|@MYsOUIL`y9^4%QN8eT)Jr;ZRYKRu(rRaA5(|*KliYt!}nhlJL zK;&NMOOtOTU2uK`engvb#6#Vb@vu4&Hi5__sQ@xtGBp%Dm_wMiVAMV~<$y9p7t&A^ z&jCFX>blrv;SOm63WX#L=}GYf^m*eu_?#q){F-Nhcg_Ap^x~vOItx zIdY<)QZf|wB6CF zq#p@3DW_@PBz`jQC9kbu%DqCpssTl{8U+&DjeYzf(&CwgDJJ12 znI;h?K|Ab&%H$o10f{YWiseGbwX@{KBzPio#h$aRjq;5=tH4zzLP^f=FLO!9gSW&- zowu++a-i-$)jq*K-hRWF3REAH(Icx0ZgF%{bbEB%4co!bsokkHjv6b2j}GdTCmMU2 zZz-fH<|&{laTRjv4ht9yITdylit|j?6xPPpE7mUy3_oI~N~hd&r*aJ0KK5S2PHNjC zyjq{F@A^;pPm=I5@w)JE@zn9s*oN3~Gwm}+GxswO*@hb}b>N#IboO-)n+%vzGXDHh zGbe9|;x4GFJ};%85%|9TgTKnOtU=47NTFd8T|bdQms7Y!uSL~eL{XMei_x$~`JzBT zIj<`3d$(k_uHRVCG+@;zN;s>xtRS+W+c?*_XxsElMZ%96nj)Pd-XSj2=C6@g0=v2t zwGlbeiPABn@~X@88)`;vQ;kyyHV2jzY(LoBhYM?eYin0ZEar3f^NoBf^2~aoh3Ajb zips%S$1pu74 z&D5`5$7)w-pSnl7Cw-xKVTR@p^9buidRTUGqiPTKSwKBK+HRe^ir$5$^rozvlP=xN z(VnE9M4T*wN5HB# z*qht)`pwei;q%gqA_NyiCF~L0cL)gx4QK&)7FaY$SBU;z(BAeyV#ZlRGQD{$Fp*bL zw-7#9b_9LQXJ;G+I<`uRGtX9a4_^;=2znE7G0}l`sko8Z5y_|+ELkjfiBZWx@dZhF zac}W7aoIF2I_-u%iD;c@KnXtUhIm?XNUM_{4;pC?65%edYUd)whN}-S;7ulG?*w5)X%H4`Z(MAAzq45NW~n!82lM z1NsrB($&(K(z{@a!cAMQ^@i60d*#XT$wkzx6&rjdLR8#*wwO`mFqvos9i# zMNFb?V^#}))X@utW)kG6oVCr{(_`P|?xdp0vdDw9;Tx>$H!LK9d1K1IP!DeRGWYDq z2}f^6Uwqlk+1*_&y4>DFUwhgA*dSGfELQ8S{Zw$*?`_+@>pG3I1K3HeT#ieO#{ls+ z?ISnlmGhNryNoOwE)Ir7eu^k>chXPlBH0ewV%nasn>SQhuceV1%-orb??mn>lK)OT z-PHEcd|Ll?AAH_fd|&+HzU2nv{(Jqn?M5rZMB89xx_V2$YX8@D;dM*9=2@49vF=wR zWUDx{xJH(;`c9b|YsrPulkX=1cyXBrohRNc=Q*pxYfIgmc7Ai9G2nyH0z?=D9eyJZ zN;Xx(c7w*hw=4UnP76=kx-I++{lw0tt`epsLnMob`{G;|c;}Zm%Q%xc`eG7?>UO^l zJG^b(iF%{|I96gU`uPaKMZ$fxGyF(=k&D2$;u+~vaZLG^ci%j%6RA_M_Qs0i#Z9!(d4w#L2L zoa)-@jq!lA8iT8!PUUNzFR$upKP;-b^?7}Ad^*oV-(nVt+H+G4&wQlDH}2bf_G3SC@WG>?p8qg(d4CxE=0En?c$Y`r<;C@8`DS{y zTSy6~NRwM3SoOB>xMW`5d3%<>WHi+fZV?ySD$4kg@>HYP4>?snMJv%LaUxkIITigB zz3x?aJ9s%Boi?`{(oOx|{E#y9BhT0UrPIP>bl}ov<}^c4W}~c&`+4!+?$GRLZ_eH7 zqNFr(<42d^eZX76i|Orf!tt5nqhd;4jG+5B*w|3yRp#kDz@ zS^h&;{}S83*e3^;78MmSb_SXO{!&;Kfc}4m{N>*Nq0Ik9&7J?h$o60U?E1-(nE{Nl zR%Xth?D{Xa|D5l?kz{7(|23zmv5T>-gT+6*-}&E!|G#MUU#9&(o%k0KXH<1Haru{A zP;+tx{^Q->ApYr;Qq$CsQlM3{vI9EPt2o#h+yBGg#emMHPF9XC4o+~)|2W4eZvS~e zVP*emU*6dCUqk;3k+5=db`iBSb^@@ne4cU`|GUA=%mrZ7v@&zC{8x~lT85SNZ?69T zT%0U_{o&x?g!^0L_{weWL74Oo0Dherlsn zO zO=bWy8`ozD|JTE?PH$Kb4E3%HZuJHI1#egP*%y^g_tK^B4X#IIDc{k-jA7%23L(%| zl+93!$^fxNfRm(v(@=PpqO!6ybM{uJltIXp4Afy^LJJAKTvPLd4N!{&myHiKXqS$z zhd=J!51rNXD?cW=yKe+P&eb$(+PpwjitfTCb~=FIwDGsB;V6VzoUJG`#gGX=As|is~SgRK^2F6V*n#sow@J9{(!JQxZhI`Ie4E@N^)PL<-2fF_evK2qFJfKv`F*-AoFX|ddNW$ zU5(KoX<1+~Ks-cIy|ZZQ|k`!Sx|@5O=;?5p8dj*-{44rIK|+pJhA zi53pn5%ESFR|bt_lZ1Z@@ttUnVxM(JX$@2Ibb$#eEL6Bsz+QCS9!f&(=!B1w$9qL& zQP7a$N$ACUO%(n`f<+&exUW=CAd@Nz8+0I2jqa5W&y6P^LU8O_kt{1* zjp{^Aoff>27%wZs%{h2JnEgobj-fa-hkW$ArgFZ`u!qotr48Zd*QX8L(l*0B-UNTP zM+WNGgfdLFZdA5X$O-+x0}0_9v@SwdkvmM4aB;iRgEs|v+0v3aRn?_(Pog+cQamVJXOhvc30}EK3epAb~ak$1GJ1%fX zy;wAr{n^fARwbSG7Ms|1>3})Yi%KEx*bdgN7GqkqwDXwGef!y66qzj$j2C5{iu;1b zTdWH$y3IAr;Dh=%rs&?fKBS zNAoamqKNJ^i@%mMB|Y~XRc7VXmX)aB?$fXP=PP&msDXPZU)Pep_E`rhiF{3maUw6= zoGDVz!4X>2SN8`}H-@!|#aeaSNW^;3#X+7Gw8@^U>{L}}nb_Suf_~6-CvwrE zZpW|fF+%7knZk}t)c%Y-=hD6ps@+;CDhFmW4+!|s_-`1CQMP(CH}58%SxYRh?1 z@6%Hm-KC}yioR(LoQA?^MUxw`-xd&z-KI!hx!)Vv-C_JB3JpfQFAuBcik%dzTb5=M z^5$1qg9UB$Ss*ZFp{_~GUlHK0cR<*VMy8TCf=HE;d4t=eATA0jl!H5~F+}5+SdSRmv;_ml;-#eK4`aBL{JSC4D4@aU1{lb{l zl0ExY8(RWO-R6XU(j64%j^T zQIqSupza)R%U)b7wjNGx0{}{7f!Wq?W6)dx(&0?46RQ4tP@%-CK%d= zJwtsTri@iT#2-G$5{=>G* zw)o7@%zMwzh!g7o^8gzPo)g0W=NzZg{q}gKp9v2+I<(S?Hxr~GC$!B761(t}tdbK| zC-CY_6ncvXrpSw^(_f)y**9zwY$6Am00RvPONcatl`9J0D4U)ujM9IV71O=<`Bxvm z2MFV!%@xn9=_^1Fd7F?a?#+>~Z)%tGO8S+yb#kj_>zS6kNNY^wsSH4_Lg+XVXyczL zNnetxG+xHLy()cPa<3}NR)~MZ;RRC7=aViV{t@|}{?2P^s=>O%`Z1Jkq`+#C{$bRW zdR3oXsmq~ipJ1^UbS=66vOKwVX!KgcRC@7#P{2OVKQB1VW6gKguq1TQDcsaA zQ#tr@S>!)&dL+C|oRS~q2a`-)=+v6*_y)s77X%+sVAOMQ;|0+&eq6g&iPccvIAA$_ z!&4BnvVm_es-A9*tGNEK$uc`e19SV zA7X|S0^As1JWUj=9n%R%y?q~r@m%mzRxpP#GaVOtyh)i>Z8<0GT*gcTL|-+LqQO~{ zp&Y}}c~#JZ*r@jws%2L>_C~GC86t|T_$aC+MwBbc%*G096=k=8#Z=T!1AhpqGe--> zcP1rG2gb%g?${WNohDg2Q&WYW^j)A*9$got%kAPv zWaHa6u&F& zZ#sZ@B3g3rTs|C;*nzA!iCL>g%H>Pz#A7;fm_g@^M}*(Y_%1lw^}$~WmJ!b}KXaGF zJc%@`c7^z4@s^dT6R2(!M3#Hr`NqgLsPU?Xxh!29`=7=lCb2sJ`~jajc`oC~#-x@FnK-`Q_yNiZfF- zbt4Pd}p73NvEhEPFC?H_|t7JpET*O2<5Q9*0H9Ff+ioHpTy~LJf<2PCqJ4?+AU5GAR znjaT5t$-ps9T9OEN%$;;?uu!>r+=(J9~bSPLRU72;;Lw2i>-6}c9h~GIkDUVRym1W zSBnsPV9OKIGm`li_a8gv6BDahuADdStrsZGGi&KpnN_P*ZB?%v4;;ODb9;>#@Fn~# zS#s(1T3gLJ%%-KpX*X^;L)D11{L=vnOxo1&F~4-a(H~%dQKk(eOe+qwCLz@IHjN?J8y~@hW zlDAz7xwR(<7Q6;6|1UuG&1-v$_QilN7GHa%!@O6wtfT65yKgS!0nU$( zAQK}8Gke8lpV4s8m%!&{emU0=-rE44Yn@Sppk@icL1O}!kEdPmjMMAv zpmxgWdopa)pqw&n-qh4+yMdb#G>9{xY(^qFlq8ZtM3hUYzyU`p657@SOo5H27p2+D zDrEfF4Nf60DS;JK*B^e?z!OnJizJ@A9B*JX1)C%y6;t>MoQi$HO9126W53u7S1GoO z1*hT~#$3Wsqz-}r*|+lBbsq8KZUQBopCvux?^@EOjz~Duo9^uun#*pkUb zHhi(Gr2aHn%QI-vGjF20!Nlgbw0Ar|!96PxFZO-PZ;R$H@XZ6V+2lH_C-<~;0r83V z(4EoB$MhZ8Zn-PW$@}#ft_#=-}~dYYs@QB@Dq{ z$jJDE7jIQG3{}4Ktp-id>Sk&(lj>UOc646?J22wff37FXWUTqAaq&D|j+&~n`fT!v zP9RBvFxYGdhRosf=4^{hB-_CC+E8Gd-Id6?W4nO`U-qNrg2!JKQ4Y%bq6|fZE3G^j zyLg{<)z-jJoVz`N4n;jaagAN`RkZk!?XT22$G1l6oY8@&Sg1;75Qk z?nxY#+^kS@eu1YSNLY?cH<@4kRpGO!_21OCqr!2+7pQ} z^&q75R>}ZQ@e+4Mc_Y| zVy$$nSXnL?wiG5>0;KlJADiJZwa|H;ns=>{Dy}QAK~CJTK_@S54v(ZkiOq(P_1fUY z!tzSQJy`09h4L)Ro;p%>eBa{{n~7Dy0#qgzjDCWG^`(Co+EHq)LGH~X(HF)^nu4#! zFp>;h`F^PVGpv>}8azZ4nu#HMlb7vzaE);XP49U}_o7SfQFgn9@eRL;&``qzeV9B< zRC{}(55DznIUq<9UNpo<@}`DpxM}`JusNB2@G(^8Od$#nO*3&r=XO4lj-k5>l=5+; zQ)mQ__Ys)kwbxm1>(rKONHp82bSv5zk{cgB+ZSq35g$r|_i(E^ z*|)DjsP^PnG@|BAumss>^de+TcCb8Y3+1x_8Nwdn6xFpDYFY@quzLJ?rnVPdEMN4@ z=_Uy^s^4dN19gGWH`)UDkj)!mFoMtr`+dP_>H!b?V92I2vN(C1M?r)zV&cf}E{pUr zlX#4NhmBQ-d2Mag$1-C9xOyrcFRW9fr;n$S2EgWK$f}tHMxdHUpUq)u+0dqxp|hs~ z>bKpxylx@vKUz8D1LRlCD_lKFkX^BYQA^Eh8x5)?!z7!^7wsFY8{OQKUfM&tC-iqI z++YS7L2LWpCZ&%lD$aq%f){mZ!9H#N-O(260yUf_jy!}4-Amh-KWb^VPj4SymR@dr zhb}bnW865DMPN^A`FHjS6l$gdfVm*+Kz$0Y+At#`RVdPqSf(beThapyP&3vtXRIDy zh(=`CTeHoCgeJ*w6HrVP`}nJwrf*m=RnTRh=i)l3L8E;m6QJ5k_JNQ_baa(q1@l?< zVEW)&P|>u*fDf!wxSj2U260G;2JwON1!uoz;Fx@aNXT7~tNeI*CVx;j|2xJ8Zl;PM zmsV|MVAZ#59z0v_#+FeFd5qQDXMSP%N$pl~Mp;RNb%B3_;hDuU?$2)L`mgmn8 z4J3)Pmsp!P=4!uPRp#2bcWe?h?7ZE=_N6hYMEmwuhw`h#Z!~(VZYhiNG1S3H^%#?T zM1HNRZ!T=HPH>HQ!#i!dZ=YHBXt(G6!0|%3m_J+O@4rAeL--^4a&^;o_+$Y-1r-Mw z3)wlFb!QY;udygmN1ZK`GMqA_#b;2WM)UbTmQ3wac=ljJt#*C0tis=4YP)R;_yRSPzNJxBRilRe$1Ees$&CaN3h!=DJ2OE$7(r zh-nQJU0#lJIiM@*!4GKhc+7bYwI+UpM!UE6(iJ3kS z(Z1}NELMC9%0e9`7Bl>wk;f@mBQQzh3vhKb@ip@VgxC-Lp1=vvA?UOPbMA1GhJY^; zQkycQJ&x2zQN=yBb<7ZZkk)%4V35@e^)YoUspeLO;X?wLCHaIzRW0OdGz0?tMm1QO zR3By*fxFwb;2A>NZon&k_DPww<3FeCtdm%=Z7m0Bu7;$iFe-399b`v5hWVKvnc%Dp zI0G_1DZK_9fn^rMe)N})zp!Me|!gElJLn&{#$&!+aHHPlNTeJvUGs=`b)L#vA-k#0?2 zA_Y-dO08K-H6?TU%*y1{Jo2um(J;dD%Dxalx|XKV+6aLFej(23PqYkW{Kf2N0hd~I z1=GS;_2C0ss)ddce_s}SeA$k)Qgm2g{yeTC|IH(96{rF_b32$welvrnqGCmt%kwW+ z4|;Vhx!dXvwv+VqpWguOW!}cEgXW4Ata??_tdpB=A8m`o*r`hyB0Ao{5xlc<2ds*v zTV=;bDKl1H9=UFq!z8Rrp>^ui997ewcn)($#9@kQZlur~XGzdnmvUpW>TK6+Wm+$M zont9;NXp4&2#_MzW~19GdT{R>{5=$k+2`Yln4s*TmEgv|O}C1i`wy?UtzhYpgeTyw zHBopyV1DB%qVT%9YY<;L+pST2g+D`zoD>N2{ z?RRF|f&zN)xk_P}Su_{RwSOkB@H4tS?F#h->iIFxAJ^*K;k%C_m~-2%9iVo)TVMVp zXwyUPoPr}H`U^vM;(PYW_^D(&wm&KV34iB*^G@A76?Cpsuh3YKTTmHP*-xA$FFD1q zNnT1Zuuuu%ZU|AoHiri`<7gL4lt{mHoM}u4W)aX3FPu5+f)x6I3P3mWY@lDFTkm&s zn873f3cX^ma`-Kk+$(6&~jkt)(M!Z_46#t$SP07Ee!pi|r83rE}Y> zJLOc0kl*MoQm8+o5xVP~C@s*|YA z`sbBbJ|+EHsr+l?>8ah4W_EMt5s_jHZ8Su>zi*o?rm~PvUEr8PUDz{5ieNssC{d8Pq;&7Z3{4E zWD36T=L^lG6Df`p#(FAzX%~N25H~i4;CG>6anUMR0i9okwzDkj9VlrQy82aJsrsDRL09W z`07#>IZS(hX(A8=iPBd3rPBn^7=GG z%u@Hxwu>nD8om6kE1xs2Tb_GGEhT;*U(YFs%K%O6%wtWXJpAoFFN()S#Yk}U%r#wm zhpsAMnmz^mxHSfSbE@39%5&su4mmGhaV(!zypnyJ-TUg&pKBuQUkK1Alt zktas3N|LP_j#1G~Njn-^C_PQSBdh5Vy(@I^f~hHhP-t=Hj+jeA37+ATNl}u-i|Hn8 z6(cAK&QL4mZ3njD$rxQ9LT;NrYSug3%>BFVS`WZmdtLAU)e%YaG0H<-6W$UBN1?u{UZO4&&hE7P8N2GXk;Pqq zzakAY?I&c;Y{s{(ZEX9DEr*F1Rs7pOj`{As5wxxozXk78S_~iH7C_r-Krs>&h0Tkn zg=gli`}{afLsra7YzgvQN~sKsz`IUfBnS?R@e{s~Kib70rENuV@FWIafbm8?7S4oE zm6YxmVDzV*D3ui?%agA&6mO%C}CWt4f-1Ahx1rMTsD#7Fxof#knNl z#$e)7Is5F)v*r##v}(r6hnRhOwJ#X0%6gpo{x#nQyT`IthC56(E{r(bSC72K+etKF|clJDPgV~l`(Ma0XzR;`8 z4i1@B*;Zgs@g`AI*@ovgZx+L()55W<*6>?uu!85>n7uA4eC9{WI!ISnM4?jT5!N0P zyl;|`Ql9yV8q%G4P;LS47Oi>GES&R74JVmZ=&wG~36V^&v)a)_04~1o*Uq6(J$>*} z-a%KIQyYzxc^m#kM8-N7H-+A9B{TDBL5Jql(S0I64gKqhMr|uBiO|uE)Q&`7u&&JK zu%ohO4Y*_j4?S7=7`X|RS1#XrZ@6_Wbce5E27wOgol+1Ja{={2gGn87gidm(ukSpP zT9*rKJVUTuu&%MAfr|7aJjOD*F7)VU>^s5tSal^le_GN;a9mF@imD5ndi7?u+_GHH zl1Ep*Ce+p^(dCXEDpLH3%!^h%zarr?m~V_&U>_}MA4noyU=SH?u#0A9BcV=G%8*gw z?nllgJZnHsZvZpDbrp5Km44$y$>e$<{n`nGO*zq%r3ztl0zbs5jGxE{ z?Kfu4h+ItXs^(FS6QXkkdZsTeVKt7WNx2enbkj$dFa{#gR=pQx&M<+ zHhDs6hUlU*8SrffsTU|2i5D@BqPK+&yle`oOmM(e*~H;(6WWMLuN*_BA9mLShaR$+ zANmSc#NqTokeaK{T}-R#Ev{cZ&0nC9&0W2rOW%yea~XytzF?bgq8nfM?F6nxrOvfP zi74~dnq-5)kVH1_E%&~;sVJ7GIrv8wcuMX?4gzi;Z4R*5rZP@tmhns>1TrqZ~m;EVe zz8x)&JLkiBy_nh}qHxlTE8rjei4u|n8`k1YheW4u0r~;MA{HWtBH1heczJ|r-nZ6; zxK4u2!mr;IU(o4eBR3!E0u4;&-5wR9Q^_C2$Ozp}7i4hv>zxq=7AIA1NGn`IG*W|9 z37J+Z^n}Eo`}q!Fan;q)JQ&h##VP{M2tfd?{E=C>$aL22JMZVR6b70eKn0$7 z4O~QLprl#-L8R<~JEPpiLE+0Q;bou z?RKXQ_u%eQZsQ6-)T#O%QHyE3o55AA+aR*Z2v5GQbBt26l&cuyFsrnX&Whs-k zLQAJZLN01QV9PAY!PvsE_h@^J_v*(@8!)=`yPR<&)sv+6;SwcjlO1hC$a4lg1{0Nt zX_RDG`O-0e8G*Wy&Mi&?YF1e|MUi%;L812NC-NI*8WOF^MX!go^xGLEuSq`fP_Wym zt{W^-{m;%be=e7QGkL2(Yu7&Uo6MU|1IVGf6fdF2e;>nv)5@lm)3|WFQm&Q7)Hi*V z$E3pSE0A$~co*MOwhZ}$x^7HZWh0tmbO-7ZbfNP+i4e4leSfr-(3OHEeHzk#=CCi_ zc}~kU^xlYkbJtu4Zbt?7aZx&p%J~&Sc5M|k7KX?Gc~K$tInGD0#&PkhX1pN40797)gA45wn>w5QTp{@Td18nQl z?!Ni60m?d}|+YU$b1)w4TMpXYt~c%~un7ttyu~iCtB){P3Qw zU+b8a)2StTV%#6B4D2s9vK%_;FyX^$mzba<4>e|;(dYc+|LI5L3+8F1fv-H5Bz*X# z*~VuS_r}T2{wcH8gpz_H1LryGUDaiBYy&(FoYi`3rps1(SbB~6x;;t(4}SB9vKH($ zG|;~;!jYzbQ1@wYw$|%hTO+OT+qUJ`o@mAeTmKXO`{Bf|n3SM9Qq1`|Ue6>2i!*O6 zygXH757*DW%!N75f780&Zh0NaUX#LFrIk=N=^mrp^5aJ=6+>5SaSIK;b@=R~^q1)~ ztLN@rcl3@@-Nd?yl^Z`x9W`B+9V;y&?IRugeev&&Gm|Y_m692lo}E#fHaD?g=N!E- z#(nXtub-CAd~-oqgIQ*EzzmJU0(SKUdu|^6rJ=6E-*f-X;RQ@`ucdAB4PP6}^Y%rk zl-2iS2q>o=xH>2C;RfzK*OCPd^$InAS{+N-CS0c1_^44xOt^`w?Ziut9ZTD_7IR%a z`B$p=0L#9;pE8zwFkBv@u{Gx5TZfsZuI~#@d+*tE+3*u@-Qml5JTFDJ+&-Hk*1M!9 zYV*8b8^R4v+;y#Ubu!t-pgTe6KEH>ulung>&1U&Y`}$Wf+>B&h9#i@uo@4EY{cMj9 z9-FUG+iNfSPd86$7T^6d<=$cS{5978VGMh6E@eMGGB4--EYr=kUR$zHZek5Tx?Dfr ztl;T?rdfNAgV&CuZu&HYofKneU}*+ler^cc3##FnS6q;q3|x}!7VN7DTY7E?+pehr zT$QchAFL1oT*#|uXsl=_xKtc0=$oGc;&Vayh_%EOMX70Az_ren zTn2EUU}kD+Y^so^01-2^Ff;=O7YHciLB$M=4b6aIgeqocXbf}%s+fTRXk|I7n6aS= zhF)VMbKtsfRCQ*c#nb3wSkxIC16QM?t23|wuJuPV&&UYV4l{F1y_Ux4>I}^-Er83g zQOpCzp8PaYN(3(TE&`us6r5QFj5kXK{h<7O;BE%c zjs;+>dgi6&1GhFn*e<}$4K^ue=E=#)NoJ-dX{o8^CT3=7X(p!TW=3gd#wkh0X-0Nj cgq1+n)EAcklQcLX7+L^>mrGUE)!&T^0E^X3lmGw# diff --git a/packages/react-native/ReactCommon/hermes/inspector/tests/InspectorTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/tests/InspectorTests.cpp deleted file mode 100644 index 0be1a7cb652..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/tests/InspectorTests.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace facebook { -namespace hermes { -namespace inspector { - -namespace debugger = facebook::hermes::debugger; -using namespace std::chrono_literals; -using Unit = folly::Unit; - -static auto constexpr kDefaultTimeout = 5000ms; - -namespace { - -int getCurrentLine(const debugger::ProgramState &state) { - return state.getStackTrace().callFrameForIndex(0).location.line; -} - -debugger::SourceLocation locationForLine(int line) { - debugger::SourceLocation loc; - loc.line = line; - return loc; -} - -} // namespace - -/* - * LambdaInspectorObserver is useful for sequencing calls to the debugger based - * on the number of onPause() callbacks. - */ - -using OnPauseFunction = - std::function; - -class LambdaInspectorObserver : public InspectorObserver { - public: - LambdaInspectorObserver(OnPauseFunction func) - : onPauseFunc_(func), pauseCount_(0) {} - ~LambdaInspectorObserver() = default; - - void onBreakpointResolved( - Inspector &inspector, - const debugger::BreakpointInfo &info) override {} - - void onContextCreated(Inspector &inspector) override {} - - void onPause(Inspector &inspector, const debugger::ProgramState &state) - override { - pauseCount_++; - onPauseFunc_(inspector, state, pauseCount_); - } - - void onResume(Inspector &inspector) override {} - - void onScriptParsed(Inspector &inspector, const ScriptInfo &info) override {} - - void onMessageAdded(Inspector &inspector, const ConsoleMessageInfo &info) - override{}; - - int getPauseCount() { - return pauseCount_; - } - - private: - OnPauseFunction onPauseFunc_; - int pauseCount_; -}; - -/* - * Helpers for running JS in a separate thread. - */ - -struct HermesDebugContext { - HermesDebugContext( - InspectorObserver &observer, - folly::Future &&finished) - : runtime(makeHermesRuntime()), - inspector( - std::make_shared(runtime), - observer, - false), - stopFlag(false), - finished(std::move(finished)) { - runtime->global().setProperty( - *runtime, - "shouldStop", - jsi::Function::createFromHostFunction( - *runtime, - jsi::PropNameID::forAscii(*runtime, "shouldStop"), - 0, - [this]( - jsi::Runtime &, - const jsi::Value &, - const jsi::Value *args, - size_t count) { - return stopFlag.load() ? jsi::Value(true) : jsi::Value(false); - })); - } - ~HermesDebugContext() = default; - - void setStopFlag() { - stopFlag.store(true); - } - - void wait(std::chrono::milliseconds timeout = kDefaultTimeout) { - std::move(finished).get(timeout); - } - - std::shared_ptr runtime; - Inspector inspector; - std::atomic stopFlag{}; - folly::Future finished; -}; - -static std::shared_ptr runScriptAsync( - InspectorObserver &observer, - const std::string &script) { - auto promise = std::make_shared>(); - auto future = promise->getFuture(); - auto context = - std::make_shared(observer, std::move(future)); - - std::thread t([=]() { - HermesRuntime::DebugFlags flags{}; - context->runtime->debugJavaScript(script, "url", flags); - promise->setValue(); - }); - t.detach(); - - return context; -} - -/* - * Tests - */ - -TEST(InspectorTests, testStepOver) { - std::string script = R"( - var a = 1 + 2; - debugger; - var b = a / 2; - var c = a + b; - var d = b - c; - var e = c * d; - var f = 10; - )"; - - // TODO: move this vector into lambdaInspectorObserver - std::vector> futures; - - OnPauseFunction onPauseFunc = [&futures]( - Inspector &inspector, - const debugger::ProgramState &state, - int pauseCount) { - switch (pauseCount) { - case 1: { - EXPECT_EQ( - state.getPauseReason(), debugger::PauseReason::DebuggerStatement); - EXPECT_EQ(getCurrentLine(state), 3); - - futures.emplace_back(inspector.stepOver()); - - break; - } - case 2: { - EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish); - EXPECT_EQ(getCurrentLine(state), 4); - - futures.emplace_back(inspector.stepOver()); - - break; - } - case 3: { - EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish); - EXPECT_EQ(getCurrentLine(state), 5); - - futures.emplace_back(inspector.resume()); - - break; - } - } - }; - - LambdaInspectorObserver observer(onPauseFunc); - std::shared_ptr context = - runScriptAsync(observer, script); - - // TODO: temporarily do this to ensure i hit failure case - std::this_thread::sleep_for(1000ms); - - futures.emplace_back(context->inspector.enable()); - - context->wait(); - folly::collectAll(futures).get(kDefaultTimeout); - - EXPECT_EQ(observer.getPauseCount(), 3); -} - -TEST(InspectorTests, testSetBreakpoint) { - std::string script = R"( - var a = 1 + 2; - debugger; - var b = a / 2; - var c = a + b; - var d = b - c; - var e = c * d; - var f = 10; - )"; - - std::vector> futures; - - OnPauseFunction onPauseFunc = [&futures]( - Inspector &inspector, - const debugger::ProgramState &state, - int pauseCount) { - switch (pauseCount) { - case 1: { - EXPECT_EQ( - state.getPauseReason(), debugger::PauseReason::DebuggerStatement); - EXPECT_EQ(getCurrentLine(state), 3); - - auto stepFuture = inspector.stepOver(); - futures.emplace_back(std::move(stepFuture)); - - break; - } - case 2: { - EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish); - EXPECT_EQ(getCurrentLine(state), 4); - - auto breakpointFuture = inspector.setBreakpoint(locationForLine(6)); - futures.emplace_back(std::move(breakpointFuture) - .thenValue([](debugger::BreakpointInfo info) { - EXPECT_EQ(info.resolvedLocation.line, 6); - })); - - auto resumeFuture = inspector.resume(); - futures.emplace_back(std::move(resumeFuture)); - - break; - } - case 3: { - EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::Breakpoint); - EXPECT_EQ(getCurrentLine(state), 6); - - auto resumeFuture = inspector.resume(); - futures.emplace_back(std::move(resumeFuture)); - - break; - } - } - }; - - LambdaInspectorObserver observer(onPauseFunc); - std::shared_ptr context = - runScriptAsync(observer, script); - - auto enablePromise = context->inspector.enable(); - futures.emplace_back(std::move(enablePromise)); - - context->wait(); - folly::collectAll(futures).get(kDefaultTimeout); - - EXPECT_EQ(observer.getPauseCount(), 3); -} - -TEST(InspectorTests, testAsyncSetBreakpoint) { - std::string script = R"( - while (!shouldStop()) { - var a = 1; - var b = 2; - var c = a + b; - var d = 10; - } - )"; - - std::vector> futures; - folly::Func stopFunc; - - OnPauseFunction onPauseFunc = [&futures, &stopFunc]( - Inspector &inspector, - const debugger::ProgramState &state, - int pauseCount) { - switch (pauseCount) { - case 1: { - EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::Breakpoint); - EXPECT_EQ(getCurrentLine(state), 4); - - auto stepFuture = inspector.stepOver(); - futures.emplace_back(std::move(stepFuture)); - - break; - } - case 2: { - EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish); - EXPECT_EQ(getCurrentLine(state), 5); - - stopFunc(); - - auto resumeFuture = inspector.resume(); - futures.emplace_back(std::move(resumeFuture)); - - break; - } - } - }; - - LambdaInspectorObserver observer(onPauseFunc); - std::shared_ptr context = - runScriptAsync(observer, script); - - stopFunc = [context]() { context->setStopFlag(); }; - - context->inspector.enable(); - - auto breakpointPromise = context->inspector.setBreakpoint(locationForLine(4)) - .thenValue([](debugger::BreakpointInfo info) { - EXPECT_EQ(info.resolvedLocation.line, 4); - }); - - context->wait(); - - futures.emplace_back(std::move(breakpointPromise)); - folly::collectAll(futures).get(kDefaultTimeout); - - EXPECT_EQ(observer.getPauseCount(), 2); -} - -TEST(InspectorTests, testDisable) { - std::string script = R"( - var a = 1 + 2; - debugger; - var b = a / 2; - var c = a + b; - var d = b - c; - var e = c * d; - var f = 10; - )"; - - std::vector> futures; - - OnPauseFunction onPauseFunc = [&futures]( - Inspector &inspector, - const debugger::ProgramState &state, - int pauseCount) { - switch (pauseCount) { - case 1: { - EXPECT_EQ( - state.getPauseReason(), debugger::PauseReason::DebuggerStatement); - EXPECT_EQ(getCurrentLine(state), 3); - - auto stepFuture = inspector.stepOver(); - futures.emplace_back(std::move(stepFuture)); - - break; - } - case 2: { - EXPECT_EQ(state.getPauseReason(), debugger::PauseReason::StepFinish); - EXPECT_EQ(getCurrentLine(state), 4); - - futures.emplace_back(inspector.setBreakpoint(locationForLine(6)) - .thenValue([](debugger::BreakpointInfo info) { - EXPECT_EQ(info.resolvedLocation.line, 6); - })); - futures.emplace_back(inspector.setBreakpoint(locationForLine(7)) - .thenValue([](debugger::BreakpointInfo info) { - EXPECT_EQ(info.resolvedLocation.line, 7); - })); - - auto detachFuture = inspector.disable(); - futures.emplace_back(std::move(detachFuture)); - - break; - } - } - }; - - LambdaInspectorObserver observer(onPauseFunc); - std::shared_ptr context = - runScriptAsync(observer, script); - - auto enablePromise = context->inspector.enable(); - futures.emplace_back(std::move(enablePromise)); - - context->wait(); - folly::collectAll(futures).get(kDefaultTimeout); - - EXPECT_EQ(observer.getPauseCount(), 2); -} - -} // namespace inspector -} // namespace hermes -} // namespace facebook diff --git a/packages/react-native/ReactCommon/hermes/inspector/tools/format b/packages/react-native/ReactCommon/hermes/inspector/tools/format deleted file mode 100755 index e22332aa571..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/tools/format +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -DIR="$(dirname "${BASH_SOURCE[0]}")" -cd "$DIR" - -FBSOURCE="$(hg root)" -CLANG_FORMAT="$FBSOURCE/tools/third-party/clang-format/clang-format" -SRC="$FBSOURCE/xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector" - -find "$SRC" '(' -name '*.h' -or -name '*.cpp' ')' -exec "$CLANG_FORMAT" -i -style=file '{}' ';' diff --git a/packages/react-native/ReactCommon/hermes/inspector/tools/message_types.txt b/packages/react-native/ReactCommon/hermes/inspector/tools/message_types.txt deleted file mode 100644 index dec6c3d4afe..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/tools/message_types.txt +++ /dev/null @@ -1,41 +0,0 @@ -Debugger.breakpointResolved -Debugger.disable -Debugger.enable -Debugger.evaluateOnCallFrame -Debugger.pause -Debugger.paused -Debugger.removeBreakpoint -Debugger.resume -Debugger.resumed -Debugger.scriptParsed -Debugger.setBreakpoint -Debugger.setBreakpointByUrl -Debugger.setBreakpointsActive -Debugger.setInstrumentationBreakpoint -Debugger.setPauseOnExceptions -Debugger.stepInto -Debugger.stepOut -Debugger.stepOver -HeapProfiler.addHeapSnapshotChunk -HeapProfiler.collectGarbage -HeapProfiler.reportHeapSnapshotProgress -HeapProfiler.takeHeapSnapshot -HeapProfiler.startTrackingHeapObjects -HeapProfiler.stopTrackingHeapObjects -HeapProfiler.startSampling -HeapProfiler.stopSampling -HeapProfiler.heapStatsUpdate -HeapProfiler.lastSeenObjectId -HeapProfiler.getObjectByHeapObjectId -HeapProfiler.getHeapObjectId -Profiler.start -Profiler.stop -Runtime.callFunctionOn -Runtime.consoleAPICalled -Runtime.evaluate -Runtime.executionContextCreated -Runtime.getHeapUsage -Runtime.getProperties -Runtime.runIfWaitingForDebugger -Runtime.globalLexicalScopeNames -Runtime.compileScript diff --git a/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen b/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen deleted file mode 100755 index 45fbfd11963..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -set -e - -DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd) - -MSGTYPES_PATH="${DIR}/message_types.txt" -HEADER_PATH="${DIR}/../chrome/MessageTypes.h" -CPP_PATH="${DIR}/../chrome/MessageTypes.cpp" - -FBSOURCE=$(hg root) -CLANG_FORMAT="${FBSOURCE}/tools/third-party/clang-format/clang-format" -SIGNEDSOURCE="${FBSOURCE}/tools/signedsource" - -pushd "../../../../../hermes-inspector-msggen" - -yarn install -yarn build - -node bin/index.js \ - --ignore-experimental \ - --include-experimental=Runtime.getProperties.generatePreview,Runtime.evaluate.generatePreview,Runtime.callFunctionOn.generatePreview,Debugger.evaluateOnCallFrame.generatePreview,Runtime.RemoteObject.preview,Runtime.RemoteObject.customPreview,Runtime.CustomPreview,Runtime.EntryPreview,Runtime.ObjectPreview,Runtime.PropertyPreview,Runtime.getHeapUsage \ - --roots "${MSGTYPES_PATH}" \ - "${HEADER_PATH}" "${CPP_PATH}" - -"${CLANG_FORMAT}" -i --style=file "${HEADER_PATH}" -"${CLANG_FORMAT}" -i --style=file "${CPP_PATH}" - -"${SIGNEDSOURCE}" sign "${HEADER_PATH}" -"${SIGNEDSOURCE}" sign "${CPP_PATH}" - -popd >/dev/null diff --git a/packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/build_and_test.sh b/packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/build_and_test.sh deleted file mode 100755 index 8ac34ea17fb..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/build_and_test.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -THIS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -source "$THIS_DIR/setup.sh" - -buck test //xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector:chrome && - buck test //xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector:detail && - buck test //xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector:inspectorlib && - buck build //xplat/js/react-native-github/packages/react-native/ReactCommon/hermes/inspector:hermes-chrome-debug-server diff --git a/packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/setup.sh b/packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/setup.sh deleted file mode 100755 index eb6c194f595..00000000000 --- a/packages/react-native/ReactCommon/hermes/inspector/tools/sandcastle/setup.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -# -# Basic setup for xplat testing in sandcastle. Based on -# xplat/hermes/facebook/sandcastle/setup.sh. - -set -x -set -e -set -o pipefail - -THIS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -ROOT_DIR=$(cd "$THIS_DIR" && hg root) - -# Enter xplat -cd "$ROOT_DIR"/xplat || exit 1 - -# Setup env -export TITLE -TITLE=$(hg log -l 1 --template "{desc|strip|firstline}") -export REV -REV=$(hg log -l 1 --template "{node}") -export AUTHOR -AUTHOR=$(hg log -l 1 --template "{author|emailuser}") - -if [ -n "$SANDCASTLE" ]; then - source automation/setup_buck.sh -fi