v8-debugger-agent-impl.cc 67.6 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 "src/base/safe_conversions.h"
10
#include "src/debug/debug-interface.h"
11 12
#include "src/inspector/injected-script.h"
#include "src/inspector/inspected-context.h"
13
#include "src/inspector/protocol/Protocol.h"
14 15 16 17 18 19 20 21 22 23
#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"
24
#include "src/inspector/v8-value-utils.h"
25 26

#include "include/v8-inspector.h"
27 28 29 30 31 32 33

namespace v8_inspector {

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

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

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

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

55
}  // namespace DebuggerAgentState
56

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

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

65 66
namespace {

67 68 69 70 71 72 73
void TranslateLocation(protocol::Debugger::Location* location,
                       WasmTranslation* wasmTranslation) {
  String16 scriptId = location->getScriptId();
  int lineNumber = location->getLineNumber();
  int columnNumber = location->getColumnNumber(-1);
  if (wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
          &scriptId, &lineNumber, &columnNumber)) {
74 75 76 77 78 79
    location->setScriptId(std::move(scriptId));
    location->setLineNumber(lineNumber);
    location->setColumnNumber(columnNumber);
  }
}

80 81 82
enum class BreakpointType {
  kByUrl = 1,
  kByUrlRegex,
83
  kByScriptHash,
84 85
  kByScriptId,
  kDebugCommand,
86
  kMonitorCommand,
87 88
  kBreakpointAtEntry,
  kInstrumentationBreakpoint
89 90 91 92 93
};

String16 generateBreakpointId(BreakpointType type,
                              const String16& scriptSelector, int lineNumber,
                              int columnNumber) {
94
  String16Builder builder;
95 96 97
  builder.appendNumber(static_cast<int>(type));
  builder.append(':');
  builder.appendNumber(lineNumber);
98
  builder.append(':');
99
  builder.appendNumber(columnNumber);
100
  builder.append(':');
101
  builder.append(scriptSelector);
102
  return builder.toString();
103 104
}

105 106 107 108 109 110 111 112 113
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();
}

114 115 116 117 118 119 120 121 122
String16 generateInstrumentationBreakpointId(const String16& instrumentation) {
  String16Builder builder;
  builder.appendNumber(
      static_cast<int>(BreakpointType::kInstrumentationBreakpoint));
  builder.append(':');
  builder.append(instrumentation);
  return builder.toString();
}

123 124 125 126 127
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;
128 129 130

  int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
  if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
131
      rawType > static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
132 133 134 135
    return false;
  }
  if (type) *type = static_cast<BreakpointType>(rawType);
  if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
136
      rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
137 138 139
      rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry) ||
      rawType == static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
    // The script and source position are not encoded in this case.
140 141 142
    return true;
  }

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  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;
}

167 168
bool positionComparator(const std::pair<int, int>& a,
                        const std::pair<int, int>& b) {
169 170 171 172
  if (a.first != b.first) return a.first < b.first;
  return a.second < b.second;
}

173 174 175
String16 breakpointHint(const V8DebuggerScript& script, int lineNumber,
                        int columnNumber) {
  int offset = script.offset(lineNumber, columnNumber);
176 177
  if (offset == V8DebuggerScript::kNoOffset) return String16();
  String16 hint =
178
      script.source(offset, kBreakpointHintMaxLength).stripWhiteSpace();
179 180 181 182 183 184 185 186
  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;
}

187 188 189
void adjustBreakpointLocation(const V8DebuggerScript& script,
                              const String16& hint, int* lineNumber,
                              int* columnNumber) {
190 191
  if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
    return;
192
  if (hint.isEmpty()) return;
193
  intptr_t sourceOffset = script.offset(*lineNumber, *columnNumber);
194 195 196 197 198
  if (sourceOffset == V8DebuggerScript::kNoOffset) return;

  intptr_t searchRegionOffset = std::max(
      sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
  size_t offset = sourceOffset - searchRegionOffset;
199 200
  String16 searchArea = script.source(searchRegionOffset,
                                      offset + kBreakpointHintMaxSearchOffset);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

  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;
216 217
  v8::debug::Location hintPosition =
      script.location(static_cast<int>(bestMatch));
218
  if (hintPosition.IsEmpty()) return;
219 220
  *lineNumber = hintPosition.GetLineNumber();
  *columnNumber = hintPosition.GetColumnNumber();
221
}
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236

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();
}

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
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;
  }
  UNREACHABLE();
  return String16();
}

262
Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
263 264 265 266
                     InjectedScript* injectedScript,
                     std::unique_ptr<Array<Scope>>* scopes) {
  *scopes = Array<Scope>::create();
  if (!injectedScript) return Response::OK();
267 268 269 270
  if (iterator->Done()) return Response::OK();

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

271 272
  for (; !iterator->Done(); iterator->Advance()) {
    std::unique_ptr<RemoteObject> object;
273 274 275
    Response result =
        injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
                                   WrapMode::kNoPreview, &object);
276
    if (!result.isSuccess()) return result;
277

278 279 280 281
    auto scope = Scope::create()
                     .setType(scopeType(iterator->GetType()))
                     .setObject(std::move(object))
                     .build();
282

283 284
    String16 name = toProtocolStringWithTypeCheck(
        isolate, iterator->GetFunctionDebugName());
285 286 287
    if (!name.isEmpty()) scope->setName(name);

    if (iterator->HasLocationInfo()) {
288 289 290 291 292 293
      v8::debug::Location start = iterator->GetStartLocation();
      scope->setStartLocation(protocol::Debugger::Location::create()
                                  .setScriptId(scriptId)
                                  .setLineNumber(start.GetLineNumber())
                                  .setColumnNumber(start.GetColumnNumber())
                                  .build());
294

295 296 297 298 299 300 301 302 303 304 305 306
      v8::debug::Location end = iterator->GetEndLocation();
      scope->setEndLocation(protocol::Debugger::Location::create()
                                .setScriptId(scriptId)
                                .setLineNumber(end.GetLineNumber())
                                .setColumnNumber(end.GetColumnNumber())
                                .build());
    }
    (*scopes)->addItem(std::move(scope));
  }
  return Response::OK();
}

