Coverage Report

Created: 2025-01-28 06:38

/src/hermes/lib/VM/Debugger/Debugger.cpp
Line
Count
Source (jump to first uncovered line)
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
#ifdef HERMES_ENABLE_DEBUGGER
9
10
#include "hermes/VM/Debugger/Debugger.h"
11
12
#include "hermes/Inst/InstDecode.h"
13
#include "hermes/Support/UTF8.h"
14
#include "hermes/VM/Callable.h"
15
#include "hermes/VM/CodeBlock.h"
16
#include "hermes/VM/JSError.h"
17
#include "hermes/VM/JSLib.h"
18
#include "hermes/VM/Operations.h"
19
#include "hermes/VM/Profiler/SamplingProfiler.h"
20
#include "hermes/VM/Runtime.h"
21
#include "hermes/VM/RuntimeModule.h"
22
#include "hermes/VM/StackFrame-inline.h"
23
#include "hermes/VM/StringView.h"
24
#pragma GCC diagnostic push
25
26
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
27
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
28
#endif
29
#ifdef HERMES_ENABLE_DEBUGGER
30
31
namespace hermes {
32
namespace vm {
33
34
using namespace hermes::inst;
35
namespace fhd = ::facebook::hermes::debugger;
36
37
// These instructions won't recursively invoke the interpreter,
38
// and we also can't easily determine where they will jump to.
39
0
static inline bool shouldSingleStep(OpCode opCode) {
40
0
  return opCode == OpCode::Throw || opCode == OpCode::SwitchImm;
41
0
}
42
43
static StringView getFunctionName(
44
    Runtime &runtime,
45
0
    const CodeBlock *codeBlock) {
46
0
  auto functionName = codeBlock->getNameMayAllocate();
47
0
  if (functionName == Predefined::getSymbolID(Predefined::emptyString)) {
48
0
    functionName = Predefined::getSymbolID(Predefined::anonymous);
49
0
  }
50
0
  return runtime.getIdentifierTable().getStringView(runtime, functionName);
51
0
}
52
53
static std::string getFileNameAsUTF8(
54
    Runtime &runtime,
55
    RuntimeModule *runtimeModule,
56
0
    uint32_t filenameId) {
57
0
  const auto *debugInfo = runtimeModule->getBytecode()->getDebugInfo();
58
0
  return debugInfo->getFilenameByID(filenameId);
59
0
}
60
61
/// \returns the IP in \p cb, which is the given \p frame (0 being the top most
62
/// frame). This is either the current program IP, or the return IP following a
63
/// call instruction.
64
static unsigned
65
0
getIPOffsetInBlock(Runtime &runtime, const CodeBlock *cb, uint32_t frame) {
66
0
  if (frame == 0) {
67
    // The current IP in cb is the current program IP if this is top most frame.
68
0
    return cb->getOffsetOf(runtime.getCurrentIP());
69
0
  }
70
71
  // Otherwise, IP for frame N is stored in frame N - 1.
72
0
  auto prevFrameInfo = runtime.stackFrameInfoByIndex(frame - 1);
73
0
  return cb->getOffsetOf(prevFrameInfo->frame->getSavedIP());
74
0
}
75
76
/// Used when accessing the scope descriptor information for a particular
77
/// bytecode.
78
struct ScopeRegAndDescriptorChain {
79
  /// Which register contains the Environment.
80
  unsigned reg;
81
82
  /// All scope descriptor chain.
83
  llvh::SmallVector<hbc::DebugScopeDescriptor, 4> scopeDescs;
84
};
85
86
/// \return a pair with its first element being the number of register that
87
/// holds the Environment at the runtime's current IP, and its second, the scope
88
/// chain containing the block and all its lexical parents, including the global
89
/// scope. \return none if the scope chain is unavailable.
90
static llvh::Optional<ScopeRegAndDescriptorChain>
91
0
scopeDescChainForBlock(Runtime &runtime, const CodeBlock *cb, uint32_t frame) {
92
0
  OptValue<hbc::DebugSourceLocation> locationOpt =
93
0
      cb->getSourceLocation(getIPOffsetInBlock(runtime, cb, frame));
94
95
0
  if (!locationOpt) {
96
0
    return llvh::None;
97
0
  }
98
99
0
  unsigned envReg = locationOpt->envReg;
100
0
  if (envReg == hbc::DebugSourceLocation::NO_REG) {
101
    // The debug information doesn't know where the environment is stored.
102
0
    return llvh::None;
103
0
  }
104
105
0
  ScopeRegAndDescriptorChain ret;
106
0
  ret.reg = envReg;
107
0
  RuntimeModule *runtimeModule = cb->getRuntimeModule();
108
0
  const hbc::BCProvider *bytecode = runtimeModule->getBytecode();
109
0
  const hbc::DebugInfo *debugInfo = bytecode->getDebugInfo();
110
111
0
  OptValue<unsigned> currentScopeDescOffset = locationOpt->scopeAddress;
112
0
  while (currentScopeDescOffset) {
113
0
    ret.scopeDescs.push_back(
114
0
        debugInfo->getScopeDescriptor(*currentScopeDescOffset));
115
116
0
    currentScopeDescOffset = ret.scopeDescs.back().parentOffset;
117
0
  }
118
119
0
  return ret;
120
0
}
121
122
/// \return the first (inner-most) scope descriptor in \p frame.
123
static OptValue<uint32_t> getScopeDescIndexForFrame(
124
    const llvh::SmallVectorImpl<hbc::DebugScopeDescriptor> &scopeDescs,
125
0
    uint32_t frame) {
126
  // newFrame is a flag indicating that the next scope descriptor belongs to a
127
  // new frame in the call stack. The default value (true) represents the fact
128
  // that the top most scope should always be included in the result.
129
0
  bool newFrame = true;
130
131
  // counts the number of new frames see in the scope descriptor chain.
132
0
  uint32_t numSeenFrames = 0;
133
0
  for (uint32_t i = 0, end = scopeDescs.size(); i < end; ++i) {
134
0
    const hbc::DebugScopeDescriptor &currScopeDesc = scopeDescs[i];
135
0
    if (newFrame) {
136
      // currScopeDesc is the top-most scope descriptor in a new frame. The
137
      // search is over if numSeenFrames equals frame.
138
0
      if (numSeenFrames == frame) {
139
0
        return i;
140
0
      }
141
0
      ++numSeenFrames;
142
0
    }
143
    // A new frame is found if currScopeDesc is not an inner scope (i.e., it is
144
    // the first scope in its function).
145
0
    newFrame = !currScopeDesc.flags.isInnerScope;
146
0
  }
147
148
0
  return llvh::None;
149
0
}
150
151
0
void Debugger::triggerAsyncPause(AsyncPauseKind kind) {
152
0
  runtime_.triggerDebuggerAsyncBreak(kind);
153
0
}
154
155
llvh::Optional<uint32_t> Debugger::findJumpTarget(
156
    CodeBlock *block,
157
0
    uint32_t offset) {
158
0
  const Inst *ip = block->getOffsetPtr(offset);
159
160
0
#define DEFINE_JUMP_LONG_VARIANT(name, nameLong) \
161
0
  case OpCode::name: {                           \
162
0
    return offset + ip->i##name.op1;             \
163
0
  }                                              \
164
0
  case OpCode::nameLong: {                       \
165
0
    return offset + ip->i##nameLong.op1;         \
166
0
  }
167
168
0
  switch (ip->opCode) {
169
0
#include "hermes/BCGen/HBC/BytecodeList.def"
170
0
    default:
171
0
      return llvh::None;
172
0
  }
173
0
#undef DEFINE_JUMP_LONG_VARIANT
174
0
}
175
176
0
void Debugger::breakAtPossibleNextInstructions(const InterpreterState &state) {
177
0
  auto nextOffset =
178
0
      state.offset + getInstSize(getRealOpCode(state.codeBlock, state.offset));
179
  // Set a breakpoint at the next instruction in the code block if this is not
180
  // the last instruction.
181
0
  if (nextOffset < state.codeBlock->getOpcodeArray().size()) {
182
0
    setStepBreakpoint(
183
0
        state.codeBlock, nextOffset, runtime_.getCurrentFrameOffset());
184
0
  }
185
  // If the instruction is a jump, set a break point at the possible
186
  // jump target; otherwise, only break at the next instruction.
187
  // This instruction could jump to itself, so this step should be after the
188
  // previous step (otherwise the Jmp will have been overwritten by a Debugger
189
  // inst, and we won't be able to find the target).
190
  //
191
  // Since we've already set a breakpoint on the next instruction, we can
192
  // skip the case where that is also the jump target.
193
0
  auto jumpTarget = findJumpTarget(state.codeBlock, state.offset);
194
0
  if (jumpTarget.hasValue() && jumpTarget.getValue() != nextOffset) {
195
0
    setStepBreakpoint(
196
0
        state.codeBlock,
197
0
        jumpTarget.getValue(),
198
0
        runtime_.getCurrentFrameOffset());
199
0
  }
200
0
}
201
202
0
inst::OpCode Debugger::getRealOpCode(CodeBlock *block, uint32_t offset) const {
203
0
  auto breakpointOpt = getBreakpointLocation(block, offset);
204
0
  if (breakpointOpt) {
205
0
    const auto *inst =
206
0
        reinterpret_cast<const inst::Inst *>(&(breakpointOpt->opCode));
207
0
    return inst->opCode;
208
0
  }
209
210
0
  auto opcodes = block->getOpcodeArray();
211
0
  assert(offset < opcodes.size() && "opCode offset out of bounds");
212
0
  const auto *inst = reinterpret_cast<const inst::Inst *>(&opcodes[offset]);
213
0
  return inst->opCode;
214
0
}
215
216
ExecutionStatus Debugger::runDebugger(
217
    Debugger::RunReason runReason,
218
0
    InterpreterState &state) {
219
0
  assert(!isDebugging_ && "can't run debugger while debugging is in progress");
220
0
  isDebugging_ = true;
221
222
  // We're going to derive a PauseReason to pass to the event observer. OptValue
223
  // is used to check our logic which is rather complicated.
224
0
  OptValue<PauseReason> pauseReason;
225
226
  // If the pause reason warrants it, this is set to be a valid breakpoint ID.
227
0
  BreakpointID breakpoint = fhd::kInvalidBreakpoint;
228
229
0
  if (runReason == RunReason::Exception) {
230
    // We hit an exception, report that we broke because of this.
231
0
    if (isUnwindingException_) {
232
      // We're currently unwinding an exception, so don't stop here
233
      // because we must have already reported the exception.
234
0
      isDebugging_ = false;
235
0
      return ExecutionStatus::EXCEPTION;
236
0
    }
237
0
    isUnwindingException_ = true;
238
0
    clearTempBreakpoints();
239
0
    pauseReason = PauseReason::Exception;
240
0
  } else if (runReason == RunReason::AsyncBreakImplicit) {
241
0
    if (curStepMode_.hasValue()) {
242
      // Avoid draining the queue or corrupting step state.
243
0
      isDebugging_ = false;
244
0
      return ExecutionStatus::RETURNED;
245
0
    }
246
0
    pauseReason = PauseReason::AsyncTriggerImplicit;
247
0
  } else if (runReason == RunReason::AsyncBreakExplicit) {
248
    // The user requested an async break, so we can clear stepping state
249
    // with the knowledge that the inspector isn't sending an immediate
250
    // continue.
251
0
    if (curStepMode_) {
252
0
      clearTempBreakpoints();
253
0
      curStepMode_ = llvh::None;
254
0
    }
255
0
    pauseReason = PauseReason::AsyncTriggerExplicit;
256
0
  } else {
257
0
    assert(runReason == RunReason::Opcode && "Unknown run reason");
258
259
    // Whether we breakpoint on all CodeBlocks, or breakpoint caller, they'll
260
    // eventually hit the installed Debugger OpCode and get here. We need to
261
    // restore any breakpoint that we delayed restoring in
262
    // processInstUnderDebuggerOpCode().
263
0
    if (restoreBreakpointIfAny()) {
264
      // And if we do get here and restored a breakpoint, it means that we're
265
      // stopping here because of the Restoration breakpoint we added from
266
      // pauseOnAllCodeBlocksToRestoreBreakpoint_ or breakpointCaller(). Clear
267
      // them out because we're not reliant on them to handle any stepping.
268
0
      clearRestorationBreakpoints();
269
270
      // If after clearing Restoration breakpoints, there is no longer a
271
      // breakpoint at the current location, then that means there isn't any
272
      // user or temp breakpoint at this location. If the instruction is also
273
      // not an actual debugger statement, then we can just exit out of the
274
      // debugger loop.
275
0
      auto breakpointOpt = getBreakpointLocation(state.codeBlock, state.offset);
276
0
      OpCode curCode = getRealOpCode(state.codeBlock, state.offset);
277
0
      if (!breakpointOpt.hasValue() && curCode != OpCode::Debugger) {
278
0
        isDebugging_ = false;
279
0
        return ExecutionStatus::RETURNED;
280
0
      }
281
0
    }
282
283
    // First, check if we have to finish a step that's in progress.
284
0
    auto breakpointOpt = getBreakpointLocation(state.codeBlock, state.offset);
285
0
    if (breakpointOpt.hasValue() &&
286
0
        (breakpointOpt->hasStepBreakpoint || breakpointOpt->onLoad)) {
287
      // We've hit a Step, which must mean we were stepping, or
288
      // pause-on-load if it's the first instruction of the global function.
289
0
      if (breakpointOpt->onLoad) {
290
0
        pauseReason = PauseReason::ScriptLoaded;
291
0
        clearTempBreakpoints();
292
0
      } else if (
293
0
          breakpointOpt->callStackDepths.count(0) ||
294
0
          breakpointOpt->callStackDepths.count(
295
0
              runtime_.getCurrentFrameOffset())) {
296
        // This is in fact a temp breakpoint we want to stop on right now.
297
0
        assert(curStepMode_ && "no step to finish");
298
0
        clearTempBreakpoints();
299
0
        auto locationOpt = getLocationForState(state);
300
301
0
        if (*curStepMode_ == StepMode::Into ||
302
0
            *curStepMode_ == StepMode::Over) {
303
          // If we're not stepping out, then we need to finish the step
304
          // in progress.
305
          // Otherwise, we just need to stop at the breakpoint site.
306
0
          while (!locationOpt.hasValue() || locationOpt->statement == 0 ||
307
0
                 sameStatementDifferentInstruction(state, preStepState_)) {
308
            // Move to the next source location.
309
0
            OpCode curCode = getRealOpCode(state.codeBlock, state.offset);
310
311
0
            if (curCode == OpCode::Ret) {
312
              // We're stepping out now.
313
0
              breakpointCaller(/*forRestorationBreakpoint*/ false);
314
0
              pauseOnAllCodeBlocks_ = true;
315
0
              curStepMode_ = StepMode::Out;
316
0
              isDebugging_ = false;
317
0
              return ExecutionStatus::RETURNED;
318
0
            }
319
320
            // These instructions won't recursively invoke the interpreter,
321
            // and we also can't easily determine where they will jump to,
322
            // so use single-step mode.
323
0
            if (shouldSingleStep(curCode)) {
324
0
              ExecutionStatus status = stepInstruction(state);
325
0
              if (status == ExecutionStatus::EXCEPTION) {
326
0
                breakpointExceptionHandler(state);
327
0
                isDebugging_ = false;
328
0
                return status;
329
0
              }
330
0
              locationOpt = getLocationForState(state);
331
0
              continue;
332
0
            }
333
334
            // Set a breakpoint at the next instruction and continue.
335
0
            breakAtPossibleNextInstructions(state);
336
0
            if (*curStepMode_ == StepMode::Into) {
337
0
              pauseOnAllCodeBlocks_ = true;
338
0
            }
339
0
            isDebugging_ = false;
340
0
            return ExecutionStatus::RETURNED;
341
0
          }
342
0
        }
343
344
        // Done stepping.
345
0
        curStepMode_ = llvh::None;
346
0
        pauseReason = PauseReason::StepFinish;
347
0
      } else {
348
        // We don't want to stop on this Step breakpoint.
349
0
        isDebugging_ = false;
350
0
        return ExecutionStatus::RETURNED;
351
0
      }
352
0
    } else {
353
0
      auto checkBreakpointCondition =
354
0
          [&](const std::string &condition) -> bool {
355
0
        if (condition.empty()) {
356
          // The empty condition is considered unset,
357
          // and we always pause on such breakpoints.
358
0
          return true;
359
0
        }
360
0
        EvalResultMetadata metadata;
361
0
        EvalArgs args;
362
0
        args.frameIdx = 0;
363
        // No handle here - we will only pass the value to toBoolean,
364
        // and no allocations should occur until then.
365
0
        HermesValue conditionResult =
366
0
            evalInFrame(args, condition, state, &metadata);
367
0
        NoAllocScope noAlloc(runtime_);
368
0
        if (metadata.isException) {
369
          // Ignore exceptions.
370
          // Cleanup is done by evalInFrame.
371
0
          return false;
372
0
        }
373
0
        noAlloc.release();
374
0
        return toBoolean(conditionResult);
375
0
      };
376
377
      // We've stopped on either a user breakpoint or a debugger statement.
378
      // Note: if we've stopped on both (breakpoint set on a debugger statement)
379
      // then we only report the breakpoint and move past it,
380
      // ignoring the debugger statement.
381
0
      if (breakpointOpt.hasValue()) {
382
0
        assert(
383
0
            breakpointOpt->user.hasValue() &&
384
0
            "must be stopped on a user breakpoint");
385
0
        const auto &condition =
386
0
            userBreakpoints_[*breakpointOpt->user].condition;
387
0
        if (checkBreakpointCondition(condition)) {
388
0
          pauseReason = PauseReason::Breakpoint;
389
0
          breakpoint = *(breakpointOpt->user);
390
0
        } else {
391
0
          isDebugging_ = false;
392
0
          return ExecutionStatus::RETURNED;
393
0
        }
394
0
      } else {
395
0
        pauseReason = PauseReason::DebuggerStatement;
396
0
      }
397
398
      // Stop stepping immediately.
399
0
      if (curStepMode_) {
400
        // If we're in a step, then the client still thinks we're debugging,
401
        // so just clear the status and clear the temp breakpoints.
402
0
        curStepMode_ = llvh::None;
403
0
        clearTempBreakpoints();
404
0
      }
405
0
    }
406
0
  }
407
408
0
  assert(pauseReason.hasValue() && "runDebugger failed to set PauseReason");
409
0
  return debuggerLoop(state, *pauseReason, breakpoint);
410
0
}
411
412
ExecutionStatus Debugger::debuggerLoop(
413
    InterpreterState &state,
414
    PauseReason pauseReason,
415
0
    BreakpointID breakpoint) {
416
0
  const InterpreterState startState = state;
417
0
  const bool startException = pauseReason == PauseReason::Exception;
418
0
  EvalResultMetadata evalResultMetadata;
419
0
  CallResult<InterpreterState> result{ExecutionStatus::EXCEPTION};
420
0
  GCScope gcScope{runtime_};
421
0
  MutableHandle<> evalResult{runtime_};
422
  // Keep the evalResult alive, even if all other handles are flushed.
423
0
  static constexpr unsigned KEEP_HANDLES = 1;
424
0
#if HERMESVM_SAMPLING_PROFILER_AVAILABLE
425
0
  SuspendSamplingProfilerRAII ssp{runtime_, "debugger"};
426
0
#endif // HERMESVM_SAMPLING_PROFILER_AVAILABLE
427
0
  while (true) {
428
0
    GCScopeMarkerRAII marker{runtime_};
429
0
    auto command = getNextCommand(
430
0
        state, pauseReason, *evalResult, evalResultMetadata, breakpoint);
431
0
    evalResult.clear();
432
0
    switch (command.type) {
433
0
      case DebugCommandType::NONE:
434
0
        break;
435
0
      case DebugCommandType::CONTINUE:
436
0
        isDebugging_ = false;
437
0
        curStepMode_ = llvh::None;
438
0
        return ExecutionStatus::RETURNED;
439
0
      case DebugCommandType::EVAL:
440
0
        evalResult = evalInFrame(
441
0
            command.evalArgs, command.text, startState, &evalResultMetadata);
442
0
        pauseReason = PauseReason::EvalComplete;
443
0
        break;
444
0
      case DebugCommandType::STEP: {
445
        // If we pause again in this function, it will be due to a step.
446
0
        pauseReason = PauseReason::StepFinish;
447
0
        const StepMode stepMode = command.stepArgs.mode;
448
        // We should only be able to step from instructions with recorded
449
        // locations.
450
0
        const auto startLocationOpt = getLocationForState(state);
451
0
        (void)startLocationOpt;
452
0
        assert(
453
0
            startLocationOpt.hasValue() &&
454
0
            "starting step from a location without debug info");
455
0
        preStepState_ = state;
456
0
        if (stepMode == StepMode::Into || stepMode == StepMode::Over) {
457
0
          if (startException) {
458
            // Paused because of a throw or we're about to throw.
459
            // Breakpoint the handler if it's there, and continue.
460
0
            breakpointExceptionHandler(state);
461
0
            isDebugging_ = false;
462
0
            curStepMode_ = stepMode;
463
0
            return ExecutionStatus::RETURNED;
464
0
          }
465
0
          while (true) {
466
            // NOTE: this loop doesn't actually allocate any handles presently,
467
            // but it could, and clearing all handles is really cheap.
468
0
            gcScope.flushToSmallCount(KEEP_HANDLES);
469
0
            OpCode curCode = getRealOpCode(state.codeBlock, state.offset);
470
471
0
            if (curCode == OpCode::Ret) {
472
0
              breakpointCaller(/*forRestorationBreakpoint*/ false);
473
0
              pauseOnAllCodeBlocks_ = true;
474
0
              isDebugging_ = false;
475
              // Equivalent to a step out.
476
0
              curStepMode_ = StepMode::Out;
477
0
              return ExecutionStatus::RETURNED;
478
0
            }
479
480
            // These instructions won't recursively invoke the interpreter,
481
            // and we also can't easily determine where they will jump to,
482
            // so use single-step mode.
483
0
            if (shouldSingleStep(curCode)) {
484
0
              ExecutionStatus status = stepInstruction(state);
485
0
              if (status == ExecutionStatus::EXCEPTION) {
486
0
                breakpointExceptionHandler(state);
487
0
                isDebugging_ = false;
488
0
                curStepMode_ = stepMode;
489
0
                return status;
490
0
              }
491
0
              auto locationOpt = getLocationForState(state);
492
0
              if (locationOpt.hasValue() && locationOpt->statement != 0 &&
493
0
                  !sameStatementDifferentInstruction(state, preStepState_)) {
494
                // We've moved on from the statement that was executing.
495
0
                break;
496
0
              }
497
0
              continue;
498
0
            }
499
500
            // Set a breakpoint at the next instruction and continue.
501
            // If there is a user installed breakpoint, we need to temporarily
502
            // uninstall the breakpoint so that we can get the correct
503
            // offset for the next instruction.
504
0
            auto breakpointOpt =
505
0
                getBreakpointLocation(state.codeBlock, state.offset);
506
0
            if (breakpointOpt) {
507
0
              uninstallBreakpoint(
508
0
                  state.codeBlock, state.offset, breakpointOpt->opCode);
509
0
            }
510
0
            breakAtPossibleNextInstructions(state);
511
0
            if (breakpointOpt) {
512
0
              state.codeBlock->installBreakpointAtOffset(state.offset);
513
0
            }
514
0
            if (stepMode == StepMode::Into) {
515
              // Stepping in could enter another code block,
516
              // so handle that by breakpointing all code blocks.
517
0
              pauseOnAllCodeBlocks_ = true;
518
0
            }
519
0
            isDebugging_ = false;
520
0
            curStepMode_ = stepMode;
521
0
            return ExecutionStatus::RETURNED;
522
0
          }
523
0
        } else {
524
0
          ExecutionStatus status;
525
0
          if (startException) {
526
0
            breakpointExceptionHandler(state);
527
0
            status = ExecutionStatus::EXCEPTION;
528
0
          } else {
529
0
            breakpointCaller(/*forRestorationBreakpoint*/ false);
530
0
            status = ExecutionStatus::RETURNED;
531
0
          }
532
          // Stepping out of here is the same as continuing.
533
0
          isDebugging_ = false;
534
0
          curStepMode_ = StepMode::Out;
535
0
          return status;
536
0
        }
537
0
        break;
538
0
      }
539
0
    }
540
0
  }
541
0
}
542
543
356
void Debugger::willExecuteModule(RuntimeModule *module, CodeBlock *codeBlock) {
544
  // This function should only be called on the main RuntimeModule and not on
545
  // any "child" RuntimeModules it may create through lazy compilation.
546
356
  assert(
547
356
      module == module->getLazyRootModule() &&
548
356
      "Expected to only run on lazy root module");
549
550
356
  if (!getShouldPauseOnScriptLoad())
551
356
    return;
552
  // We want to pause on the first instruction of this module.
553
  // Add a breakpoint on the first opcode of its global function.
554
0
  auto globalFunctionIndex = module->getBytecode()->getGlobalFunctionIndex();
555
0
  auto globalCode = module->getCodeBlockMayAllocate(globalFunctionIndex);
556
0
  setOnLoadBreakpoint(globalCode, 0);
557
0
}
558
559
597
void Debugger::willUnloadModule(RuntimeModule *module) {
560
597
  if (tempBreakpoints_.size() == 0 && restorationBreakpoints_.size() == 0 &&
561
597
      userBreakpoints_.size() == 0) {
562
597
    return;
563
597
  }
564
565
0
  llvh::DenseSet<CodeBlock *> unloadingBlocks;
566
0
  for (auto *block : module->getFunctionMap()) {
567
0
    if (block) {
568
0
      unloadingBlocks.insert(block);
569
0
    }
570
0
  }
571
572
0
  for (auto &bp : userBreakpoints_) {
573
0
    if (unloadingBlocks.count(bp.second.codeBlock)) {
574
0
      unresolveBreakpointLocation(bp.second);
575
0
    }
576
0
  }
577
578
0
  auto cleanNonUserBreakpoint = [&](Breakpoint &bp) {
579
0
    if (!unloadingBlocks.count(bp.codeBlock))
580
0
      return false;
581
582
0
    auto *ptr = bp.codeBlock->getOffsetPtr(bp.offset);
583
0
    auto it = breakpointLocations_.find(ptr);
584
0
    if (it != breakpointLocations_.end()) {
585
0
      auto &location = it->second;
586
0
      assert(!location.user.hasValue() && "Unexpected user breakpoint");
587
0
      uninstallBreakpoint(bp.codeBlock, bp.offset, location.opCode);
588
0
      breakpointLocations_.erase(it);
589
0
    }
590
0
    return true;
591
0
  };
592
593
0
  tempBreakpoints_.erase(
594
0
      std::remove_if(
595
0
          tempBreakpoints_.begin(),
596
0
          tempBreakpoints_.end(),
597
0
          cleanNonUserBreakpoint),
598
0
      tempBreakpoints_.end());
599
600
0
  restorationBreakpoints_.erase(
601
0
      std::remove_if(
602
0
          restorationBreakpoints_.begin(),
603
0
          restorationBreakpoints_.end(),
604
0
          cleanNonUserBreakpoint),
605
0
      restorationBreakpoints_.end());
606
0
}
607
608
0
void Debugger::resolveBreakpoints(CodeBlock *codeBlock) {
609
0
  for (auto &it : userBreakpoints_) {
610
0
    auto &breakpoint = it.second;
611
0
    if (!breakpoint.isResolved()) {
612
0
      resolveBreakpointLocation(breakpoint);
613
0
      if (breakpoint.isResolved() && breakpoint.enabled) {
614
0
        setUserBreakpoint(breakpoint.codeBlock, breakpoint.offset, it.first);
615
0
        if (breakpointResolvedCallback_) {
616
0
          breakpointResolvedCallback_(it.first);
617
0
        }
618
0
      }
619
0
    }
620
0
  }
621
0
}
622
623
auto Debugger::getCallFrameInfo(const CodeBlock *codeBlock, uint32_t ipOffset)
624
0
    const -> CallFrameInfo {
625
0
  GCScopeMarkerRAII marker{runtime_};
626
0
  CallFrameInfo frameInfo;
627
0
  if (!codeBlock) {
628
0
    frameInfo.functionName = "(native)";
629
0
  } else {
630
    // The caller doesn't expect that this function is allocating new handles,
631
    // so make sure we aren't.
632
0
    GCScopeMarkerRAII gcMarker{runtime_};
633
634
0
    llvh::SmallVector<char16_t, 64> storage;
635
0
    UTF16Ref functionName =
636
0
        getFunctionName(runtime_, codeBlock).getUTF16Ref(storage);
637
0
    convertUTF16ToUTF8WithReplacements(frameInfo.functionName, functionName);
638
0
    auto locationOpt = codeBlock->getSourceLocation(ipOffset);
639
0
    if (locationOpt) {
640
0
      frameInfo.location.line = locationOpt->line;
641
0
      frameInfo.location.column = locationOpt->column;
642
0
      frameInfo.location.fileId = resolveScriptId(
643
0
          codeBlock->getRuntimeModule(), locationOpt->filenameId);
644
0
      frameInfo.location.fileName = getFileNameAsUTF8(
645
0
          runtime_, codeBlock->getRuntimeModule(), locationOpt->filenameId);
646
0
    }
647
0
  }
648
0
  return frameInfo;
649
0
}
650
651
0
auto Debugger::getStackTrace() const -> StackTrace {
652
  // It's ok for the frame to be a native frame (i.e. null CodeBlock and null
653
  // IP), but there must be a frame.
654
0
  assert(
655
0
      runtime_.getCurrentFrame() &&
656
0
      "Must have at least one stack frame to call this function");
657
0
  using fhd::CallFrameInfo;
658
0
  GCScopeMarkerRAII marker{runtime_};
659
0
  MutableHandle<> displayName{runtime_};
660
0
  MutableHandle<JSObject> propObj{runtime_};
661
0
  std::vector<CallFrameInfo> frames;
662
  // Note that we are iterating backwards from the top.
663
  // Also note that each frame saves its caller's code block and IP (the
664
  // SavedCodeBlock and SavedIP). We obtain the current code location by getting
665
  // the Callee CodeBlock of the top frame.
666
0
  const CodeBlock *codeBlock =
667
0
      runtime_.getCurrentFrame()->getCalleeCodeBlock(runtime_);
668
0
  const inst::Inst *ip = runtime_.getCurrentIP();
669
0
  GCScopeMarkerRAII marker2{runtime_};
670
0
  for (auto cf : runtime_.getStackFrames()) {
671
0
    marker2.flush();
672
0
    uint32_t ipOffset = (codeBlock && ip) ? codeBlock->getOffsetOf(ip) : 0;
673
0
    CallFrameInfo frameInfo = getCallFrameInfo(codeBlock, ipOffset);
674
0
    if (auto callableHandle = Handle<Callable>::dyn_vmcast(
675
0
            Handle<>(&cf.getCalleeClosureOrCBRef()))) {
676
0
      NamedPropertyDescriptor desc;
677
0
      propObj = JSObject::getNamedDescriptorPredefined(
678
0
          callableHandle, runtime_, Predefined::displayName, desc);
679
0
      if (propObj) {
680
0
        auto displayNameRes = JSObject::getNamedSlotValue(
681
0
            createPseudoHandle(*propObj), runtime_, desc);
682
0
        if (LLVM_UNLIKELY(displayNameRes == ExecutionStatus::EXCEPTION)) {
683
0
          displayName = HermesValue::encodeUndefinedValue();
684
0
        } else {
685
0
          displayName = std::move(*displayNameRes);
686
0
          if (displayName->isString()) {
687
0
            llvh::SmallVector<char16_t, 64> storage;
688
0
            displayName->getString()->appendUTF16String(storage);
689
0
            convertUTF16ToUTF8WithReplacements(frameInfo.functionName, storage);
690
0
          }
691
0
        }
692
0
      }
693
0
    }
694
0
    frames.push_back(frameInfo);
695
696
0
    codeBlock = cf.getSavedCodeBlock();
697
0
    ip = cf.getSavedIP();
698
0
    if (!codeBlock && ip) {
699
      // If we have a saved IP but no saved code block, this was a bound call.
700
      // Go up one frame and get the callee code block but use the current
701
      // frame's saved IP.
702
0
      StackFramePtr prev = cf->getPreviousFrame();
703
0
      assert(prev && "bound function calls must have a caller");
704
0
      if (CodeBlock *parentCB = prev->getCalleeCodeBlock(runtime_)) {
705
0
        codeBlock = parentCB;
706
0
      }
707
0
    }
708
0
  }
709
0
  return StackTrace(std::move(frames));
710
0
}
711
712
0
auto Debugger::createBreakpoint(const SourceLocation &loc) -> BreakpointID {
713
0
  using fhd::kInvalidBreakpoint;
714
715
0
  OptValue<hbc::DebugSearchResult> locationOpt{llvh::None};
716
717
0
  Breakpoint breakpoint{};
718
0
  breakpoint.requestedLocation = loc;
719
  // Breakpoints are enabled by default.
720
0
  breakpoint.enabled = true;
721
0
  bool resolved = resolveBreakpointLocation(breakpoint);
722
723
0
  BreakpointID breakpointId;
724
0
  if (resolved) {
725
0
    auto breakpointLoc =
726
0
        getBreakpointLocation(breakpoint.codeBlock, breakpoint.offset);
727
0
    if (breakpointLoc.hasValue() && breakpointLoc->user) {
728
      // Don't set duplicate user breakpoint.
729
0
      return kInvalidBreakpoint;
730
0
    }
731
732
0
    breakpointId = nextBreakpointId_++;
733
0
    setUserBreakpoint(breakpoint.codeBlock, breakpoint.offset, breakpointId);
734
0
  } else {
735
0
    breakpointId = nextBreakpointId_++;
736
0
  }
737
738
0
  userBreakpoints_[breakpointId] = std::move(breakpoint);
739
740
0
  return breakpointId;
741
0
}
742
743
0
void Debugger::setBreakpointCondition(BreakpointID id, std::string condition) {
744
0
  auto it = userBreakpoints_.find(id);
745
746
0
  if (it == userBreakpoints_.end()) {
747
0
    return;
748
0
  }
749
750
0
  auto &breakpoint = it->second;
751
0
  breakpoint.condition = std::move(condition);
752
0
}
753
754
0
void Debugger::deleteBreakpoint(BreakpointID id) {
755
0
  auto it = userBreakpoints_.find(id);
756
757
0
  if (it == userBreakpoints_.end()) {
758
0
    return;
759
0
  }
760
761
0
  auto &breakpoint = it->second;
762
0
  if (breakpoint.enabled && breakpoint.isResolved()) {
763
0
    unsetUserBreakpoint(breakpoint);
764
0
  }
765
0
  userBreakpoints_.erase(it);
766
0
}
767
768
0
void Debugger::deleteAllBreakpoints() {
769
0
  for (auto &it : userBreakpoints_) {
770
0
    auto &breakpoint = it.second;
771
0
    if (breakpoint.enabled && breakpoint.isResolved()) {
772
0
      unsetUserBreakpoint(breakpoint);
773
0
    }
774
0
  }
775
0
  userBreakpoints_.clear();
776
0
}
777
778
0
void Debugger::setBreakpointEnabled(BreakpointID id, bool enable) {
779
0
  auto it = userBreakpoints_.find(id);
780
781
0
  if (it == userBreakpoints_.end()) {
782
0
    return;
783
0
  }
784
785
0
  auto &breakpoint = it->second;
786
0
  if (enable && !breakpoint.enabled) {
787
0
    breakpoint.enabled = true;
788
0
    if (breakpoint.isResolved()) {
789
0
      setUserBreakpoint(breakpoint.codeBlock, breakpoint.offset, id);
790
0
    }
791
0
  } else if (!enable && breakpoint.enabled) {
792
0
    breakpoint.enabled = false;
793
0
    if (breakpoint.isResolved()) {
794
0
      unsetUserBreakpoint(breakpoint);
795
0
    }
796
0
  }
797
0
}
798
799
llvh::Optional<const Debugger::BreakpointLocation>
800
0
Debugger::getBreakpointLocation(CodeBlock *codeBlock, uint32_t offset) const {
801
0
  return getBreakpointLocation(codeBlock->getOffsetPtr(offset));
802
0
}
803
804
auto Debugger::installBreakpoint(CodeBlock *codeBlock, uint32_t offset)
805
0
    -> BreakpointLocation & {
806
0
  auto opcodes = codeBlock->getOpcodeArray();
807
0
  assert(offset < opcodes.size() && "invalid offset to set breakpoint");
808
0
  auto &location =
809
0
      breakpointLocations_
810
0
          .try_emplace(codeBlock->getOffsetPtr(offset), opcodes[offset])
811
0
          .first->second;
812
0
  if (location.count() == 0) {
813
    // count used to be 0, so patch this in now that the count > 0.
814
0
    codeBlock->installBreakpointAtOffset(offset);
815
0
  }
816
0
  return location;
817
0
}
818
819
void Debugger::uninstallBreakpoint(
820
    CodeBlock *codeBlock,
821
    uint32_t offset,
822
0
    hbc::opcode_atom_t opCode) {
823
  // Check to see if we had temporarily kept the breakpoint uninstalled. If we
824
  // already did, and it's to be removed, then we don't need to restore it
825
  // anymore.
826
0
  if (breakpointToRestore_.first == codeBlock &&
827
0
      breakpointToRestore_.second == offset) {
828
0
    breakpointToRestore_ = {nullptr, 0};
829
0
  } else {
830
0
    codeBlock->uninstallBreakpointAtOffset(offset, opCode);
831
0
  }
832
0
}
833
834
void Debugger::setUserBreakpoint(
835
    CodeBlock *codeBlock,
836
    uint32_t offset,
837
0
    BreakpointID id) {
838
0
  BreakpointLocation &location = installBreakpoint(codeBlock, offset);
839
0
  location.user = id;
840
0
}
841
842
void Debugger::doSetNonUserBreakpoint(
843
    CodeBlock *codeBlock,
844
    uint32_t offset,
845
    uint32_t callStackDepth,
846
0
    bool isStepBreakpoint) {
847
0
  BreakpointLocation &location = installBreakpoint(codeBlock, offset);
848
0
  std::vector<Breakpoint> &breakpoints =
849
0
      isStepBreakpoint ? tempBreakpoints_ : restorationBreakpoints_;
850
0
  if (location.callStackDepths.count(callStackDepth) == 0) {
851
0
    location.callStackDepths.insert(callStackDepth);
852
0
  }
853
854
0
  if ((isStepBreakpoint && !location.hasStepBreakpoint) ||
855
0
      (!isStepBreakpoint && !location.hasRestorationBreakpoint)) {
856
    // Leave the resolved location empty for now,
857
    // let the caller fill it in lazily.
858
0
    Breakpoint breakpoint{};
859
0
    breakpoint.codeBlock = codeBlock;
860
0
    breakpoint.offset = offset;
861
0
    breakpoint.enabled = true;
862
0
    breakpoints.push_back(breakpoint);
863
0
  }
864
865
0
  if (isStepBreakpoint) {
866
0
    location.hasStepBreakpoint = true;
867
0
  } else {
868
0
    location.hasRestorationBreakpoint = true;
869
0
  }
870
0
}
871
872
void Debugger::setStepBreakpoint(
873
    CodeBlock *codeBlock,
874
    uint32_t offset,
875
0
    uint32_t callStackDepth) {
876
0
  doSetNonUserBreakpoint(
877
0
      codeBlock, offset, callStackDepth, /*isStepBreakpoint*/ true);
878
0
}
879
880
0
void Debugger::setOnLoadBreakpoint(CodeBlock *codeBlock, uint32_t offset) {
881
0
  BreakpointLocation &location = installBreakpoint(codeBlock, offset);
882
  // Leave the resolved location empty for now,
883
  // let the caller fill it in lazily.
884
0
  Breakpoint breakpoint{};
885
0
  breakpoint.codeBlock = codeBlock;
886
0
  breakpoint.offset = offset;
887
0
  breakpoint.enabled = true;
888
0
  assert(!location.onLoad && "can't set duplicate on-load breakpoint");
889
0
  location.onLoad = true;
890
0
  tempBreakpoints_.push_back(breakpoint);
891
0
  assert(location.count() && "invalid count following set breakpoint");
892
0
}
893
894
0
void Debugger::unsetUserBreakpoint(const Breakpoint &breakpoint) {
895
0
  CodeBlock *codeBlock = breakpoint.codeBlock;
896
0
  uint32_t offset = breakpoint.offset;
897
898
0
  auto opcodes = codeBlock->getOpcodeArray();
899
0
  (void)opcodes;
900
0
  assert(offset < opcodes.size() && "invalid offset to set breakpoint");
901
902
0
  const Inst *offsetPtr = codeBlock->getOffsetPtr(offset);
903
904
0
  auto locIt = breakpointLocations_.find(offsetPtr);
905
0
  assert(
906
0
      locIt != breakpointLocations_.end() &&
907
0
      "can't unset a non-existent breakpoint");
908
909
0
  auto &location = locIt->second;
910
911
0
  assert(location.user && "no user breakpoints to unset");
912
0
  location.user = llvh::None;
913
0
  if (location.count() == 0) {
914
    // No more reason to keep this location around.
915
    // Unpatch it from the opcode stream and delete it from the map.
916
0
    uninstallBreakpoint(codeBlock, offset, location.opCode);
917
0
    breakpointLocations_.erase(offsetPtr);
918
0
  }
919
0
}
920
921
0
void Debugger::setEntryBreakpointForCodeBlock(CodeBlock *codeBlock) {
922
0
  assert(!codeBlock->isLazy() && "can't set breakpoint on a lazy codeblock");
923
0
  assert(
924
0
      (pauseOnAllCodeBlocks_ || pauseOnAllCodeBlocksToRestoreBreakpoint_) &&
925
0
      "can't set temp breakpoint while not stepping");
926
0
  if (pauseOnAllCodeBlocks_) {
927
0
    setStepBreakpoint(codeBlock, 0, 0);
928
0
  }
929
0
  if (pauseOnAllCodeBlocksToRestoreBreakpoint_) {
930
0
    setRestorationBreakpoint(codeBlock, 0, 0);
931
0
  }
932
0
}
933
934
0
void Debugger::breakpointCaller(bool forRestorationBreakpoint) {
935
0
  auto callFrames = runtime_.getStackFrames();
936
937
0
  assert(callFrames.begin() != callFrames.end() && "empty call stack");
938
939
  // Go through the callStack backwards to find the first place we can break.
940
0
  auto frameIt = callFrames.begin();
941
0
  const Inst *ip = nullptr;
942
0
  for (; frameIt != callFrames.end(); ++frameIt) {
943
0
    ip = frameIt->getSavedIP();
944
0
    if (ip) {
945
0
      break;
946
0
    }
947
0
  }
948
0
  if (!ip) {
949
0
    return;
950
0
  }
951
  // If the ip was saved in the stack frame, the caller is the function
952
  // that we want to return to. The code block might not be saved in this
953
  // frame, so we need to find that in the frame below.
954
0
  do {
955
0
    frameIt++;
956
0
    assert(
957
0
        frameIt != callFrames.end() &&
958
0
        "The frame that has saved ip cannot be the bottom frame");
959
0
  } while (!frameIt->getCalleeCodeBlock(runtime_));
960
  // In the frame below, the 'calleeClosureORCB' register contains
961
  // the code block we need.
962
0
  CodeBlock *codeBlock = frameIt->getCalleeCodeBlock(runtime_);
963
0
  assert(codeBlock && "The code block must exist since we have ip");
964
  // Track the call stack depth that the breakpoint would be set on.
965
966
0
  uint32_t offset = codeBlock->getOffsetOf(ip);
967
0
  uint32_t newOffset = offset + getInstSize(getRealOpCode(codeBlock, offset));
968
969
0
  if (forRestorationBreakpoint) {
970
0
    setRestorationBreakpoint(
971
0
        codeBlock, newOffset, runtime_.calcFrameOffset(frameIt));
972
0
  } else {
973
0
    setStepBreakpoint(codeBlock, newOffset, runtime_.calcFrameOffset(frameIt));
974
0
  }
975
0
}
976
977
0
void Debugger::breakpointExceptionHandler(const InterpreterState &state) {
978
0
  auto target = findCatchTarget(state);
979
0
  if (!target) {
980
0
    return;
981
0
  }
982
0
  auto *codeBlock = target->first.codeBlock;
983
0
  auto offset = target->first.offset;
984
0
  setStepBreakpoint(codeBlock, offset, target->second);
985
0
}
986
987
0
void Debugger::doClearNonUserBreakpoints(bool isStepBreakpoint) {
988
0
  llvh::SmallVector<const Inst *, 4> toErase{};
989
990
0
  std::vector<Breakpoint> &breakpointsToClear =
991
0
      isStepBreakpoint ? tempBreakpoints_ : restorationBreakpoints_;
992
993
0
  for (const auto &breakpoint : breakpointsToClear) {
994
0
    auto *codeBlock = breakpoint.codeBlock;
995
0
    auto offset = breakpoint.offset;
996
0
    const Inst *inst = codeBlock->getOffsetPtr(offset);
997
0
    auto it = breakpointLocations_.find(inst);
998
0
    if (it == breakpointLocations_.end()) {
999
0
      continue;
1000
0
    }
1001
0
    auto &location = it->second;
1002
1003
0
    if (isStepBreakpoint) {
1004
0
      location.hasStepBreakpoint = false;
1005
0
      if (location.hasRestorationBreakpoint) {
1006
0
        continue;
1007
0
      }
1008
0
    } else {
1009
0
      location.hasRestorationBreakpoint = false;
1010
0
      if (location.hasStepBreakpoint) {
1011
0
        continue;
1012
0
      }
1013
0
    }
1014
1015
0
    if (location.count()) {
1016
0
      location.callStackDepths.clear();
1017
0
      location.onLoad = false;
1018
0
      if (location.count() == 0) {
1019
0
        uninstallBreakpoint(codeBlock, offset, location.opCode);
1020
0
        toErase.push_back(inst);
1021
0
      }
1022
0
    }
1023
0
  }
1024
0
  for (const Inst *inst : toErase) {
1025
0
    breakpointLocations_.erase(inst);
1026
0
  }
1027
0
  breakpointsToClear.clear();
1028
0
}
1029
1030
0
void Debugger::clearTempBreakpoints() {
1031
0
  doClearNonUserBreakpoints(/*isStepBreakpoint*/ true);
1032
0
  pauseOnAllCodeBlocks_ = false;
1033
0
}
1034
1035
void Debugger::setRestorationBreakpoint(
1036
    CodeBlock *codeBlock,
1037
    uint32_t offset,
1038
0
    uint32_t callStackDepth) {
1039
0
  doSetNonUserBreakpoint(
1040
0
      codeBlock, offset, callStackDepth, /*isStepBreakpoint*/ false);
1041
0
}
1042
1043
0
bool Debugger::restoreBreakpointIfAny() {
1044
0
  if (breakpointToRestore_.first != nullptr) {
1045
0
    breakpointToRestore_.first->installBreakpointAtOffset(
1046
0
        breakpointToRestore_.second);
1047
0
    breakpointToRestore_ = {nullptr, 0};
1048
0
    return true;
1049
0
  }
1050
0
  return false;
1051
0
}
1052
1053
0
void Debugger::clearRestorationBreakpoints() {
1054
0
  doClearNonUserBreakpoints(/*isStepBreakpoint*/ false);
1055
0
  pauseOnAllCodeBlocksToRestoreBreakpoint_ = false;
1056
0
}
1057
1058
0
ExecutionStatus Debugger::stepInstruction(InterpreterState &state) {
1059
0
  auto *codeBlock = state.codeBlock;
1060
0
  uint32_t offset = state.offset;
1061
0
  assert(
1062
0
      getRealOpCode(codeBlock, offset) != OpCode::Ret &&
1063
0
      "can't stepInstruction in Ret, use step-out semantics instead");
1064
0
  assert(
1065
0
      shouldSingleStep(getRealOpCode(codeBlock, offset)) &&
1066
0
      "can't stepInstruction through Call, use step-in semantics instead");
1067
0
  auto locationOpt = getBreakpointLocation(codeBlock, offset);
1068
0
  ExecutionStatus status;
1069
0
  InterpreterState newState{state};
1070
0
  if (locationOpt.hasValue()) {
1071
    // Temporarily uninstall the breakpoint so we can run the real instruction.
1072
0
    uninstallBreakpoint(codeBlock, offset, locationOpt->opCode);
1073
0
    status = runtime_.stepFunction(newState);
1074
0
    codeBlock->installBreakpointAtOffset(offset);
1075
0
  } else {
1076
0
    status = runtime_.stepFunction(newState);
1077
0
  }
1078
1079
0
  if (status != ExecutionStatus::EXCEPTION)
1080
0
    state = newState;
1081
0
  return status;
1082
0
}
1083
1084
ExecutionStatus Debugger::processInstUnderDebuggerOpCode(
1085
0
    InterpreterState &state) {
1086
0
  auto *codeBlock = state.codeBlock;
1087
0
  uint32_t offset = state.offset;
1088
0
  InterpreterState newState{state};
1089
0
  const inst::Inst *ip = codeBlock->getOffsetPtr(offset);
1090
1091
0
  auto locationOpt = getBreakpointLocation(codeBlock, offset);
1092
0
  if (locationOpt.hasValue()) {
1093
0
    uninstallBreakpoint(codeBlock, offset, locationOpt->opCode);
1094
0
    if (ip->opCode == OpCode::Debugger) {
1095
      // Breakpointed a debugger instruction, so move past it
1096
      // since we've already called the debugger on this instruction.
1097
0
      newState.offset = offset + 1;
1098
0
      state = newState;
1099
0
    } else if (ip->opCode == OpCode::Ret || isCallType(ip->opCode)) {
1100
0
      if (ip->opCode == OpCode::Ret) {
1101
        // Breakpoint the caller to make sure we'll get a chance to restore the
1102
        // uninstalled breakpoint.
1103
0
        breakpointCaller(/*forRestorationBreakpoint*/ true);
1104
0
      }
1105
1106
      // Set pause on all CodeBlocks so that we get a chance to restore the
1107
      // uninstalled breakpoint.
1108
0
      pauseOnAllCodeBlocksToRestoreBreakpoint_ = true;
1109
1110
      // For Ret & call opcodes, we won't recursively call the Interpreter.
1111
      // Instead, we'll leave the breakpoint uninstalled so that the Interpreter
1112
      // can continue to execute the real instruction. Then at the next
1113
      // opportunity we'll install the breakpoint back. This variable keeps
1114
      // track of the breakpoint to restore.
1115
0
      breakpointToRestore_ = {codeBlock, offset};
1116
0
    } else {
1117
0
      runtime_.setCurrentIP(ip);
1118
0
      ExecutionStatus status = runtime_.stepFunction(newState);
1119
0
      runtime_.invalidateCurrentIP();
1120
0
      codeBlock->installBreakpointAtOffset(offset);
1121
0
      if (status == ExecutionStatus::EXCEPTION) {
1122
0
        return status;
1123
0
      }
1124
0
      state = newState;
1125
0
    }
1126
0
  } else if (ip->opCode == OpCode::Debugger) {
1127
    // No breakpoint and we've already run the debugger, just continue on.
1128
0
    newState.offset = offset + 1;
1129
0
    state = newState;
1130
0
  }
1131
  // Else, if the current instruction is no longer a debugger instruction,
1132
  // we're just going to keep executing from the current IP. So no change to
1133
  // InterpreterState.
1134
1135
0
  return ExecutionStatus::RETURNED;
1136
0
}
1137
1138
/// Starting from scope \p i, add and \p return the number of variables in the
1139
/// frame. The frame ends in the first scope that's not an inner scope.
1140
static unsigned getFrameSize(
1141
    const llvh::SmallVector<hbc::DebugScopeDescriptor, 4> &scopeDescs,
1142
0
    uint32_t i) {
1143
0
  unsigned frameSize = 0;
1144
1145
0
  do {
1146
0
    frameSize += scopeDescs[i].names.size();
1147
0
  } while (scopeDescs[i++].flags.isInnerScope);
1148
1149
0
  return frameSize;
1150
0
}
1151
1152
0
auto Debugger::getLexicalInfoInFrame(uint32_t frame) const -> LexicalInfo {
1153
0
  auto frameInfo = runtime_.stackFrameInfoByIndex(frame);
1154
0
  assert(frameInfo && "Invalid frame");
1155
1156
0
  LexicalInfo result;
1157
0
  if (frameInfo->isGlobal) {
1158
    // Globals not yet supported.
1159
    // TODO: support them. For now we have an empty entry for the global scope.
1160
0
    result.variableCountsByScope_.push_back(0);
1161
0
    return result;
1162
0
  }
1163
0
  const CodeBlock *cb = frameInfo->frame->getCalleeCodeBlock(runtime_);
1164
0
  if (!cb) {
1165
    // Native functions have no saved code block.
1166
0
    result.variableCountsByScope_.push_back(0);
1167
0
    return result;
1168
0
  }
1169
1170
0
  llvh::Optional<ScopeRegAndDescriptorChain> envRegAndDescChain =
1171
0
      scopeDescChainForBlock(runtime_, cb, frame);
1172
0
  if (!envRegAndDescChain) {
1173
    // Binary was compiled without variable debug info.
1174
0
    result.variableCountsByScope_.push_back(0);
1175
0
    return result;
1176
0
  }
1177
1178
0
  const llvh::SmallVector<hbc::DebugScopeDescriptor, 4> &scopeDescs =
1179
0
      envRegAndDescChain->scopeDescs;
1180
0
  uint32_t currFrame = 0;
1181
0
  while (auto idx = getScopeDescIndexForFrame(scopeDescs, currFrame++)) {
1182
0
    result.variableCountsByScope_.push_back(getFrameSize(scopeDescs, *idx));
1183
0
  }
1184
0
  return result;
1185
0
}
1186
1187
HermesValue Debugger::getVariableInFrame(
1188
    uint32_t frame,
1189
    uint32_t scopeDepth,
1190
    uint32_t variableIndex,
1191
0
    std::string *outName) const {
1192
0
  GCScope gcScope{runtime_};
1193
0
  auto frameInfo = runtime_.stackFrameInfoByIndex(frame);
1194
0
  assert(frameInfo && "Invalid frame");
1195
1196
0
  const HermesValue undefined = HermesValue::encodeUndefinedValue();
1197
1198
  // Clear the outgoing info so we don't leave stale data there.
1199
0
  if (outName)
1200
0
    outName->clear();
1201
1202
0
  if (frameInfo->isGlobal) {
1203
    // Globals not yet supported.
1204
    // TODO: support them.
1205
0
    return undefined;
1206
0
  }
1207
0
  const CodeBlock *cb = frameInfo->frame->getCalleeCodeBlock(runtime_);
1208
0
  assert(cb && "Unexpectedly null code block");
1209
0
  llvh::Optional<ScopeRegAndDescriptorChain> envRegAndDescChain =
1210
0
      scopeDescChainForBlock(runtime_, cb, frame);
1211
0
  if (!envRegAndDescChain) {
1212
    // Binary was compiled without variable debug info.
1213
0
    return undefined;
1214
0
  }
1215
1216
0
  const llvh::SmallVector<hbc::DebugScopeDescriptor, 4> &scopeDescs =
1217
0
      envRegAndDescChain->scopeDescs;
1218
1219
  // Find the scope desc for the requested scope. This is the inner most scope
1220
  // in the given frame.
1221
0
  auto idx = getScopeDescIndexForFrame(scopeDescs, scopeDepth);
1222
0
  if (!idx) {
1223
    // Invalid scope frame.
1224
0
    return undefined;
1225
0
  }
1226
1227
  // Find the first (top most) scope in the scope chain.
1228
0
  const PinnedHermesValue &envPHV =
1229
0
      (&frameInfo->frame.getFirstLocalRef())[envRegAndDescChain->reg];
1230
0
  assert(envPHV.isObject() && dyn_vmcast<Environment>(envPHV));
1231
1232
  // Descend the environment chain to the desired depth, or stop at null. We may
1233
  // get a null environment if it has not been created.
1234
0
  MutableHandle<Environment> env(runtime_, vmcast<Environment>(envPHV));
1235
0
  unsigned varScopeIndex = *idx;
1236
0
  for (uint32_t i = varScopeIndex; env && i > 0; --i) {
1237
0
    env = env->getParentEnvironment(runtime_);
1238
0
  }
1239
1240
  // Now find variableIndex in the current frame. variableIndex could be
1241
  // indexing into an outer scope, thus we need to find the real target scope
1242
  // within the current frame.
1243
0
  bool newFrame = false;
1244
0
  while (env && env->getSize() <= variableIndex) {
1245
    // If newFrame was set to true on the previous iteration, then this
1246
    // iteration is now accessing variables in an Environment that doesn't
1247
    // belong to the requested frame.
1248
0
    assert(!newFrame && "accessing variables from another frame");
1249
0
    (void)newFrame;
1250
1251
    // Adjust the variableIndex to take into account the current environment.
1252
0
    variableIndex -= env->getSize();
1253
0
    env = env->getParentEnvironment(runtime_);
1254
1255
    // Sanity-check: ensuring that this loop doesn't cross the frame boundary,
1256
    // i.e., the current scopeDescs[varScopeIndex] os an inner scope.
1257
0
    newFrame = !scopeDescs[varScopeIndex++].flags.isInnerScope;
1258
0
  }
1259
1260
0
  if (!env) {
1261
0
    return undefined;
1262
0
  }
1263
0
  assert(varScopeIndex < scopeDescs.size() && "OOB scope desc access");
1264
1265
  // If the caller needs the variable name, populate it.
1266
0
  if (outName)
1267
0
    *outName = scopeDescs[varScopeIndex].names[variableIndex];
1268
1269
  // Now we can get the variable, or undefined if we have no environment.
1270
0
  return env->slot(variableIndex);
1271
0
}
1272
1273
0
HermesValue Debugger::getThisValue(uint32_t frame) const {
1274
0
  const auto frameInfo = runtime_.stackFrameInfoByIndex(frame);
1275
0
  assert(frameInfo && "Invalid frame");
1276
1277
0
  if (frameInfo->isGlobal) {
1278
    // "this" value in the global frame is the global object.
1279
0
    return runtime_.getGlobal().getHermesValue();
1280
0
  }
1281
1282
0
  return frameInfo->frame.getThisArgRef();
1283
0
}
1284
1285
HermesValue Debugger::getExceptionAsEvalResult(
1286
0
    EvalResultMetadata *outMetadata) {
1287
0
  outMetadata->isException = true;
1288
1289
0
  Handle<> thrownValue = runtime_.makeHandle(runtime_.getThrownValue());
1290
0
  assert(!thrownValue->isEmpty() && "Runtime did not throw");
1291
0
  runtime_.clearThrownValue();
1292
1293
  // Set the exceptionDetails.text to toString_RJS() of the thrown value.
1294
  // TODO: rationalize what should happen if toString_RJS() itself throws.
1295
0
  auto res = toString_RJS(runtime_, thrownValue);
1296
0
  if (res != ExecutionStatus::EXCEPTION) {
1297
0
    llvh::SmallVector<char16_t, 64> errorText;
1298
0
    res->get()->appendUTF16String(errorText);
1299
0
    convertUTF16ToUTF8WithReplacements(
1300
0
        outMetadata->exceptionDetails.text, errorText);
1301
0
  }
1302
1303
  // Try to fetch the stack trace. It may not exist; for example, if the
1304
  // exception was a parse error in eval(), then the exception will be set
1305
  // directly and the stack trace will not be collected.
1306
0
  if (auto errorHandle = Handle<JSError>::dyn_vmcast(thrownValue)) {
1307
0
    if (auto stackTracePtr = errorHandle->getStackTrace()) {
1308
      // Copy the stack trace to ensure it's not moved out from under us.
1309
0
      const auto stackTraceCopy = *stackTracePtr;
1310
0
      std::vector<CallFrameInfo> frames;
1311
0
      frames.reserve(stackTraceCopy.size());
1312
0
      for (const StackTraceInfo &sti : stackTraceCopy)
1313
0
        frames.push_back(getCallFrameInfo(sti.codeBlock, sti.bytecodeOffset));
1314
0
      outMetadata->exceptionDetails.stackTrace_ = StackTrace{std::move(frames)};
1315
0
    }
1316
0
  }
1317
0
  return *thrownValue;
1318
0
}
1319
1320
HermesValue Debugger::evalInFrame(
1321
    const EvalArgs &args,
1322
    const std::string &src,
1323
    const InterpreterState &state,
1324
0
    EvalResultMetadata *outMetadata) {
1325
0
  GCScope gcScope{runtime_};
1326
0
  *outMetadata = EvalResultMetadata{};
1327
0
  uint32_t frame = args.frameIdx;
1328
0
  auto frameInfo = runtime_.stackFrameInfoByIndex(frame);
1329
0
  if (!frameInfo) {
1330
0
    return HermesValue::encodeUndefinedValue();
1331
0
  }
1332
1333
0
  MutableHandle<> resultHandle(runtime_);
1334
0
  bool singleFunction = false;
1335
1336
0
  const CodeBlock *cb = frameInfo->frame->getCalleeCodeBlock(runtime_);
1337
0
  llvh::Optional<ScopeRegAndDescriptorChain> envRegAndScopeChain =
1338
0
      scopeDescChainForBlock(runtime_, cb, frame);
1339
1340
  // Interpreting code requires that the `thrownValue_` is empty.
1341
  // Save it temporarily so we can restore it after the evalInEnvironment.
1342
0
  Handle<> savedThrownValue = runtime_.makeHandle(runtime_.getThrownValue());
1343
0
  runtime_.clearThrownValue();
1344
1345
0
  CallResult<HermesValue> result{ExecutionStatus::EXCEPTION};
1346
1347
0
  if (!envRegAndScopeChain) {
1348
0
    result = runtime_.raiseError("Can't evalInFrame: Environment not found");
1349
0
  } else {
1350
    // Use the Environment for the current instruction.
1351
0
    const PinnedHermesValue &env =
1352
0
        (&frameInfo->frame.getFirstLocalRef())[envRegAndScopeChain->reg];
1353
0
    assert(env.isObject() && dyn_vmcast<Environment>(env));
1354
1355
    // Create the scope chain. The scope chain should represent each
1356
    // Scope/Environment's names (without any accessible name from other
1357
    // scopes).
1358
0
    ScopeChain chain;
1359
0
    for (const hbc::DebugScopeDescriptor &scopeDesc :
1360
0
         envRegAndScopeChain->scopeDescs) {
1361
0
      chain.scopes.emplace_back();
1362
0
      ScopeChainItem &scopeItem = chain.scopes.back();
1363
0
      for (const llvh::StringRef &name : scopeDesc.names) {
1364
0
        scopeItem.variables.push_back(name);
1365
0
      }
1366
0
    }
1367
1368
0
    result = evalInEnvironment(
1369
0
        runtime_,
1370
0
        src,
1371
0
        Handle<Environment>::vmcast(runtime_, env),
1372
0
        chain,
1373
0
        Handle<>(&frameInfo->frame->getThisArgRef()),
1374
0
        false,
1375
0
        singleFunction);
1376
0
  }
1377
1378
  // Check if an exception was thrown.
1379
0
  if (result.getStatus() == ExecutionStatus::EXCEPTION) {
1380
0
    resultHandle = getExceptionAsEvalResult(outMetadata);
1381
0
  } else {
1382
0
    assert(
1383
0
        !result->isEmpty() &&
1384
0
        "eval result should not be empty unless exception was thrown");
1385
0
    resultHandle = *result;
1386
0
  }
1387
1388
0
  runtime_.setThrownValue(savedThrownValue.getHermesValue());
1389
0
  return *resultHandle;
1390
0
}
1391
1392
llvh::Optional<std::pair<InterpreterState, uint32_t>> Debugger::findCatchTarget(
1393
0
    const InterpreterState &state) const {
1394
0
  auto *codeBlock = state.codeBlock;
1395
0
  auto offset = state.offset;
1396
0
  auto frames = runtime_.getStackFrames();
1397
0
  for (auto it = frames.begin(), e = frames.end(); it != e; ++it) {
1398
0
    if (codeBlock) {
1399
0
      auto handlerOffset = codeBlock->findCatchTargetOffset(offset);
1400
0
      if (handlerOffset != -1) {
1401
0
        return std::make_pair(
1402
0
            InterpreterState(codeBlock, handlerOffset),
1403
0
            runtime_.calcFrameOffset(it));
1404
0
      }
1405
0
    }
1406
0
    codeBlock = it->getSavedCodeBlock();
1407
0
    if (codeBlock) {
1408
0
      offset = codeBlock->getOffsetOf(it->getSavedIP());
1409
0
    }
1410
0
  }
1411
0
  return llvh::None;
1412
0
}
1413
1414
0
bool Debugger::resolveBreakpointLocation(Breakpoint &breakpoint) const {
1415
0
  using fhd::kInvalidLocation;
1416
0
  assert(!breakpoint.isResolved() && "breakpoint already resolved");
1417
1418
0
  OptValue<hbc::DebugSearchResult> locationOpt{};
1419
1420
0
#ifndef HERMESVM_LEAN
1421
  // If we could have lazy code blocks, compile them before we try to resolve.
1422
  // Eagerly compile code blocks that may contain the location.
1423
  // This is done using a search in which we enumerate all CodeBlocks in the
1424
  // runtime module, and we visit any code blocks which are lazy and check
1425
  // their ASTs to see if the breakpoint location is in them.
1426
  // Note that this works because we have the start and end locations
1427
  // exactly when a CodeBlock is lazy, because that's only when the AST exists.
1428
  // If it is, we compile the CodeBlock and start over,
1429
  // skipping any CodeBlocks we've seen before.
1430
0
  GCScope gcScope{runtime_};
1431
0
  for (auto &runtimeModule : runtime_.getRuntimeModules()) {
1432
0
    llvh::DenseSet<CodeBlock *> visited{};
1433
0
    std::vector<CodeBlock *> toVisit{};
1434
0
    for (uint32_t i = 0, e = runtimeModule.getNumCodeBlocks(); i < e; ++i) {
1435
0
      GCScopeMarkerRAII marker{gcScope};
1436
      // Use getCodeBlock to ensure they get initialized (but not compiled).
1437
0
      toVisit.push_back(runtimeModule.getCodeBlockMayAllocate(i));
1438
0
    }
1439
1440
0
    while (!toVisit.empty()) {
1441
0
      GCScopeMarkerRAII marker{gcScope};
1442
0
      CodeBlock *codeBlock = toVisit.back();
1443
0
      toVisit.pop_back();
1444
1445
0
      if (!codeBlock || !codeBlock->isLazy()) {
1446
        // When looking for a lazy code block to expand,
1447
        // there's no point looking at the non-lazy ones.
1448
0
        continue;
1449
0
      }
1450
1451
0
      if (visited.count(codeBlock) > 0) {
1452
        // We've already been here.
1453
0
        continue;
1454
0
      }
1455
1456
0
      visited.insert(codeBlock);
1457
0
      auto start = codeBlock->getLazyFunctionStartLoc();
1458
0
      auto end = codeBlock->getLazyFunctionEndLoc();
1459
1460
0
      const auto &request = breakpoint.requestedLocation;
1461
0
      if ((start.line < request.line && request.line < end.line) ||
1462
0
          ((start.line == request.line || request.line == end.line) &&
1463
0
           (start.col <= request.column && request.column <= end.col))) {
1464
        // The code block probably contains the breakpoint we want to set.
1465
        // First, we compile it.
1466
0
        if (LLVM_UNLIKELY(
1467
0
                codeBlock->lazyCompile(runtime_) ==
1468
0
                ExecutionStatus::EXCEPTION)) {
1469
          // TODO: how to better handle this?
1470
0
          runtime_.clearThrownValue();
1471
0
        }
1472
1473
        // We've found the codeBlock at this level and expanded it,
1474
        // so there's no point continuing the search.
1475
        // Abandon the current toVisit queue and repopulate it.
1476
0
        toVisit.clear();
1477
1478
        // Compiling the function will add more functions to the runtimeModule.
1479
        // Re-add them all so we can continue the search.
1480
0
        for (uint32_t i = 0, e = runtimeModule.getNumCodeBlocks(); i < e; ++i) {
1481
0
          GCScopeMarkerRAII marker2{gcScope};
1482
          // Use getCodeBlock to ensure they get initialized (but not compiled).
1483
0
          toVisit.push_back(runtimeModule.getCodeBlockMayAllocate(i));
1484
0
        }
1485
0
      }
1486
0
    }
1487
0
  }
1488
0
#endif
1489
1490
  // Iterate backwards through runtime modules, under the assumption that
1491
  // modules at the end of the list were added more recently, and are more
1492
  // likely to match the user's intention.
1493
  // Specifically, this will check any user source before runtime modules loaded
1494
  // by the VM.
1495
0
  for (auto it = runtime_.getRuntimeModules().rbegin();
1496
0
       it != runtime_.getRuntimeModules().rend();
1497
0
       ++it) {
1498
0
    auto &runtimeModule = *it;
1499
0
    GCScope gcScope{runtime_};
1500
1501
0
    if (!runtimeModule.isInitialized()) {
1502
      // Uninitialized module.
1503
0
      continue;
1504
0
    }
1505
0
    if (!runtimeModule.getBytecode()->getDebugInfo()) {
1506
      // No debug info in this module, keep going.
1507
0
      continue;
1508
0
    }
1509
1510
0
    const auto *debugInfo = runtimeModule.getBytecode()->getDebugInfo();
1511
0
    const auto &fileRegions = debugInfo->viewFiles();
1512
0
    if (fileRegions.empty()) {
1513
0
      continue;
1514
0
    }
1515
1516
0
    uint32_t resolvedFileId = kInvalidLocation;
1517
0
    std::string resolvedFileName{};
1518
1519
0
    if (!breakpoint.requestedLocation.fileName.empty()) {
1520
0
      for (const auto &region : fileRegions) {
1521
0
        std::string storage =
1522
0
            getFileNameAsUTF8(runtime_, &runtimeModule, region.filenameId);
1523
0
        llvh::StringRef storageRef{storage};
1524
0
        if (storageRef.consume_back(breakpoint.requestedLocation.fileName)) {
1525
0
          resolvedFileId = region.filenameId;
1526
0
          resolvedFileName = std::move(storage);
1527
0
          break;
1528
0
        }
1529
0
      }
1530
0
    } else if (breakpoint.requestedLocation.fileId != kInvalidLocation) {
1531
0
      for (const auto &region : fileRegions) {
1532
        // We don't yet have a convincing story for debugging CommonJS, so for
1533
        // now just assert that we're still living in the one-file-per-RM world.
1534
        // TODO(T84976604): Properly handle setting breakpoints when there are
1535
        // multiple JS files per HBC file.
1536
0
        assert(
1537
0
            region.filenameId == 0 && "Unexpected multiple filenames per RM");
1538
0
        if (resolveScriptId(&runtimeModule, region.filenameId) ==
1539
0
            breakpoint.requestedLocation.fileId) {
1540
0
          resolvedFileId = region.filenameId;
1541
0
          resolvedFileName =
1542
0
              getFileNameAsUTF8(runtime_, &runtimeModule, resolvedFileId);
1543
0
          break;
1544
0
        }
1545
0
      }
1546
0
    } else {
1547
      // No requested file, just pick the first one.
1548
0
      resolvedFileId = fileRegions.front().filenameId;
1549
0
      resolvedFileName =
1550
0
          getFileNameAsUTF8(runtime_, &runtimeModule, resolvedFileId);
1551
0
    }
1552
1553
0
    if (resolvedFileId == kInvalidLocation) {
1554
      // Unable to find the file here.
1555
0
      continue;
1556
0
    }
1557
1558
0
    locationOpt = debugInfo->getAddressForLocation(
1559
0
        resolvedFileId,
1560
0
        breakpoint.requestedLocation.line,
1561
0
        breakpoint.requestedLocation.column == kInvalidLocation
1562
0
            ? llvh::None
1563
0
            : OptValue<uint32_t>{breakpoint.requestedLocation.column});
1564
1565
0
    if (locationOpt.hasValue()) {
1566
0
      breakpoint.codeBlock =
1567
0
          runtimeModule.getCodeBlockMayAllocate(locationOpt->functionIndex);
1568
0
      breakpoint.offset = locationOpt->bytecodeOffset;
1569
1570
0
      SourceLocation resolvedLocation;
1571
0
      resolvedLocation.line = locationOpt->line;
1572
0
      resolvedLocation.column = locationOpt->column;
1573
0
      resolvedLocation.fileId = resolveScriptId(&runtimeModule, resolvedFileId);
1574
0
      resolvedLocation.fileName = std::move(resolvedFileName);
1575
0
      breakpoint.resolvedLocation = resolvedLocation;
1576
0
      return true;
1577
0
    }
1578
0
  }
1579
1580
0
  return false;
1581
0
}
1582
1583
0
void Debugger::unresolveBreakpointLocation(Breakpoint &breakpoint) {
1584
0
  assert(breakpoint.isResolved() && "Breakpoint already unresolved");
1585
0
  if (breakpoint.enabled) {
1586
0
    unsetUserBreakpoint(breakpoint);
1587
0
  }
1588
0
  breakpoint.resolvedLocation.reset();
1589
0
  breakpoint.codeBlock = nullptr;
1590
0
  breakpoint.offset = -1;
1591
0
}
1592
1593
0
auto Debugger::getSourceMappingUrl(ScriptID scriptId) const -> String {
1594
0
  for (auto &runtimeModule : runtime_.getRuntimeModules()) {
1595
0
    if (!runtimeModule.isInitialized()) {
1596
      // Uninitialized module.
1597
0
      continue;
1598
0
    }
1599
1600
0
    auto *debugInfo = runtimeModule.getBytecode()->getDebugInfo();
1601
0
    if (!debugInfo) {
1602
      // No debug info in this module, keep going.
1603
0
      continue;
1604
0
    }
1605
1606
0
    for (const auto &file : debugInfo->viewFiles()) {
1607
0
      if (resolveScriptId(&runtimeModule, file.filenameId) == scriptId) {
1608
0
        if (file.sourceMappingUrlId == fhd::kInvalidBreakpoint) {
1609
0
          return "";
1610
0
        }
1611
0
        return getFileNameAsUTF8(
1612
0
            runtime_, &runtimeModule, file.sourceMappingUrlId);
1613
0
      }
1614
0
    }
1615
0
  }
1616
1617
0
  return "";
1618
0
}
1619
1620
0
auto Debugger::getLoadedScripts() const -> std::vector<SourceLocation> {
1621
0
  std::vector<SourceLocation> loadedScripts;
1622
0
  for (auto &runtimeModule : runtime_.getRuntimeModules()) {
1623
0
    if (!runtimeModule.isInitialized()) {
1624
      // Uninitialized module.
1625
0
      continue;
1626
0
    }
1627
    // Only include a RuntimeModule if it's the root module
1628
0
    if (runtimeModule.getLazyRootModule() != &runtimeModule) {
1629
0
      continue;
1630
0
    }
1631
1632
0
    auto *debugInfo = runtimeModule.getBytecode()->getDebugInfo();
1633
0
    if (!debugInfo) {
1634
      // No debug info in this module, keep going.
1635
0
      continue;
1636
0
    }
1637
1638
    // Same as the temp breakpoint we set in Debugger::willExecuteModule() for
1639
    // pausing on script load.
1640
0
    auto globalFunctionIndex =
1641
0
        runtimeModule.getBytecode()->getGlobalFunctionIndex();
1642
0
    auto globalCodeBlock =
1643
0
        runtimeModule.getCodeBlockMayAllocate(globalFunctionIndex);
1644
0
    OptValue<hbc::DebugSourceLocation> debugSrcLoc =
1645
0
        globalCodeBlock->getSourceLocation();
1646
0
    if (!debugSrcLoc) {
1647
0
      continue;
1648
0
    }
1649
1650
0
    SourceLocation loc;
1651
0
    loc.fileId = resolveScriptId(&runtimeModule, debugSrcLoc->filenameId);
1652
0
    loc.line = debugSrcLoc->line;
1653
0
    loc.column = debugSrcLoc->column;
1654
0
    loc.fileName = debugInfo->getFilenameByID(debugSrcLoc->filenameId);
1655
1656
0
    loadedScripts.push_back(loc);
1657
0
  }
1658
0
  return loadedScripts;
1659
0
}
1660
1661
auto Debugger::resolveScriptId(
1662
    RuntimeModule *runtimeModule,
1663
0
    uint32_t filenameId) const -> ScriptID {
1664
0
  return runtimeModule->getScriptID();
1665
0
}
1666
1667
} // namespace vm
1668
} // namespace hermes
1669
1670
#endif
1671
1672
#endif // HERMES_ENABLE_DEBUGGER