This repository has been archived on 2022-03-12. You can view files and clone it, but cannot push or open issues or pull requests.
2021-04-02 02:24:13 +03:00

305 lines
9.9 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef HERMES_DEBUGGERAPI_H
#define HERMES_DEBUGGERAPI_H
#ifdef HERMES_ENABLE_DEBUGGER
#include <hermes/hermes.h>
#include <cassert>
#include <memory>
#include <vector>
#include "hermes/Public/DebuggerTypes.h"
// Forward declarations of internal types.
namespace hermes {
namespace vm {
class CodeBlock;
class Debugger;
struct DebugCommand;
class HermesValue;
} // namespace vm
} // namespace hermes
namespace facebook {
namespace hermes {
class HermesRuntime;
namespace debugger {
class Debugger;
class EventObserver;
/// Represents a variable in the debugger.
struct VariableInfo {
/// Name of the variable in the source.
String name;
/// Value of the variable.
::facebook::jsi::Value value;
};
/// An EvalResult represents the result of an Eval command.
struct EvalResult {
/// The resulting JavaScript object, or the thrown exception.
::facebook::jsi::Value value;
/// Indicates that the result was an exception.
bool isException = false;
/// If isException is true, details about the exception.
ExceptionDetails exceptionDetails;
EvalResult(EvalResult &&) = default;
EvalResult() = default;
EvalResult(
::facebook::jsi::Value value,
bool isException,
ExceptionDetails exceptionDetails)
: value(std::move(value)),
isException(isException),
exceptionDetails(std::move(exceptionDetails)) {}
};
/// ProgramState represents the state of a paused program. An instance of
/// ProgramState is available as the getProgramState() member function of class
/// Debugger.
class ProgramState {
public:
/// \return the reason for the Pause.
PauseReason getPauseReason() const {
return pauseReason_;
}
/// \return the breakpoint if the PauseReason is Breakpoint, otherwise
/// kInvalidBreakpoint.
BreakpointID getBreakpoint() const {
return breakpoint_;
}
/// \return the evaluation result if the PauseReason is due to EvalComplete.
EvalResult getEvalResult() const;
/// \returns a stack trace for the current execution.
const StackTrace &getStackTrace() const {
return stackTrace_;
}
/// \returns lexical information about the state in a given frame.
LexicalInfo getLexicalInfo(uint32_t frameIndex) const;
/// \return information about a variable in a given lexical scope, in a given
/// frame.
VariableInfo getVariableInfo(
uint32_t frameIndex,
ScopeDepth scopeDepth,
uint32_t variableIndexInScope) const;
/// \return information about the `this` value at a given stack depth.
VariableInfo getVariableInfoForThis(uint32_t frameIndex) const;
/// \return the number of variables in a given frame.
/// This is deprecated: prefer using getLexicalInfoInFrame().
uint32_t getVariablesCountInFrame(uint32_t frameIndex) const {
auto info = getLexicalInfo(frameIndex);
uint32_t result = 0;
for (ScopeDepth i = 0, max = info.getScopesCount(); i < max; i++)
result += info.getVariablesCountInScope(i);
return result;
}
/// \return info for a variable at a given index \p variableIndex, in a given
/// frame at index \p frameIndex.
/// This is deprecated. Prefer the getVariableInfo() that takes three
/// parameters.
VariableInfo getVariableInfo(uint32_t frameIndex, uint32_t variableIndex)
const {
LexicalInfo info = getLexicalInfo(frameIndex);
uint32_t remaining = variableIndex;
for (ScopeDepth scope = 0;; scope++) {
assert(scope < info.getScopesCount() && "Index out of bounds");
uint32_t count = info.getVariablesCountInScope(scope);
if (remaining < count) {
return getVariableInfo(frameIndex, scope, remaining);
}
remaining -= count;
}
}
private:
friend Debugger;
/// ProgramState must not be copied, because some of its implementation
/// requires querying the live program state and so the state must not be
/// retained after the pause returns.
/// ProgramState must not be copied.
ProgramState(const ProgramState &) = delete;
ProgramState &operator=(const ProgramState &) = delete;
::hermes::vm::Debugger *impl() const;
ProgramState(Debugger *dbg) : dbg_(dbg) {}
Debugger *dbg_;
PauseReason pauseReason_{};
StackTrace stackTrace_;
EvalResult evalResult_;
BreakpointID breakpoint_{kInvalidBreakpoint};
};
/// Command represents an action that you can request the debugger to perform
/// when returned from didPause().
class Command {
public:
/// Commands may be moved.
Command(Command &&);
Command &operator=(Command &&);
~Command();
/// \return a Command that steps with the given StepMode \p mode.
static Command step(StepMode mode);
/// \return a Command that continues execution.
static Command continueExecution();
/// \return a Command that evaluates JavaScript code \p src in the
/// frame at index \p frameIndex.
static Command eval(const String &src, uint32_t frameIndex);
private:
friend Debugger;
explicit Command(::hermes::vm::DebugCommand &&);
std::unique_ptr<::hermes::vm::DebugCommand> debugCommand_;
};
/// Debugger allows access to the Hermes debugging functionality. An instance of
/// Debugger is available from HermesRuntime, and also passed to your
/// EventObserver.
class Debugger {
public:
/// Set the Debugger event observer. The event observer is notified of
/// debugging event, specifically when the program pauses. This is simply a
/// raw pointer: it is the client's responsibility to clear the event observer
/// if the event observer is deallocated before the Debugger.
void setEventObserver(EventObserver *observer);
/// Sets the property %isDebuggerAttached in %DebuggerInternal object.
void setIsDebuggerAttached(bool isAttached);
/// Asynchronously triggers a pause. This may be called from any thread. This
/// is inherently racey and the exact point at which the program pauses is not
/// guaranteed. You can discover when the program has paused through the event
/// observer.
void triggerAsyncPause(AsyncPauseKind kind);
/// \return the ProgramState representing the state of the paused program.
/// This may only be invoked when the program is paused.
const ProgramState &getProgramState() const {
return state_;
}
/// \return the source map URL for the \p fileId.
String getSourceMappingUrl(uint32_t fileId) const;
/// -- Breakpoint Management --
/// Sets a breakpoint on a given SourceLocation.
/// \return the ID of the breakpoint, 0 if it wasn't created.
BreakpointID setBreakpoint(SourceLocation loc);
/// Sets the condition on breakpoint \p breakpoint.
/// The condition will be stored with the breakpoint,
/// and if non-empty, will be executed to determine whether to actually
/// pause on the breakpoint; only if ToBoolean(condition) is true
/// and does not throw will the debugger pause on \p breakpoint.
/// \param condition the code to execute to determine whether to break;
/// if empty, the condition is considered to not be set.
void setBreakpointCondition(BreakpointID breakpoint, const String &condition);
/// Deletes a breakpoint.
void deleteBreakpoint(BreakpointID breakpoint);
/// Deletes all breakpoints.
void deleteAllBreakpoints();
/// Mark a breakpoint as enabled. Breakpoints are by default enabled.
void setBreakpointEnabled(BreakpointID breakpoint, bool enable);
/// \return information on a breakpoint.
BreakpointInfo getBreakpointInfo(BreakpointID breakpoint);
/// \return a list of extant breakpoints.
std::vector<BreakpointID> getBreakpoints();
/// Set whether the debugger should pause when an exception is thrown.
void setPauseOnThrowMode(PauseOnThrowMode mode);
/// \return whether the debugger pauses when an exception is thrown.
PauseOnThrowMode getPauseOnThrowMode() const;
/// Set whether the debugger should pause after a script was loaded.
void setShouldPauseOnScriptLoad(bool flag);
/// \return whether the debugger should pause after a script was loaded.
bool getShouldPauseOnScriptLoad() const;
private:
friend std::unique_ptr<HermesRuntime> hermes::makeHermesRuntime(
const ::hermes::vm::RuntimeConfig &);
friend std::unique_ptr<jsi::ThreadSafeRuntime>
hermes::makeThreadSafeHermesRuntime(const ::hermes::vm::RuntimeConfig &);
friend ProgramState;
/// Debuggers may not be moved or copied.
Debugger(const Debugger &) = delete;
void operator=(const Debugger &) = delete;
Debugger(Debugger &&) = delete;
void operator=(Debugger &&) = delete;
/// Implementation detail used by ProgramState.
::facebook::jsi::Value jsiValueFromHermesValue(::hermes::vm::HermesValue hv);
explicit Debugger(
::facebook::hermes::HermesRuntime *runtime,
::hermes::vm::Debugger *impl);
::facebook::hermes::HermesRuntime *const runtime_;
EventObserver *eventObserver_ = nullptr;
::hermes::vm::Debugger *impl_;
ProgramState state_;
};
/// A subclass of EventObserver may be set on the Debugger via
/// setEventObserver(). It receives notifications when the Debugger pauses.
class EventObserver {
public:
/// didPause() is invoked when the JavaScript program has paused. The
/// The Debugger \p debugger can be used to manipulate breakpoints and enqueue
/// debugger commands such as stepping, etc. It can also be used to discover
/// the call stack and variables via debugger.getProgramState().
/// \return a Command for the debugger to perform.
virtual Command didPause(Debugger &debugger) = 0;
/// Invoked when the debugger resolves a previously unresolved breakpoint.
/// Note that the debugger is *not* paused during this,
/// and thus debugger.getProgramState() is not valid.
/// This callback may not invoke JavaScript or enqueue debugger commands.
virtual void breakpointResolved(Debugger &debugger, BreakpointID breakpoint) {
}
virtual ~EventObserver();
};
} // namespace debugger
} // namespace hermes
} // namespace facebook
#endif // HERMES_ENABLE_DEBUGGER
#endif // HERMES_DEBUGGERAPI_H