307 308 309 310 311 312 313 314 315 316
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;
}
317 318
}  // namespace

319 320 321 322 323 324 325 326 327
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),
328
      m_isolate(m_inspector->isolate()) {}
329

330
V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default;
331

332
void V8DebuggerAgentImpl::enableImpl() {
333 334 335 336
  m_enabled = true;
  m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
  m_debugger->enable();

337 338 339 340 341
  std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts =
      m_debugger->getCompiledScripts(m_session->contextGroupId(), this);
  for (auto& script : compiledScripts) {
    didParseSource(std::move(script), true);
  }
342

343 344
  m_breakpointsActive = true;
  m_debugger->setBreakpointsActive(true);
345 346 347

  if (isPaused()) {
    didPause(0, v8::Local<v8::Value>(), std::vector<v8::debug::BreakpointId>(),
348
             v8::debug::kException, false, false, false);
349
  }
350 351
}

352 353 354 355
Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
                                     String16* outDebuggerId) {
  m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
      maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
356 357
  *outDebuggerId = debuggerIdToString(
      m_debugger->debuggerIdFor(m_session->contextGroupId()));
358
  if (enabled()) return Response::OK();
359

360 361
  if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
    return Response::Error("Script execution is prohibited");
362

363 364
  enableImpl();
  return Response::OK();
365 366
}

367 368
Response V8DebuggerAgentImpl::disable() {
  if (!enabled()) return Response::OK();
369

370 371
  m_state->remove(DebuggerAgentState::breakpointsByRegex);
  m_state->remove(DebuggerAgentState::breakpointsByUrl);
372
  m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
373
  m_state->remove(DebuggerAgentState::breakpointHints);
374
  m_state->remove(DebuggerAgentState::instrumentationBreakpoints);
375

376
  m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
377
                      v8::debug::NoBreakOnException);
378 379
  m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);

380 381 382 383
  if (m_breakpointsActive) {
    m_debugger->setBreakpointsActive(false);
    m_breakpointsActive = false;
  }
384
  m_blackboxedPositions.clear();
385 386 387
  m_blackboxPattern.reset();
  resetBlackboxedStateCache();
  m_scripts.clear();
388 389
  m_cachedScriptIds.clear();
  m_cachedScriptSize = 0;
390 391
  for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
    v8::debug::RemoveBreakpoint(m_isolate, it.first);
392
  }
393
  m_breakpointIdToDebuggerBreakpointIds.clear();
394
  m_debuggerBreakpointIdToBreakpointId.clear();
395 396 397
  m_debugger->setAsyncCallStackDepth(this, 0);
  clearBreakDetails();
  m_skipAllPauses = false;
398
  m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
399 400 401
  m_state->remove(DebuggerAgentState::blackboxPattern);
  m_enabled = false;
  m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
402
  m_debugger->disable();
403
  return Response::OK();
404 405 406 407 408 409 410 411 412
}

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

413
  enableImpl();
414

415
  int pauseState = v8::debug::NoBreakOnException;
416
  m_state->getInteger(DebuggerAgentState::pauseOnExceptionsState, &pauseState);
417
  setPauseOnExceptionsImpl(pauseState);
418 419 420 421 422 423 424 425 426 427 428 429

  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)) {
430
    setBlackboxPattern(blackboxPattern);
431 432 433
  }
}

434 435
Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
436 437 438
  if (m_breakpointsActive == active) return Response::OK();
  m_breakpointsActive = active;
  m_debugger->setBreakpointsActive(active);
439 440
  if (!active && !m_breakReason.empty()) {
    clearBreakDetails();
441
    m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
442
  }
443
  return Response::OK();
444 445
}

446
Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
447
  m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
448
  m_skipAllPauses = skip;
449
  return Response::OK();
450 451
}

452
static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
453 454 455 456 457 458 459 460 461 462 463 464 465
                    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;
    }
    default:
      UNREACHABLE();
      return false;
466 467 468
  }
}

469 470
Response V8DebuggerAgentImpl::setBreakpointByUrl(
    int lineNumber, Maybe<String16> optionalURL,
471 472 473
    Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash,
    Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition,
    String16* outBreakpointId,
474 475 476
    std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
  *locations = Array<protocol::Debugger::Location>::create();

477 478 479 480 481 482 483
  int specified = (optionalURL.isJust() ? 1 : 0) +
                  (optionalURLRegex.isJust() ? 1 : 0) +
                  (optionalScriptHash.isJust() ? 1 : 0);
  if (specified != 1) {
    return Response::Error(
        "Either url or urlRegex or scriptHash must be specified.");
  }
484 485 486
  int columnNumber = 0;
  if (optionalColumnNumber.isJust()) {
    columnNumber = optionalColumnNumber.fromJust();
487
    if (columnNumber < 0) return Response::Error("Incorrect column number");
488 489
  }

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
  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);
506
  protocol::DictionaryValue* breakpoints;
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
  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();
525 526
  }
  if (breakpoints->get(breakpointId)) {
527
    return Response::Error("Breakpoint at specified location already exists.");
528
  }
