v8-debugger.cc 44.1 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/inspector/v8-debugger.h"
6

7 8 9 10 11
#include "include/v8-container.h"
#include "include/v8-context.h"
#include "include/v8-function.h"
#include "include/v8-microtask-queue.h"
#include "include/v8-util.h"
12
#include "src/inspector/inspected-context.h"
13
#include "src/inspector/protocol/Protocol.h"
14 15 16
#include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger-agent-impl.h"
#include "src/inspector/v8-inspector-impl.h"
17 18
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-runtime-agent-impl.h"
19
#include "src/inspector/v8-stack-trace-impl.h"
20
#include "src/inspector/v8-value-utils.h"
21 22 23 24 25

namespace v8_inspector {

namespace {

26
static const size_t kMaxAsyncTaskStacks = 8 * 1024;
27
static const int kNoBreakpointId = 0;
28

29
template <typename Map>
30
void cleanupExpiredWeakPointers(Map& map) {
31 32 33 34 35 36 37 38 39
  for (auto it = map.begin(); it != map.end();) {
    if (it->second.expired()) {
      it = map.erase(it);
    } else {
      ++it;
    }
  }
}

40
class MatchPrototypePredicate : public v8::debug::QueryObjectPredicate {
41
 public:
42 43 44 45
  MatchPrototypePredicate(V8InspectorImpl* inspector,
                          v8::Local<v8::Context> context,
                          v8::Local<v8::Object> prototype)
      : m_inspector(inspector), m_context(context), m_prototype(prototype) {}
46 47

  bool Filter(v8::Local<v8::Object> object) override {
48
    if (object->IsModuleNamespaceObject()) return false;
49 50 51 52
    v8::Local<v8::Context> objectContext;
    if (!v8::debug::GetCreationContext(object).ToLocal(&objectContext)) {
      return false;
    }
53 54
    if (objectContext != m_context) return false;
    if (!m_inspector->client()->isInspectableHeapObject(object)) return false;
55
    // Get prototype chain for current object until first visited prototype.
56 57
    for (v8::Local<v8::Value> prototype = object->GetPrototype();
         prototype->IsObject();
58
         prototype = prototype.As<v8::Object>()->GetPrototype()) {
59
      if (m_prototype == prototype) return true;
60
    }
61
    return false;
62 63 64
  }

 private:
65
  V8InspectorImpl* m_inspector;
66
  v8::Local<v8::Context> m_context;
67
  v8::Local<v8::Value> m_prototype;
68
};
69

70 71 72 73 74 75 76
}  // namespace

V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
    : m_isolate(isolate),
      m_inspector(inspector),
      m_enableCount(0),
      m_ignoreScriptParsedEventsCounter(0),
77
      m_continueToLocationBreakpointId(kNoBreakpointId),
78
      m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
79
      m_maxAsyncCallStackDepth(0),
80 81
      m_maxCallStackSizeToCapture(
          V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture),
82
      m_pauseOnExceptionsState(v8::debug::NoBreakOnException) {}
83

84
V8Debugger::~V8Debugger() {
85 86 87
  m_isolate->RemoveCallCompletedCallback(
      &V8Debugger::terminateExecutionCompletedCallback);
  m_isolate->RemoveMicrotasksCompletedCallback(
88
      &V8Debugger::terminateExecutionCompletedCallbackIgnoringData);
89
}
90 91 92 93

void V8Debugger::enable() {
  if (m_enableCount++) return;
  v8::HandleScope scope(m_isolate);
94
  v8::debug::SetDebugDelegate(m_isolate, this);
95
  m_isolate->AddNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback, this);
96 97
  v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
  m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
98
#if V8_ENABLE_WEBASSEMBLY
99
  v8::debug::TierDownAllModulesPerIsolate(m_isolate);
100
#endif  // V8_ENABLE_WEBASSEMBLY
101 102 103
}

void V8Debugger::disable() {
104 105 106 107 108 109 110 111 112 113 114 115
  if (isPaused()) {
    bool scheduledOOMBreak = m_scheduledOOMBreak;
    bool hasAgentAcceptsPause = false;
    m_inspector->forEachSession(
        m_pausedContextGroupId, [&scheduledOOMBreak, &hasAgentAcceptsPause](
                                    V8InspectorSessionImpl* session) {
          if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
            hasAgentAcceptsPause = true;
          }
        });
    if (!hasAgentAcceptsPause) m_inspector->client()->quitMessageLoopOnPause();
  }
116
  if (--m_enableCount) return;
117
  clearContinueToLocation();
118
  m_taskWithScheduledBreak = nullptr;
119 120 121
  m_externalAsyncTaskPauseRequested = false;
  m_taskWithScheduledBreakPauseRequested = false;
  m_pauseOnNextCallRequested = false;
122
  m_pauseOnAsyncCall = false;
123
#if V8_ENABLE_WEBASSEMBLY
124
  v8::debug::TierUpAllModulesPerIsolate(m_isolate);
125
#endif  // V8_ENABLE_WEBASSEMBLY
126
  v8::debug::SetDebugDelegate(m_isolate, nullptr);
127 128 129
  m_isolate->RemoveNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback,
                                         m_originalHeapLimit);
  m_originalHeapLimit = 0;
130 131
}

132 133 134 135
bool V8Debugger::isPausedInContextGroup(int contextGroupId) const {
  return isPaused() && m_pausedContextGroupId == contextGroupId;
}

136
bool V8Debugger::enabled() const { return m_enableCount > 0; }
137

138 139 140
std::vector<std::unique_ptr<V8DebuggerScript>> V8Debugger::getCompiledScripts(
    int contextGroupId, V8DebuggerAgentImpl* agent) {
  std::vector<std::unique_ptr<V8DebuggerScript>> result;
141
  v8::HandleScope scope(m_isolate);
142 143
  v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate);
  v8::debug::GetLoadedScripts(m_isolate, scripts);
144
  for (size_t i = 0; i < scripts.Size(); ++i) {
145
    v8::Local<v8::debug::Script> script = scripts.Get(i);
146
    if (!script->WasCompiled()) continue;
147 148 149 150
    if (!script->IsEmbedded()) {
      int contextId;
      if (!script->ContextId().To(&contextId)) continue;
      if (m_inspector->contextGroupId(contextId) != contextGroupId) continue;
151
    }
152
    result.push_back(V8DebuggerScript::Create(m_isolate, script, false, agent,
153
                                              m_inspector->client()));
154
  }
