v8-debugger-agent-impl.cc 72.5 KB
Newer Older
1 2 3 4
// Copyright 2015 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 6
#include "src/inspector/v8-debugger-agent-impl.h"

7 8
#include <algorithm>

9
#include "../../third_party/inspector_protocol/crdtp/json.h"
10
#include "include/v8-inspector.h"
11
#include "src/base/safe_conversions.h"
12
#include "src/debug/debug-interface.h"
13 14
#include "src/inspector/injected-script.h"
#include "src/inspector/inspected-context.h"
15
#include "src/inspector/protocol/Debugger.h"
16
#include "src/inspector/protocol/Protocol.h"
17 18 19 20 21 22 23 24 25 26
#include "src/inspector/remote-object-id.h"
#include "src/inspector/search-util.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger-script.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-regex.h"
#include "src/inspector/v8-runtime-agent-impl.h"
#include "src/inspector/v8-stack-trace-impl.h"
27
#include "src/inspector/v8-value-utils.h"
28

29 30 31 32 33 34
namespace v8_inspector {

using protocol::Array;
using protocol::Maybe;
using protocol::Debugger::BreakpointId;
using protocol::Debugger::CallFrame;
35
using protocol::Debugger::Scope;
36 37
using protocol::Runtime::ExceptionDetails;
using protocol::Runtime::RemoteObject;
38 39 40 41
using protocol::Runtime::ScriptId;

namespace InstrumentationEnum =
    protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum;
42 43 44 45 46 47

namespace DebuggerAgentState {
static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
static const char asyncCallStackDepth[] = "asyncCallStackDepth";
static const char blackboxPattern[] = "blackboxPattern";
static const char debuggerEnabled[] = "debuggerEnabled";
48
static const char skipAllPauses[] = "skipAllPauses";
49

50 51
static const char breakpointsByRegex[] = "breakpointsByRegex";
static const char breakpointsByUrl[] = "breakpointsByUrl";
52
static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
53
static const char breakpointHints[] = "breakpointHints";
54
static const char instrumentationBreakpoints[] = "instrumentationBreakpoints";
55

56
}  // namespace DebuggerAgentState
57

58 59 60 61
static const char kBacktraceObjectGroup[] = "backtrace";
static const char kDebuggerNotEnabled[] = "Debugger agent is not enabled";
static const char kDebuggerNotPaused[] =
    "Can only perform operation while paused.";
62

63 64 65
static const size_t kBreakpointHintMaxLength = 128;
static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;

66 67
namespace {

68 69 70
enum class BreakpointType {
  kByUrl = 1,
  kByUrlRegex,
71
  kByScriptHash,
72 73
  kByScriptId,
  kDebugCommand,
74
  kMonitorCommand,
75 76
  kBreakpointAtEntry,
  kInstrumentationBreakpoint
77 78 79 80 81
};

String16 generateBreakpointId(BreakpointType type,
                              const String16& scriptSelector, int lineNumber,
                              int columnNumber) {
82
  String16Builder builder;
83 84 85
  builder.appendNumber(static_cast<int>(type));
  builder.append(':');
  builder.appendNumber(lineNumber);
86
  builder.append(':');
87
  builder.appendNumber(columnNumber);
88
  builder.append(':');
89
  builder.append(scriptSelector);
90
  return builder.toString();
91 92
}

93 94 95 96 97 98 99 100 101
String16 generateBreakpointId(BreakpointType type,
                              v8::Local<v8::Function> function) {
  String16Builder builder;
  builder.appendNumber(static_cast<int>(type));
  builder.append(':');
  builder.appendNumber(v8::debug::GetDebuggingId(function));
  return builder.toString();
}

102 103 104 105 106 107 108 109 110
String16 generateInstrumentationBreakpointId(const String16& instrumentation) {
  String16Builder builder;
  builder.appendNumber(
      static_cast<int>(BreakpointType::kInstrumentationBreakpoint));
  builder.append(':');
  builder.append(instrumentation);
  return builder.toString();
}

111 112 113 114 115
bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
                       String16* scriptSelector = nullptr,
                       int* lineNumber = nullptr, int* columnNumber = nullptr) {
  size_t typeLineSeparator = breakpointId.find(':');
  if (typeLineSeparator == String16::kNotFound) return false;
116 117 118

  int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
  if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
119
      rawType > static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
120 121 122 123
    return false;
  }
  if (type) *type = static_cast<BreakpointType>(rawType);
  if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
124
      rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
125 126 127
      rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry) ||
      rawType == static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
    // The script and source position are not encoded in this case.
128 129 130
    return true;
  }

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
  size_t lineColumnSeparator = breakpointId.find(':', typeLineSeparator + 1);
  if (lineColumnSeparator == String16::kNotFound) return false;
  size_t columnSelectorSeparator =
      breakpointId.find(':', lineColumnSeparator + 1);
  if (columnSelectorSeparator == String16::kNotFound) return false;
  if (scriptSelector) {
    *scriptSelector = breakpointId.substring(columnSelectorSeparator + 1);
  }
  if (lineNumber) {
    *lineNumber = breakpointId
                      .substring(typeLineSeparator + 1,
                                 lineColumnSeparator - typeLineSeparator - 1)
                      .toInteger();
  }
  if (columnNumber) {
    *columnNumber =
        breakpointId
            .substring(lineColumnSeparator + 1,
                       columnSelectorSeparator - lineColumnSeparator - 1)
            .toInteger();
  }
  return true;
}

155 156
bool positionComparator(const std::pair<int, int>& a,
                        const std::pair<int, int>& b) {
157 158 159 160
  if (a.first != b.first) return a.first < b.first;
  return a.second < b.second;
}

161 162 163
String16 breakpointHint(const V8DebuggerScript& script, int lineNumber,
                        int columnNumber) {
  int offset = script.offset(lineNumber, columnNumber);
164 165
  if (offset == V8DebuggerScript::kNoOffset) return String16();
  String16 hint =
166
      script.source(offset, kBreakpointHintMaxLength).stripWhiteSpace();
167 168 169 170 171 172 173 174
  for (size_t i = 0; i < hint.length(); ++i) {
    if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') {
      return hint.substring(0, i);
    }
  }
  return hint;
}

175 176 177
void adjustBreakpointLocation(const V8DebuggerScript& script,
                              const String16& hint, int* lineNumber,
                              int* columnNumber) {
178 179
  if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
    return;
180
  if (hint.isEmpty()) return;
181
  intptr_t sourceOffset = script.offset(*lineNumber, *columnNumber);
182 183 184 185 186
  if (sourceOffset == V8DebuggerScript::kNoOffset) return;

  intptr_t searchRegionOffset = std::max(
      sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
  size_t offset = sourceOffset - searchRegionOffset;
187 188
  String16 searchArea = script.source(searchRegionOffset,
                                      offset + kBreakpointHintMaxSearchOffset);
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

  size_t nextMatch = searchArea.find(hint, offset);
  size_t prevMatch = searchArea.reverseFind(hint, offset);
  if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
    return;
  }
  size_t bestMatch;
  if (nextMatch == String16::kNotFound) {
    bestMatch = prevMatch;
  } else if (prevMatch == String16::kNotFound) {
    bestMatch = nextMatch;
  } else {
    bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
  }
  bestMatch += searchRegionOffset;
204 205
  v8::debug::Location hintPosition =
      script.location(static_cast<int>(bestMatch));
206
  if (hintPosition.IsEmpty()) return;
207 208
  *lineNumber = hintPosition.GetLineNumber();
  *columnNumber = hintPosition.GetColumnNumber();
209
}
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224

String16 breakLocationType(v8::debug::BreakLocationType type) {
  switch (type) {
    case v8::debug::kCallBreakLocation:
      return protocol::Debugger::BreakLocation::TypeEnum::Call;
    case v8::debug::kReturnBreakLocation:
      return protocol::Debugger::BreakLocation::TypeEnum::Return;
    case v8::debug::kDebuggerStatementBreakLocation:
      return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
    case v8::debug::kCommonBreakLocation:
      return String16();
  }
  return String16();
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
  switch (type) {
    case v8::debug::ScopeIterator::ScopeTypeGlobal:
      return Scope::TypeEnum::Global;
    case v8::debug::ScopeIterator::ScopeTypeLocal:
      return Scope::TypeEnum::Local;
    case v8::debug::ScopeIterator::ScopeTypeWith:
      return Scope::TypeEnum::With;
    case v8::debug::ScopeIterator::ScopeTypeClosure:
      return Scope::TypeEnum::Closure;
    case v8::debug::ScopeIterator::ScopeTypeCatch:
      return Scope::TypeEnum::Catch;
    case v8::debug::ScopeIterator::ScopeTypeBlock:
      return Scope::TypeEnum::Block;
    case v8::debug::ScopeIterator::ScopeTypeScript:
      return Scope::TypeEnum::Script;
    case v8::debug::ScopeIterator::ScopeTypeEval:
      return Scope::TypeEnum::Eval;
    case v8::debug::ScopeIterator::ScopeTypeModule:
      return Scope::TypeEnum::Module;
245 246
    case v8::debug::ScopeIterator::ScopeTypeWasmExpressionStack:
      return Scope::TypeEnum::WasmExpressionStack;
247 248 249 250 251
  }
  UNREACHABLE();
  return String16();
}