529

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

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

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
    const String16& functionObjectId, Maybe<String16> optionalCondition,
    String16* outBreakpointId) {
  InjectedScript::ObjectScope scope(m_session, functionObjectId);
  Response response = scope.initialize();
  if (!response.isSuccess()) return response;
  if (!scope.object()->IsFunction()) {
    return Response::Error("Could not find function with given id");
  }
  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()) {
    return Response::Error("Breakpoint at specified location already exists.");
  }
  v8::Local<v8::String> condition =
      toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
  setBreakpointImpl(breakpointId, function, condition);
  *outBreakpointId = breakpointId;
  return Response::OK();
}

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

612 613
Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
614 615 616 617 618 619
  BreakpointType type;
  String16 selector;
  if (!parseBreakpointId(breakpointId, &type, &selector)) {
    return Response::OK();
  }
  protocol::DictionaryValue* breakpoints = nullptr;
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
  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;
638 639 640 641
    case BreakpointType::kInstrumentationBreakpoint:
      breakpoints =
          m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
      break;
642 643
    default:
      break;
644 645 646 647 648
  }
  if (breakpoints) breakpoints->remove(breakpointId);
  protocol::DictionaryValue* breakpointHints =
      m_state->getObject(DebuggerAgentState::breakpointHints);
  if (breakpointHints) breakpointHints->remove(breakpointId);
649 650
  removeBreakpointImpl(breakpointId);
  return Response::OK();
651 652
}

653
void V8DebuggerAgentImpl::removeBreakpointImpl(const String16& breakpointId) {
654 655 656 657 658
  DCHECK(enabled());
  BreakpointIdToDebuggerBreakpointIdsMap::iterator
      debuggerBreakpointIdsIterator =
          m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
  if (debuggerBreakpointIdsIterator ==
659
      m_breakpointIdToDebuggerBreakpointIds.end()) {
660
    return;
661 662 663
  }
  for (const auto& id : debuggerBreakpointIdsIterator->second) {
    v8::debug::RemoveBreakpoint(m_isolate, id);
664
    m_debuggerBreakpointIdToBreakpointId.erase(id);
665 666 667 668
  }
  m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
}

669 670
Response V8DebuggerAgentImpl::getPossibleBreakpoints(
    std::unique_ptr<protocol::Debugger::Location> start,
671
    Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
672 673
    std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
        locations) {
674 675 676 677 678 679
  String16 scriptId = start->getScriptId();

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

680 681 682
  v8::debug::Location v8Start(start->getLineNumber(),
                              start->getColumnNumber(0));
  v8::debug::Location v8End;
683 684 685 686 687 688 689 690
  if (end.isJust()) {
    if (end.fromJust()->getScriptId() != scriptId)
      return Response::Error("Locations should contain the same scriptId");
    int line = end.fromJust()->getLineNumber();
    int column = end.fromJust()->getColumnNumber(0);
    if (line < 0 || column < 0)
      return Response::Error(
          "end.lineNumber and end.columnNumber should be >= 0");
691
    v8End = v8::debug::Location(line, column);
692 693 694
  }
  auto it = m_scripts.find(scriptId);
  if (it == m_scripts.end()) return Response::Error("Script not found");
695
  std::vector<v8::debug::BreakLocation> v8Locations;
696 697
  {
    v8::HandleScope handleScope(m_isolate);
698 699 700 701 702 703
    int contextId = it->second->executionContextId();
    InspectedContext* inspected = m_inspector->getContext(contextId);
    if (!inspected) {
      return Response::Error("Cannot retrive script context");
    }
    v8::Context::Scope contextScope(inspected->context());
704 705
    v8::MicrotasksScope microtasks(m_isolate,
                                   v8::MicrotasksScope::kDoNotRunMicrotasks);
706 707 708
    v8::TryCatch tryCatch(m_isolate);
    it->second->getPossibleBreakpoints(
        v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations);
709
  }
710

711
  *locations = protocol::Array<protocol::Debugger::BreakLocation>::create();
712
  for (size_t i = 0; i < v8Locations.size(); ++i) {
713 714 715 716 717 718 719 720 721 722
    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()));
    }
    (*locations)->addItem(std::move(breakLocation));
723 724 725 726
  }
  return Response::OK();
}

727
Response V8DebuggerAgentImpl::continueToLocation(
728 729
    std::unique_ptr<protocol::Debugger::Location> location,
    Maybe<String16> targetCallFrames) {
730
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
731
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
732 733 734 735 736 737 738 739 740
  ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
  if (it == m_scripts.end()) {
    return Response::Error("Cannot continue to specified location");
  }
  V8DebuggerScript* script = it->second.get();
  int contextId = script->executionContextId();
  InspectedContext* inspected = m_inspector->getContext(contextId);
  if (!inspected)
    return Response::Error("Cannot continue to specified location");
741
  v8::HandleScope handleScope(m_isolate);
742
  v8::Context::Scope contextScope(inspected->context());
743
  return m_debugger->continueToLocation(
744
      m_session->contextGroupId(), script, std::move(location),
745 746
      targetCallFrames.fromMaybe(
          protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
747 748
}

749 750 751 752 753
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);
754 755 756 757 758 759 760
  std::pair<int64_t, int64_t> debuggerId;
  if (inStackTraceId->hasDebuggerId()) {
    debuggerId =
        m_debugger->debuggerIdFor(inStackTraceId->getDebuggerId(String16()));
  } else {
    debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
  }
761 762 763 764 765 766 767 768 769
  V8StackTraceId v8StackTraceId(id, debuggerId);
  if (!isOk || v8StackTraceId.IsInvalid()) {
    return Response::Error("Invalid stack trace id");
  }
  auto stack =
      m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
  if (!stack) {
    return Response::Error("Stack trace with given id is not found");
  }
770 771
  *outStackTrace = stack->buildInspectorObject(
      m_debugger, m_debugger->maxAsyncCallChainDepth());
772 773 774
  return Response::OK();
}