155
  return result;
156 157
}

158
void V8Debugger::setBreakpointsActive(bool active) {
159
  if (!enabled()) {
160
    UNREACHABLE();
161
  }
162 163
  m_breakpointsActiveCount += active ? 1 : -1;
  v8::debug::SetBreakPointsActive(m_isolate, m_breakpointsActiveCount);
164 165
}

166
v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
167
  DCHECK(enabled());
168
  return m_pauseOnExceptionsState;
169 170 171
}

void V8Debugger::setPauseOnExceptionsState(
172
    v8::debug::ExceptionBreakState pauseOnExceptionsState) {
173
  DCHECK(enabled());
174
  if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
175
  v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
176
  m_pauseOnExceptionsState = pauseOnExceptionsState;
177 178
}

179
void V8Debugger::setPauseOnNextCall(bool pause, int targetContextGroupId) {
180
  if (isPaused()) return;
181 182 183 184 185
  DCHECK(targetContextGroupId);
  if (!pause && m_targetContextGroupId &&
      m_targetContextGroupId != targetContextGroupId) {
    return;
  }
186 187 188 189 190 191 192 193 194 195 196 197 198
  if (pause) {
    bool didHaveBreak = hasScheduledBreakOnNextFunctionCall();
    m_pauseOnNextCallRequested = true;
    if (!didHaveBreak) {
      m_targetContextGroupId = targetContextGroupId;
      v8::debug::SetBreakOnNextFunctionCall(m_isolate);
    }
  } else {
    m_pauseOnNextCallRequested = false;
    if (!hasScheduledBreakOnNextFunctionCall()) {
      v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
    }
  }
199 200 201
}

bool V8Debugger::canBreakProgram() {
202
  return v8::debug::CanBreakProgram(m_isolate);
203 204
}

205
void V8Debugger::breakProgram(int targetContextGroupId) {
206
  DCHECK(canBreakProgram());
207
  // Don't allow nested breaks.
208
  if (isPaused()) return;
209 210
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;
211
  v8::debug::BreakRightNow(m_isolate);
212 213
}

214 215 216 217 218 219
void V8Debugger::interruptAndBreak(int targetContextGroupId) {
  // Don't allow nested breaks.
  if (isPaused()) return;
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;
  m_isolate->RequestInterrupt(
220 221 222 223 224
      [](v8::Isolate* isolate, void*) {
        v8::debug::BreakRightNow(
            isolate,
            v8::debug::BreakReasons({v8::debug::BreakReason::kScheduled}));
      },
225 226 227
      nullptr);
}

228 229
void V8Debugger::continueProgram(int targetContextGroupId,
                                 bool terminateOnResume) {
230
  if (m_pausedContextGroupId != targetContextGroupId) return;
231 232 233 234 235 236
  if (isPaused()) {
    if (terminateOnResume) {
      v8::debug::SetTerminateOnResume(m_isolate);
    }
    m_inspector->client()->quitMessageLoopOnPause();
  }
237 238
}

239 240 241 242 243 244 245 246
void V8Debugger::breakProgramOnAssert(int targetContextGroupId) {
  if (!enabled()) return;
  if (m_pauseOnExceptionsState == v8::debug::NoBreakOnException) return;
  // Don't allow nested breaks.
  if (isPaused()) return;
  if (!canBreakProgram()) return;
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;
247 248
  v8::debug::BreakRightNow(
      m_isolate, v8::debug::BreakReasons({v8::debug::BreakReason::kAssert}));
249 250
}

251 252
void V8Debugger::stepIntoStatement(int targetContextGroupId,
                                   bool breakOnAsyncCall) {
253
  DCHECK(isPaused());
254 255
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;
256
  m_pauseOnAsyncCall = breakOnAsyncCall;
257
  v8::debug::PrepareStep(m_isolate, v8::debug::StepInto);
258
  continueProgram(targetContextGroupId);
259 260
}

261
void V8Debugger::stepOverStatement(int targetContextGroupId) {
262
  DCHECK(isPaused());
263 264
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;
265
  v8::debug::PrepareStep(m_isolate, v8::debug::StepOver);
266
  continueProgram(targetContextGroupId);
267 268
}

269
void V8Debugger::stepOutOfFunction(int targetContextGroupId) {
270
  DCHECK(isPaused());
271 272
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;
273
  v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
274
  continueProgram(targetContextGroupId);
275 276
}

277 278 279
void V8Debugger::terminateExecution(
    std::unique_ptr<TerminateExecutionCallback> callback) {
  if (m_terminateExecutionCallback) {
280
    if (callback) {
281 282
      callback->sendFailure(Response::ServerError(
          "There is current termination request in progress"));
283
    }
284 285 286 287 288 289
    return;
  }
  m_terminateExecutionCallback = std::move(callback);
  m_isolate->AddCallCompletedCallback(
      &V8Debugger::terminateExecutionCompletedCallback);
  m_isolate->AddMicrotasksCompletedCallback(
290
      &V8Debugger::terminateExecutionCompletedCallbackIgnoringData);
291 292 293
  m_isolate->TerminateExecution();
}

294 295 296
void V8Debugger::reportTermination() {
  if (!m_terminateExecutionCallback) return;
  m_isolate->RemoveCallCompletedCallback(
297
      &V8Debugger::terminateExecutionCompletedCallback);
298
  m_isolate->RemoveMicrotasksCompletedCallback(
299
      &V8Debugger::terminateExecutionCompletedCallbackIgnoringData);
300 301 302 303 304 305
  m_isolate->CancelTerminateExecution();
  m_terminateExecutionCallback->sendSuccess();
  m_terminateExecutionCallback.reset();
}

void V8Debugger::terminateExecutionCompletedCallback(v8::Isolate* isolate) {
306 307 308
  V8InspectorImpl* inspector =
      static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
  V8Debugger* debugger = inspector->debugger();
309
  debugger->reportTermination();
310 311
}

312 313 314 315 316
void V8Debugger::terminateExecutionCompletedCallbackIgnoringData(
    v8::Isolate* isolate, void*) {
  terminateExecutionCompletedCallback(isolate);
}