252
Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
253 254
                     InjectedScript* injectedScript,
                     std::unique_ptr<Array<Scope>>* scopes) {
255
  *scopes = std::make_unique<Array<Scope>>();
256 257
  if (!injectedScript) return Response::Success();
  if (iterator->Done()) return Response::Success();
258 259 260

  String16 scriptId = String16::fromInteger(iterator->GetScriptId());

261 262
  for (; !iterator->Done(); iterator->Advance()) {
    std::unique_ptr<RemoteObject> object;
263 264 265
    Response result =
        injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
                                   WrapMode::kNoPreview, &object);
266
    if (!result.IsSuccess()) return result;
267

268 269 270 271
    auto scope = Scope::create()
                     .setType(scopeType(iterator->GetType()))
                     .setObject(std::move(object))
                     .build();
272

273 274
    String16 name = toProtocolStringWithTypeCheck(
        isolate, iterator->GetFunctionDebugName());
275 276 277
    if (!name.isEmpty()) scope->setName(name);

    if (iterator->HasLocationInfo()) {
278 279 280 281 282 283
      v8::debug::Location start = iterator->GetStartLocation();
      scope->setStartLocation(protocol::Debugger::Location::create()
                                  .setScriptId(scriptId)
                                  .setLineNumber(start.GetLineNumber())
                                  .setColumnNumber(start.GetColumnNumber())
                                  .build());
284

285 286 287 288 289 290 291
      v8::debug::Location end = iterator->GetEndLocation();
      scope->setEndLocation(protocol::Debugger::Location::create()
                                .setScriptId(scriptId)
                                .setLineNumber(end.GetLineNumber())
                                .setColumnNumber(end.GetColumnNumber())
                                .build());
    }
292
    (*scopes)->emplace_back(std::move(scope));
293
  }
294
  return Response::Success();
295 296
}

297 298 299 300 301 302 303 304 305 306
protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
                                             const String16& key) {
  protocol::DictionaryValue* value = object->getObject(key);
  if (value) return value;
  std::unique_ptr<protocol::DictionaryValue> newDictionary =
      protocol::DictionaryValue::create();
  value = newDictionary.get();
  object->setObject(key, std::move(newDictionary));
  return value;
}
307 308
}  // namespace

309 310 311 312 313 314 315 316 317
V8DebuggerAgentImpl::V8DebuggerAgentImpl(
    V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
    protocol::DictionaryValue* state)
    : m_inspector(session->inspector()),
      m_debugger(m_inspector->debugger()),
      m_session(session),
      m_enabled(false),
      m_state(state),
      m_frontend(frontendChannel),
318
      m_isolate(m_inspector->isolate()) {}
319

320
V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default;
321

322
void V8DebuggerAgentImpl::enableImpl() {
323 324 325 326
  m_enabled = true;
  m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
  m_debugger->enable();

327 328 329 330 331
  std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts =
      m_debugger->getCompiledScripts(m_session->contextGroupId(), this);
  for (auto& script : compiledScripts) {
    didParseSource(std::move(script), true);
  }
332

333 334
  m_breakpointsActive = true;
  m_debugger->setBreakpointsActive(true);
335 336 337

  if (isPaused()) {
    didPause(0, v8::Local<v8::Value>(), std::vector<v8::debug::BreakpointId>(),
338
             v8::debug::kException, false, false, false);
339
  }
340 341
}

342 343 344 345
Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
                                     String16* outDebuggerId) {
  m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
      maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
346 347
  *outDebuggerId =
      m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
348
  if (enabled()) return Response::Success();
349

350
  if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
351
    return Response::ServerError("Script execution is prohibited");
352

353
  enableImpl();
354
  return Response::Success();
355 356
}

357
Response V8DebuggerAgentImpl::disable() {
358
  if (!enabled()) return Response::Success();
359

360 361
  m_state->remove(DebuggerAgentState::breakpointsByRegex);
  m_state->remove(DebuggerAgentState::breakpointsByUrl);
362
  m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
363
  m_state->remove(DebuggerAgentState::breakpointHints);
364
  m_state->remove(DebuggerAgentState::instrumentationBreakpoints);
365

366
  m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
367
                      v8::debug::NoBreakOnException);
368 369
  m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);

370 371 372 373
  if (m_breakpointsActive) {
    m_debugger->setBreakpointsActive(false);
    m_breakpointsActive = false;
  }
374
  m_blackboxedPositions.clear();
375 376 377
  m_blackboxPattern.reset();
  resetBlackboxedStateCache();
  m_scripts.clear();
378 379
  m_cachedScriptIds.clear();
  m_cachedScriptSize = 0;
380 381
  for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
    v8::debug::RemoveBreakpoint(m_isolate, it.first);
382
  }
383
  m_breakpointIdToDebuggerBreakpointIds.clear();
384
  m_debuggerBreakpointIdToBreakpointId.clear();
385 386 387
  m_debugger->setAsyncCallStackDepth(this, 0);
  clearBreakDetails();
  m_skipAllPauses = false;
388
  m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
389 390 391
  m_state->remove(DebuggerAgentState::blackboxPattern);
  m_enabled = false;
  m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
392
  m_debugger->disable();
393
  return Response::Success();
394 395 396 397 398 399 400 401 402
}

void V8DebuggerAgentImpl::restore() {
  DCHECK(!m_enabled);
  if (!m_state->booleanProperty(DebuggerAgentState::debuggerEnabled, false))
    return;
  if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
    return;

403
  enableImpl();
404

405
  int pauseState = v8::debug::NoBreakOnException;
406
  m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
407
  setPauseOnExceptionsImpl(pauseState);
408 409 410 411 412 413 414 415 416 417 418 419

  m_skipAllPauses =
      m_state->booleanProperty(DebuggerAgentState::skipAllPauses, false);

  int asyncCallStackDepth = 0;
  m_state->getInteger(DebuggerAgentState::asyncCallStackDepth,
                      &asyncCallStackDepth);
  m_debugger->setAsyncCallStackDepth(this, asyncCallStackDepth);

  String16 blackboxPattern;
  if (m_state->getString(DebuggerAgentState::blackboxPattern,
                         &blackboxPattern)) {
420
    setBlackboxPattern(blackboxPattern);
421 422 423
  }
}

424
Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
425 426
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
  if (m_breakpointsActive == active) return Response::Success();
427 428
  m_breakpointsActive = active;
  m_debugger->setBreakpointsActive(active);
429 430
  if (!active && !m_breakReason.empty()) {
    clearBreakDetails();
431
    m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
432
  }
433
  return Response::Success();
434 435
}

436
Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
437
  m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
438
  m_skipAllPauses = skip;
439
  return Response::Success();
440 441
}

442
static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
443 444 445 446 447 448 449 450 451 452
                    BreakpointType type, const String16& selector) {
  switch (type) {
    case BreakpointType::kByUrl:
      return script.sourceURL() == selector;
    case BreakpointType::kByScriptHash:
      return script.hash() == selector;
    case BreakpointType::kByUrlRegex: {
      V8Regex regex(inspector, selector, true);
      return regex.match(script.sourceURL()) != -1;
    }
453 454 455
    case BreakpointType::kByScriptId: {
      return script.scriptId() == selector;
    }
456 457
    default:
      return false;
458 459 460
  }
}