775 776 777 778
bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
                                               const v8::debug::Location& start,
                                               const v8::debug::Location& end) {
  ScriptsMap::iterator it = m_scripts.find(scriptId);
779 780 781 782 783 784 785 786 787 788
  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;
  }
789
  auto itBlackboxedPositions = m_blackboxedPositions.find(scriptId);
790 791 792 793
  if (itBlackboxedPositions == m_blackboxedPositions.end()) return false;

  const std::vector<std::pair<int, int>>& ranges =
      itBlackboxedPositions->second;
794
  auto itStartRange = std::lower_bound(
795
      ranges.begin(), ranges.end(),
796 797 798 799 800 801
      std::make_pair(start.GetLineNumber(), start.GetColumnNumber()),
      positionComparator);
  auto itEndRange = std::lower_bound(
      itStartRange, ranges.end(),
      std::make_pair(end.GetLineNumber(), end.GetColumnNumber()),
      positionComparator);
802 803 804
  // Ranges array contains positions in script where blackbox state is changed.
  // [(0,0) ... ranges[0]) isn't blackboxed, [ranges[0] ... ranges[1]) is
  // blackboxed...
805 806
  return itStartRange == itEndRange &&
         std::distance(ranges.begin(), itStartRange) % 2;
807 808
}

809 810 811 812
bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
  return enabled() && (isOOMBreak || !m_skipAllPauses);
}

813
std::unique_ptr<protocol::Debugger::Location>
814 815 816 817
V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
                                       const String16& scriptId,
                                       const String16& condition,
                                       int lineNumber, int columnNumber) {
818
  v8::HandleScope handles(m_isolate);
819
  DCHECK(enabled());
820 821

  ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
822
  if (scriptIterator == m_scripts.end()) return nullptr;
823
  V8DebuggerScript* script = scriptIterator->second.get();
824
  if (lineNumber < script->startLine() || script->endLine() < lineNumber) {
825
    return nullptr;
826
  }
827

828
  v8::debug::BreakpointId debuggerBreakpointId;
829
  v8::debug::Location location(lineNumber, columnNumber);
830 831 832 833 834 835
  int contextId = script->executionContextId();
  InspectedContext* inspected = m_inspector->getContext(contextId);
  if (!inspected) return nullptr;

  {
    v8::Context::Scope contextScope(inspected->context());
836
    if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
837 838 839
      return nullptr;
    }
  }
840

841
  m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
842 843
  m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
      debuggerBreakpointId);
844 845 846 847 848 849

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

852 853 854 855 856 857 858 859 860 861 862 863 864
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);
}

865 866 867
Response V8DebuggerAgentImpl::searchInContent(
    const String16& scriptId, const String16& query,
    Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
868 869 870
    std::unique_ptr<Array<protocol::Debugger::SearchMatch>>* results) {
  v8::HandleScope handles(m_isolate);
  ScriptsMap::iterator it = m_scripts.find(scriptId);
871 872
  if (it == m_scripts.end())
    return Response::Error("No script for id: " + scriptId);
873 874

  std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
875
      searchInTextByLinesImpl(m_session, it->second->source(0), query,
876
                              optionalCaseSensitive.fromMaybe(false),
877 878 879 880
                              optionalIsRegex.fromMaybe(false));
  *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
  for (size_t i = 0; i < matches.size(); ++i)
    (*results)->addItem(std::move(matches[i]));
881
  return Response::OK();
882 883
}

884 885
Response V8DebuggerAgentImpl::setScriptSource(
    const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
886
    Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
887 888
    Maybe<bool>* stackChanged,
    Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
889
    Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
890
    Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
891
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
892

893 894 895 896
  ScriptsMap::iterator it = m_scripts.find(scriptId);
  if (it == m_scripts.end()) {
    return Response::Error("No script with given id found");
  }
897
  int contextId = it->second->executionContextId();
898
  InspectedContext* inspected = m_inspector->getContext(contextId);
899 900 901 902 903 904
  if (!inspected) {
    return Response::InternalError();
  }
  v8::HandleScope handleScope(m_isolate);
  v8::Local<v8::Context> context = inspected->context();
  v8::Context::Scope contextScope(context);
905 906 907 908 909 910 911

  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())
912
            .setText(toProtocolString(m_isolate, result.message))
913 914 915 916 917 918
            .setLineNumber(result.line_number != -1 ? result.line_number - 1
                                                    : 0)
            .setColumnNumber(result.column_number != -1 ? result.column_number
                                                        : 0)
            .build();
    return Response::OK();
919
  } else {
920
    *stackChanged = result.stack_changed;
921
  }
922
  std::unique_ptr<Array<CallFrame>> callFrames;
923
  Response response = currentCallFrames(&callFrames);
924
  if (!response.isSuccess()) return response;
925 926
  *newCallFrames = std::move(callFrames);
  *asyncStackTrace = currentAsyncStackTrace();
927
  *asyncStackTraceId = currentExternalStackTrace();
928
  return Response::OK();
929 930
}

931 932
Response V8DebuggerAgentImpl::restartFrame(
    const String16& callFrameId,
933
    std::unique_ptr<Array<CallFrame>>* newCallFrames,
934 935
    Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
    Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
936
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
937
  InjectedScript::CallFrameScope scope(m_session, callFrameId);
938
  Response response = scope.initialize();
939
  if (!response.isSuccess()) return response;
940 941 942
  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
  if (it->Done()) {
943
    return Response::Error("Could not find call frame with given id");
944 945
  }
  if (!it->Restart()) {
946
    return Response::InternalError();
947
  }
948 949
  response = currentCallFrames(newCallFrames);
  if (!response.isSuccess()) return response;
950
  *asyncStackTrace = currentAsyncStackTrace();
951
  *asyncStackTraceId = currentExternalStackTrace();
952
  return Response::OK();
953 954
}