317
Response V8Debugger::continueToLocation(
318
    int targetContextGroupId, V8DebuggerScript* script,
319 320
    std::unique_ptr<protocol::Debugger::Location> location,
    const String16& targetCallFrames) {
321 322 323
  DCHECK(isPaused());
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;
324 325 326 327
  v8::debug::Location v8Location(location->getLineNumber(),
                                 location->getColumnNumber(0));
  if (script->setBreakpoint(String16(), &v8Location,
                            &m_continueToLocationBreakpointId)) {
328 329 330
    m_continueToLocationTargetCallFrames = targetCallFrames;
    if (m_continueToLocationTargetCallFrames !=
        protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
331 332
      m_continueToLocationStack = V8StackTraceImpl::capture(
          this, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
333 334
      DCHECK(m_continueToLocationStack);
    }
335 336
    continueProgram(targetContextGroupId);
    // TODO(kozyatinskiy): Return actual line and column number.
337
    return Response::Success();
338
  } else {
339
    return Response::ServerError("Cannot continue to specified location");
340 341 342
  }
}

343 344 345 346 347 348 349 350 351 352 353 354
bool V8Debugger::restartFrame(int targetContextGroupId, int callFrameOrdinal) {
  DCHECK(isPaused());
  DCHECK(targetContextGroupId);
  m_targetContextGroupId = targetContextGroupId;

  if (v8::debug::PrepareRestartFrame(m_isolate, callFrameOrdinal)) {
    continueProgram(targetContextGroupId);
    return true;
  }
  return false;
}

355 356 357 358
bool V8Debugger::shouldContinueToCurrentLocation() {
  if (m_continueToLocationTargetCallFrames ==
      protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
    return true;
359
  }
360 361
  std::unique_ptr<V8StackTraceImpl> currentStack = V8StackTraceImpl::capture(
      this, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
362 363 364 365 366 367 368 369 370
  if (m_continueToLocationTargetCallFrames ==
      protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
    return m_continueToLocationStack->isEqualIgnoringTopFrame(
        currentStack.get());
  }
  return true;
}

void V8Debugger::clearContinueToLocation() {
371 372 373
  if (m_continueToLocationBreakpointId == kNoBreakpointId) return;
  v8::debug::RemoveBreakpoint(m_isolate, m_continueToLocationBreakpointId);
  m_continueToLocationBreakpointId = kNoBreakpointId;
374 375
  m_continueToLocationTargetCallFrames = String16();
  m_continueToLocationStack.reset();
376 377
}

378 379 380
void V8Debugger::handleProgramBreak(
    v8::Local<v8::Context> pausedContext, v8::Local<v8::Value> exception,
    const std::vector<v8::debug::BreakpointId>& breakpointIds,
381 382
    v8::debug::BreakReasons breakReasons,
    v8::debug::ExceptionType exceptionType, bool isUncaught) {
383
  // Don't allow nested breaks.
384
  if (isPaused()) return;
385

386 387 388 389 390
  int contextGroupId = m_inspector->contextGroupId(pausedContext);
  if (m_targetContextGroupId && contextGroupId != m_targetContextGroupId) {
    v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
    return;
  }
391 392 393 394 395 396 397 398 399 400

  DCHECK(hasScheduledBreakOnNextFunctionCall() ==
         (m_taskWithScheduledBreakPauseRequested ||
          m_externalAsyncTaskPauseRequested || m_pauseOnNextCallRequested));
  if (m_taskWithScheduledBreakPauseRequested ||
      m_externalAsyncTaskPauseRequested)
    breakReasons.Add(v8::debug::BreakReason::kAsyncStep);
  if (m_pauseOnNextCallRequested)
    breakReasons.Add(v8::debug::BreakReason::kAgent);

401
  m_targetContextGroupId = 0;
402
  m_pauseOnNextCallRequested = false;
403 404
  m_pauseOnAsyncCall = false;
  m_taskWithScheduledBreak = nullptr;
405 406
  m_externalAsyncTaskPauseRequested = false;
  m_taskWithScheduledBreakPauseRequested = false;
407 408

  bool scheduledOOMBreak = m_scheduledOOMBreak;
409 410
  DCHECK(scheduledOOMBreak ==
         breakReasons.contains(v8::debug::BreakReason::kOOM));
411
  bool hasAgents = false;
412

413 414
  m_inspector->forEachSession(
      contextGroupId,
415 416 417
      [&scheduledOOMBreak, &hasAgents](V8InspectorSessionImpl* session) {
        if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak))
          hasAgents = true;
418 419
      });
  if (!hasAgents) return;
420

421 422 423 424
  if (breakpointIds.size() == 1 &&
      breakpointIds[0] == m_continueToLocationBreakpointId) {
    v8::Context::Scope contextScope(pausedContext);
    if (!shouldContinueToCurrentLocation()) return;
425
  }
426
  clearContinueToLocation();
427

428
  DCHECK(contextGroupId);
429
  m_pausedContextGroupId = contextGroupId;
430 431

  m_inspector->forEachSession(
432 433 434
      contextGroupId,
      [&pausedContext, &exception, &breakpointIds, &exceptionType, &isUncaught,
       &scheduledOOMBreak, &breakReasons](V8InspectorSessionImpl* session) {
435
        if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
436 437
          session->debuggerAgent()->didPause(
              InspectedContext::contextId(pausedContext), exception,
438
              breakpointIds, exceptionType, isUncaught, breakReasons);
439 440
        }
      });
441
  {
442
    v8::Context::Scope scope(pausedContext);
443
    m_inspector->client()->runMessageLoopOnPause(contextGroupId);
444
    m_pausedContextGroupId = 0;
445
  }
446 447
  m_inspector->forEachSession(contextGroupId,
                              [](V8InspectorSessionImpl* session) {
448 449
                                if (session->debuggerAgent()->enabled()) {
                                  session->debuggerAgent()->clearBreakDetails();
450
                                  session->debuggerAgent()->didContinue();
451
                                }
452 453
                              });

454 455
  if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
  m_scheduledOOMBreak = false;
456 457
}