461 462
Response V8DebuggerAgentImpl::setBreakpointByUrl(
    int lineNumber, Maybe<String16> optionalURL,
463 464 465
    Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash,
    Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition,
    String16* outBreakpointId,
466
    std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
467
  *locations = std::make_unique<Array<protocol::Debugger::Location>>();
468

469 470 471 472
  int specified = (optionalURL.isJust() ? 1 : 0) +
                  (optionalURLRegex.isJust() ? 1 : 0) +
                  (optionalScriptHash.isJust() ? 1 : 0);
  if (specified != 1) {
473
    return Response::ServerError(
474 475
        "Either url or urlRegex or scriptHash must be specified.");
  }
476 477 478
  int columnNumber = 0;
  if (optionalColumnNumber.isJust()) {
    columnNumber = optionalColumnNumber.fromJust();
479 480
    if (columnNumber < 0)
      return Response::ServerError("Incorrect column number");
481 482
  }

483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
  BreakpointType type = BreakpointType::kByUrl;
  String16 selector;
  if (optionalURLRegex.isJust()) {
    selector = optionalURLRegex.fromJust();
    type = BreakpointType::kByUrlRegex;
  } else if (optionalURL.isJust()) {
    selector = optionalURL.fromJust();
    type = BreakpointType::kByUrl;
  } else if (optionalScriptHash.isJust()) {
    selector = optionalScriptHash.fromJust();
    type = BreakpointType::kByScriptHash;
  }

  String16 condition = optionalCondition.fromMaybe(String16());
  String16 breakpointId =
      generateBreakpointId(type, selector, lineNumber, columnNumber);
499
  protocol::DictionaryValue* breakpoints;
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
  switch (type) {
    case BreakpointType::kByUrlRegex:
      breakpoints =
          getOrCreateObject(m_state, DebuggerAgentState::breakpointsByRegex);
      break;
    case BreakpointType::kByUrl:
      breakpoints = getOrCreateObject(
          getOrCreateObject(m_state, DebuggerAgentState::breakpointsByUrl),
          selector);
      break;
    case BreakpointType::kByScriptHash:
      breakpoints = getOrCreateObject(
          getOrCreateObject(m_state,
                            DebuggerAgentState::breakpointsByScriptHash),
          selector);
      break;
    default:
      UNREACHABLE();
518 519
  }
  if (breakpoints->get(breakpointId)) {
520 521
    return Response::ServerError(
        "Breakpoint at specified location already exists.");
522
  }
523

524
  String16 hint;
525
  for (const auto& script : m_scripts) {
526
    if (!matches(m_inspector, *script.second, type, selector)) continue;
527 528 529 530 531 532
    if (!hint.isEmpty()) {
      adjustBreakpointLocation(*script.second, hint, &lineNumber,
                               &columnNumber);
    }
    std::unique_ptr<protocol::Debugger::Location> location = setBreakpointImpl(
        breakpointId, script.first, condition, lineNumber, columnNumber);
533
    if (location && type != BreakpointType::kByUrlRegex) {
534
      hint = breakpointHint(*script.second, lineNumber, columnNumber);
535
    }
536
    if (location) (*locations)->emplace_back(std::move(location));
537
  }
538 539 540 541 542 543
  breakpoints->setString(breakpointId, condition);
  if (!hint.isEmpty()) {
    protocol::DictionaryValue* breakpointHints =
        getOrCreateObject(m_state, DebuggerAgentState::breakpointHints);
    breakpointHints->setString(breakpointId, hint);
  }
544
  *outBreakpointId = breakpointId;
545
  return Response::Success();
546 547
}

548
Response V8DebuggerAgentImpl::setBreakpoint(
549
    std::unique_ptr<protocol::Debugger::Location> location,
550
    Maybe<String16> optionalCondition, String16* outBreakpointId,
551
    std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
552 553 554
  String16 breakpointId = generateBreakpointId(
      BreakpointType::kByScriptId, location->getScriptId(),
      location->getLineNumber(), location->getColumnNumber(0));
555 556
  if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
      m_breakpointIdToDebuggerBreakpointIds.end()) {
557 558
    return Response::ServerError(
        "Breakpoint at specified location already exists.");
559
  }
560 561 562 563
  *actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
                                      optionalCondition.fromMaybe(String16()),
                                      location->getLineNumber(),
                                      location->getColumnNumber(0));
564 565
  if (!*actualLocation)
    return Response::ServerError("Could not resolve breakpoint");
566
  *outBreakpointId = breakpointId;
567
  return Response::Success();
568 569
}

570 571 572 573 574
Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
    const String16& functionObjectId, Maybe<String16> optionalCondition,
    String16* outBreakpointId) {
  InjectedScript::ObjectScope scope(m_session, functionObjectId);
  Response response = scope.initialize();
575
  if (!response.IsSuccess()) return response;
576
  if (!scope.object()->IsFunction()) {
577
    return Response::ServerError("Could not find function with given id");
578 579 580 581 582 583 584
  }
  v8::Local<v8::Function> function =
      v8::Local<v8::Function>::Cast(scope.object());
  String16 breakpointId =
      generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
  if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
      m_breakpointIdToDebuggerBreakpointIds.end()) {
585 586
    return Response::ServerError(
        "Breakpoint at specified location already exists.");
587 588 589 590 591
  }
  v8::Local<v8::String> condition =
      toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
  setBreakpointImpl(breakpointId, function, condition);
  *outBreakpointId = breakpointId;
592
  return Response::Success();
593 594
}

595 596
Response V8DebuggerAgentImpl::setInstrumentationBreakpoint(
    const String16& instrumentation, String16* outBreakpointId) {
597
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
598 599 600 601
  String16 breakpointId = generateInstrumentationBreakpointId(instrumentation);
  protocol::DictionaryValue* breakpoints = getOrCreateObject(
      m_state, DebuggerAgentState::instrumentationBreakpoints);
  if (breakpoints->get(breakpointId)) {
602 603
    return Response::ServerError(
        "Instrumentation breakpoint is already enabled.");
604 605 606
  }
  breakpoints->setBoolean(breakpointId, true);
  *outBreakpointId = breakpointId;
607
  return Response::Success();
608 609
}

610
Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
611
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
612 613 614
  BreakpointType type;
  String16 selector;
  if (!parseBreakpointId(breakpointId, &type, &selector)) {
615
    return Response::Success();
616 617
  }
  protocol::DictionaryValue* breakpoints = nullptr;
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
  switch (type) {
    case BreakpointType::kByUrl: {
      protocol::DictionaryValue* breakpointsByUrl =
          m_state->getObject(DebuggerAgentState::breakpointsByUrl);
      if (breakpointsByUrl) {
        breakpoints = breakpointsByUrl->getObject(selector);
      }
    } break;
    case BreakpointType::kByScriptHash: {
      protocol::DictionaryValue* breakpointsByScriptHash =
          m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
      if (breakpointsByScriptHash) {
        breakpoints = breakpointsByScriptHash->getObject(selector);
      }
    } break;
    case BreakpointType::kByUrlRegex:
      breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
      break;
636 637 638 639
    case BreakpointType::kInstrumentationBreakpoint:
      breakpoints =
          m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
      break;
640 641
    default:
      break;
642 643 644 645 646
  }
  if (breakpoints) breakpoints->remove(breakpointId);
  protocol::DictionaryValue* breakpointHints =
      m_state->getObject(DebuggerAgentState::breakpointHints);
  if (breakpointHints) breakpointHints->remove(breakpointId);
647 648 649 650 651 652 653 654 655 656 657 658

  // Get a list of scripts to remove breakpoints.
  // TODO(duongn): we can do better here if from breakpoint id we can tell it is
  // not Wasm breakpoint.
  std::vector<V8DebuggerScript*> scripts;
  for (const auto& scriptIter : m_scripts) {
    if (!matches(m_inspector, *scriptIter.second, type, selector)) continue;
    V8DebuggerScript* script = scriptIter.second.get();
    scripts.push_back(script);
  }
  removeBreakpointImpl(breakpointId, scripts);

659
  return Response::Success();
660 661
}

662 663 664
void V8DebuggerAgentImpl::removeBreakpointImpl(
    const String16& breakpointId,
    const std::vector<V8DebuggerScript*>& scripts) {
665 666 667 668 669
  DCHECK(enabled());
  BreakpointIdToDebuggerBreakpointIdsMap::iterator
      debuggerBreakpointIdsIterator =
          m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
  if (debuggerBreakpointIdsIterator ==
670
      m_breakpointIdToDebuggerBreakpointIds.end()) {
671
    return;
672 673
  }
  for (const auto& id : debuggerBreakpointIdsIterator->second) {
674 675 676
    for (auto& script : scripts) {
      script->removeWasmBreakpoint(id);
    }
677
    v8::debug::RemoveBreakpoint(m_isolate, id);
678
    m_debuggerBreakpointIdToBreakpointId.erase(id);
679 680 681 682
  }
  m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
}

683 684
Response V8DebuggerAgentImpl::getPossibleBreakpoints(
    std::unique_ptr<protocol::Debugger::Location> start,
685
    Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
686 687
    std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
        locations) {
688 689 690
  String16 scriptId = start->getScriptId();

  if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
691
    return Response::ServerError(
692 693
        "start.lineNumber and start.columnNumber should be >= 0");

694 695 696
  v8::debug::Location v8Start(start->getLineNumber(),
                              start->getColumnNumber(0));
  v8::debug::Location v8End;
697 698
  if (end.isJust()) {
    if (end.fromJust()->getScriptId() != scriptId)
699 700
      return Response::ServerError(
          "Locations should contain the same scriptId");
701 702 703
    int line = end.fromJust()->getLineNumber();
    int column = end.fromJust()->getColumnNumber(0);
    if (line < 0 || column < 0)
704
      return Response::ServerError(
705
          "end.lineNumber and end.columnNumber should be >= 0");
706
    v8End = v8::debug::Location(line, column);
707 708
  }
  auto it = m_scripts.find(scriptId);
709
  if (it == m_scripts.end()) return Response::ServerError("Script not found");
710
  std::vector<v8::debug::BreakLocation> v8Locations;
711 712
  {
    v8::HandleScope handleScope(m_isolate);
713 714 715
    int contextId = it->second->executionContextId();
    InspectedContext* inspected = m_inspector->getContext(contextId);
    if (!inspected) {
716
      return Response::ServerError("Cannot retrive script context");
717 718
    }
    v8::Context::Scope contextScope(inspected->context());
719 720
    v8::MicrotasksScope microtasks(m_isolate,
                                   v8::MicrotasksScope::kDoNotRunMicrotasks);
721 722 723
    v8::TryCatch tryCatch(m_isolate);
    it->second->getPossibleBreakpoints(
        v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations);
724
  }
725

726 727
  *locations =
      std::make_unique<protocol::Array<protocol::Debugger::BreakLocation>>();
728
  for (size_t i = 0; i < v8Locations.size(); ++i) {
729 730 731 732 733 734 735 736 737
    std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
        protocol::Debugger::BreakLocation::create()
            .setScriptId(scriptId)
            .setLineNumber(v8Locations[i].GetLineNumber())
            .setColumnNumber(v8Locations[i].GetColumnNumber())
            .build();
    if (v8Locations[i].type() != v8::debug::kCommonBreakLocation) {
      breakLocation->setType(breakLocationType(v8Locations[i].type()));
    }
738
    (*locations)->emplace_back(std::move(breakLocation));
739
  }
740
  return Response::Success();
741 742
}