955 956 957
Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
                                              String16* scriptSource) {
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
958
  ScriptsMap::iterator it = m_scripts.find(scriptId);
959 960
  if (it == m_scripts.end())
    return Response::Error("No script for id: " + scriptId);
961
  *scriptSource = it->second->source(0);
962
  return Response::OK();
963 964
}

965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
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);
}

981 982 983
void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
    const String16& breakReason,
    std::unique_ptr<protocol::DictionaryValue> data) {
984
  if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
985
  if (m_breakReason.empty()) {
986
    m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
987
  }
988
  pushBreakDetails(breakReason, std::move(data));
989 990 991
}

void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
992
  if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
993
  if (m_breakReason.size() == 1) {
994
    m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
995 996
  }
  popBreakDetails();
997 998
}

999 1000
Response V8DebuggerAgentImpl::pause() {
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
1001
  if (isPaused()) return Response::OK();
1002 1003 1004 1005 1006 1007 1008
  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);
1009
  }
1010
  return Response::OK();
1011 1012
}

1013
Response V8DebuggerAgentImpl::resume() {
1014
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1015
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1016
  m_debugger->continueProgram(m_session->contextGroupId());
1017
  return Response::OK();
1018 1019
}

1020
Response V8DebuggerAgentImpl::stepOver() {
1021
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1022
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1023
  m_debugger->stepOverStatement(m_session->contextGroupId());
1024
  return Response::OK();
1025 1026
}

1027
Response V8DebuggerAgentImpl::stepInto(Maybe<bool> inBreakOnAsyncCall) {
1028
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1029
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1030 1031
  m_debugger->stepIntoStatement(m_session->contextGroupId(),
                                inBreakOnAsyncCall.fromMaybe(false));
1032
  return Response::OK();
1033 1034
}

1035
Response V8DebuggerAgentImpl::stepOut() {
1036
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1037
  m_session->releaseObjectGroup(kBacktraceObjectGroup);
1038
  m_debugger->stepOutOfFunction(m_session->contextGroupId());
1039
  return Response::OK();
1040 1041
}

1042 1043
Response V8DebuggerAgentImpl::pauseOnAsyncCall(
    std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
1044
  bool isOk = false;
1045
  int64_t stackTraceId = inParentStackTraceId->getId().toInteger64(&isOk);
1046
  if (!isOk) {
1047
    return Response::Error("Invalid stack trace id");
1048
  }
1049
  m_debugger->pauseOnAsyncCall(m_session->contextGroupId(), stackTraceId,
1050
                               inParentStackTraceId->getDebuggerId(String16()));
1051 1052 1053
  return Response::OK();
}

1054 1055 1056
Response V8DebuggerAgentImpl::setPauseOnExceptions(
    const String16& stringPauseState) {
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
1057
  v8::debug::ExceptionBreakState pauseState;
1058
  if (stringPauseState == "none") {
1059
    pauseState = v8::debug::NoBreakOnException;
1060
  } else if (stringPauseState == "all") {
1061
    pauseState = v8::debug::BreakOnAnyException;
1062
  } else if (stringPauseState == "uncaught") {
1063
    pauseState = v8::debug::BreakOnUncaughtException;
1064
  } else {
1065 1066
    return Response::Error("Unknown pause on exceptions mode: " +
                           stringPauseState);
1067
  }
1068
  setPauseOnExceptionsImpl(pauseState);
1069
  return Response::OK();
1070 1071
}

1072
void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
1073 1074
  // TODO(dgozman): this changes the global state and forces all context groups
  // to pause. We should make this flag be per-context-group.
1075
  m_debugger->setPauseOnExceptionsState(
1076
      static_cast<v8::debug::ExceptionBreakState>(pauseState));
1077
  m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
1078 1079
}

1080 1081 1082 1083
Response V8DebuggerAgentImpl::evaluateOnCallFrame(
    const String16& callFrameId, const String16& expression,
    Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
    Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
1084 1085
    Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
    std::unique_ptr<RemoteObject>* result,
1086
    Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
1087
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1088
  InjectedScript::CallFrameScope scope(m_session, callFrameId);
1089
  Response response = scope.initialize();
1090
  if (!response.isSuccess()) return response;
1091
  if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
1092 1093
  if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();

1094 1095 1096 1097 1098
  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
  if (it->Done()) {
    return Response::Error("Could not find call frame with given id");
  }
1099 1100 1101

  v8::MaybeLocal<v8::Value> maybeResultValue;
  {
1102
    V8InspectorImpl::EvaluateScope evaluateScope(scope);
1103 1104 1105 1106 1107 1108 1109
    if (timeout.isJust()) {
      response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
      if (!response.isSuccess()) return response;
    }
    maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
                                    throwOnSideEffect.fromMaybe(false));
  }
1110 1111
  // Re-initialize after running client's code, as it could have destroyed
  // context or session.
1112
  response = scope.initialize();
1113
  if (!response.isSuccess()) return response;
1114 1115 1116
  WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
                                                   : WrapMode::kNoPreview;
  if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
1117
  return scope.injectedScript()->wrapEvaluateResult(
1118 1119
      maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), mode,
      result, exceptionDetails);
1120 1121
}

1122 1123
Response V8DebuggerAgentImpl::setVariableValue(
    int scopeNumber, const String16& variableName,
1124 1125
    std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
    const String16& callFrameId) {
1126
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
1127
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1128
  InjectedScript::CallFrameScope scope(m_session, callFrameId);
1129
  Response response = scope.initialize();
1130
  if (!response.isSuccess()) return response;
1131
  v8::Local<v8::Value> newValue;
1132 1133
  response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
                                                         &newValue);