458 459 460 461 462 463 464 465 466 467 468 469
namespace {

size_t HeapLimitForDebugging(size_t initial_heap_limit) {
  const size_t kDebugHeapSizeFactor = 4;
  size_t max_limit = std::numeric_limits<size_t>::max() / 4;
  return std::min(max_limit, initial_heap_limit * kDebugHeapSizeFactor);
}

}  // anonymous namespace

size_t V8Debugger::nearHeapLimitCallback(void* data, size_t current_heap_limit,
                                         size_t initial_heap_limit) {
470
  V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
471
  thisPtr->m_originalHeapLimit = current_heap_limit;
472
  thisPtr->m_scheduledOOMBreak = true;
473 474
  v8::Local<v8::Context> context =
      thisPtr->m_isolate->GetEnteredOrMicrotaskContext();
475
  thisPtr->m_targetContextGroupId =
476
      context.IsEmpty() ? 0 : thisPtr->m_inspector->contextGroupId(context);
477
  thisPtr->m_isolate->RequestInterrupt(
478 479 480 481 482 483 484 485
      [](v8::Isolate* isolate, void*) {
        // There's a redundancy  between setting `m_scheduledOOMBreak` and
        // passing the reason along in `BreakRightNow`. The
        // `m_scheduledOOMBreak` is used elsewhere, so we cannot remove it. And
        // for being explicit, we still pass the break reason along.
        v8::debug::BreakRightNow(
            isolate, v8::debug::BreakReasons({v8::debug::BreakReason::kOOM}));
      },
486
      nullptr);
487
  return HeapLimitForDebugging(initial_heap_limit);
488 489
}

490
void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
491
                                bool is_live_edited, bool has_compile_error) {
492 493
  if (m_ignoreScriptParsedEventsCounter != 0) return;

494 495
  int contextId;
  if (!script->ContextId().To(&contextId)) return;
496 497 498 499 500 501

  v8::Isolate* isolate = m_isolate;
  V8InspectorClient* client = m_inspector->client();

  m_inspector->forEachSession(
      m_inspector->contextGroupId(contextId),
502 503
      [isolate, &script, has_compile_error, is_live_edited,
       client](V8InspectorSessionImpl* session) {
504 505
        auto agent = session->debuggerAgent();
        if (!agent->enabled()) return;
506 507 508 509
        agent->didParseSource(
            V8DebuggerScript::Create(isolate, script, is_live_edited, agent,
                                     client),
            !has_compile_error);
510
      });
511 512
}

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
void V8Debugger::BreakOnInstrumentation(
    v8::Local<v8::Context> pausedContext,
    v8::debug::BreakpointId instrumentationId) {
  // Don't allow nested breaks.
  if (isPaused()) return;

  int contextGroupId = m_inspector->contextGroupId(pausedContext);
  bool hasAgents = false;
  m_inspector->forEachSession(
      contextGroupId, [&hasAgents](V8InspectorSessionImpl* session) {
        if (session->debuggerAgent()->acceptsPause(false /* isOOMBreak */))
          hasAgents = true;
      });
  if (!hasAgents) return;

  m_pausedContextGroupId = contextGroupId;
  m_inspector->forEachSession(
      contextGroupId, [instrumentationId](V8InspectorSessionImpl* session) {
        if (session->debuggerAgent()->acceptsPause(false /* isOOMBreak */)) {
          session->debuggerAgent()->didPauseOnInstrumentation(
              instrumentationId);
        }
      });
  {
    v8::Context::Scope scope(pausedContext);
    m_inspector->client()->runMessageLoopOnPause(contextGroupId);
    m_pausedContextGroupId = 0;
  }

  m_inspector->forEachSession(contextGroupId,
                              [](V8InspectorSessionImpl* session) {
                                if (session->debuggerAgent()->enabled())
                                  session->debuggerAgent()->didContinue();
                              });
}

549
void V8Debugger::BreakProgramRequested(
550
    v8::Local<v8::Context> pausedContext,
551
    const std::vector<v8::debug::BreakpointId>& break_points_hit,
552
    v8::debug::BreakReasons reasons) {
553
  handleProgramBreak(pausedContext, v8::Local<v8::Value>(), break_points_hit,
554
                     reasons);
555 556 557 558
}

void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
                                 v8::Local<v8::Value> exception,
559 560
                                 v8::Local<v8::Value> promise, bool isUncaught,
                                 v8::debug::ExceptionType exceptionType) {
561
  std::vector<v8::debug::BreakpointId> break_points_hit;
562 563 564 565
  handleProgramBreak(
      pausedContext, exception, break_points_hit,
      v8::debug::BreakReasons({v8::debug::BreakReason::kException}),
      exceptionType, isUncaught);
566 567
}

568 569 570
bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
                                      const v8::debug::Location& start,
                                      const v8::debug::Location& end) {
571 572
  int contextId;
  if (!script->ContextId().To(&contextId)) return false;
573 574 575 576 577 578 579 580 581 582 583 584 585
  bool hasAgents = false;
  bool allBlackboxed = true;
  String16 scriptId = String16::fromInteger(script->Id());
  m_inspector->forEachSession(
      m_inspector->contextGroupId(contextId),
      [&hasAgents, &allBlackboxed, &scriptId, &start,
       &end](V8InspectorSessionImpl* session) {
        V8DebuggerAgentImpl* agent = session->debuggerAgent();
        if (!agent->enabled()) return;
        hasAgents = true;
        allBlackboxed &= agent->isFunctionBlackboxed(scriptId, start, end);
      });
  return hasAgents && allBlackboxed;
586 587
}

588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
bool V8Debugger::ShouldBeSkipped(v8::Local<v8::debug::Script> script, int line,
                                 int column) {
  int contextId;
  if (!script->ContextId().To(&contextId)) return false;

  bool hasAgents = false;
  bool allShouldBeSkipped = true;
  String16 scriptId = String16::fromInteger(script->Id());
  m_inspector->forEachSession(
      m_inspector->contextGroupId(contextId),
      [&hasAgents, &allShouldBeSkipped, &scriptId, line,
       column](V8InspectorSessionImpl* session) {
        V8DebuggerAgentImpl* agent = session->debuggerAgent();
        if (!agent->enabled()) return;
        hasAgents = true;
        const bool skip = agent->shouldBeSkipped(scriptId, line, column);
        allShouldBeSkipped &= skip;
      });
  return hasAgents && allShouldBeSkipped;
}