743
Response V8DebuggerAgentImpl::continueToLocation(
744 745
    std::unique_ptr<protocol::Debugger::Location> location,
    Maybe<String16> targetCallFrames) {
746 747
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
748 749
  ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
  if (it == m_scripts.end()) {
750
    return Response::ServerError("Cannot continue to specified location");
751 752 753 754 755
  }
  V8DebuggerScript* script = it->second.get();
  int contextId = script->executionContextId();
  InspectedContext* inspected = m_inspector->getContext(contextId);
  if (!inspected)
756
    return Response::ServerError("Cannot continue to specified location");
757
  v8::HandleScope handleScope(m_isolate);
758
  v8::Context::Scope contextScope(inspected->context());
759
  return m_debugger->continueToLocation(
760
      m_session->contextGroupId(), script, std::move(location),
761 762
      targetCallFrames.fromMaybe(
          protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
763 764
}

765 766 767 768 769
Response V8DebuggerAgentImpl::getStackTrace(
    std::unique_ptr<protocol::Runtime::StackTraceId> inStackTraceId,
    std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
  bool isOk = false;
  int64_t id = inStackTraceId->getId().toInteger64(&isOk);
770
  if (!isOk) return Response::ServerError("Invalid stack trace id");
771 772

  V8DebuggerId debuggerId;
773
  if (inStackTraceId->hasDebuggerId()) {
774
    debuggerId = V8DebuggerId(inStackTraceId->getDebuggerId(String16()));
775 776 777
  } else {
    debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
  }
778 779
  if (!debuggerId.isValid())
    return Response::ServerError("Invalid stack trace id");
780 781 782

  V8StackTraceId v8StackTraceId(id, debuggerId.pair());
  if (v8StackTraceId.IsInvalid())
783
    return Response::ServerError("Invalid stack trace id");
784 785 786
  auto stack =
      m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
  if (!stack) {
787
    return Response::ServerError("Stack trace with given id is not found");
788
  }
789 790
  *outStackTrace = stack->buildInspectorObject(
      m_debugger, m_debugger->maxAsyncCallChainDepth());
791
  return Response::Success();
792 793
}

794 795 796 797
bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
                                               const v8::debug::Location& start,
                                               const v8::debug::Location& end) {
  ScriptsMap::iterator it = m_scripts.find(scriptId);
798 799 800 801 802 803 804 805 806 807
  if (it == m_scripts.end()) {
    // Unknown scripts are blackboxed.
    return true;
  }
  if (m_blackboxPattern) {
    const String16& scriptSourceURL = it->second->sourceURL();
    if (!scriptSourceURL.isEmpty() &&
        m_blackboxPattern->match(scriptSourceURL) != -1)
      return true;
  }
808
  auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
809 810 811 812
  if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;

  const std::vector<std::pair<int, int>>& ranges =
      itBlackboxedPositions->second;
813
  auto itStartRange = std::lower_bound(
814
      ranges.begin(), ranges.end(),
815 816 817 818 819 820
      std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
      positionComparator);
  auto itEndRange = std::lower_bound(
      itStartRange, ranges.end(),
      std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
      positionComparator);
821 822 823
  // Ranges array contains positions in script where blackbox state is changed.
  // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
  // blackboxed...
824 825
  return itStartRange == itEndRange &&
         std::distance(ranges.begin(), itStartRange) % 2;
826 827
}

828 829 830 831
bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
  return enabled() && (isOOMBreak || !m_skipAllPauses);
}

832
std::unique_ptr<protocol::Debugger::Location>
833 834 835 836
V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
                                       const String16& scriptId,
                                       const String16& condition,
                                       int lineNumber, int columnNumber) {
837
  v8::HandleScope handles(m_isolate);
838
  DCHECK(enabled());
839 840

  ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
841
  if (scriptIterator == m_scripts.end()) return nullptr;
842
  V8DebuggerScript* script = scriptIterator->second.get();
843
  if (lineNumber < script->startLine() || script->endLine() < lineNumber) {
844
    return nullptr;
845
  }
846

847
  v8::debug::BreakpointId debuggerBreakpointId;
848
  v8::debug::Location location(lineNumber, columnNumber);
849 850 851 852 853 854
  int contextId = script->executionContextId();
  InspectedContext* inspected = m_inspector->getContext(contextId);
  if (!inspected) return nullptr;

  {
    v8::Context::Scope contextScope(inspected->context());
855
    if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
856 857 858
      return nullptr;
    }
  }
859

860
  m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
861 862
  m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
      debuggerBreakpointId);
863 864 865 866 867 868

  return protocol::Debugger::Location::create()
      .setScriptId(scriptId)
      .setLineNumber(location.GetLineNumber())
      .setColumnNumber(location.GetColumnNumber())
      .build();
869 870
}

871 872 873 874 875 876 877 878 879 880 881 882 883
void V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
                                            v8::Local<v8::Function> function,
                                            v8::Local<v8::String> condition) {
  v8::debug::BreakpointId debuggerBreakpointId;
  if (!v8::debug::SetFunctionBreakpoint(function, condition,
                                        &debuggerBreakpointId)) {
    return;
  }
  m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
  m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
      debuggerBreakpointId);
}

884 885 886
Response V8DebuggerAgentImpl::searchInContent(
    const String16& scriptId, const String16& query,
    Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
887 888 889
    std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
  v8::HandleScope handles(m_isolate);
  ScriptsMap::iterator it = m_scripts.find(scriptId);
890
  if (it == m_scripts.end())
891
    return Response::ServerError("No script for id: " + scriptId.utf8());
892

893 894 895 896
  *results = std::make_unique<protocol::Array<protocol::Debugger::SearchMatch>>(
      searchInTextByLinesImpl(m_session, it->second->source(0), query,
                              optionalCaseSensitive.fromMaybe(false),
                              optionalIsRegex.fromMaybe(false)));
897
  return Response::Success();
898 899
}

900 901
Response V8DebuggerAgentImpl::setScriptSource(
    const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
902
    Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
903 904
    Maybe<bool>* stackChanged,
    Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
905
    Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
906
    Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
907
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
908

909 910
  ScriptsMap::iterator it = m_scripts.find(scriptId);
  if (it == m_scripts.end()) {
911
    return Response::ServerError("No script with given id found");
912
  }
913
  int contextId = it->second->executionContextId();
914
  InspectedContext* inspected = m_inspector->getContext(contextId);
915 916 917 918 919 920
  if (!inspected) {
    return Response::InternalError();
  }
  v8::HandleScope handleScope(m_isolate);
  v8::Local<v8::Context> context = inspected->context();
  v8::Context::Scope contextScope(context);
921 922 923 924 925 926 927

  v8::debug::LiveEditResult result;
  it->second->setSource(newContent, dryRun.fromMaybe(false), &result);
  if (result.status != v8::debug::LiveEditResult::OK) {
    *optOutCompileError =
        protocol::Runtime::ExceptionDetails::create()
            .setExceptionId(m_inspector->nextExceptionId())
928
            .setText(toProtocolString(m_isolate, result.message))
929 930 931 932 933
            .setLineNumber(result.line_number != -1 ? result.line_number - 1
                                                    : 0)
            .setColumnNumber(result.column_number != -1 ? result.column_number
                                                        : 0)
            .build();
934
    return Response::Success();
935
  } else {
936
    *stackChanged = result.stack_changed;
937
  }
938
  std::unique_ptr<Array<CallFrame>> callFrames;
939
  Response response = currentCallFrames(&callFrames);
940
  if (!response.IsSuccess()) return response;
941 942
  *newCallFrames = std::move(callFrames);
  *asyncStackTrace = currentAsyncStackTrace();
943
  *asyncStackTraceId = currentExternalStackTrace();
944
  return Response::Success();
945 946
}

947 948
Response V8DebuggerAgentImpl::restartFrame(
    const String16& callFrameId,
949
    std::unique_ptr<Array<CallFrame>>* newCallFrames,
950 951
    Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
    Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
952
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
953
  InjectedScript::CallFrameScope scope(m_session, callFrameId);
954
  Response response = scope.initialize();
955
  if (!response.IsSuccess()) return response;
956 957 958
  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
  if (it->Done()) {
959
    return Response::ServerError("Could not find call frame with given id");
960 961
  }
  if (!it->Restart()) {
962
    return Response::InternalError();
963
  }
964
  response = currentCallFrames(newCallFrames);
965
  if (!response.IsSuccess()) return response;
966
  *asyncStackTrace = currentAsyncStackTrace();
967
  *asyncStackTraceId = currentExternalStackTrace();
968
  return Response::Success();
969 970
}