1134
  if (!response.isSuccess()) return response;
1135

1136 1137 1138
  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
  if (it->Done()) {
1139
    return Response::Error("Could not find call frame with given id");
1140 1141 1142 1143 1144 1145 1146 1147 1148
  }
  auto scopeIterator = it->GetScopeIterator();
  while (!scopeIterator->Done() && scopeNumber > 0) {
    --scopeNumber;
    scopeIterator->Advance();
  }
  if (scopeNumber != 0) {
    return Response::Error("Could not find scope with given number");
  }
1149

1150 1151 1152
  if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
                                       newValue) ||
      scope.tryCatch().HasCaught()) {
1153
    return Response::InternalError();
1154
  }
1155
  return Response::OK();
1156 1157
}

1158 1159 1160 1161
Response V8DebuggerAgentImpl::setReturnValue(
    std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
1162
  v8::HandleScope handleScope(m_isolate);
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
  auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
  if (iterator->Done()) {
    return Response::Error("Could not find top call frame");
  }
  if (iterator->GetReturnValue().IsEmpty()) {
    return Response::Error(
        "Could not update return value at non-return position");
  }
  InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
  Response response = scope.initialize();
  if (!response.isSuccess()) return response;
  v8::Local<v8::Value> newValue;
  response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
                                                         &newValue);
  if (!response.isSuccess()) return response;
  v8::debug::SetReturnValue(m_isolate, newValue);
  return Response::OK();
}

1182
Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
1183 1184 1185
  if (!enabled() && !m_session->runtimeAgent()->enabled()) {
    return Response::Error(kDebuggerNotEnabled);
  }
1186 1187
  m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
  m_debugger->setAsyncCallStackDepth(this, depth);
1188
  return Response::OK();
1189 1190
}

1191
Response V8DebuggerAgentImpl::setBlackboxPatterns(
1192 1193 1194
    std::unique_ptr<protocol::Array<String16>> patterns) {
  if (!patterns->length()) {
    m_blackboxPattern = nullptr;
1195
    resetBlackboxedStateCache();
1196
    m_state->remove(DebuggerAgentState::blackboxPattern);
1197
    return Response::OK();
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
  }

  String16Builder patternBuilder;
  patternBuilder.append('(');
  for (size_t i = 0; i < patterns->length() - 1; ++i) {
    patternBuilder.append(patterns->get(i));
    patternBuilder.append("|");
  }
  patternBuilder.append(patterns->get(patterns->length() - 1));
  patternBuilder.append(')');
  String16 pattern = patternBuilder.toString();
1209 1210
  Response response = setBlackboxPattern(pattern);
  if (!response.isSuccess()) return response;
1211
  resetBlackboxedStateCache();
1212
  m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
1213
  return Response::OK();
1214 1215
}

1216
Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
1217 1218
  std::unique_ptr<V8Regex> regex(new V8Regex(
      m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
1219 1220
  if (!regex->isValid())
    return Response::Error("Pattern parser error: " + regex->errorMessage());
1221
  m_blackboxPattern = std::move(regex);
1222
  return Response::OK();
1223 1224
}

1225 1226 1227 1228 1229 1230
void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
  for (const auto& it : m_scripts) {
    it.second->resetBlackboxedStateCache();
  }
}

1231 1232
Response V8DebuggerAgentImpl::setBlackboxedRanges(
    const String16& scriptId,
1233 1234
    std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
        inPositions) {
1235 1236
  auto it = m_scripts.find(scriptId);
  if (it == m_scripts.end())
1237
    return Response::Error("No script with passed id.");
1238 1239 1240

  if (!inPositions->length()) {
    m_blackboxedPositions.erase(scriptId);
1241
    it->second->resetBlackboxedStateCache();
1242
    return Response::OK();
1243 1244 1245 1246 1247 1248
  }

  std::vector<std::pair<int, int>> positions;
  positions.reserve(inPositions->length());
  for (size_t i = 0; i < inPositions->length(); ++i) {
    protocol::Debugger::ScriptPosition* position = inPositions->get(i);
1249 1250 1251 1252
    if (position->getLineNumber() < 0)
      return Response::Error("Position missing 'line' or 'line' < 0.");
    if (position->getColumnNumber() < 0)
      return Response::Error("Position missing 'column' or 'column' < 0.");
1253 1254 1255 1256 1257 1258 1259 1260 1261
    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;
1262 1263
    return Response::Error(
        "Input positions array is not sorted or contains duplicate values.");
1264 1265 1266
  }

  m_blackboxedPositions[scriptId] = positions;
1267
  it->second->resetBlackboxedStateCache();
1268
  return Response::OK();
1269 1270
}

1271 1272
Response V8DebuggerAgentImpl::currentCallFrames(
    std::unique_ptr<Array<CallFrame>>* result) {
1273
  if (!isPaused()) {
1274 1275 1276
    *result = Array<CallFrame>::create();
    return Response::OK();
  }
1277
  v8::HandleScope handles(m_isolate);
1278 1279 1280 1281 1282
  *result = Array<CallFrame>::create();
  auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
  int frameOrdinal = 0;
  for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
    int contextId = iterator->GetContextId();
1283 1284
    InjectedScript* injectedScript = nullptr;
    if (contextId) m_session->findInjectedScript(contextId, injectedScript);
1285
    String16 callFrameId =
1286 1287 1288 1289 1290 1291
        RemoteCallFrameId::serialize(contextId, frameOrdinal);

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

    std::unique_ptr<Array<Scope>> scopes;
    auto scopeIterator = iterator->GetScopeIterator();
1292 1293
    Response res =
        buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
1294
    if (!res.isSuccess()) return res;
1295

1296
    std::unique_ptr<RemoteObject> protocolReceiver;
1297
    if (injectedScript) {
1298 1299
      v8::Local<v8::Value> receiver;
      if (iterator->GetReceiver().ToLocal(&receiver)) {
1300 1301 1302
        res =
            injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
                                       WrapMode::kNoPreview, &protocolReceiver);
1303 1304 1305 1306 1307 1308 1309
        if (!res.isSuccess()) return res;
      }
    }
    if (!protocolReceiver) {
      protocolReceiver = RemoteObject::create()
                             .setType(RemoteObject::TypeEnum::Undefined)
                             .build();
1310 1311
    }