609 610
void V8Debugger::AsyncEventOccurred(v8::debug::DebugAsyncActionType type,
                                    int id, bool isBlackboxed) {
611
  // Async task events from Promises are given misaligned pointers to prevent
612 613
  // from overlapping with other Blink task identifiers.
  void* task = reinterpret_cast<void*>(id * 2 + 1);
614
  switch (type) {
615
    case v8::debug::kDebugPromiseThen:
616
      asyncTaskScheduledForStack(toStringView("Promise.then"), task, false);
617
      if (!isBlackboxed) asyncTaskCandidateForStepping(task);
618
      break;
619
    case v8::debug::kDebugPromiseCatch:
620
      asyncTaskScheduledForStack(toStringView("Promise.catch"), task, false);
621
      if (!isBlackboxed) asyncTaskCandidateForStepping(task);
622
      break;
623
    case v8::debug::kDebugPromiseFinally:
624
      asyncTaskScheduledForStack(toStringView("Promise.finally"), task, false);
625
      if (!isBlackboxed) asyncTaskCandidateForStepping(task);
626
      break;
627
    case v8::debug::kDebugWillHandle:
628 629
      asyncTaskStartedForStack(task);
      asyncTaskStartedForStepping(task);
630 631
      break;
    case v8::debug::kDebugDidHandle:
632 633
      asyncTaskFinishedForStack(task);
      asyncTaskFinishedForStepping(task);
634
      break;
635 636
    case v8::debug::kDebugAwait: {
      asyncTaskScheduledForStack(toStringView("await"), task, false, true);
637
      break;
638
    }
639
  }
640 641
}

642 643
std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
  return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
644 645
}

646 647 648 649 650
V8StackTraceId V8Debugger::currentExternalParent() {
  return m_currentExternalParent.empty() ? V8StackTraceId()
                                         : m_currentExternalParent.back();
}

651 652 653
v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
    v8::Local<v8::Context> context, v8::Local<v8::Value> value,
    ScopeTargetKind kind) {
654
  v8::Local<v8::Value> scopesValue;
655
  std::unique_ptr<v8::debug::ScopeIterator> iterator;
656 657
  switch (kind) {
    case FUNCTION:
658
      iterator = v8::debug::ScopeIterator::CreateForFunction(
659
          m_isolate, value.As<v8::Function>());
660 661
      break;
    case GENERATOR:
662 663 664 665 666
      v8::Local<v8::debug::GeneratorObject> generatorObject =
          v8::debug::GeneratorObject::Cast(value);
      if (!generatorObject->IsSuspended()) return v8::MaybeLocal<v8::Value>();

      iterator = v8::debug::ScopeIterator::CreateForGeneratorObject(
667
          m_isolate, value.As<v8::Object>());
668 669
      break;
  }
670
  if (!iterator) return v8::MaybeLocal<v8::Value>();
671 672
  v8::Local<v8::Array> result = v8::Array::New(m_isolate);
  if (!result->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) {
673
    return v8::MaybeLocal<v8::Value>();
674 675 676 677
  }

  for (; !iterator->Done(); iterator->Advance()) {
    v8::Local<v8::Object> scope = v8::Object::New(m_isolate);
678
    if (!addInternalObject(context, scope, V8InternalValueType::kScope))
679
      return v8::MaybeLocal<v8::Value>();
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
    String16 nameSuffix = toProtocolStringWithTypeCheck(
        m_isolate, iterator->GetFunctionDebugName());
    String16 description;
    if (nameSuffix.length()) nameSuffix = " (" + nameSuffix + ")";
    switch (iterator->GetType()) {
      case v8::debug::ScopeIterator::ScopeTypeGlobal:
        description = "Global" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeLocal:
        description = "Local" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeWith:
        description = "With Block" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeClosure:
        description = "Closure" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeCatch:
        description = "Catch" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeBlock:
        description = "Block" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeScript:
        description = "Script" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeEval:
        description = "Eval" + nameSuffix;
        break;
      case v8::debug::ScopeIterator::ScopeTypeModule:
        description = "Module" + nameSuffix;
        break;
712 713 714
      case v8::debug::ScopeIterator::ScopeTypeWasmExpressionStack:
        description = "Wasm Expression Stack" + nameSuffix;
        break;
715 716 717
    }
    v8::Local<v8::Object> object = iterator->GetObject();
    createDataProperty(context, scope,
718 719
                       toV8StringInternalized(m_isolate, "description"),
                       toV8String(m_isolate, description));
720 721 722 723
    createDataProperty(context, scope,
                       toV8StringInternalized(m_isolate, "object"), object);
    createDataProperty(context, result, result->Length(), scope);
  }
724
  if (!addInternalObject(context, result, V8InternalValueType::kScopeList))
725
    return v8::MaybeLocal<v8::Value>();
726
  return result;
727 728
}

729 730 731 732 733 734 735 736 737 738
v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
    v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
  return getTargetScopes(context, function, FUNCTION);
}

v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
    v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
  return getTargetScopes(context, generator, GENERATOR);
}

739
v8::MaybeLocal<v8::Array> V8Debugger::collectionsEntries(
740
    v8::Local<v8::Context> context, v8::Local<v8::Value> collection) {
741 742 743
  v8::Isolate* isolate = context->GetIsolate();
  v8::Local<v8::Array> entries;
  bool isKeyValue = false;
744 745 746
  if (!collection->IsObject() || !collection.As<v8::Object>()
                                      ->PreviewEntries(&isKeyValue)
                                      .ToLocal(&entries)) {
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
    return v8::MaybeLocal<v8::Array>();
  }

  v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
  CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
  if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
           .FromMaybe(false))
    return v8::MaybeLocal<v8::Array>();
  for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
    v8::Local<v8::Value> item;
    if (!entries->Get(context, i).ToLocal(&item)) continue;
    v8::Local<v8::Value> value;
    if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
    v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
    if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
      continue;
    createDataProperty(
        context, wrapper,
        toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
    if (isKeyValue) {
      createDataProperty(context, wrapper,
                         toV8StringInternalized(isolate, "value"), value);
    }
770
    if (!addInternalObject(context, wrapper, V8InternalValueType::kEntry))
771 772 773 774 775 776 777
      continue;
    createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
                       wrapper);
  }
  return wrappedEntries;
}