971 972 973
Response V8DebuggerAgentImpl::getScriptSource(
    const String16& scriptId, String16* scriptSource,
    Maybe<protocol::Binary>* bytecode) {
974
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
975
  ScriptsMap::iterator it = m_scripts.find(scriptId);
976
  if (it == m_scripts.end())
977
    return Response::ServerError("No script for id: " + scriptId.utf8());
978
  *scriptSource = it->second->source(0);
979 980 981 982
  v8::MemorySpan<const uint8_t> span;
  if (it->second->wasmBytecode().To(&span)) {
    *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
  }
983
  return Response::Success();
984 985
}

986 987
Response V8DebuggerAgentImpl::getWasmBytecode(const String16& scriptId,
                                              protocol::Binary* bytecode) {
988
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
989 990
  ScriptsMap::iterator it = m_scripts.find(scriptId);
  if (it == m_scripts.end())
991
    return Response::ServerError("No script for id: " + scriptId.utf8());
992 993
  v8::MemorySpan<const uint8_t> span;
  if (!it->second->wasmBytecode().To(&span))
994 995
    return Response::ServerError("Script with id " + scriptId.utf8() +
                                 " is not WebAssembly");
996
  *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
997
  return Response::Success();
998 999
}

1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
void V8DebuggerAgentImpl::pushBreakDetails(
    const String16& breakReason,
    std::unique_ptr<protocol::DictionaryValue> breakAuxData) {
  m_breakReason.push_back(std::make_pair(breakReason, std::move(breakAuxData)));
}

void V8DebuggerAgentImpl::popBreakDetails() {
  if (m_breakReason.empty()) return;
  m_breakReason.pop_back();
}

void V8DebuggerAgentImpl::clearBreakDetails() {
  std::vector<BreakReason> emptyBreakReason;
  m_breakReason.swap(emptyBreakReason);
}

1016 1017 1018
void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
    const String16& breakReason,
    std::unique_ptr<protocol::DictionaryValue> data) {
1019
  if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1020
  if (m_breakReason.empty()) {
1021
    m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1022
  }
1023
  pushBreakDetails(breakReason, std::move(data));
1024 1025 1026
}

void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
1027
  if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
1028
  if (m_breakReason.size() == 1) {
1029
    m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
1030 1031
  }
  popBreakDetails();
1032 1033
}

1034
Response V8DebuggerAgentImpl::pause() {
1035 1036
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
  if (isPaused()) return Response::Success();
1037 1038 1039 1040 1041 1042 1043
  if (m_debugger->canBreakProgram()) {
    m_debugger->interruptAndBreak(m_session->contextGroupId());
  } else {
    if (m_breakReason.empty()) {
      m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
    }
    pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
1044
  }
1045
  return Response::Success();
1046 1047
}

1048
Response V8DebuggerAgentImpl::resume(Maybe<bool> terminateOnResume) {
1049
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1050
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1051 1052
  m_debugger->continueProgram(m_session->contextGroupId(),
                              terminateOnResume.fromMaybe(false));
1053
  return Response::Success();
1054 1055
}

1056
Response V8DebuggerAgentImpl::stepOver() {
1057
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1058
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1059
  m_debugger->stepOverStatement(m_session->contextGroupId());
1060
  return Response::Success();
1061 1062
}

1063
Response V8DebuggerAgentImpl::stepInto(Maybe<bool> inBreakOnAsyncCall) {
1064
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1065
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1066 1067
  m_debugger->stepIntoStatement(m_session->contextGroupId(),
                                inBreakOnAsyncCall.fromMaybe(false));
1068
  return Response::Success();
1069 1070
}

1071
Response V8DebuggerAgentImpl::stepOut() {
1072
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1073
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1074
  m_debugger->stepOutOfFunction(m_session->contextGroupId());
1075
  return Response::Success();
1076 1077
}

1078 1079
Response V8DebuggerAgentImpl::pauseOnAsyncCall(
    std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1080
  // Deprecated, just return OK.
1081
  return Response::Success();
1082 1083
}

1084 1085
Response V8DebuggerAgentImpl::setPauseOnExceptions(
    const String16& stringPauseState) {
1086
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
1087
  v8::debug::ExceptionBreakState pauseState;
1088
  if (stringPauseState == "none") {
1089
    pauseState = v8::debug::NoBreakOnException;
1090
  } else if (stringPauseState == "all") {
1091
    pauseState = v8::debug::BreakOnAnyException;
1092
  } else if (stringPauseState == "uncaught") {
1093
    pauseState = v8::debug::BreakOnUncaughtException;
1094
  } else {
1095 1096
    return Response::ServerError("Unknown pause on exceptions mode: " +
                                 stringPauseState.utf8());
1097
  }
1098
  setPauseOnExceptionsImpl(pauseState);
1099
  return Response::Success();
1100 1101
}

1102
void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
1103 1104
  // TODO(dgozman): this changes the global state and forces all context groups
  // to pause. We should make this flag be per-context-group.
1105
  m_debugger->setPauseOnExceptionsState(
1106
      static_cast<v8::debug::ExceptionBreakState>(pauseState));
1107
  m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
1108 1109
}

1110 1111 1112 1113
Response V8DebuggerAgentImpl::evaluateOnCallFrame(
    const String16& callFrameId, const String16& expression,
    Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
    Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
1114 1115
    Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
    std::unique_ptr<RemoteObject>* result,
1116
    Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
1117
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1118
  InjectedScript::CallFrameScope scope(m_session, callFrameId);
1119
  Response response = scope.initialize();
1120
  if (!response.IsSuccess()) return response;
1121
  if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
1122 1123
  if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();

1124 1125 1126
  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
  if (it->Done()) {
1127
    return Response::ServerError("Could not find call frame with given id");
1128
  }
1129 1130 1131

  v8::MaybeLocal<v8::Value> maybeResultValue;
  {
1132
    V8InspectorImpl::EvaluateScope evaluateScope(scope);
1133 1134
    if (timeout.isJust()) {
      response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
1135
      if (!response.IsSuccess()) return response;
1136 1137 1138 1139
    }
    maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
                                    throwOnSideEffect.fromMaybe(false));
  }
1140 1141
  // Re-initialize after running client's code, as it could have destroyed
  // context or session.
1142
  response = scope.initialize();
1143
  if (!response.IsSuccess()) return response;
1144 1145 1146
  WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
                                                   : WrapMode::kNoPreview;
  if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
1147
  return scope.injectedScript()->wrapEvaluateResult(
1148 1149
      maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), mode,
      result, exceptionDetails);
1150 1151
}

1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
Response V8DebuggerAgentImpl::executeWasmEvaluator(
    const String16& callFrameId, const protocol::Binary& evaluator,
    Maybe<double> timeout,
    std::unique_ptr<protocol::Runtime::RemoteObject>* result,
    Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
  if (!v8::debug::StackTraceIterator::SupportsWasmDebugEvaluate()) {
    return Response::ServerError(
        "--wasm-expose-debug-eval is required to execte evaluator modules");
  }
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
  InjectedScript::CallFrameScope scope(m_session, callFrameId);
  Response response = scope.initialize();
  if (!response.IsSuccess()) return response;

  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
  std::unique_ptr<v8::debug::StackTraceIterator> it =
      v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
  if (it->Done()) {
    return Response::ServerError("Could not find call frame with given id");
  }
  if (!it->GetScript()->IsWasm()) {
    return Response::ServerError(
        "executeWasmEvaluator can only be called on WebAssembly frames");
  }

  v8::MaybeLocal<v8::Value> maybeResultValue;
  {
    V8InspectorImpl::EvaluateScope evaluateScope(scope);
    if (timeout.isJust()) {
      response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
      if (!response.IsSuccess()) return response;
    }
    v8::MaybeLocal<v8::String> eval_result =
        it->EvaluateWasm({evaluator.data(), evaluator.size()}, frameOrdinal);
    if (!eval_result.IsEmpty()) maybeResultValue = eval_result.ToLocalChecked();
  }

  // Re-initialize after running client's code, as it could have destroyed
  // context or session.
  response = scope.initialize();
  if (!response.IsSuccess()) return response;

  String16 object_group = "";
  InjectedScript* injected_script = scope.injectedScript();
  return injected_script->wrapEvaluateResult(maybeResultValue, scope.tryCatch(),
                                             object_group, WrapMode::kNoPreview,
                                             result, exceptionDetails);
}

1201 1202
Response V8DebuggerAgentImpl::setVariableValue(
    int scopeNumber, const String16& variableName,
1203 1204
    std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
    const String16& callFrameId) {
1205 1206
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1207
  InjectedScript::CallFrameScope scope(m_session, callFrameId);
1208
  Response response = scope.initialize();
1209
  if (!response.IsSuccess()) return response;
1210
  v8::Local<v8::Value> newValue;
1211 1212
  response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
                                                         &newValue);
1213
  if (!response.IsSuccess()) return response;
1214

