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 f2daf5a4fd0..00000000000 Binary files a/packages/react-native/ReactCommon/hermes/inspector/docs/InspectorFSM.pdf and /dev/null differ 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