778 779 780
v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
    v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
  v8::Local<v8::Array> properties;
781
  if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
782
    return v8::MaybeLocal<v8::Array>();
783 784 785 786 787
  v8::Local<v8::Array> entries;
  if (collectionsEntries(context, value).ToLocal(&entries)) {
    createDataProperty(context, properties, properties->Length(),
                       toV8StringInternalized(m_isolate, "[[Entries]]"));
    createDataProperty(context, properties, properties->Length(), entries);
788 789
  }
  if (value->IsGeneratorObject()) {
790 791 792 793 794 795
    v8::Local<v8::Value> scopes;
    if (generatorScopes(context, value).ToLocal(&scopes)) {
      createDataProperty(context, properties, properties->Length(),
                         toV8StringInternalized(m_isolate, "[[Scopes]]"));
      createDataProperty(context, properties, properties->Length(), scopes);
    }
796 797 798 799
  }
  if (value->IsFunction()) {
    v8::Local<v8::Function> function = value.As<v8::Function>();
    v8::Local<v8::Value> scopes;
800
    if (functionScopes(context, function).ToLocal(&scopes)) {
801 802 803 804 805 806 807 808
      createDataProperty(context, properties, properties->Length(),
                         toV8StringInternalized(m_isolate, "[[Scopes]]"));
      createDataProperty(context, properties, properties->Length(), scopes);
    }
  }
  return properties;
}

809 810
v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context,
                                              v8::Local<v8::Object> prototype) {
811 812
  v8::Isolate* isolate = context->GetIsolate();
  v8::PersistentValueVector<v8::Object> v8Objects(isolate);
813
  MatchPrototypePredicate predicate(m_inspector, context, prototype);
814 815 816 817 818 819 820 821 822 823 824 825 826
  v8::debug::QueryObjects(context, &predicate, &v8Objects);

  v8::MicrotasksScope microtasksScope(isolate,
                                      v8::MicrotasksScope::kDoNotRunMicrotasks);
  v8::Local<v8::Array> resultArray = v8::Array::New(
      m_inspector->isolate(), static_cast<int>(v8Objects.Size()));
  for (size_t i = 0; i < v8Objects.Size(); ++i) {
    createDataProperty(context, resultArray, static_cast<int>(i),
                       v8Objects.Get(i));
  }
  return resultArray;
}

827
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
828
    v8::Local<v8::StackTrace> v8StackTrace) {
829 830
  return V8StackTraceImpl::create(
      this, v8StackTrace, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
}

void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
  if (depth <= 0)
    m_maxAsyncCallStackDepthMap.erase(agent);
  else
    m_maxAsyncCallStackDepthMap[agent] = depth;

  int maxAsyncCallStackDepth = 0;
  for (const auto& pair : m_maxAsyncCallStackDepthMap) {
    if (pair.second > maxAsyncCallStackDepth)
      maxAsyncCallStackDepth = pair.second;
  }

  if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
846
  // TODO(dgozman): ideally, this should be per context group.
847
  m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
848 849
  m_inspector->client()->maxAsyncCallStackDepthChanged(
      m_maxAsyncCallStackDepth);
850
  if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
851 852
  v8::debug::SetAsyncEventDelegate(m_isolate,
                                   maxAsyncCallStackDepth ? this : nullptr);
853 854
}

855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
void V8Debugger::setMaxCallStackSizeToCapture(V8RuntimeAgentImpl* agent,
                                              int size) {
  if (size < 0) {
    m_maxCallStackSizeToCaptureMap.erase(agent);
  } else {
    m_maxCallStackSizeToCaptureMap[agent] = size;
  }

  // The following logic is a bit complicated to decipher because we
  // want to retain backwards compatible semantics:
  //
  // (a) When no `Runtime` domain is enabled, we stick to the default
  //     maximum call stack size, but don't let V8 collect stack traces
  //     for uncaught exceptions.
  // (b) When `Runtime` is enabled for at least one front-end, we compute
  //     the maximum of the requested maximum call stack sizes of all the
  //     front-ends whose `Runtime` domains are enabled (which might be 0),
  //     and ask V8 to collect stack traces for uncaught exceptions.
  //
  // The latter allows performance test automation infrastructure to drive
  // browser via `Runtime` domain while still minimizing the performance
  // overhead of having the inspector attached - see the relevant design
  // document https://bit.ly/v8-cheaper-inspector-stack-traces for more
  if (m_maxCallStackSizeToCaptureMap.empty()) {
    m_maxCallStackSizeToCapture =
        V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture;
    m_isolate->SetCaptureStackTraceForUncaughtExceptions(false);
  } else {
    m_maxCallStackSizeToCapture = 0;
    for (auto const& pair : m_maxCallStackSizeToCaptureMap) {
      if (m_maxCallStackSizeToCapture < pair.second)
        m_maxCallStackSizeToCapture = pair.second;
    }
    m_isolate->SetCaptureStackTraceForUncaughtExceptions(
        m_maxCallStackSizeToCapture > 0, m_maxCallStackSizeToCapture);
  }
}

893 894
std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
    int contextGroupId, const V8StackTraceId& id) {
895
  if (debuggerIdFor(contextGroupId).pair() != id.debugger_id) return nullptr;
896 897 898 899 900 901 902 903 904 905 906 907 908 909
  auto it = m_storedStackTraces.find(id.id);
  if (it == m_storedStackTraces.end()) return nullptr;
  return it->second.lock();
}

V8StackTraceId V8Debugger::storeCurrentStackTrace(
    const StringView& description) {
  if (!m_maxAsyncCallStackDepth) return V8StackTraceId();

  v8::HandleScope scope(m_isolate);
  int contextGroupId = currentContextGroupId();
  if (!contextGroupId) return V8StackTraceId();

  std::shared_ptr<AsyncStackTrace> asyncStack =
910
      AsyncStackTrace::capture(this, toString16(description));
911 912
  if (!asyncStack) return V8StackTraceId();

913 914
  uintptr_t id = AsyncStackTrace::store(this, asyncStack);

915 916 917
  m_allAsyncStacks.push_back(std::move(asyncStack));
  collectOldAsyncStacksIfNeeded();

918 919 920 921 922 923
  bool shouldPause =
      m_pauseOnAsyncCall && contextGroupId == m_targetContextGroupId;
  if (shouldPause) {
    m_pauseOnAsyncCall = false;
    v8::debug::ClearStepping(m_isolate);  // Cancel step into.
  }
924
  return V8StackTraceId(id, debuggerIdFor(contextGroupId).pair(), shouldPause);
925 926
}