1215 1216 1217
  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
  if (it->Done()) {
1218
    return Response::ServerError("Could not find call frame with given id");
1219 1220 1221 1222 1223 1224 1225
  }
  auto scopeIterator = it->GetScopeIterator();
  while (!scopeIterator->Done() && scopeNumber > 0) {
    --scopeNumber;
    scopeIterator->Advance();
  }
  if (scopeNumber != 0) {
1226
    return Response::ServerError("Could not find scope with given number");
1227
  }
1228

1229 1230 1231
  if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
                                       newValue) ||
      scope.tryCatch().HasCaught()) {
1232
    return Response::InternalError();
1233
  }
1234
  return Response::Success();
1235 1236
}

1237 1238
Response V8DebuggerAgentImpl::setReturnValue(
    std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
1239 1240
  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
1241
  v8::HandleScope handleScope(m_isolate);
1242 1243
  auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
  if (iterator->Done()) {
1244
    return Response::ServerError("Could not find top call frame");
1245 1246
  }
  if (iterator->GetReturnValue().IsEmpty()) {
1247
    return Response::ServerError(
1248 1249 1250 1251
        "Could not update return value at non-return position");
  }
  InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
  Response response = scope.initialize();
1252
  if (!response.IsSuccess()) return response;
1253 1254 1255
  v8::Local<v8::Value> newValue;
  response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
                                                         &newValue);
1256
  if (!response.IsSuccess()) return response;
1257
  v8::debug::SetReturnValue(m_isolate, newValue);
1258
  return Response::Success();
1259 1260
}

1261
Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
1262
  if (!enabled() && !m_session->runtimeAgent()->enabled()) {
1263
    return Response::ServerError(kDebuggerNotEnabled);
1264
  }
1265 1266
  m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
  m_debugger->setAsyncCallStackDepth(this, depth);
1267
  return Response::Success();
1268 1269
}

1270
Response V8DebuggerAgentImpl::setBlackboxPatterns(
1271
    std::unique_ptr<protocol::Array<String16>> patterns) {
1272
  if (patterns->empty()) {
1273
    m_blackboxPattern = nullptr;
1274
    resetBlackboxedStateCache();
1275
    m_state->remove(DebuggerAgentState::blackboxPattern);
1276
    return Response::Success();
1277 1278 1279 1280
  }

  String16Builder patternBuilder;
  patternBuilder.append('(');
1281 1282
  for (size_t i = 0; i < patterns->size() - 1; ++i) {
    patternBuilder.append((*patterns)[i]);
1283 1284
    patternBuilder.append("|");
  }
1285
  patternBuilder.append(patterns->back());
1286 1287
  patternBuilder.append(')');
  String16 pattern = patternBuilder.toString();
1288
  Response response = setBlackboxPattern(pattern);
1289
  if (!response.IsSuccess()) return response;
1290
  resetBlackboxedStateCache();
1291
  m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
1292
  return Response::Success();
1293 1294
}

1295
Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
1296 1297
  std::unique_ptr<V8Regex> regex(new V8Regex(
      m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
1298
  if (!regex->isValid())
1299 1300
    return Response::ServerError("Pattern parser error: " +
                                 regex->errorMessage().utf8());
1301
  m_blackboxPattern = std::move(regex);
1302
  return Response::Success();
1303 1304
}

1305 1306 1307 1308 1309 1310
void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
  for (const auto& it : m_scripts) {
    it.second->resetBlackboxedStateCache();
  }
}

1311 1312
Response V8DebuggerAgentImpl::setBlackboxedRanges(
    const String16& scriptId,
1313 1314
    std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
        inPositions) {
1315 1316
  auto it = m_scripts.find(scriptId);
  if (it == m_scripts.end())
1317
    return Response::ServerError("No script with passed id.");
1318

1319
  if (inPositions->empty()) {
1320
    m_blackboxedPositions.erase(scriptId);
1321
    it->second->resetBlackboxedStateCache();
1322
    return Response::Success();
1323 1324 1325
  }

  std::vector<std::pair<int, int>> positions;
1326 1327 1328
  positions.reserve(inPositions->size());
  for (const std::unique_ptr<protocol::Debugger::ScriptPosition>& position :
       *inPositions) {
1329
    if (position->getLineNumber() < 0)
1330
      return Response::ServerError("Position missing 'line' or 'line' < 0.");
1331
    if (position->getColumnNumber() < 0)
1332 1333
      return Response::ServerError(
          "Position missing 'column' or 'column' < 0.");
1334 1335 1336 1337 1338 1339 1340 1341 1342
    positions.push_back(
        std::make_pair(position->getLineNumber(), position->getColumnNumber()));
  }

  for (size_t i = 1; i < positions.size(); ++i) {
    if (positions[i - 1].first < positions[i].first) continue;
    if (positions[i - 1].first == positions[i].first &&
        positions[i - 1].second < positions[i].second)
      continue;
1343
    return Response::ServerError(
1344
        "Input positions array is not sorted or contains duplicate values.");
1345 1346 1347
  }

  m_blackboxedPositions[scriptId] = positions;
1348
  it->second->resetBlackboxedStateCache();
1349
  return Response::Success();
1350 1351
}

1352 1353
Response V8DebuggerAgentImpl::currentCallFrames(
    std::unique_ptr<Array<CallFrame>>* result) {
1354
  if (!isPaused()) {
1355
    *result = std::make_unique<Array<CallFrame>>();
1356
    return Response::Success();
1357
  }
1358
  v8::HandleScope handles(m_isolate);
1359
  *result = std::make_unique<Array<CallFrame>>();
1360 1361 1362 1363
  auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
  int frameOrdinal = 0;
  for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
    int contextId = iterator->GetContextId();
1364 1365
    InjectedScript* injectedScript = nullptr;
    if (contextId) m_session->findInjectedScript(contextId, injectedScript);
1366
    String16 callFrameId =
1367 1368 1369 1370 1371 1372
        RemoteCallFrameId::serialize(contextId, frameOrdinal);

    v8::debug::Location loc = iterator->GetSourceLocation();

    std::unique_ptr<Array<Scope>> scopes;
    auto scopeIterator = iterator->GetScopeIterator();
1373 1374
    Response res =
        buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
1375
    if (!res.IsSuccess()) return res;
1376

1377
    std::unique_ptr<RemoteObject> protocolReceiver;
1378
    if (injectedScript) {
1379 1380
      v8::Local<v8::Value> receiver;
      if (iterator->GetReceiver().ToLocal(&receiver)) {
1381 1382 1383
        res =
            injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
                                       WrapMode::kNoPreview, &protocolReceiver);
1384
        if (!res.IsSuccess()) return res;
1385 1386 1387 1388 1389 1390
      }
    }
    if (!protocolReceiver) {
      protocolReceiver = RemoteObject::create()
                             .setType(RemoteObject::TypeEnum::Undefined)
                             .build();
1391 1392
    }

1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
    v8::Local<v8::debug::Script> script = iterator->GetScript();
    DCHECK(!script.IsEmpty());
    std::unique_ptr<protocol::Debugger::Location> location =
        protocol::Debugger::Location::create()
            .setScriptId(String16::fromInteger(script->Id()))
            .setLineNumber(loc.GetLineNumber())
            .setColumnNumber(loc.GetColumnNumber())
            .build();
    String16 scriptId = String16::fromInteger(script->Id());
    ScriptsMap::iterator scriptIterator =
        m_scripts.find(location->getScriptId());
    String16 url;
    if (scriptIterator != m_scripts.end()) {
      url = scriptIterator->second->sourceURL();
    }

1409 1410 1411 1412 1413 1414 1415 1416 1417
    auto frame = CallFrame::create()
                     .setCallFrameId(callFrameId)
                     .setFunctionName(toProtocolString(
                         m_isolate, iterator->GetFunctionDebugName()))
                     .setLocation(std::move(location))
                     .setUrl(url)
                     .setScopeChain(std::move(scopes))
                     .setThis(std::move(protocolReceiver))
                     .build();
1418 1419 1420 1421 1422 1423 1424 1425 1426

    v8::Local<v8::Function> func = iterator->GetFunction();
    if (!func.IsEmpty()) {
      frame->setFunctionLocation(
          protocol::Debugger::Location::create()
              .setScriptId(String16::fromInteger(func->ScriptId()))
              .setLineNumber(func->GetScriptLineNumber())
              .setColumnNumber(func->GetScriptColumnNumber())
              .build());
1427 1428
    }

1429 1430 1431 1432
    v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
    if (!returnValue.IsEmpty() && injectedScript) {
      std::unique_ptr<RemoteObject> value;
      res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
1433
                                       WrapMode::kNoPreview, &value);
1434
      if (!res.IsSuccess()) return res;
1435
      frame->setReturnValue(std::move(value));
1436
    }
1437
    (*result)->emplace_back(std::move(frame));
1438
  }
1439
  return Response::Success();
1440 1441
}

