V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
v8-debugger.cc
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/inspector/v8-debugger.h"
6 
7 #include "src/inspector/inspected-context.h"
8 #include "src/inspector/protocol/Protocol.h"
9 #include "src/inspector/string-util.h"
10 #include "src/inspector/v8-debugger-agent-impl.h"
11 #include "src/inspector/v8-inspector-impl.h"
12 #include "src/inspector/v8-inspector-session-impl.h"
13 #include "src/inspector/v8-runtime-agent-impl.h"
14 #include "src/inspector/v8-stack-trace-impl.h"
15 #include "src/inspector/v8-value-utils.h"
16 
17 #include "include/v8-util.h"
18 
19 namespace v8_inspector {
20 
21 namespace {
22 
23 static const int kMaxAsyncTaskStacks = 128 * 1024;
24 static const int kNoBreakpointId = 0;
25 
26 template <typename Map>
27 void cleanupExpiredWeakPointers(Map& map) {
28  for (auto it = map.begin(); it != map.end();) {
29  if (it->second.expired()) {
30  it = map.erase(it);
31  } else {
32  ++it;
33  }
34  }
35 }
36 
37 class MatchPrototypePredicate : public v8::debug::QueryObjectPredicate {
38  public:
39  MatchPrototypePredicate(V8InspectorImpl* inspector,
40  v8::Local<v8::Context> context,
41  v8::Local<v8::Object> prototype)
42  : m_inspector(inspector), m_context(context), m_prototype(prototype) {}
43 
44  bool Filter(v8::Local<v8::Object> object) override {
45  v8::Local<v8::Context> objectContext = object->CreationContext();
46  if (objectContext != m_context) return false;
47  if (!m_inspector->client()->isInspectableHeapObject(object)) return false;
48  // Get prototype chain for current object until first visited prototype.
49  for (v8::Local<v8::Value> prototype = object->GetPrototype();
50  prototype->IsObject();
51  prototype = prototype.As<v8::Object>()->GetPrototype()) {
52  if (m_prototype == prototype) return true;
53  }
54  return false;
55  }
56 
57  private:
58  V8InspectorImpl* m_inspector;
59  v8::Local<v8::Context> m_context;
60  v8::Local<v8::Value> m_prototype;
61 };
62 } // namespace
63 
64 V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
65  : m_isolate(isolate),
66  m_inspector(inspector),
67  m_enableCount(0),
68  m_ignoreScriptParsedEventsCounter(0),
69  m_continueToLocationBreakpointId(kNoBreakpointId),
70  m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
71  m_maxAsyncCallStackDepth(0),
72  m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
73  m_wasmTranslation(isolate) {}
74 
75 V8Debugger::~V8Debugger() {
76  m_isolate->RemoveCallCompletedCallback(
77  &V8Debugger::terminateExecutionCompletedCallback);
78  m_isolate->RemoveMicrotasksCompletedCallback(
79  &V8Debugger::terminateExecutionCompletedCallback);
80 }
81 
82 void V8Debugger::enable() {
83  if (m_enableCount++) return;
84  v8::HandleScope scope(m_isolate);
85  v8::debug::SetDebugDelegate(m_isolate, this);
86  m_isolate->AddNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback, this);
87  v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
88  m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
89 }
90 
91 void V8Debugger::disable() {
92  if (isPaused()) {
93  bool scheduledOOMBreak = m_scheduledOOMBreak;
94  bool hasAgentAcceptsPause = false;
95  m_inspector->forEachSession(
96  m_pausedContextGroupId, [&scheduledOOMBreak, &hasAgentAcceptsPause](
97  V8InspectorSessionImpl* session) {
98  if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
99  hasAgentAcceptsPause = true;
100  }
101  });
102  if (!hasAgentAcceptsPause) m_inspector->client()->quitMessageLoopOnPause();
103  }
104  if (--m_enableCount) return;
105  clearContinueToLocation();
106  m_taskWithScheduledBreak = nullptr;
107  m_taskWithScheduledBreakDebuggerId = String16();
108  m_pauseOnAsyncCall = false;
109  m_wasmTranslation.Clear();
110  v8::debug::SetDebugDelegate(m_isolate, nullptr);
111  m_isolate->RemoveNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback,
112  m_originalHeapLimit);
113  m_originalHeapLimit = 0;
114 }
115 
116 bool V8Debugger::isPausedInContextGroup(int contextGroupId) const {
117  return isPaused() && m_pausedContextGroupId == contextGroupId;
118 }
119 
120 bool V8Debugger::enabled() const { return m_enableCount > 0; }
121 
122 void V8Debugger::getCompiledScripts(
123  int contextGroupId,
124  std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
125  v8::HandleScope scope(m_isolate);
127  v8::debug::GetLoadedScripts(m_isolate, scripts);
128  for (size_t i = 0; i < scripts.Size(); ++i) {
129  v8::Local<v8::debug::Script> script = scripts.Get(i);
130  if (!script->WasCompiled()) continue;
131  if (script->IsEmbedded()) {
132  result.push_back(V8DebuggerScript::Create(m_isolate, script, false,
133  m_inspector->client()));
134  continue;
135  }
136  int contextId;
137  if (!script->ContextId().To(&contextId)) continue;
138  if (m_inspector->contextGroupId(contextId) != contextGroupId) continue;
139  result.push_back(V8DebuggerScript::Create(m_isolate, script, false,
140  m_inspector->client()));
141  }
142 }
143 
144 void V8Debugger::setBreakpointsActive(bool active) {
145  if (!enabled()) {
146  UNREACHABLE();
147  return;
148  }
149  m_breakpointsActiveCount += active ? 1 : -1;
150  v8::debug::SetBreakPointsActive(m_isolate, m_breakpointsActiveCount);
151 }
152 
153 v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
154  DCHECK(enabled());
155  return m_pauseOnExceptionsState;
156 }
157 
158 void V8Debugger::setPauseOnExceptionsState(
159  v8::debug::ExceptionBreakState pauseOnExceptionsState) {
160  DCHECK(enabled());
161  if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
162  v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
163  m_pauseOnExceptionsState = pauseOnExceptionsState;
164 }
165 
166 void V8Debugger::setPauseOnNextCall(bool pause, int targetContextGroupId) {
167  if (isPaused()) return;
168  DCHECK(targetContextGroupId);
169  if (!pause && m_targetContextGroupId &&
170  m_targetContextGroupId != targetContextGroupId) {
171  return;
172  }
173  m_targetContextGroupId = targetContextGroupId;
174  m_breakRequested = pause;
175  if (pause)
176  v8::debug::SetBreakOnNextFunctionCall(m_isolate);
177  else
178  v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
179 }
180 
181 bool V8Debugger::canBreakProgram() {
182  return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
183 }
184 
185 void V8Debugger::breakProgram(int targetContextGroupId) {
186  DCHECK(canBreakProgram());
187  // Don't allow nested breaks.
188  if (isPaused()) return;
189  DCHECK(targetContextGroupId);
190  m_targetContextGroupId = targetContextGroupId;
191  v8::debug::BreakRightNow(m_isolate);
192 }
193 
194 void V8Debugger::interruptAndBreak(int targetContextGroupId) {
195  // Don't allow nested breaks.
196  if (isPaused()) return;
197  DCHECK(targetContextGroupId);
198  m_targetContextGroupId = targetContextGroupId;
199  m_isolate->RequestInterrupt(
200  [](v8::Isolate* isolate, void*) { v8::debug::BreakRightNow(isolate); },
201  nullptr);
202 }
203 
204 void V8Debugger::continueProgram(int targetContextGroupId) {
205  if (m_pausedContextGroupId != targetContextGroupId) return;
206  if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
207 }
208 
209 void V8Debugger::breakProgramOnAssert(int targetContextGroupId) {
210  if (!enabled()) return;
211  if (m_pauseOnExceptionsState == v8::debug::NoBreakOnException) return;
212  // Don't allow nested breaks.
213  if (isPaused()) return;
214  if (!canBreakProgram()) return;
215  DCHECK(targetContextGroupId);
216  m_targetContextGroupId = targetContextGroupId;
217  m_scheduledAssertBreak = true;
218  v8::debug::BreakRightNow(m_isolate);
219 }
220 
221 void V8Debugger::stepIntoStatement(int targetContextGroupId,
222  bool breakOnAsyncCall) {
223  DCHECK(isPaused());
224  DCHECK(targetContextGroupId);
225  if (asyncStepOutOfFunction(targetContextGroupId, true)) return;
226  m_targetContextGroupId = targetContextGroupId;
227  m_pauseOnAsyncCall = breakOnAsyncCall;
228  v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
229  continueProgram(targetContextGroupId);
230 }
231 
232 void V8Debugger::stepOverStatement(int targetContextGroupId) {
233  DCHECK(isPaused());
234  DCHECK(targetContextGroupId);
235  if (asyncStepOutOfFunction(targetContextGroupId, true)) return;
236  m_targetContextGroupId = targetContextGroupId;
237  v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
238  continueProgram(targetContextGroupId);
239 }
240 
241 void V8Debugger::stepOutOfFunction(int targetContextGroupId) {
242  DCHECK(isPaused());
243  DCHECK(targetContextGroupId);
244  if (asyncStepOutOfFunction(targetContextGroupId, false)) return;
245  m_targetContextGroupId = targetContextGroupId;
246  v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
247  continueProgram(targetContextGroupId);
248 }
249 
250 bool V8Debugger::asyncStepOutOfFunction(int targetContextGroupId,
251  bool onlyAtReturn) {
252  auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
253  DCHECK(!iterator->Done());
254  bool atReturn = !iterator->GetReturnValue().IsEmpty();
255  iterator->Advance();
256  // Synchronous stack has more then one frame.
257  if (!iterator->Done()) return false;
258  // There is only one synchronous frame but we are not at return position and
259  // user requests stepOver or stepInto.
260  if (onlyAtReturn && !atReturn) return false;
261  // If we are inside async function, current async parent was captured when
262  // async function was suspended first time and we install that stack as
263  // current before resume async function. So it represents current async
264  // function.
265  auto current = currentAsyncParent();
266  if (!current) return false;
267  // Lookup for parent async function.
268  auto parent = current->parent();
269  if (parent.expired()) return false;
270  // Parent async stack will have suspended task id iff callee async function
271  // is awaiting current async function. We can make stepOut there only in this
272  // case.
273  void* parentTask =
274  std::shared_ptr<AsyncStackTrace>(parent)->suspendedTaskId();
275  if (!parentTask) return false;
276  pauseOnAsyncCall(targetContextGroupId,
277  reinterpret_cast<uintptr_t>(parentTask), String16());
278  continueProgram(targetContextGroupId);
279  return true;
280 }
281 
282 void V8Debugger::scheduleStepIntoAsync(
283  std::unique_ptr<ScheduleStepIntoAsyncCallback> callback,
284  int targetContextGroupId) {
285  DCHECK(isPaused());
286  DCHECK(targetContextGroupId);
287  if (m_stepIntoAsyncCallback) {
288  m_stepIntoAsyncCallback->sendFailure(Response::Error(
289  "Current scheduled step into async was overriden with new one."));
290  }
291  m_targetContextGroupId = targetContextGroupId;
292  m_stepIntoAsyncCallback = std::move(callback);
293 }
294 
295 void V8Debugger::pauseOnAsyncCall(int targetContextGroupId, uintptr_t task,
296  const String16& debuggerId) {
297  DCHECK(targetContextGroupId);
298  m_targetContextGroupId = targetContextGroupId;
299 
300  m_taskWithScheduledBreak = reinterpret_cast<void*>(task);
301  m_taskWithScheduledBreakDebuggerId = debuggerId;
302 }
303 
304 void V8Debugger::terminateExecution(
305  std::unique_ptr<TerminateExecutionCallback> callback) {
306  if (m_terminateExecutionCallback) {
307  if (callback) {
308  callback->sendFailure(
309  Response::Error("There is current termination request in progress"));
310  }
311  return;
312  }
313  m_terminateExecutionCallback = std::move(callback);
314  m_isolate->AddCallCompletedCallback(
315  &V8Debugger::terminateExecutionCompletedCallback);
316  m_isolate->AddMicrotasksCompletedCallback(
317  &V8Debugger::terminateExecutionCompletedCallback);
318  m_isolate->TerminateExecution();
319 }
320 
321 void V8Debugger::terminateExecutionCompletedCallback(v8::Isolate* isolate) {
322  isolate->RemoveCallCompletedCallback(
323  &V8Debugger::terminateExecutionCompletedCallback);
324  isolate->RemoveMicrotasksCompletedCallback(
325  &V8Debugger::terminateExecutionCompletedCallback);
326  V8InspectorImpl* inspector =
327  static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
328  V8Debugger* debugger = inspector->debugger();
329  debugger->m_isolate->CancelTerminateExecution();
330  if (debugger->m_terminateExecutionCallback) {
331  debugger->m_terminateExecutionCallback->sendSuccess();
332  debugger->m_terminateExecutionCallback.reset();
333  }
334 }
335 
336 Response V8Debugger::continueToLocation(
337  int targetContextGroupId, V8DebuggerScript* script,
338  std::unique_ptr<protocol::Debugger::Location> location,
339  const String16& targetCallFrames) {
340  DCHECK(isPaused());
341  DCHECK(targetContextGroupId);
342  m_targetContextGroupId = targetContextGroupId;
343  v8::debug::Location v8Location(location->getLineNumber(),
344  location->getColumnNumber(0));
345  if (script->setBreakpoint(String16(), &v8Location,
346  &m_continueToLocationBreakpointId)) {
347  m_continueToLocationTargetCallFrames = targetCallFrames;
348  if (m_continueToLocationTargetCallFrames !=
349  protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
350  m_continueToLocationStack = captureStackTrace(true);
351  DCHECK(m_continueToLocationStack);
352  }
353  continueProgram(targetContextGroupId);
354  // TODO(kozyatinskiy): Return actual line and column number.
355  return Response::OK();
356  } else {
357  return Response::Error("Cannot continue to specified location");
358  }
359 }
360 
361 bool V8Debugger::shouldContinueToCurrentLocation() {
362  if (m_continueToLocationTargetCallFrames ==
363  protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
364  return true;
365  }
366  std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true);
367  if (m_continueToLocationTargetCallFrames ==
368  protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
369  return m_continueToLocationStack->isEqualIgnoringTopFrame(
370  currentStack.get());
371  }
372  return true;
373 }
374 
375 void V8Debugger::clearContinueToLocation() {
376  if (m_continueToLocationBreakpointId == kNoBreakpointId) return;
377  v8::debug::RemoveBreakpoint(m_isolate, m_continueToLocationBreakpointId);
378  m_continueToLocationBreakpointId = kNoBreakpointId;
379  m_continueToLocationTargetCallFrames = String16();
380  m_continueToLocationStack.reset();
381 }
382 
383 void V8Debugger::handleProgramBreak(
384  v8::Local<v8::Context> pausedContext, v8::Local<v8::Value> exception,
385  const std::vector<v8::debug::BreakpointId>& breakpointIds,
386  v8::debug::ExceptionType exceptionType, bool isUncaught) {
387  // Don't allow nested breaks.
388  if (isPaused()) return;
389 
390  int contextGroupId = m_inspector->contextGroupId(pausedContext);
391  if (m_targetContextGroupId && contextGroupId != m_targetContextGroupId) {
392  v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
393  return;
394  }
395  m_targetContextGroupId = 0;
396  if (m_stepIntoAsyncCallback) {
397  m_stepIntoAsyncCallback->sendFailure(
398  Response::Error("No async tasks were scheduled before pause."));
399  m_stepIntoAsyncCallback.reset();
400  }
401  m_breakRequested = false;
402  m_pauseOnAsyncCall = false;
403  m_taskWithScheduledBreak = nullptr;
404  m_taskWithScheduledBreakDebuggerId = String16();
405 
406  bool scheduledOOMBreak = m_scheduledOOMBreak;
407  bool scheduledAssertBreak = m_scheduledAssertBreak;
408  bool hasAgents = false;
409  m_inspector->forEachSession(
410  contextGroupId,
411  [&scheduledOOMBreak, &hasAgents](V8InspectorSessionImpl* session) {
412  if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak))
413  hasAgents = true;
414  });
415  if (!hasAgents) return;
416 
417  if (breakpointIds.size() == 1 &&
418  breakpointIds[0] == m_continueToLocationBreakpointId) {
419  v8::Context::Scope contextScope(pausedContext);
420  if (!shouldContinueToCurrentLocation()) return;
421  }
422  clearContinueToLocation();
423 
424  DCHECK(contextGroupId);
425  m_pausedContextGroupId = contextGroupId;
426 
427  m_inspector->forEachSession(
428  contextGroupId, [&pausedContext, &exception, &breakpointIds,
429  &exceptionType, &isUncaught, &scheduledOOMBreak,
430  &scheduledAssertBreak](V8InspectorSessionImpl* session) {
431  if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
432  session->debuggerAgent()->didPause(
433  InspectedContext::contextId(pausedContext), exception,
434  breakpointIds, exceptionType, isUncaught, scheduledOOMBreak,
435  scheduledAssertBreak);
436  }
437  });
438  {
439  v8::Context::Scope scope(pausedContext);
440  m_inspector->client()->runMessageLoopOnPause(contextGroupId);
441  m_pausedContextGroupId = 0;
442  }
443  m_inspector->forEachSession(contextGroupId,
444  [](V8InspectorSessionImpl* session) {
445  if (session->debuggerAgent()->enabled())
446  session->debuggerAgent()->didContinue();
447  });
448 
449  if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
450  m_scheduledOOMBreak = false;
451  m_scheduledAssertBreak = false;
452 }
453 
454 namespace {
455 
456 size_t HeapLimitForDebugging(size_t initial_heap_limit) {
457  const size_t kDebugHeapSizeFactor = 4;
458  size_t max_limit = std::numeric_limits<size_t>::max() / 4;
459  return std::min(max_limit, initial_heap_limit * kDebugHeapSizeFactor);
460 }
461 
462 } // anonymous namespace
463 
464 size_t V8Debugger::nearHeapLimitCallback(void* data, size_t current_heap_limit,
465  size_t initial_heap_limit) {
466  V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
467  thisPtr->m_originalHeapLimit = current_heap_limit;
468  thisPtr->m_scheduledOOMBreak = true;
469  v8::Local<v8::Context> context =
470  thisPtr->m_isolate->GetEnteredOrMicrotaskContext();
471  thisPtr->m_targetContextGroupId =
472  context.IsEmpty() ? 0 : thisPtr->m_inspector->contextGroupId(context);
473  thisPtr->m_isolate->RequestInterrupt(
474  [](v8::Isolate* isolate, void*) { v8::debug::BreakRightNow(isolate); },
475  nullptr);
476  return HeapLimitForDebugging(initial_heap_limit);
477 }
478 
479 void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
480  bool is_live_edited, bool has_compile_error) {
481  int contextId;
482  if (!script->ContextId().To(&contextId)) return;
483  if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
484  WasmTranslation* wasmTranslation = &m_wasmTranslation;
485  m_inspector->forEachSession(
486  m_inspector->contextGroupId(contextId),
487  [&script, &wasmTranslation](V8InspectorSessionImpl* session) {
488  if (!session->debuggerAgent()->enabled()) return;
489  wasmTranslation->AddScript(script.As<v8::debug::WasmScript>(),
490  session->debuggerAgent());
491  });
492  } else if (m_ignoreScriptParsedEventsCounter == 0) {
493  v8::Isolate* isolate = m_isolate;
494  V8InspectorClient* client = m_inspector->client();
495  m_inspector->forEachSession(
496  m_inspector->contextGroupId(contextId),
497  [&isolate, &script, &has_compile_error, &is_live_edited,
498  &client](V8InspectorSessionImpl* session) {
499  if (!session->debuggerAgent()->enabled()) return;
500  session->debuggerAgent()->didParseSource(
501  V8DebuggerScript::Create(isolate, script, is_live_edited, client),
502  !has_compile_error);
503  });
504  }
505 }
506 
507 void V8Debugger::BreakProgramRequested(
508  v8::Local<v8::Context> pausedContext,
509  const std::vector<v8::debug::BreakpointId>& break_points_hit) {
510  handleProgramBreak(pausedContext, v8::Local<v8::Value>(), break_points_hit);
511 }
512 
513 void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
514  v8::Local<v8::Value> exception,
515  v8::Local<v8::Value> promise, bool isUncaught,
516  v8::debug::ExceptionType exceptionType) {
517  std::vector<v8::debug::BreakpointId> break_points_hit;
518  handleProgramBreak(pausedContext, exception, break_points_hit, exceptionType,
519  isUncaught);
520 }
521 
522 bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
523  const v8::debug::Location& start,
524  const v8::debug::Location& end) {
525  int contextId;
526  if (!script->ContextId().To(&contextId)) return false;
527  bool hasAgents = false;
528  bool allBlackboxed = true;
529  String16 scriptId = String16::fromInteger(script->Id());
530  m_inspector->forEachSession(
531  m_inspector->contextGroupId(contextId),
532  [&hasAgents, &allBlackboxed, &scriptId, &start,
533  &end](V8InspectorSessionImpl* session) {
534  V8DebuggerAgentImpl* agent = session->debuggerAgent();
535  if (!agent->enabled()) return;
536  hasAgents = true;
537  allBlackboxed &= agent->isFunctionBlackboxed(scriptId, start, end);
538  });
539  return hasAgents && allBlackboxed;
540 }
541 
542 void V8Debugger::AsyncEventOccurred(v8::debug::DebugAsyncActionType type,
543  int id, bool isBlackboxed) {
544  // Async task events from Promises are given misaligned pointers to prevent
545  // from overlapping with other Blink task identifiers.
546  void* task = reinterpret_cast<void*>(id * 2 + 1);
547  switch (type) {
548  case v8::debug::kDebugPromiseThen:
549  asyncTaskScheduledForStack("Promise.then", task, false);
550  if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
551  break;
552  case v8::debug::kDebugPromiseCatch:
553  asyncTaskScheduledForStack("Promise.catch", task, false);
554  if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
555  break;
556  case v8::debug::kDebugPromiseFinally:
557  asyncTaskScheduledForStack("Promise.finally", task, false);
558  if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
559  break;
560  case v8::debug::kDebugWillHandle:
561  asyncTaskStartedForStack(task);
562  asyncTaskStartedForStepping(task);
563  break;
564  case v8::debug::kDebugDidHandle:
565  asyncTaskFinishedForStack(task);
566  asyncTaskFinishedForStepping(task);
567  break;
568  case v8::debug::kAsyncFunctionSuspended: {
569  if (m_asyncTaskStacks.find(task) == m_asyncTaskStacks.end()) {
570  asyncTaskScheduledForStack("async function", task, true);
571  }
572  auto stackIt = m_asyncTaskStacks.find(task);
573  if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
574  std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
575  stack->setSuspendedTaskId(task);
576  }
577  break;
578  }
579  case v8::debug::kAsyncFunctionFinished:
580  asyncTaskCanceledForStack(task);
581  break;
582  }
583 }
584 
585 std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
586  return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
587 }
588 
589 V8StackTraceId V8Debugger::currentExternalParent() {
590  return m_currentExternalParent.empty() ? V8StackTraceId()
591  : m_currentExternalParent.back();
592 }
593 
594 v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
596  ScopeTargetKind kind) {
597  v8::Local<v8::Value> scopesValue;
598  std::unique_ptr<v8::debug::ScopeIterator> iterator;
599  switch (kind) {
600  case FUNCTION:
601  iterator = v8::debug::ScopeIterator::CreateForFunction(
602  m_isolate, v8::Local<v8::Function>::Cast(value));
603  break;
604  case GENERATOR:
605  v8::Local<v8::debug::GeneratorObject> generatorObject =
606  v8::debug::GeneratorObject::Cast(value);
607  if (!generatorObject->IsSuspended()) return v8::MaybeLocal<v8::Value>();
608 
609  iterator = v8::debug::ScopeIterator::CreateForGeneratorObject(
610  m_isolate, v8::Local<v8::Object>::Cast(value));
611  break;
612  }
613  if (!iterator) return v8::MaybeLocal<v8::Value>();
614  v8::Local<v8::Array> result = v8::Array::New(m_isolate);
615  if (!result->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) {
616  return v8::MaybeLocal<v8::Value>();
617  }
618 
619  for (; !iterator->Done(); iterator->Advance()) {
620  v8::Local<v8::Object> scope = v8::Object::New(m_isolate);
621  if (!addInternalObject(context, scope, V8InternalValueType::kScope))
622  return v8::MaybeLocal<v8::Value>();
623  String16 nameSuffix = toProtocolStringWithTypeCheck(
624  m_isolate, iterator->GetFunctionDebugName());
625  String16 description;
626  if (nameSuffix.length()) nameSuffix = " (" + nameSuffix + ")";
627  switch (iterator->GetType()) {
628  case v8::debug::ScopeIterator::ScopeTypeGlobal:
629  description = "Global" + nameSuffix;
630  break;
631  case v8::debug::ScopeIterator::ScopeTypeLocal:
632  description = "Local" + nameSuffix;
633  break;
634  case v8::debug::ScopeIterator::ScopeTypeWith:
635  description = "With Block" + nameSuffix;
636  break;
637  case v8::debug::ScopeIterator::ScopeTypeClosure:
638  description = "Closure" + nameSuffix;
639  break;
640  case v8::debug::ScopeIterator::ScopeTypeCatch:
641  description = "Catch" + nameSuffix;
642  break;
643  case v8::debug::ScopeIterator::ScopeTypeBlock:
644  description = "Block" + nameSuffix;
645  break;
646  case v8::debug::ScopeIterator::ScopeTypeScript:
647  description = "Script" + nameSuffix;
648  break;
649  case v8::debug::ScopeIterator::ScopeTypeEval:
650  description = "Eval" + nameSuffix;
651  break;
652  case v8::debug::ScopeIterator::ScopeTypeModule:
653  description = "Module" + nameSuffix;
654  break;
655  }
656  v8::Local<v8::Object> object = iterator->GetObject();
657  createDataProperty(context, scope,
658  toV8StringInternalized(m_isolate, "description"),
659  toV8String(m_isolate, description));
660  createDataProperty(context, scope,
661  toV8StringInternalized(m_isolate, "object"), object);
662  createDataProperty(context, result, result->Length(), scope);
663  }
664  if (!addInternalObject(context, result, V8InternalValueType::kScopeList))
665  return v8::MaybeLocal<v8::Value>();
666  return result;
667 }
668 
669 v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
671  return getTargetScopes(context, function, FUNCTION);
672 }
673 
674 v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
675  v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
676  return getTargetScopes(context, generator, GENERATOR);
677 }
678 
679 v8::MaybeLocal<v8::Array> V8Debugger::collectionsEntries(
681  v8::Isolate* isolate = context->GetIsolate();
682  v8::Local<v8::Array> entries;
683  bool isKeyValue = false;
684  if (!value->IsObject() ||
685  !value.As<v8::Object>()->PreviewEntries(&isKeyValue).ToLocal(&entries)) {
686  return v8::MaybeLocal<v8::Array>();
687  }
688 
689  v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
690  CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
691  if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
692  .FromMaybe(false))
693  return v8::MaybeLocal<v8::Array>();
694  for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
696  if (!entries->Get(context, i).ToLocal(&item)) continue;
697  v8::Local<v8::Value> value;
698  if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
699  v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
700  if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
701  continue;
702  createDataProperty(
703  context, wrapper,
704  toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
705  if (isKeyValue) {
706  createDataProperty(context, wrapper,
707  toV8StringInternalized(isolate, "value"), value);
708  }
709  if (!addInternalObject(context, wrapper, V8InternalValueType::kEntry))
710  continue;
711  createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
712  wrapper);
713  }
714  return wrappedEntries;
715 }
716 
717 v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
719  v8::Local<v8::Array> properties;
720  if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
721  return v8::MaybeLocal<v8::Array>();
722  v8::Local<v8::Array> entries;
723  if (collectionsEntries(context, value).ToLocal(&entries)) {
724  createDataProperty(context, properties, properties->Length(),
725  toV8StringInternalized(m_isolate, "[[Entries]]"));
726  createDataProperty(context, properties, properties->Length(), entries);
727  }
728  if (value->IsGeneratorObject()) {
729  v8::Local<v8::Value> scopes;
730  if (generatorScopes(context, value).ToLocal(&scopes)) {
731  createDataProperty(context, properties, properties->Length(),
732  toV8StringInternalized(m_isolate, "[[Scopes]]"));
733  createDataProperty(context, properties, properties->Length(), scopes);
734  }
735  }
736  if (value->IsFunction()) {
737  v8::Local<v8::Function> function = value.As<v8::Function>();
738  v8::Local<v8::Value> scopes;
739  if (functionScopes(context, function).ToLocal(&scopes)) {
740  createDataProperty(context, properties, properties->Length(),
741  toV8StringInternalized(m_isolate, "[[Scopes]]"));
742  createDataProperty(context, properties, properties->Length(), scopes);
743  }
744  }
745  return properties;
746 }
747 
748 v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context,
749  v8::Local<v8::Object> prototype) {
750  v8::Isolate* isolate = context->GetIsolate();
751  v8::PersistentValueVector<v8::Object> v8Objects(isolate);
752  MatchPrototypePredicate predicate(m_inspector, context, prototype);
753  v8::debug::QueryObjects(context, &predicate, &v8Objects);
754 
755  v8::MicrotasksScope microtasksScope(isolate,
756  v8::MicrotasksScope::kDoNotRunMicrotasks);
757  v8::Local<v8::Array> resultArray = v8::Array::New(
758  m_inspector->isolate(), static_cast<int>(v8Objects.Size()));
759  for (size_t i = 0; i < v8Objects.Size(); ++i) {
760  createDataProperty(context, resultArray, static_cast<int>(i),
761  v8Objects.Get(i));
762  }
763  return resultArray;
764 }
765 
766 std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
767  v8::Local<v8::StackTrace> v8StackTrace) {
768  return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace,
769  V8StackTraceImpl::maxCallStackSizeToCapture);
770 }
771 
772 void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
773  if (depth <= 0)
774  m_maxAsyncCallStackDepthMap.erase(agent);
775  else
776  m_maxAsyncCallStackDepthMap[agent] = depth;
777 
778  int maxAsyncCallStackDepth = 0;
779  for (const auto& pair : m_maxAsyncCallStackDepthMap) {
780  if (pair.second > maxAsyncCallStackDepth)
781  maxAsyncCallStackDepth = pair.second;
782  }
783 
784  if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
785  // TODO(dgozman): ideally, this should be per context group.
786  m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
787  m_inspector->client()->maxAsyncCallStackDepthChanged(
788  m_maxAsyncCallStackDepth);
789  if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
790  v8::debug::SetAsyncEventDelegate(m_isolate,
791  maxAsyncCallStackDepth ? this : nullptr);
792 }
793 
794 std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
795  int contextGroupId, const V8StackTraceId& id) {
796  if (debuggerIdFor(contextGroupId) != id.debugger_id) return nullptr;
797  auto it = m_storedStackTraces.find(id.id);
798  if (it == m_storedStackTraces.end()) return nullptr;
799  return it->second.lock();
800 }
801 
802 V8StackTraceId V8Debugger::storeCurrentStackTrace(
803  const StringView& description) {
804  if (!m_maxAsyncCallStackDepth) return V8StackTraceId();
805 
806  v8::HandleScope scope(m_isolate);
807  int contextGroupId = currentContextGroupId();
808  if (!contextGroupId) return V8StackTraceId();
809 
810  std::shared_ptr<AsyncStackTrace> asyncStack =
811  AsyncStackTrace::capture(this, contextGroupId, toString16(description),
812  V8StackTraceImpl::maxCallStackSizeToCapture);
813  if (!asyncStack) return V8StackTraceId();
814 
815  uintptr_t id = AsyncStackTrace::store(this, asyncStack);
816 
817  m_allAsyncStacks.push_back(std::move(asyncStack));
818  ++m_asyncStacksCount;
819  collectOldAsyncStacksIfNeeded();
820 
821  asyncTaskCandidateForStepping(reinterpret_cast<void*>(id), false);
822 
823  return V8StackTraceId(id, debuggerIdFor(contextGroupId));
824 }
825 
826 uintptr_t V8Debugger::storeStackTrace(
827  std::shared_ptr<AsyncStackTrace> asyncStack) {
828  uintptr_t id = ++m_lastStackTraceId;
829  m_storedStackTraces[id] = asyncStack;
830  return id;
831 }
832 
833 void V8Debugger::externalAsyncTaskStarted(const V8StackTraceId& parent) {
834  if (!m_maxAsyncCallStackDepth || parent.IsInvalid()) return;
835  m_currentExternalParent.push_back(parent);
836  m_currentAsyncParent.emplace_back();
837  m_currentTasks.push_back(reinterpret_cast<void*>(parent.id));
838 
839  if (m_breakRequested) return;
840  if (!m_taskWithScheduledBreakDebuggerId.isEmpty() &&
841  reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) == parent.id &&
842  m_taskWithScheduledBreakDebuggerId ==
843  debuggerIdToString(parent.debugger_id)) {
844  v8::debug::SetBreakOnNextFunctionCall(m_isolate);
845  }
846 }
847 
848 void V8Debugger::externalAsyncTaskFinished(const V8StackTraceId& parent) {
849  if (!m_maxAsyncCallStackDepth || m_currentExternalParent.empty()) return;
850  m_currentExternalParent.pop_back();
851  m_currentAsyncParent.pop_back();
852  DCHECK(m_currentTasks.back() == reinterpret_cast<void*>(parent.id));
853  m_currentTasks.pop_back();
854 
855  if (m_taskWithScheduledBreakDebuggerId.isEmpty() ||
856  reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) != parent.id ||
857  m_taskWithScheduledBreakDebuggerId !=
858  debuggerIdToString(parent.debugger_id)) {
859  return;
860  }
861  m_taskWithScheduledBreak = nullptr;
862  m_taskWithScheduledBreakDebuggerId = String16();
863  if (m_breakRequested) return;
864  v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
865 }
866 
867 void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
868  bool recurring) {
869  asyncTaskScheduledForStack(toString16(taskName), task, recurring);
870  asyncTaskCandidateForStepping(task, true);
871 }
872 
873 void V8Debugger::asyncTaskCanceled(void* task) {
874  asyncTaskCanceledForStack(task);
875  asyncTaskCanceledForStepping(task);
876 }
877 
878 void V8Debugger::asyncTaskStarted(void* task) {
879  asyncTaskStartedForStack(task);
880  asyncTaskStartedForStepping(task);
881 }
882 
883 void V8Debugger::asyncTaskFinished(void* task) {
884  asyncTaskFinishedForStepping(task);
885  asyncTaskFinishedForStack(task);
886 }
887 
888 void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
889  void* task, bool recurring) {
890  if (!m_maxAsyncCallStackDepth) return;
891  v8::HandleScope scope(m_isolate);
892  std::shared_ptr<AsyncStackTrace> asyncStack =
893  AsyncStackTrace::capture(this, currentContextGroupId(), taskName,
894  V8StackTraceImpl::maxCallStackSizeToCapture);
895  if (asyncStack) {
896  m_asyncTaskStacks[task] = asyncStack;
897  if (recurring) m_recurringTasks.insert(task);
898  m_allAsyncStacks.push_back(std::move(asyncStack));
899  ++m_asyncStacksCount;
900  collectOldAsyncStacksIfNeeded();
901  }
902 }
903 
904 void V8Debugger::asyncTaskCanceledForStack(void* task) {
905  if (!m_maxAsyncCallStackDepth) return;
906  m_asyncTaskStacks.erase(task);
907  m_recurringTasks.erase(task);
908 }
909 
910 void V8Debugger::asyncTaskStartedForStack(void* task) {
911  if (!m_maxAsyncCallStackDepth) return;
912  // Needs to support following order of events:
913  // - asyncTaskScheduled
914  // <-- attached here -->
915  // - asyncTaskStarted
916  // - asyncTaskCanceled <-- canceled before finished
917  // <-- async stack requested here -->
918  // - asyncTaskFinished
919  m_currentTasks.push_back(task);
920  AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
921  if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
922  std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
923  stack->setSuspendedTaskId(nullptr);
924  m_currentAsyncParent.push_back(stack);
925  } else {
926  m_currentAsyncParent.emplace_back();
927  }
928  m_currentExternalParent.emplace_back();
929 }
930 
931 void V8Debugger::asyncTaskFinishedForStack(void* task) {
932  if (!m_maxAsyncCallStackDepth) return;
933  // We could start instrumenting half way and the stack is empty.
934  if (!m_currentTasks.size()) return;
935  DCHECK(m_currentTasks.back() == task);
936  m_currentTasks.pop_back();
937 
938  m_currentAsyncParent.pop_back();
939  m_currentExternalParent.pop_back();
940 
941  if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
942  asyncTaskCanceledForStack(task);
943  }
944 }
945 
946 void V8Debugger::asyncTaskCandidateForStepping(void* task, bool isLocal) {
947  int contextGroupId = currentContextGroupId();
948  if (m_pauseOnAsyncCall && contextGroupId) {
949  if (isLocal) {
950  m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
951  reinterpret_cast<uintptr_t>(task), std::make_pair(0, 0));
952  } else {
953  m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
954  reinterpret_cast<uintptr_t>(task), debuggerIdFor(contextGroupId));
955  }
956  breakProgram(m_targetContextGroupId);
957  m_scheduledAsyncCall = v8_inspector::V8StackTraceId();
958  return;
959  }
960  if (!m_stepIntoAsyncCallback) return;
961  DCHECK(m_targetContextGroupId);
962  if (contextGroupId != m_targetContextGroupId) return;
963  m_taskWithScheduledBreak = task;
964  v8::debug::ClearStepping(m_isolate);
965  m_stepIntoAsyncCallback->sendSuccess();
966  m_stepIntoAsyncCallback.reset();
967 }
968 
969 void V8Debugger::asyncTaskStartedForStepping(void* task) {
970  if (m_breakRequested) return;
971  // TODO(kozyatinskiy): we should search task in async chain to support
972  // blackboxing.
973  if (m_taskWithScheduledBreakDebuggerId.isEmpty() &&
974  task == m_taskWithScheduledBreak) {
975  v8::debug::SetBreakOnNextFunctionCall(m_isolate);
976  }
977 }
978 
979 void V8Debugger::asyncTaskFinishedForStepping(void* task) {
980  if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
981  task != m_taskWithScheduledBreak) {
982  return;
983  }
984  m_taskWithScheduledBreak = nullptr;
985  if (m_breakRequested) return;
986  v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
987 }
988 
989 void V8Debugger::asyncTaskCanceledForStepping(void* task) {
990  if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
991  task != m_taskWithScheduledBreak)
992  return;
993  m_taskWithScheduledBreak = nullptr;
994 }
995 
996 void V8Debugger::allAsyncTasksCanceled() {
997  m_asyncTaskStacks.clear();
998  m_recurringTasks.clear();
999  m_currentAsyncParent.clear();
1000  m_currentExternalParent.clear();
1001  m_currentTasks.clear();
1002 
1003  m_framesCache.clear();
1004  m_allAsyncStacks.clear();
1005  m_asyncStacksCount = 0;
1006 }
1007 
1008 void V8Debugger::muteScriptParsedEvents() {
1009  ++m_ignoreScriptParsedEventsCounter;
1010 }
1011 
1012 void V8Debugger::unmuteScriptParsedEvents() {
1013  --m_ignoreScriptParsedEventsCounter;
1014  DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
1015 }
1016 
1017 std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
1018  bool fullStack) {
1019  if (!m_isolate->InContext()) return nullptr;
1020 
1021  v8::HandleScope handles(m_isolate);
1022  int contextGroupId = currentContextGroupId();
1023  if (!contextGroupId) return nullptr;
1024 
1025  int stackSize = 1;
1026  if (fullStack) {
1027  stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
1028  } else {
1029  m_inspector->forEachSession(
1030  contextGroupId, [&stackSize](V8InspectorSessionImpl* session) {
1031  if (session->runtimeAgent()->enabled())
1032  stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
1033  });
1034  }
1035  return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
1036 }
1037 
1038 int V8Debugger::currentContextGroupId() {
1039  if (!m_isolate->InContext()) return 0;
1040  return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
1041 }
1042 
1043 void V8Debugger::collectOldAsyncStacksIfNeeded() {
1044  if (m_asyncStacksCount <= m_maxAsyncCallStacks) return;
1045  int halfOfLimitRoundedUp =
1046  m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
1047  while (m_asyncStacksCount > halfOfLimitRoundedUp) {
1048  m_allAsyncStacks.pop_front();
1049  --m_asyncStacksCount;
1050  }
1051  cleanupExpiredWeakPointers(m_asyncTaskStacks);
1052  cleanupExpiredWeakPointers(m_storedStackTraces);
1053  for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) {
1054  if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) {
1055  it = m_recurringTasks.erase(it);
1056  } else {
1057  ++it;
1058  }
1059  }
1060  cleanupExpiredWeakPointers(m_framesCache);
1061 }
1062 
1063 std::shared_ptr<StackFrame> V8Debugger::symbolize(
1064  v8::Local<v8::StackFrame> v8Frame) {
1065  auto it = m_framesCache.end();
1066  int frameId = 0;
1067  if (m_maxAsyncCallStackDepth) {
1068  frameId = v8::debug::GetStackFrameId(v8Frame);
1069  it = m_framesCache.find(frameId);
1070  }
1071  if (it != m_framesCache.end() && !it->second.expired()) {
1072  return std::shared_ptr<StackFrame>(it->second);
1073  }
1074  std::shared_ptr<StackFrame> frame(new StackFrame(isolate(), v8Frame));
1075  // TODO(clemensh): Figure out a way to do this translation only right before
1076  // sending the stack trace over wire.
1077  if (v8Frame->IsWasm()) frame->translate(&m_wasmTranslation);
1078  if (m_maxAsyncCallStackDepth) {
1079  m_framesCache[frameId] = frame;
1080  }
1081  return frame;
1082 }
1083 
1084 void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
1085  m_maxAsyncCallStacks = 0;
1086  collectOldAsyncStacksIfNeeded();
1087  m_maxAsyncCallStacks = limit;
1088 }
1089 
1090 std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(int contextGroupId) {
1091  auto it = m_contextGroupIdToDebuggerId.find(contextGroupId);
1092  if (it != m_contextGroupIdToDebuggerId.end()) return it->second;
1093  std::pair<int64_t, int64_t> debuggerId(
1094  v8::debug::GetNextRandomInt64(m_isolate),
1095  v8::debug::GetNextRandomInt64(m_isolate));
1096  if (!debuggerId.first && !debuggerId.second) ++debuggerId.first;
1097  m_contextGroupIdToDebuggerId.insert(
1098  it, std::make_pair(contextGroupId, debuggerId));
1099  m_serializedDebuggerIdToDebuggerId.insert(
1100  std::make_pair(debuggerIdToString(debuggerId), debuggerId));
1101  return debuggerId;
1102 }
1103 
1104 std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(
1105  const String16& serializedDebuggerId) {
1106  auto it = m_serializedDebuggerIdToDebuggerId.find(serializedDebuggerId);
1107  if (it != m_serializedDebuggerIdToDebuggerId.end()) return it->second;
1108  return std::make_pair(0, 0);
1109 }
1110 
1111 bool V8Debugger::addInternalObject(v8::Local<v8::Context> context,
1112  v8::Local<v8::Object> object,
1113  V8InternalValueType type) {
1114  int contextId = InspectedContext::contextId(context);
1115  InspectedContext* inspectedContext = m_inspector->getContext(contextId);
1116  return inspectedContext ? inspectedContext->addInternalObject(object, type)
1117  : false;
1118 }
1119 
1120 void V8Debugger::dumpAsyncTaskStacksStateForTest() {
1121  fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount);
1122  fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
1123  fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size());
1124  fprintf(stdout, "\n");
1125 }
1126 
1127 } // namespace v8_inspector
Local< Value > GetPrototype()
Definition: api.cc:4341
V8_INLINE bool IsEmpty() const
Definition: v8.h:195
bool IsWasm() const
Definition: api.cc:2984
static Local< Array > New(Isolate *isolate, int length=0)
Definition: api.cc:6966
V8_INLINE Local< S > As() const
Definition: v8.h:266
V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local< S > *out) const
Definition: v8.h:347
V8_WARN_UNUSED_RESULT Maybe< bool > SetPrototype(Local< Context > context, Local< Value > prototype)
Definition: api.cc:4349
Definition: libplatform.h:13