927 928 929 930 931 932 933
uintptr_t V8Debugger::storeStackTrace(
    std::shared_ptr<AsyncStackTrace> asyncStack) {
  uintptr_t id = ++m_lastStackTraceId;
  m_storedStackTraces[id] = asyncStack;
  return id;
}

934 935 936 937 938
void V8Debugger::externalAsyncTaskStarted(const V8StackTraceId& parent) {
  if (!m_maxAsyncCallStackDepth || parent.IsInvalid()) return;
  m_currentExternalParent.push_back(parent);
  m_currentAsyncParent.emplace_back();
  m_currentTasks.push_back(reinterpret_cast<void*>(parent.id));
939

940 941 942 943 944 945
  if (!parent.should_pause) return;
  bool didHaveBreak = hasScheduledBreakOnNextFunctionCall();
  m_externalAsyncTaskPauseRequested = true;
  if (didHaveBreak) return;
  m_targetContextGroupId = currentContextGroupId();
  v8::debug::SetBreakOnNextFunctionCall(m_isolate);
946 947 948 949 950 951 952 953
}

void V8Debugger::externalAsyncTaskFinished(const V8StackTraceId& parent) {
  if (!m_maxAsyncCallStackDepth || m_currentExternalParent.empty()) return;
  m_currentExternalParent.pop_back();
  m_currentAsyncParent.pop_back();
  DCHECK(m_currentTasks.back() == reinterpret_cast<void*>(parent.id));
  m_currentTasks.pop_back();
954

955 956 957
  if (!parent.should_pause) return;
  m_externalAsyncTaskPauseRequested = false;
  if (hasScheduledBreakOnNextFunctionCall()) return;
958
  v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
959 960
}

961 962
void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
                                    bool recurring) {
963
  asyncTaskScheduledForStack(taskName, task, recurring);
964
  asyncTaskCandidateForStepping(task);
965 966
}

967 968 969 970 971 972 973 974 975 976 977 978
void V8Debugger::asyncTaskCanceled(void* task) {
  asyncTaskCanceledForStack(task);
  asyncTaskCanceledForStepping(task);
}

void V8Debugger::asyncTaskStarted(void* task) {
  asyncTaskStartedForStack(task);
  asyncTaskStartedForStepping(task);
}

void V8Debugger::asyncTaskFinished(void* task) {
  asyncTaskFinishedForStepping(task);
979
  asyncTaskFinishedForStack(task);
980 981
}

982
void V8Debugger::asyncTaskScheduledForStack(const StringView& taskName,
983 984
                                            void* task, bool recurring,
                                            bool skipTopFrame) {
985 986
  if (!m_maxAsyncCallStackDepth) return;
  v8::HandleScope scope(m_isolate);
987 988
  std::shared_ptr<AsyncStackTrace> asyncStack =
      AsyncStackTrace::capture(this, toString16(taskName), skipTopFrame);
989 990
  if (asyncStack) {
    m_asyncTaskStacks[task] = asyncStack;
991
    if (recurring) m_recurringTasks.insert(task);
992
    m_allAsyncStacks.push_back(std::move(asyncStack));
993
    collectOldAsyncStacksIfNeeded();
994 995 996
  }
}

997
void V8Debugger::asyncTaskCanceledForStack(void* task) {
998 999 1000 1001 1002
  if (!m_maxAsyncCallStackDepth) return;
  m_asyncTaskStacks.erase(task);
  m_recurringTasks.erase(task);
}

1003
void V8Debugger::asyncTaskStartedForStack(void* task) {
1004 1005 1006 1007 1008 1009 1010 1011
  if (!m_maxAsyncCallStackDepth) return;
  // Needs to support following order of events:
  // - asyncTaskScheduled
  //   <-- attached here -->
  // - asyncTaskStarted
  // - asyncTaskCanceled <-- canceled before finished
  //   <-- async stack requested here -->
  // - asyncTaskFinished
1012
  m_currentTasks.push_back(task);
1013
  AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
1014 1015 1016
  if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
    std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
    m_currentAsyncParent.push_back(stack);
1017 1018 1019
  } else {
    m_currentAsyncParent.emplace_back();
  }
1020
  m_currentExternalParent.emplace_back();
1021 1022
}

1023
void V8Debugger::asyncTaskFinishedForStack(void* task) {
1024 1025
  if (!m_maxAsyncCallStackDepth) return;
  // We could start instrumenting half way and the stack is empty.
1026
  if (!m_currentTasks.size()) return;
1027 1028 1029
  DCHECK(m_currentTasks.back() == task);
  m_currentTasks.pop_back();

1030
  m_currentAsyncParent.pop_back();
1031
  m_currentExternalParent.pop_back();
1032

1033
  if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
1034
    asyncTaskCanceledForStack(task);
1035
  }
1036 1037
}

1038
void V8Debugger::asyncTaskCandidateForStepping(void* task) {
1039
  if (!m_pauseOnAsyncCall) return;
1040 1041
  int contextGroupId = currentContextGroupId();
  if (contextGroupId != m_targetContextGroupId) return;
1042 1043 1044
  m_taskWithScheduledBreak = task;
  m_pauseOnAsyncCall = false;
  v8::debug::ClearStepping(m_isolate);  // Cancel step into.
1045 1046 1047
}

void V8Debugger::asyncTaskStartedForStepping(void* task) {
1048 1049
  // TODO(kozyatinskiy): we should search task in async chain to support
  // blackboxing.
1050 1051 1052 1053 1054 1055
  if (task != m_taskWithScheduledBreak) return;
  bool didHaveBreak = hasScheduledBreakOnNextFunctionCall();
  m_taskWithScheduledBreakPauseRequested = true;
  if (didHaveBreak) return;
  m_targetContextGroupId = currentContextGroupId();
  v8::debug::SetBreakOnNextFunctionCall(m_isolate);
1056 1057 1058
}