1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328
    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();
    TranslateLocation(location.get(), m_debugger->wasmTranslation());
    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();
    }

1329 1330 1331 1332 1333 1334 1335 1336 1337
    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();
1338 1339 1340 1341 1342 1343 1344 1345 1346

    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());
1347 1348
    }

1349 1350 1351 1352
    v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
    if (!returnValue.IsEmpty() && injectedScript) {
      std::unique_ptr<RemoteObject> value;
      res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
1353
                                       WrapMode::kNoPreview, &value);
1354 1355
      if (!res.isSuccess()) return res;
      frame->setReturnValue(std::move(value));
1356
    }
1357
    (*result)->addItem(std::move(frame));
1358
  }
1359
  return Response::OK();
1360 1361
}

1362 1363 1364 1365 1366 1367
std::unique_ptr<protocol::Runtime::StackTrace>
V8DebuggerAgentImpl::currentAsyncStackTrace() {
  std::shared_ptr<AsyncStackTrace> asyncParent =
      m_debugger->currentAsyncParent();
  if (!asyncParent) return nullptr;
  return asyncParent->buildInspectorObject(
1368
      m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
1369 1370
}

1371 1372 1373 1374 1375 1376 1377 1378 1379 1380
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))
      .setDebuggerId(debuggerIdToString(externalParent.debugger_id))
      .build();
}

1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
std::unique_ptr<protocol::Runtime::StackTraceId>
V8DebuggerAgentImpl::currentScheduledAsyncCall() {
  v8_inspector::V8StackTraceId scheduledAsyncCall =
      m_debugger->scheduledAsyncCall();
  if (scheduledAsyncCall.IsInvalid()) return nullptr;
  std::unique_ptr<protocol::Runtime::StackTraceId> asyncCallStackTrace =
      protocol::Runtime::StackTraceId::create()
          .setId(stackTraceIdToString(scheduledAsyncCall.id))
          .build();
  // TODO(kozyatinskiy): extract this check to IsLocal function.
  if (scheduledAsyncCall.debugger_id.first ||
      scheduledAsyncCall.debugger_id.second) {
    asyncCallStackTrace->setDebuggerId(
        debuggerIdToString(scheduledAsyncCall.debugger_id));
  }
  return asyncCallStackTrace;
}

1399
bool V8DebuggerAgentImpl::isPaused() const {
1400
  return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
1401
}
1402

1403 1404 1405
void V8DebuggerAgentImpl::didParseSource(
    std::unique_ptr<V8DebuggerScript> script, bool success) {
  v8::HandleScope handles(m_isolate);
1406 1407 1408 1409 1410 1411
  if (!success) {
    DCHECK(!script->isSourceLoadedLazily());
    String16 scriptSource = script->source(0);
    script->setSourceURL(findSourceURL(scriptSource, false));
    script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
  }
1412

1413 1414 1415 1416
  int contextId = script->executionContextId();
  int contextGroupId = m_inspector->contextGroupId(contextId);
  InspectedContext* inspected =
      m_inspector->getContext(contextGroupId, contextId);
1417
  std::unique_ptr<protocol::DictionaryValue> executionContextAuxData;
1418 1419 1420
  if (inspected) {
    // Script reused between different groups/sessions can have a stale
    // execution context id.
1421
    executionContextAuxData = protocol::DictionaryValue::cast(
1422 1423
        protocol::StringUtil::parseJSON(inspected->auxData()));
  }
1424
  bool isLiveEdit = script->isLiveEdit();
1425
  bool hasSourceURLComment = script->hasSourceURLComment();
1426
  bool isModule = script->isModule();
1427 1428 1429
  String16 scriptId = script->scriptId();
  String16 scriptURL = script->sourceURL();

1430
  m_scripts[scriptId] = std::move(script);
1431 1432 1433
  // 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();
1434 1435 1436 1437

  ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
  DCHECK(scriptIterator != m_scripts.end());
  V8DebuggerScript* scriptRef = scriptIterator->second.get();
1438 1439 1440 1441 1442
  // 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();
1443 1444

  Maybe<String16> sourceMapURLParam = scriptRef->sourceMappingURL();
1445
  Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
1446 1447
      std::move(executionContextAuxData));
  const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
1448 1449
  const bool* hasSourceURLParam =
      hasSourceURLComment ? &hasSourceURLComment : nullptr;
1450
  const bool* isModuleParam = isModule ? &isModule : nullptr;
1451 1452 1453
  std::unique_ptr<V8StackTraceImpl> stack =
      V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
  std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
1454 1455 1456
      stack && !stack->isEmpty()
          ? stack->buildInspectorObjectImpl(m_debugger, 0)
          : nullptr;
1457 1458

  if (!success) {
1459
    m_frontend.scriptFailedToParse(
1460
        scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
1461
        scriptRef->endLine(), scriptRef->endColumn(), contextId,
1462
        scriptRef->hash(), std::move(executionContextAuxDataParam),
1463
        std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
1464
        scriptRef->length(), std::move(stackTrace));
1465
    return;
1466
  }