1442 1443 1444 1445 1446 1447
std::unique_ptr<protocol::Runtime::StackTrace>
V8DebuggerAgentImpl::currentAsyncStackTrace() {
  std::shared_ptr<AsyncStackTrace> asyncParent =
      m_debugger->currentAsyncParent();
  if (!asyncParent) return nullptr;
  return asyncParent->buildInspectorObject(
1448
      m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
1449 1450
}

1451 1452 1453 1454 1455 1456
std::unique_ptr<protocol::Runtime::StackTraceId>
V8DebuggerAgentImpl::currentExternalStackTrace() {
  V8StackTraceId externalParent = m_debugger->currentExternalParent();
  if (externalParent.IsInvalid()) return nullptr;
  return protocol::Runtime::StackTraceId::create()
      .setId(stackTraceIdToString(externalParent.id))
1457
      .setDebuggerId(V8DebuggerId(externalParent.debugger_id).toString())
1458 1459 1460
      .build();
}

1461
bool V8DebuggerAgentImpl::isPaused() const {
1462
  return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
1463
}
1464

1465 1466 1467 1468 1469 1470 1471 1472 1473
static String16 getScriptLanguage(const V8DebuggerScript& script) {
  switch (script.getLanguage()) {
    case V8DebuggerScript::Language::WebAssembly:
      return protocol::Debugger::ScriptLanguageEnum::WebAssembly;
    case V8DebuggerScript::Language::JavaScript:
      return protocol::Debugger::ScriptLanguageEnum::JavaScript;
  }
}

1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506
static const char* getDebugSymbolTypeName(
    v8::debug::WasmScript::DebugSymbolsType type) {
  switch (type) {
    case v8::debug::WasmScript::DebugSymbolsType::None:
      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::None;
    case v8::debug::WasmScript::DebugSymbolsType::SourceMap:
      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
          SourceMap;
    case v8::debug::WasmScript::DebugSymbolsType::EmbeddedDWARF:
      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
          EmbeddedDWARF;
    case v8::debug::WasmScript::DebugSymbolsType::ExternalDWARF:
      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
          ExternalDWARF;
  }
}

static std::unique_ptr<protocol::Debugger::DebugSymbols> getDebugSymbols(
    const V8DebuggerScript& script) {
  v8::debug::WasmScript::DebugSymbolsType type;
  if (!script.getDebugSymbolsType().To(&type)) return {};

  std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
      v8_inspector::protocol::Debugger::DebugSymbols::create()
          .setType(getDebugSymbolTypeName(type))
          .build();
  String16 externalUrl;
  if (script.getExternalDebugSymbolsURL().To(&externalUrl)) {
    debugSymbols->setExternalURL(externalUrl);
  }
  return debugSymbols;
}

1507 1508 1509
void V8DebuggerAgentImpl::didParseSource(
    std::unique_ptr<V8DebuggerScript> script, bool success) {
  v8::HandleScope handles(m_isolate);
1510 1511 1512 1513 1514 1515
  if (!success) {
    DCHECK(!script->isSourceLoadedLazily());
    String16 scriptSource = script->source(0);
    script->setSourceURL(findSourceURL(scriptSource, false));
    script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
  }
1516

1517 1518 1519 1520
  int contextId = script->executionContextId();
  int contextGroupId = m_inspector->contextGroupId(contextId);
  InspectedContext* inspected =
      m_inspector->getContext(contextGroupId, contextId);
1521
  std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1522 1523 1524
  if (inspected) {
    // Script reused between different groups/sessions can have a stale
    // execution context id.
1525 1526 1527 1528
    const String16& aux = inspected->auxData();
    std::vector<uint8_t> cbor;
    v8_crdtp::json::ConvertJSONToCBOR(
        v8_crdtp::span<uint16_t>(aux.characters16(), aux.length()), &cbor);
1529
    executionContextAuxData = protocol::DictionaryValue::cast(
1530
        protocol::Value::parseBinary(cbor.data(), cbor.size()));
1531
  }
1532
  bool isLiveEdit = script->isLiveEdit();
1533
  bool hasSourceURLComment = script->hasSourceURLComment();
1534
  bool isModule = script->isModule();
1535 1536
  String16 scriptId = script->scriptId();
  String16 scriptURL = script->sourceURL();
1537 1538 1539 1540 1541
  String16 scriptLanguage = getScriptLanguage(*script);
  Maybe<int> codeOffset =
      script->getLanguage() == V8DebuggerScript::Language::JavaScript
          ? Maybe<int>()
          : script->codeOffset();
1542 1543
  std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
      getDebugSymbols(*script);
1544

1545
  m_scripts[scriptId] = std::move(script);
1546 1547 1548
  // Release the strong reference to get notified when debugger is the only
  // one that holds the script. Has to be done after script added to m_scripts.
  m_scripts[scriptId]->MakeWeak();
1549 1550 1551 1552

  ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
  DCHECK(scriptIterator != m_scripts.end());
  V8DebuggerScript* scriptRef = scriptIterator->second.get();
1553 1554 1555 1556 1557
  // V8 could create functions for parsed scripts before reporting and asks
  // inspector about blackboxed state, we should reset state each time when we
  // make any change that change isFunctionBlackboxed output - adding parsed
  // script is changing.
  scriptRef->resetBlackboxedStateCache();
1558 1559

  Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
1560
  Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
1561 1562
      std::move(executionContextAuxData));
  const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1563 1564
  const bool* hasSourceURLParam =
      hasSourceURLComment ? &hasSourceURLComment : nullptr;
1565
  const bool* isModuleParam = isModule ? &isModule : nullptr;
1566 1567 1568
  std::unique_ptr<V8StackTraceImpl> stack =
      V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
  std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
1569 1570 1571
      stack && !stack->isEmpty()
          ? stack->buildInspectorObjectImpl(m_debugger, 0)
          : nullptr;
1572 1573

  if (!success) {
1574
    m_frontend.scriptFailedToParse(
1575
        scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1576
        scriptRef->endLine(), scriptRef->endColumn(), contextId,
1577
        scriptRef->hash(), std::move(executionContextAuxDataParam),
1578
        std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
1579 1580
        scriptRef->length(), std::move(stackTrace), std::move(codeOffset),
        std::move(scriptLanguage));
1581
    return;
1582
  }
1583

1584 1585 1586 1587 1588
  if (scriptRef->isSourceLoadedLazily()) {
    m_frontend.scriptParsed(
        scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(),
        std::move(executionContextAuxDataParam), isLiveEditParam,
        std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0,
1589 1590
        std::move(stackTrace), std::move(codeOffset), std::move(scriptLanguage),
        std::move(debugSymbols));
1591 1592 1593 1594 1595 1596
  } else {
    m_frontend.scriptParsed(
        scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
        scriptRef->endLine(), scriptRef->endColumn(), contextId,
        scriptRef->hash(), std::move(executionContextAuxDataParam),
        isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
1597
        isModuleParam, scriptRef->length(), std::move(stackTrace),
1598 1599
        std::move(codeOffset), std::move(scriptLanguage),
        std::move(debugSymbols));
1600
  }
1601

1602
  std::vector<protocol::DictionaryValue*> potentialBreakpoints;
1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616
  if (!scriptURL.isEmpty()) {
    protocol::DictionaryValue* breakpointsByUrl =
        m_state->getObject(DebuggerAgentState::breakpointsByUrl);
    if (breakpointsByUrl) {
      potentialBreakpoints.push_back(breakpointsByUrl->getObject(scriptURL));
    }
    potentialBreakpoints.push_back(
        m_state->getObject(DebuggerAgentState::breakpointsByRegex));
  }
  protocol::DictionaryValue* breakpointsByScriptHash =
      m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
  if (breakpointsByScriptHash) {
    potentialBreakpoints.push_back(
        breakpointsByScriptHash->getObject(scriptRef->hash()));
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632
  }
  protocol::DictionaryValue* breakpointHints =
      m_state->getObject(DebuggerAgentState::breakpointHints);
  for (auto breakpoints : potentialBreakpoints) {
    if (!breakpoints) continue;
    for (size_t i = 0; i < breakpoints->size(); ++i) {
      auto breakpointWithCondition = breakpoints->at(i);
      String16 breakpointId = breakpointWithCondition.first;

      BreakpointType type;
      String16 selector;
      int lineNumber = 0;
      int columnNumber = 0;
      parseBreakpointId(breakpointId, &type, &selector, &lineNumber,
                        &columnNumber);

1633
      if (!matches(m_inspector, *scriptRef, type, selector)) continue;
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646
      String16 condition;
      breakpointWithCondition.second->asString(&condition);
      String16 hint;
      bool hasHint =
          breakpointHints && breakpointHints->getString(breakpointId, &hint);
      if (hasHint) {
        adjustBreakpointLocation(*scriptRef, hint, &lineNumber, &columnNumber);
      }
      std::unique_ptr<protocol::Debugger::Location> location =
          setBreakpointImpl(breakpointId, scriptId, condition, lineNumber,
                            columnNumber);
      if (location)
        m_frontend.breakpointResolved(breakpointId, std::move(location));
1647
    }
1648
  }
1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682
  setScriptInstrumentationBreakpointIfNeeded(scriptRef);
}