void V8Debugger::asyncTaskFinishedForStepping(void* task) {
1059
  if (task != m_taskWithScheduledBreak) return;
1060
  m_taskWithScheduledBreak = nullptr;
1061 1062
  m_taskWithScheduledBreakPauseRequested = false;
  if (hasScheduledBreakOnNextFunctionCall()) return;
1063
  v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
1064 1065 1066
}

void V8Debugger::asyncTaskCanceledForStepping(void* task) {
1067
  asyncTaskFinishedForStepping(task);
1068 1069
}

1070 1071 1072
void V8Debugger::allAsyncTasksCanceled() {
  m_asyncTaskStacks.clear();
  m_recurringTasks.clear();
1073
  m_currentAsyncParent.clear();
1074
  m_currentExternalParent.clear();
1075
  m_currentTasks.clear();
1076 1077

  m_allAsyncStacks.clear();
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
}

void V8Debugger::muteScriptParsedEvents() {
  ++m_ignoreScriptParsedEventsCounter;
}

void V8Debugger::unmuteScriptParsedEvents() {
  --m_ignoreScriptParsedEventsCounter;
  DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
}

std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
    bool fullStack) {
1091
  int contextGroupId = currentContextGroupId();
1092 1093
  if (!contextGroupId) return nullptr;

1094
  int stackSize = 1;
1095
  if (fullStack) {
1096
    stackSize = V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture;
1097 1098
  } else {
    m_inspector->forEachSession(
1099
        contextGroupId, [this, &stackSize](V8InspectorSessionImpl* session) {
1100
          if (session->runtimeAgent()->enabled())
1101
            stackSize = maxCallStackSizeToCapture();
1102
        });
1103
  }
1104
  return V8StackTraceImpl::capture(this, stackSize);
1105 1106
}

1107 1108
int V8Debugger::currentContextGroupId() {
  if (!m_isolate->InContext()) return 0;
1109
  v8::HandleScope handleScope(m_isolate);
1110 1111 1112
  return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
}

1113
void V8Debugger::collectOldAsyncStacksIfNeeded() {
1114 1115
  if (m_allAsyncStacks.size() <= m_maxAsyncCallStacks) return;
  size_t halfOfLimitRoundedUp =
1116
      m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
1117
  while (m_allAsyncStacks.size() > halfOfLimitRoundedUp) {
1118 1119
    m_allAsyncStacks.pop_front();
  }
1120
  cleanupExpiredWeakPointers(m_asyncTaskStacks);
1121
  cleanupExpiredWeakPointers(m_cachedStackFrames);
1122
  cleanupExpiredWeakPointers(m_storedStackTraces);
1123 1124 1125 1126 1127 1128
  for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) {
    if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) {
      it = m_recurringTasks.erase(it);
    } else {
      ++it;
    }
1129
  }
1130 1131
}

1132 1133
std::shared_ptr<StackFrame> V8Debugger::symbolize(
    v8::Local<v8::StackFrame> v8Frame) {
1134
  int scriptId = v8Frame->GetScriptId();
1135 1136 1137
  auto location = v8Frame->GetLocation();
  int lineNumber = location.GetLineNumber();
  int columnNumber = location.GetColumnNumber();
1138
  CachedStackFrameKey key{scriptId, lineNumber, columnNumber};
1139
  auto functionName = toProtocolString(isolate(), v8Frame->GetFunctionName());
1140 1141 1142
  auto it = m_cachedStackFrames.find(key);
  if (it != m_cachedStackFrames.end() && !it->second.expired()) {
    auto stackFrame = it->second.lock();
1143 1144 1145 1146 1147 1148
    if (stackFrame->functionName() == functionName) {
      DCHECK_EQ(
          stackFrame->sourceURL(),
          toProtocolString(isolate(), v8Frame->GetScriptNameOrSourceURL()));
      return stackFrame;
    }
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
  }
  auto sourceURL =
      toProtocolString(isolate(), v8Frame->GetScriptNameOrSourceURL());
  auto hasSourceURLComment =
      v8Frame->GetScriptName() != v8Frame->GetScriptNameOrSourceURL();
  auto stackFrame = std::make_shared<StackFrame>(
      std::move(functionName), scriptId, std::move(sourceURL), lineNumber,
      columnNumber, hasSourceURLComment);
  m_cachedStackFrames.emplace(key, stackFrame);
  return stackFrame;
1159 1160
}

1161 1162 1163 1164 1165 1166
void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
  m_maxAsyncCallStacks = 0;
  collectOldAsyncStacksIfNeeded();
  m_maxAsyncCallStacks = limit;
}

1167
internal::V8DebuggerId V8Debugger::debuggerIdFor(int contextGroupId) {
1168 1169
  auto it = m_contextGroupIdToDebuggerId.find(contextGroupId);
  if (it != m_contextGroupIdToDebuggerId.end()) return it->second;
1170 1171
  internal::V8DebuggerId debuggerId =
      internal::V8DebuggerId::generate(m_inspector);
1172 1173 1174 1175 1176
  m_contextGroupIdToDebuggerId.insert(
      it, std::make_pair(contextGroupId, debuggerId));
  return debuggerId;
}

1177 1178 1179
bool V8Debugger::addInternalObject(v8::Local<v8::Context> context,
                                   v8::Local<v8::Object> object,
                                   V8InternalValueType type) {
1180 1181 1182 1183
  int contextId = InspectedContext::contextId(context);
  InspectedContext* inspectedContext = m_inspector->getContext(contextId);
  return inspectedContext ? inspectedContext->addInternalObject(object, type)
                          : false;
1184 1185
}

1186
void V8Debugger::dumpAsyncTaskStacksStateForTest() {
1187
  fprintf(stdout, "Async stacks count: %zu\n", m_allAsyncStacks.size());
1188 1189 1190 1191 1192
  fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
  fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size());
  fprintf(stdout, "\n");
}

1193 1194 1195 1196 1197
bool V8Debugger::hasScheduledBreakOnNextFunctionCall() const {
  return m_pauseOnNextCallRequested || m_taskWithScheduledBreakPauseRequested ||
         m_externalAsyncTaskPauseRequested;
}

1198
}  // namespace v8_inspector