Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/include/hermes/VM/Debugger/Debugger.h
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_VM_DEBUGGER_DEBUGGER_H
9
#define HERMES_VM_DEBUGGER_DEBUGGER_H
10
11
#ifdef HERMES_ENABLE_DEBUGGER
12
13
#include "hermes/BCGen/HBC/BytecodeInstructionGenerator.h"
14
#include "hermes/BCGen/HBC/DebugInfo.h"
15
#include "hermes/Inst/Inst.h"
16
#include "hermes/Public/DebuggerTypes.h"
17
#include "hermes/Support/OptValue.h"
18
#include "hermes/VM/Debugger/DebugCommand.h"
19
#include "hermes/VM/HermesValue.h"
20
#include "hermes/VM/InterpreterState.h"
21
#include "hermes/VM/RuntimeModule.h"
22
#include "llvh/ADT/DenseSet.h"
23
#include "llvh/ADT/MapVector.h"
24
25
#include <atomic>
26
#include <cstdint>
27
#include <string>
28
29
namespace hermes {
30
namespace vm {
31
class HermesValue;
32
class CodeBlock;
33
class Runtime;
34
} // namespace vm
35
} // namespace hermes
36
37
namespace hermes {
38
namespace vm {
39
40
/// Main debugger object that receives commands from an external source,
41
/// and passes them back to the interpreter loop, which handles them.
42
class Debugger {
43
 public:
44
  struct EvalResultMetadata;
45
46
  using DidPauseCallback = std::function<DebugCommand(
47
      InterpreterState state,
48
      ::facebook::hermes::debugger::PauseReason pauseReason,
49
      HermesValue evalResult,
50
      const EvalResultMetadata &evalResultMetadata,
51
      ::facebook::hermes::debugger::BreakpointID)>;
52
53
  using BreakpointResolvedCallback =
54
      std::function<void(facebook::hermes::debugger::BreakpointID)>;
55
56
 private:
57
  friend DebugCommand;
58
59
  using PauseOnThrowMode = ::facebook::hermes::debugger::PauseOnThrowMode;
60
  using BreakpointID = ::facebook::hermes::debugger::BreakpointID;
61
  using BreakpointInfo = ::facebook::hermes::debugger::BreakpointInfo;
62
  using CallFrameInfo = ::facebook::hermes::debugger::CallFrameInfo;
63
  using ExceptionDetails = ::facebook::hermes::debugger::ExceptionDetails;
64
  using PauseReason = ::facebook::hermes::debugger::PauseReason;
65
  using SourceLocation = ::facebook::hermes::debugger::SourceLocation;
66
  using StackTrace = ::facebook::hermes::debugger::StackTrace;
67
  using StepMode = ::facebook::hermes::debugger::StepMode;
68
  using String = ::facebook::hermes::debugger::String;
69
  using LexicalInfo = ::facebook::hermes::debugger::LexicalInfo;
70
  using ScriptID = ::facebook::hermes::debugger::ScriptID;
71
  using AsyncPauseKind = ::facebook::hermes::debugger::AsyncPauseKind;
72
73
  Runtime &runtime_;
74
75
  /// Function handling pauses.
76
  DidPauseCallback didPauseCallback_;
77
78
  /// Function handling breakpoint resolution.
79
  BreakpointResolvedCallback breakpointResolvedCallback_;
80
81
  /// Logical breakpoint.
82
  struct Breakpoint {
83
    CodeBlock *codeBlock;
84
    uint32_t offset;
85
86
    /// Whether the breakpoint is "enabled" from the user's perspective.
87
    /// Note that this is set independently of resolved.
88
    bool enabled;
89
90
    /// Condition on which the breakpoint should trigger.
91
    /// If empty, the breakpoint will always trigger at the location it's set.
92
    std::string condition{};
93
94
    /// Requested location of the breakpoint.
95
    SourceLocation requestedLocation;
96
    /// Resolved location of the breakpoint.
97
    /// Optionally filled in for resolved user breakpoints.
98
    llvh::Optional<SourceLocation> resolvedLocation{llvh::None};
99
100
0
    bool isResolved() const {
101
0
      return resolvedLocation.hasValue();
102
0
    }
103
  };
104
105
  /// Breakpoints that were set by the user.
106
  /// Every breakpoint has a unique ID that is never reused.
107
  /// Use a MapVector to iterate in insertion order.
108
  llvh::MapVector<BreakpointID, Breakpoint> userBreakpoints_{};
109
  BreakpointID nextBreakpointId_{1};
110
111
  /// One-shot breakpoints that are used for stepping commands.
112
  /// These are typically cleared immediately after breaking.
113
  std::vector<Breakpoint> tempBreakpoints_{};
114
115
  /// One-shot breakpoints that are used for restoring breakpoint after using \p
116
  /// processInstUnderDebuggerOpCode(). These are cleared immediately after
117
  /// breaking.
118
  std::vector<Breakpoint> restorationBreakpoints_{};
119
120
  /// Physical breakpoint location.
121
  struct BreakpointLocation {
122
    /// Opcode that was replaced.
123
    hbc::opcode_atom_t opCode;
124
125
    /// If this location has a user breakpoint set,
126
    /// then this is set to the ID of the user breakpoint.
127
    /// Else, it's set to None.
128
    OptValue<BreakpointID> user{llvh::None};
129
130
    /// Whether this location has an on-load breakpoint set.
131
    bool onLoad{false};
132
    /// Whether this location has a Step breakpoint set.
133
    bool hasStepBreakpoint{false};
134
    /// Whether this location has a Restoration breakpoint set.
135
    bool hasRestorationBreakpoint{false};
136
137
    /// This is the set of callStackDepths at which we are supposed to stop
138
    /// on this breakpoint for Step breakpoints.
139
    /// The size of this set is the number of Step breakpoints.
140
    /// We enforce that there is only one Step breakpoint
141
    /// per physical location, because Step breakpoints
142
    /// should be set in one location and cleared immediately when hit.
143
    /// If a depth != 0, then only break when the runtime call stack
144
    /// has exactly depth elements in it, and if a depth == 0,
145
    /// then break on any runtime call stack size.
146
    llvh::DenseSet<uint32_t> callStackDepths{};
147
148
0
    BreakpointLocation(hbc::opcode_atom_t opCode) : opCode(opCode) {}
149
150
    /// Total number of logical breakpoints set at this location.
151
0
    uint32_t count() const {
152
0
      return callStackDepths.size() + (user ? 1 : 0) + (onLoad ? 1 : 0);
153
0
    }
154
  };
155
156
  llvh::DenseMap<const inst::Inst *, BreakpointLocation> breakpointLocations_{};
157
158
  /// The debugger is currently executing instructions.
159
  bool isDebugging_{false};
160
161
  /// If not None, the debugger was issued a STEP instruction of some sort,
162
  /// and it hasn't completed yet.
163
  /// The value indicates the type of step we're trying to take.
164
  OptValue<StepMode> curStepMode_{llvh::None};
165
166
  /// If true, all code blocks are breakpointed,
167
  /// and the debugger should stop on entering any code blocks.
168
  bool pauseOnAllCodeBlocks_{false};
169
170
  /// Similar to \p pauseOnAllCodeBlocks_, except this flag specifically
171
  /// indicates whether Restoration breakpoint should be created. This can be
172
  /// set at the same time as \p pauseOnAllCodeBlocks_. If both are set, then in
173
  /// \p setEntryBreakpointForCodeBlock(), both a Step breakpoint and a
174
  /// Restoration breakpoint will be created at the same location. In \p
175
  /// runDebugger(), the Restoration breakpoint will be handled first and be
176
  /// cleared out, then if there is a Step breakpoint due to \p
177
  /// pauseOnAllCodeBlocks_, that will be handled as if the Restoration
178
  /// breakpoint was never there.
179
  bool pauseOnAllCodeBlocksToRestoreBreakpoint_{false};
180
181
  /// Stores the CodeBlock and offset where in \p
182
  /// processInstUnderDebuggerOpCode() we purposely keep breakpoint uninstalled
183
  /// to reuse the Interpreter loop.
184
  std::pair<CodeBlock *, uint32_t> breakpointToRestore_{nullptr, 0};
185
186
  /// What conditions the debugger needs to stop on exceptions.
187
  PauseOnThrowMode pauseOnThrowMode_{PauseOnThrowMode::None};
188
189
  // When true, debugger is moving on after reporting an exception.
190
  // This is used to ensure we don't report an exception to the user twice
191
  // when pauseOnThrowMode_ is not None. The Interpreter can't distinguish
192
  // between exceptions thrown in native functions vs. thrown by users
193
  // when there's a native stack frame in the middle, so we need to account for
194
  // that by setting this flag to not stop between a caught exception
195
  // and its exception handler. This works because there should be no ability
196
  // to throw a new exception between reporting it and stepping to its handler.
197
  bool isUnwindingException_{false};
198
199
  /// Whether the Debugger should pause after a script has loaded, before it
200
  /// begins executing.
201
  bool pauseOnScriptLoad_{false};
202
203
  /// The last location we were at before we resumed execution in mid-step.
204
  /// This is only valid if curStepMode_ is not None.
205
  InterpreterState preStepState_{};
206
207
  // Whether an user has attached to any Inspector.
208
  // It is exposed to JS via a property %DebuggerInternal.isDebuggerAttached
209
  std::atomic<bool> isDebuggerAttached_{false};
210
211
 public:
212
113
  explicit Debugger(Runtime &runtime) : runtime_(runtime) {}
213
214
  /// Reasons why the interpreter may invoke the debugger. Note this is a more
215
  /// limited set than PauseReason, because the Interpreter cannot distinguish
216
  /// between debugger opcodes as part of the Debugger command versus those
217
  /// inserted for breakpoints.
218
  enum class RunReason {
219
    /// The Interpreter hit a Debugger opcode.
220
    Opcode,
221
222
    /// The Interpreter hit an exception.
223
    Exception,
224
225
    /// The Interpreter is reacting to an async break request from the user.
226
    /// Any current stepping state should be cleared.
227
    AsyncBreakExplicit,
228
229
    /// The Interpreter is reacting to an async break request from the
230
    /// inspector.
231
    /// Any current stepping state should be *preserved*.
232
    AsyncBreakImplicit,
233
  };
234
235
  /// An EvalResultMetadata is a subset of EvalResult in DebuggerAPI.h, lacking
236
  /// a jsi::Value field.
237
  struct EvalResultMetadata {
238
    bool isException = false;
239
    ExceptionDetails exceptionDetails;
240
  };
241
242
  /// Runs debugger commands until execution continues.
243
  /// \param runReason the reason that the interpreter invoked the debugger.
244
  /// \param[in,out] state the debugging state.
245
  /// \return status after running the debugger.
246
  /// Ensures that if we stopped on an exception, the state doesn't change and
247
  /// the status is RETURNED.
248
  /// If the status is EXCEPTION, then the state is the state at the instruction
249
  /// that threw the exception - this allows the interpreter to handle the
250
  /// exception directly from the instruction that threw.
251
  ExecutionStatus runDebugger(RunReason runReason, InterpreterState &state);
252
253
0
  bool isDebugging() const {
254
0
    return isDebugging_;
255
0
  }
256
257
  /// Should only be called when there is a current frame. Do not call while not
258
  /// in the interpreter loop.
259
  /// \return the current stack trace.
260
  StackTrace getStackTrace() const;
261
262
  llvh::Optional<const BreakpointLocation> getBreakpointLocation(
263
0
      const inst::Inst *ip) const {
264
0
    auto it = breakpointLocations_.find(ip);
265
0
    if (it == breakpointLocations_.end()) {
266
0
      return llvh::None;
267
0
    }
268
0
    return {it->second};
269
0
  }
270
  llvh::Optional<const BreakpointLocation> getBreakpointLocation(
271
      CodeBlock *codeBlock,
272
      uint32_t offset) const;
273
274
113
  void setDidPauseCallback(DidPauseCallback callback) {
275
113
    didPauseCallback_ = std::move(callback);
276
113
  }
277
278
113
  void setBreakpointResolvedCallback(BreakpointResolvedCallback callback) {
279
113
    breakpointResolvedCallback_ = std::move(callback);
280
113
  }
281
282
0
  void setShouldPauseOnScriptLoad(bool flag) {
283
0
    pauseOnScriptLoad_ = flag;
284
0
  }
285
286
260
  bool getShouldPauseOnScriptLoad() const {
287
260
    return pauseOnScriptLoad_;
288
260
  }
289
290
0
  void setPauseOnThrowMode(PauseOnThrowMode mode) {
291
0
    pauseOnThrowMode_ = mode;
292
0
  }
293
294
41
  PauseOnThrowMode getPauseOnThrowMode() const {
295
41
    return pauseOnThrowMode_;
296
41
  }
297
298
  /// Sets the property %isDebuggerAttached in the %DebuggerInternal object. Can
299
  /// be called from any thread.
300
0
  void setIsDebuggerAttached(bool isAttached) {
301
0
    isDebuggerAttached_ = isAttached;
302
0
  }
303
304
  /// Gets the property %isDebuggerAttached in the %DebuggerInternal object. Can
305
  /// be called from any thread.
306
0
  bool getIsDebuggerAttached() const {
307
0
    return isDebuggerAttached_;
308
0
  }
309
310
  /// Signal to the debugger that we are done unwinding an exception.
311
  /// This means that we can begin reporting exceptions to the user again
312
  /// if the user has requested them.
313
0
  void finishedUnwindingException() {
314
0
    isUnwindingException_ = false;
315
0
  }
316
317
  /// Request an async pause. This may be called from any thread, or a signal
318
  /// handler.
319
  void triggerAsyncPause(AsyncPauseKind kind);
320
321
  /// Creates a user breakpoint given filename, line, and column.
322
  /// \param loc the location to set the breakpoint.
323
  /// \return the id of the new breakpoint, kInvalidBreakpoint if none was
324
  /// created.
325
  BreakpointID createBreakpoint(const SourceLocation &loc);
326
327
  /// Sets the condition on a breakpoint.
328
  /// \param id the breakpoint to change the condition on.
329
  /// \param condition if None, unset the condition, else set the condition.
330
  void setBreakpointCondition(BreakpointID id, std::string condition);
331
332
  /// Deletes the breakpoint given.
333
  void deleteBreakpoint(BreakpointID id);
334
335
  /// Deletes all breakpoints.
336
  void deleteAllBreakpoints();
337
338
  /// Enables/disables the breakpoint given.
339
  /// \param id the id of the breakpoint to edit.
340
  /// \param enable if true, enable breakpoint \p id.
341
  void setBreakpointEnabled(BreakpointID id, bool enable);
342
343
  /// \return the breakpoint information for breakpoint \p id.
344
  /// If the supplied id is not valid, then the result has id ==
345
  /// kInvalidBreakpoint.
346
0
  BreakpointInfo getBreakpointInfo(BreakpointID id) const {
347
0
    auto it = userBreakpoints_.find(id);
348
0
    if (it == userBreakpoints_.end()) {
349
      // Invalid input.
350
0
      BreakpointInfo result{};
351
0
      result.id = ::facebook::hermes::debugger::kInvalidBreakpoint;
352
0
      return result;
353
0
    }
354
355
0
    const auto &breakpoint = it->second;
356
0
    BreakpointInfo result{};
357
0
    result.id = id;
358
0
    result.enabled = breakpoint.enabled;
359
0
    result.resolved = breakpoint.isResolved();
360
0
    result.requestedLocation = breakpoint.requestedLocation;
361
0
    if (breakpoint.isResolved()) {
362
0
      result.resolvedLocation = *breakpoint.resolvedLocation;
363
0
    }
364
0
    return result;
365
0
  }
366
367
  /// \return a list of valid user breakpoint IDs.
368
0
  std::vector<BreakpointID> getBreakpoints() const {
369
0
    std::vector<BreakpointID> result{};
370
0
    result.reserve(userBreakpoints_.size());
371
0
    for (const auto &it : userBreakpoints_) {
372
0
      result.push_back(it.first);
373
0
    }
374
0
    return result;
375
0
  }
376
377
  /// \return the number of variables in the given frame \p frame.
378
  LexicalInfo getLexicalInfoInFrame(uint32_t frame) const;
379
380
  /// \return the variable at \p variableIndex at scope depth \p scopeDepth in
381
  /// frame \p frame. 0 is the topmost frame, corresponding to \p topBlock. If
382
  /// \p outName is not null, populate it with the variable's name.
383
  HermesValue getVariableInFrame(
384
      uint32_t frame,
385
      uint32_t scopeDepth,
386
      uint32_t variableIndex,
387
      std::string *outName = nullptr) const;
388
389
  /// \param frame the index of the frame, with 0 being the topmost.
390
  /// \return the 'this' value at \p frame.
391
  HermesValue getThisValue(uint32_t frame) const;
392
393
  /// Report to the debugger that the runtime will execute a module given by \p
394
  /// module. The debugger may propagate a pause to the client.
395
  void willExecuteModule(RuntimeModule *module, CodeBlock *codeBlock);
396
397
  /// Report to the debugger that the runtime will unload a RuntimeModule.
398
  /// The debugger should unresolve breakpoints in that module.
399
  void willUnloadModule(RuntimeModule *module);
400
401
  /// Report to the debugger that the runtime will execute a codeBlock given by
402
  /// \p codeBlock.
403
  /// For example, this is used to set up the codeBlock while stepping.
404
1.95k
  void willEnterCodeBlock(CodeBlock *codeBlock) {
405
1.95k
    if (LLVM_UNLIKELY(pauseOnAllCodeBlocks_)) {
406
0
      setEntryBreakpointForCodeBlock(codeBlock);
407
0
    }
408
1.95k
  }
409
410
  /// Resolve any unresolved breakpoints.
411
  /// Intended for use following compilation or loading of a new function.
412
  /// \param codeBlock the code block that was compiled before this call.
413
  void resolveBreakpoints(CodeBlock *codeBlock);
414
415
  /// \return the source map URL for \p scriptId, empty string if non exists.
416
  String getSourceMappingUrl(ScriptID scriptId) const;
417
418
  /// \return list of loaded scripts that haven't been garbage collected
419
  std::vector<SourceLocation> getLoadedScripts() const;
420
421
  /// Find the handler for an exception thrown at \p state.
422
  /// \return llvh::None if no handler is found, else return the state of the
423
  /// handler and the offset of its frame.
424
  llvh::Optional<std::pair<InterpreterState, uint32_t>> findCatchTarget(
425
      const InterpreterState &state) const;
426
427
  /// Attempt to resolve the \p filenameId to a script ID based on the table.
428
  /// \return the ScriptID of the given filenameId.
429
  ScriptID resolveScriptId(RuntimeModule *runtimeModule, uint32_t filenameId)
430
      const;
431
432
  /// Get the actual OpCode produced from the source without being affected by
433
  /// any user installed breakpoint "Debugger" OpCode overrides.
434
  inst::OpCode getRealOpCode(CodeBlock *block, uint32_t offset) const;
435
436
  /// Processes an instruction that had Debugger OpCode installed on top of it.
437
  /// This is meant to be done after Interpreter encounters Debugger OpCode. It
438
  /// differs from \p stepInstruction() in that this function will handle Ret.
439
  /// Ret requires special logic in the case if it's returning to native code.
440
  /// The Interpreter has special logic to exit out of the Interpreter if we're
441
  /// returning to native code. But when \p stepFunction() is used, we have
442
  /// recursive Interpreter loop and the expectation is to exit out of both.
443
  /// This function will avoid using stepFunction() for Ret. It will instead
444
  /// keep breakpoint uninstalled and re-use the same Interpreter loop.
445
  /// \param[in,out] InterpreterState for where the Interpreter is currently at,
446
  /// and gets filled with where Interpreter should resume executing
447
  /// \return status after processing
448
  ExecutionStatus processInstUnderDebuggerOpCode(InterpreterState &state);
449
450
 private:
451
  /// The primary debugger command loop.
452
  ExecutionStatus debuggerLoop(
453
      InterpreterState &state,
454
      PauseReason pauseReason,
455
      BreakpointID breakpoint);
456
457
  /// Gets a BreakpointLocation from breakpointLocations_ if it exists
458
  /// for the given codeBlock and offset, else creates one.
459
  /// Used by other functions which should be called to set breakpoints.
460
  /// Installs a breakpoint at that location, doesn't modify it.
461
  /// \return the location at which the breakpoint was installed.
462
  BreakpointLocation &installBreakpoint(CodeBlock *codeBlock, uint32_t offset);
463
464
  /// Helper function to uninstall a breakpoint. Always use this function to
465
  /// uninstall breakpoints. This takes care of the case when we purposely keep
466
  /// breakpoint uninstalled in \p processInstUnderDebuggerOpCode().
467
  void uninstallBreakpoint(
468
      CodeBlock *codeBlock,
469
      uint32_t offset,
470
      hbc::opcode_atom_t opCode);
471
472
  /// Set a user breakpoint at \p offset in \p codeBlock.
473
  /// Increments the count at a breakpoint if it already exists.
474
  /// If the physical breakpoint isn't enabled yet, patches the debugger
475
  /// instruction in.
476
  /// Sets the breakpoint ID to \p id.
477
  void
478
  setUserBreakpoint(CodeBlock *codeBlock, uint32_t offset, BreakpointID id);
479
480
  /// Should not be called directly except from \p setStepBreakpoint() or \p
481
  /// setRestorationBreakpoint(). This function sets a non-user breakpoint at \p
482
  /// offset in \p codeBlock. Increments the count at a breakpoint if it already
483
  /// exists. If the physical breakpoint isn't enabled yet, patches the debugger
484
  /// instruction in.
485
  /// The breakpoint is set to be a one-shot.
486
  /// If \p callStackDepth != 0, then the Interpreter should only
487
  /// break if the callStack is exactly \p callStackDepth frames long.
488
  /// If \p callStackDepth == 0, then the Step breakpoint should break
489
  /// regardless of the actual depth of the call stack.
490
  /// /param isStepBreakpoint true if creating a Step breakpoint, false for
491
  /// Restoration breakpoint.
492
  void doSetNonUserBreakpoint(
493
      CodeBlock *codeBlock,
494
      uint32_t offset,
495
      uint32_t callStackDepth,
496
      bool isStepBreakpoint);
497
498
  /// Set a stepping breakpoint at \p offset in \p codeBlock.
499
  void setStepBreakpoint(
500
      CodeBlock *codeBlock,
501
      uint32_t offset,
502
      uint32_t callStackDepth);
503
504
  /// Set an on-load breakpoint at \p offset in \p codeBlock.
505
  /// Increments the count at a breakpoint if it already exists.
506
  /// If the physical breakpoint isn't enabled yet, patches the debugger
507
  /// instruction in.
508
  /// The breakpoint is set to be a one-shot.
509
  void setOnLoadBreakpoint(CodeBlock *codeBlock, uint32_t offset);
510
511
  /// Unset user breakpoint \p breakpoint.
512
  /// Removes the location from breakpointLocations_ if no longer needed.
513
  void unsetUserBreakpoint(const Breakpoint &breakpoint);
514
515
  /// Creates a step breakpoint at offset 0 of \p codeBlock,
516
  /// to be used while in the middle of a step and entering a
517
  /// codeBlock for the first time.
518
  /// Requires that getShouldPauseOnAllCodeBlocks is true.
519
  /// The breakpoint will clear on the next call to clearTempBreakpoints.
520
  void setEntryBreakpointForCodeBlock(CodeBlock *codeBlock);
521
522
  /// Add a non-user breakpoint to the last interpreted function on the
523
  /// call stack, at the next offset after the saved return address.
524
  /// This allows stepping out of the currently executing function.
525
  /// \param forRestorationBreakpoint whether to create as a restoration
526
  /// breakpoint or a Step breakpoint
527
  void breakpointCaller(bool forRestorationBreakpoint);
528
529
  /// Add a Step breakpoint at the exception handler for the current state.
530
  /// Does nothing if the exception is uncaught.
531
  void breakpointExceptionHandler(const InterpreterState &state);
532
533
  /// Should not be called directly except from \p clearTempBreakpoints() or \p
534
  /// clearRestorationBreakpoints(). This function clears all of the given type
535
  /// of non-user breakpoint that have been set.
536
  /// /param isStepBreakpoint true to clear Step breakpoints, false to clear
537
  /// Restoration breakpoints.
538
  void doClearNonUserBreakpoints(bool isStepBreakpoint);
539
540
  /// Clear all the Step and OnLoad breakpoints that have been set.
541
  void clearTempBreakpoints();
542
543
  /// Set a Restoration breakpoint at \p offset in \p codeBlock.
544
  void setRestorationBreakpoint(
545
      CodeBlock *codeBlock,
546
      uint32_t offset,
547
      uint32_t callStackDepth);
548
549
  /// Re-install any breakpoint stored by \p breakpointToRestore_.
550
  /// \return true if there was a breakpoint to restore, false otherwise.
551
  bool restoreBreakpointIfAny();
552
553
  /// Clear all the Restoration breakpoints that have been set.
554
  void clearRestorationBreakpoints();
555
556
  /// Steps a single instruction.
557
  /// Requires that the current instruction steps to somewhere else in the
558
  /// current function.
559
  /// If the current instruction is a call or a return, don't use this function.
560
  ExecutionStatus stepInstruction(InterpreterState &state);
561
562
  /// Evaluate \p src rooted in the stack frame specified in \p args. 0 is the
563
  /// topmost frame, corresponding to \p state. Populate \p outMetadata
564
  /// with metadata for the result.
565
  /// \return the resulting HermesValue.
566
  HermesValue evalInFrame(
567
      const EvalArgs &args,
568
      const std::string &src,
569
      const InterpreterState &state,
570
      EvalResultMetadata *outMetadata);
571
572
  /// Given that the runtime threw an exception, clear the thrown value, and
573
  /// populate the \p outMetadata. \return the thrown value.
574
  HermesValue getExceptionAsEvalResult(EvalResultMetadata *outMetadata);
575
576
  /// Requires that we're currently debugging.
577
  /// If the queue is empty, invoke the didPauseCallback_ (or command line,
578
  /// provided that stdin is interactive), with the given interpeter state \p
579
  /// state and pause reason \p pause.
580
  /// If the pause is EvalComplete, the evalResult is given in \p evalResult;
581
  /// othewrise the evalResult should be ignored.
582
  /// \return the next command to run in a paused interpreter loop.
583
  DebugCommand getNextCommand(
584
      InterpreterState state,
585
      PauseReason pauseReason,
586
      HermesValue evalResult,
587
      const EvalResultMetadata &evalMetadata,
588
0
      BreakpointID breakpoint) {
589
    // If we have a didPauseCallback, invoke it. If we don't have a callback,
590
    // fall back to simply continuing.
591
0
    if (didPauseCallback_)
592
0
      return didPauseCallback_(
593
0
          state, pauseReason, evalResult, evalMetadata, breakpoint);
594
595
0
    return DebugCommand::makeContinue();
596
0
  }
597
598
  /// \return true if the states are on different instructions within the same
599
  /// statement.
600
  /// Ensure different instructions to allow for stepping to the same
601
  /// statement within a loop.
602
  inline bool sameStatementDifferentInstruction(
603
      const InterpreterState &a,
604
0
      const InterpreterState &b) const {
605
0
    auto aLoc = getLocationForState(a);
606
0
    auto bLoc = getLocationForState(b);
607
608
    // Same statement in the same codeBlock, but different offsets.
609
0
    return a.codeBlock == b.codeBlock && aLoc->statement == bLoc->statement &&
610
0
        a.offset != b.offset;
611
0
  }
612
613
  OptValue<hbc::DebugSourceLocation> getLocationForState(
614
0
      const InterpreterState &state) const {
615
0
    return state.codeBlock->getSourceLocation(state.offset);
616
0
  }
617
618
  /// Attempt to resolve the requestedLocation in \p breakpoint.
619
  /// If successful, sets breakpoint.resolvedLocation to the resolved location.
620
  /// Requires that \p breakpoint is not yet resolved.
621
  /// \return true if the breakpoint is successfully resolved.
622
  bool resolveBreakpointLocation(Breakpoint &breakpoint) const;
623
624
  /// Unresolves the breakpoint.
625
  void unresolveBreakpointLocation(Breakpoint &breakpoint);
626
627
  /// \return a CallFrameInfo for a given code block \p codeBlock and IP offset
628
  /// into it \p ipOffset.
629
  CallFrameInfo getCallFrameInfo(const CodeBlock *codeBlock, uint32_t offset)
630
      const;
631
632
  /// Get the jump target for an instruction (if it is a jump).
633
  llvh::Optional<uint32_t> findJumpTarget(CodeBlock *block, uint32_t offset);
634
635
  /// Set breakpoints at all possible next instructions after the current one.
636
  void breakAtPossibleNextInstructions(const InterpreterState &state);
637
};
638
639
} // namespace vm
640
} // namespace hermes
641
642
#endif // HERMES_ENABLE_DEBUGGER
643
#endif