void V8DebuggerAgentImpl::setScriptInstrumentationBreakpointIfNeeded(
    V8DebuggerScript* scriptRef) {
  protocol::DictionaryValue* breakpoints =
      m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
  if (!breakpoints) return;
  bool isBlackboxed = isFunctionBlackboxed(
      scriptRef->scriptId(), v8::debug::Location(0, 0),
      v8::debug::Location(scriptRef->endLine(), scriptRef->endColumn()));
  if (isBlackboxed) return;

  String16 sourceMapURL = scriptRef->sourceMappingURL();
  String16 breakpointId = generateInstrumentationBreakpointId(
      InstrumentationEnum::BeforeScriptExecution);
  if (!breakpoints->get(breakpointId)) {
    if (sourceMapURL.isEmpty()) return;
    breakpointId = generateInstrumentationBreakpointId(
        InstrumentationEnum::BeforeScriptWithSourceMapExecution);
    if (!breakpoints->get(breakpointId)) return;
  }
  v8::debug::BreakpointId debuggerBreakpointId;
  if (!scriptRef->setBreakpointOnRun(&debuggerBreakpointId)) return;
  std::unique_ptr<protocol::DictionaryValue> data =
      protocol::DictionaryValue::create();
  data->setString("url", scriptRef->sourceURL());
  data->setString("scriptId", scriptRef->scriptId());
  if (!sourceMapURL.isEmpty()) data->setString("sourceMapURL", sourceMapURL);

  m_breakpointsOnScriptRun[debuggerBreakpointId] = std::move(data);
  m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
  m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
      debuggerBreakpointId);
1683 1684
}

1685 1686 1687
void V8DebuggerAgentImpl::didPause(
    int contextId, v8::Local<v8::Value> exception,
    const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
1688 1689
    v8::debug::ExceptionType exceptionType, bool isUncaught, bool isOOMBreak,
    bool isAssert) {
1690 1691
  v8::HandleScope handles(m_isolate);

1692 1693
  std::vector<BreakReason> hitReasons;

1694
  if (isOOMBreak) {
1695 1696
    hitReasons.push_back(
        std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
1697 1698 1699
  } else if (isAssert) {
    hitReasons.push_back(std::make_pair(
        protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
1700
  } else if (!exception.IsEmpty()) {
1701
    InjectedScript* injectedScript = nullptr;
1702
    m_session->findInjectedScript(contextId, injectedScript);
1703
    if (injectedScript) {
1704
      String16 breakReason =
1705
          exceptionType == v8::debug::kPromiseRejection
1706 1707
              ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
              : protocol::Debugger::Paused::ReasonEnum::Exception;
1708
      std::unique_ptr<protocol::Runtime::RemoteObject> obj;
1709 1710
      injectedScript->wrapObject(exception, kBacktraceObjectGroup,
                                 WrapMode::kNoPreview, &obj);
1711
      std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1712
      if (obj) {
1713 1714
        breakAuxData = obj->toValue();
        breakAuxData->setBoolean("uncaught", isUncaught);
1715
      } else {
1716
        breakAuxData = nullptr;
1717
      }
1718 1719
      hitReasons.push_back(
          std::make_pair(breakReason, std::move(breakAuxData)));
1720 1721 1722
    }
  }

1723
  auto hitBreakpointIds = std::make_unique<Array<String16>>();
1724

1725
  for (const auto& id : hitBreakpoints) {
1726 1727 1728 1729 1730 1731 1732 1733
    auto it = m_breakpointsOnScriptRun.find(id);
    if (it != m_breakpointsOnScriptRun.end()) {
      hitReasons.push_back(std::make_pair(
          protocol::Debugger::Paused::ReasonEnum::Instrumentation,
          std::move(it->second)));
      m_breakpointsOnScriptRun.erase(it);
      continue;
    }
1734 1735 1736
    auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
    if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
      continue;
1737
    }
1738
    const String16& breakpointId = breakpointIterator->second;
1739
    hitBreakpointIds->emplace_back(breakpointId);
1740 1741 1742 1743 1744
    BreakpointType type;
    parseBreakpointId(breakpointId, &type);
    if (type != BreakpointType::kDebugCommand) continue;
    hitReasons.push_back(std::make_pair(
        protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
1745 1746
  }

1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772
  for (size_t i = 0; i < m_breakReason.size(); ++i) {
    hitReasons.push_back(std::move(m_breakReason[i]));
  }
  clearBreakDetails();

  String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
  std::unique_ptr<protocol::DictionaryValue> breakAuxData;
  if (hitReasons.size() == 1) {
    breakReason = hitReasons[0].first;
    breakAuxData = std::move(hitReasons[0].second);
  } else if (hitReasons.size() > 1) {
    breakReason = protocol::Debugger::Paused::ReasonEnum::Ambiguous;
    std::unique_ptr<protocol::ListValue> reasons =
        protocol::ListValue::create();
    for (size_t i = 0; i < hitReasons.size(); ++i) {
      std::unique_ptr<protocol::DictionaryValue> reason =
          protocol::DictionaryValue::create();
      reason->setString("reason", hitReasons[i].first);
      if (hitReasons[i].second)
        reason->setObject("auxData", std::move(hitReasons[i].second));
      reasons->pushValue(std::move(reason));
    }
    breakAuxData = protocol::DictionaryValue::create();
    breakAuxData->setArray("reasons", std::move(reasons));
  }

1773 1774
  std::unique_ptr<Array<CallFrame>> protocolCallFrames;
  Response response = currentCallFrames(&protocolCallFrames);
1775
  if (!response.IsSuccess())
1776
    protocolCallFrames = std::make_unique<Array<CallFrame>>();
1777

1778 1779
  m_frontend.paused(std::move(protocolCallFrames), breakReason,
                    std::move(breakAuxData), std::move(hitBreakpointIds),
1780
                    currentAsyncStackTrace(), currentExternalStackTrace());
1781 1782 1783 1784 1785
}

void V8DebuggerAgentImpl::didContinue() {
  clearBreakDetails();
  m_frontend.resumed();
1786
  m_frontend.flush();
1787 1788 1789 1790 1791
}

void V8DebuggerAgentImpl::breakProgram(
    const String16& breakReason,
    std::unique_ptr<protocol::DictionaryValue> data) {
1792
  if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return;
1793 1794 1795
  std::vector<BreakReason> currentScheduledReason;
  currentScheduledReason.swap(m_breakReason);
  pushBreakDetails(breakReason, std::move(data));
1796 1797 1798 1799 1800 1801 1802 1803 1804

  int contextGroupId = m_session->contextGroupId();
  int sessionId = m_session->sessionId();
  V8InspectorImpl* inspector = m_inspector;
  m_debugger->breakProgram(contextGroupId);
  // Check that session and |this| are still around.
  if (!inspector->sessionById(contextGroupId, sessionId)) return;
  if (!enabled()) return;

1805 1806
  popBreakDetails();
  m_breakReason.swap(currentScheduledReason);
1807
  if (!m_breakReason.empty()) {
1808
    m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1809
  }
1810 1811
}

1812 1813 1814
void V8DebuggerAgentImpl::setBreakpointFor(v8::Local<v8::Function> function,
                                           v8::Local<v8::String> condition,
                                           BreakpointSource source) {
1815 1816 1817
  String16 breakpointId = generateBreakpointId(
      source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
                                             : BreakpointType::kMonitorCommand,
1818
      function);
1819 1820 1821 1822
  if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
      m_breakpointIdToDebuggerBreakpointIds.end()) {
    return;
  }
1823
  setBreakpointImpl(breakpointId, function, condition);
1824 1825
}

1826 1827
void V8DebuggerAgentImpl::removeBreakpointFor(v8::Local<v8::Function> function,
                                              BreakpointSource source) {
1828 1829 1830
  String16 breakpointId = generateBreakpointId(
      source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
                                             : BreakpointType::kMonitorCommand,
1831
      function);
1832 1833
  std::vector<V8DebuggerScript*> scripts;
  removeBreakpointImpl(breakpointId, scripts);
1834 1835 1836 1837 1838
}

void V8DebuggerAgentImpl::reset() {
  if (!enabled()) return;
  m_blackboxedPositions.clear();
1839 1840
  resetBlackboxedStateCache();
  m_scripts.clear();
1841 1842
  m_cachedScriptIds.clear();
  m_cachedScriptSize = 0;
1843 1844
}

1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855
void V8DebuggerAgentImpl::ScriptCollected(const V8DebuggerScript* script) {
  DCHECK_NE(m_scripts.find(script->scriptId()), m_scripts.end());
  m_cachedScriptIds.push_back(script->scriptId());
  // TODO(alph): Properly calculate size when sources are one-byte strings.
  m_cachedScriptSize += script->length() * sizeof(uint16_t);

  while (m_cachedScriptSize > m_maxScriptCacheSize) {
    const String16& scriptId = m_cachedScriptIds.front();
    size_t scriptSize = m_scripts[scriptId]->length() * sizeof(uint16_t);
    DCHECK_GE(m_cachedScriptSize, scriptSize);
    m_cachedScriptSize -= scriptSize;
1856
    m_scripts.erase(scriptId);
1857
    m_cachedScriptIds.pop_front();
1858 1859 1860
  }
}

1861
}  // namespace v8_inspector