1467

1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
  // TODO(herhut, dgozman): Report correct length for WASM if needed for
  // coverage. Or do not send the length at all and change coverage instead.
  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,
        std::move(stackTrace));
  } 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,
        isModuleParam, scriptRef->length(), std::move(stackTrace));
1483
  }
1484

1485
  std::vector<protocol::DictionaryValue*> potentialBreakpoints;
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499
  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()));
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515
  }
  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);

1516
      if (!matches(m_inspector, *scriptRef, type, selector)) continue;
1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
      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));
1530
    }
1531
  }
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
  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);
1566 1567
}

1568 1569 1570
void V8DebuggerAgentImpl::didPause(
    int contextId, v8::Local<v8::Value> exception,
    const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
1571 1572
    v8::debug::ExceptionType exceptionType, bool isUncaught, bool isOOMBreak,
    bool isAssert) {
1573 1574
  v8::HandleScope handles(m_isolate);

1575 1576
  std::vector<BreakReason> hitReasons;

1577
  if (isOOMBreak) {
1578 1579
    hitReasons.push_back(
        std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
1580 1581 1582
  } else if (isAssert) {
    hitReasons.push_back(std::make_pair(
        protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
1583
  } else if (!exception.IsEmpty()) {
1584
    InjectedScript* injectedScript = nullptr;
1585
    m_session->findInjectedScript(contextId, injectedScript);
1586
    if (injectedScript) {
1587
      String16 breakReason =
1588
          exceptionType == v8::debug::kPromiseRejection
1589 1590
              ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
              : protocol::Debugger::Paused::ReasonEnum::Exception;
1591
      std::unique_ptr<protocol::Runtime::RemoteObject> obj;
1592 1593
      injectedScript->wrapObject(exception, kBacktraceObjectGroup,
                                 WrapMode::kNoPreview, &obj);
1594
      std::unique_ptr<protocol::DictionaryValue> breakAuxData;
1595
      if (obj) {
1596 1597
        breakAuxData = obj->toValue();
        breakAuxData->setBoolean("uncaught", isUncaught);
1598
      } else {
1599
        breakAuxData = nullptr;
1600
      }
1601 1602
      hitReasons.push_back(
          std::make_pair(breakReason, std::move(breakAuxData)));
1603 1604 1605 1606 1607
    }
  }

  std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();

1608
  for (const auto& id : hitBreakpoints) {
1609 1610 1611 1612 1613 1614 1615 1616
    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;
    }
1617 1618 1619
    auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
    if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
      continue;
1620
    }
1621 1622 1623 1624 1625 1626 1627
    const String16& breakpointId = breakpointIterator->second;
    hitBreakpointIds->addItem(breakpointId);
    BreakpointType type;
    parseBreakpointId(breakpointId, &type);
    if (type != BreakpointType::kDebugCommand) continue;
    hitReasons.push_back(std::make_pair(
        protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
1628 1629
  }

1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655
  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));
  }

1656 1657 1658
  std::unique_ptr<Array<CallFrame>> protocolCallFrames;
  Response response = currentCallFrames(&protocolCallFrames);
  if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
1659

1660 1661
  m_frontend.paused(std::move(protocolCallFrames), breakReason,
                    std::move(breakAuxData), std::move(hitBreakpointIds),
1662
                    currentAsyncStackTrace(), currentExternalStackTrace(),
1663
                    currentScheduledAsyncCall());
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
}

void V8DebuggerAgentImpl::didContinue() {
  clearBreakDetails();
  m_frontend.resumed();
}

void V8DebuggerAgentImpl::breakProgram(
    const String16& breakReason,
    std::unique_ptr<protocol::DictionaryValue> data) {
1674
  if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return;
1675 1676 1677
  std::vector<BreakReason> currentScheduledReason;
  currentScheduledReason.swap(m_breakReason);
  pushBreakDetails(breakReason, std::move(data));
1678 1679 1680 1681 1682 1683 1684 1685 1686

  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;

1687 1688
  popBreakDetails();
  m_breakReason.swap(currentScheduledReason);
1689
  if (!m_breakReason.empty()) {
1690
    m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
1691
  }
1692 1693
}

1694 1695 1696
void V8DebuggerAgentImpl::setBreakpointFor(v8::Local<v8::Function> function,
                                           v8::Local<v8::String> condition,
                                           BreakpointSource source) {
1697 1698 1699
  String16 breakpointId = generateBreakpointId(
      source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
                                             : BreakpointType::kMonitorCommand,
1700
      function);
1701 1702 1703 1704
  if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
      m_breakpointIdToDebuggerBreakpointIds.end()) {
    return;
  }
1705
  setBreakpointImpl(breakpointId, function, condition);
1706 1707
}

1708 1709
void V8DebuggerAgentImpl::removeBreakpointFor(v8::Local<v8::Function> function,
                                              BreakpointSource source) {
1710 1711 1712
  String16 breakpointId = generateBreakpointId(
      source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
                                             : BreakpointType::kMonitorCommand,
1713
      function);
1714
  removeBreakpointImpl(breakpointId);
1715 1716 1717 1718 1719
}

void V8DebuggerAgentImpl::reset() {
  if (!enabled()) return;
  m_blackboxedPositions.clear();
1720 1721
  resetBlackboxedStateCache();
  m_scripts.clear();
1722 1723
  m_cachedScriptIds.clear();
  m_cachedScriptSize = 0;
1724 1725 1726
  m_breakpointIdToDebuggerBreakpointIds.clear();
}

1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737
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;
1738
    m_scripts.erase(scriptId);
1739
    m_cachedScriptIds.pop_front();
1740 1741 1742
  }
}

1743
}  // namespace v8_inspector