| Index: src/inspector/v8-stack-trace-impl.cc
 | 
| diff --git a/src/inspector/v8-stack-trace-impl.cc b/src/inspector/v8-stack-trace-impl.cc
 | 
| index ed82d1264adc18c9f858ef9aca9cd18d0df09624..8044e8f42c9dc305a4be0df9e24c8d68f450576e 100644
 | 
| --- a/src/inspector/v8-stack-trace-impl.cc
 | 
| +++ b/src/inspector/v8-stack-trace-impl.cc
 | 
| @@ -4,10 +4,12 @@
 | 
|  
 | 
|  #include "src/inspector/v8-stack-trace-impl.h"
 | 
|  
 | 
| -#include <algorithm>
 | 
| -
 | 
| +#include "src/inspector/string-util.h"
 | 
| +#include "src/inspector/v8-debugger-agent-impl.h"
 | 
|  #include "src/inspector/v8-debugger.h"
 | 
| -#include "src/inspector/wasm-translation.h"
 | 
| +#include "src/inspector/v8-inspector-impl.h"
 | 
| +
 | 
| +#include "include/v8-version.h"
 | 
|  
 | 
|  namespace v8_inspector {
 | 
|  
 | 
| @@ -15,115 +17,77 @@
 | 
|  
 | 
|  static const v8::StackTrace::StackTraceOptions stackTraceOptions =
 | 
|      static_cast<v8::StackTrace::StackTraceOptions>(
 | 
| -        v8::StackTrace::kDetailed |
 | 
| +        v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset |
 | 
| +        v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
 | 
| +        v8::StackTrace::kFunctionName |
 | 
|          v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
 | 
|  
 | 
| -std::vector<V8StackTraceImpl::Frame> toFramesVector(
 | 
| -    V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
 | 
| -    int maxStackSize) {
 | 
| -  DCHECK(debugger->isolate()->InContext());
 | 
| -  int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
 | 
| -  std::vector<V8StackTraceImpl::Frame> frames;
 | 
| -  for (int i = 0; i < frameCount; ++i) {
 | 
| -    v8::Local<v8::StackFrame> v8Frame = v8StackTrace->GetFrame(i);
 | 
| -    frames.emplace_back(v8Frame);
 | 
| -    // TODO(clemensh): Figure out a way to do this translation only right before
 | 
| -    // sending the stack trace over wire.
 | 
| -    if (v8Frame->IsWasm()) frames.back().translate(debugger->wasmTranslation());
 | 
| -  }
 | 
| -  return frames;
 | 
| -}
 | 
| -
 | 
| -void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
 | 
| -                         std::shared_ptr<AsyncStackTrace>* asyncParent,
 | 
| -                         std::shared_ptr<AsyncStackTrace>* asyncCreation,
 | 
| -                         int* maxAsyncDepth) {
 | 
| -  *asyncParent = debugger->currentAsyncParent();
 | 
| -  *asyncCreation = debugger->currentAsyncCreation();
 | 
| -  if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
 | 
| -
 | 
| -  DCHECK(!*asyncParent || !*asyncCreation ||
 | 
| -         (*asyncParent)->contextGroupId() ==
 | 
| -             (*asyncCreation)->contextGroupId());
 | 
| -  // Do not accidentally append async call chain from another group. This should
 | 
| -  // not happen if we have proper instrumentation, but let's double-check to be
 | 
| -  // safe.
 | 
| -  if (contextGroupId && *asyncParent && (*asyncParent)->contextGroupId() &&
 | 
| -      (*asyncParent)->contextGroupId() != contextGroupId) {
 | 
| -    asyncParent->reset();
 | 
| -    asyncCreation->reset();
 | 
| -    if (maxAsyncDepth) *maxAsyncDepth = 0;
 | 
| -    return;
 | 
| -  }
 | 
| -
 | 
| -  // Only the top stack in the chain may be empty and doesn't contain creation
 | 
| -  // stack, so ensure that second stack is non-empty (it's the top of appended
 | 
| -  // chain).
 | 
| -  if (*asyncParent && !(*asyncCreation) && !(*asyncParent)->creation().lock() &&
 | 
| -      (*asyncParent)->isEmpty()) {
 | 
| -    *asyncParent = (*asyncParent)->parent().lock();
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
 | 
| -    const std::vector<V8StackTraceImpl::Frame>& frames,
 | 
| -    const std::shared_ptr<AsyncStackTrace>& asyncParent,
 | 
| -    const std::shared_ptr<AsyncStackTrace>& asyncCreation, int maxAsyncDepth) {
 | 
| -  std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
 | 
| -      inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
 | 
| -  for (size_t i = 0; i < frames.size(); i++) {
 | 
| -    inspectorFrames->addItem(frames[i].buildInspectorObject());
 | 
| -  }
 | 
| -  std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
 | 
| -      protocol::Runtime::StackTrace::create()
 | 
| -          .setCallFrames(std::move(inspectorFrames))
 | 
| -          .build();
 | 
| -  if (asyncParent && maxAsyncDepth > 0) {
 | 
| -    stackTrace->setParent(asyncParent->buildInspectorObject(asyncCreation.get(),
 | 
| -                                                            maxAsyncDepth - 1));
 | 
| -  }
 | 
| -  return stackTrace;
 | 
| +V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame,
 | 
| +                                WasmTranslation* wasmTranslation) {
 | 
| +  String16 scriptId = String16::fromInteger(frame->GetScriptId());
 | 
| +  String16 sourceName;
 | 
| +  v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
 | 
| +  if (!sourceNameValue.IsEmpty())
 | 
| +    sourceName = toProtocolString(sourceNameValue);
 | 
| +
 | 
| +  String16 functionName;
 | 
| +  v8::Local<v8::String> functionNameValue(frame->GetFunctionName());
 | 
| +  if (!functionNameValue.IsEmpty())
 | 
| +    functionName = toProtocolString(functionNameValue);
 | 
| +
 | 
| +  int sourceLineNumber = frame->GetLineNumber() - 1;
 | 
| +  int sourceColumn = frame->GetColumn() - 1;
 | 
| +  // TODO(clemensh): Figure out a way to do this translation only right before
 | 
| +  // sending the stack trace over wire.
 | 
| +  if (frame->IsWasm()) {
 | 
| +    wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
 | 
| +        &scriptId, &sourceLineNumber, &sourceColumn);
 | 
| +  }
 | 
| +  return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
 | 
| +                                 sourceLineNumber + 1, sourceColumn + 1);
 | 
| +}
 | 
| +
 | 
| +void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
 | 
| +                    std::vector<V8StackTraceImpl::Frame>& frames,
 | 
| +                    size_t maxStackSize, v8::Isolate* isolate,
 | 
| +                    V8Debugger* debugger) {
 | 
| +  DCHECK(isolate->InContext());
 | 
| +  int frameCount = stackTrace->GetFrameCount();
 | 
| +  if (frameCount > static_cast<int>(maxStackSize))
 | 
| +    frameCount = static_cast<int>(maxStackSize);
 | 
| +  WasmTranslation* wasmTranslation = debugger->wasmTranslation();
 | 
| +  for (int i = 0; i < frameCount; i++) {
 | 
| +    v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
 | 
| +    frames.push_back(toFrame(stackFrame, wasmTranslation));
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  }  //  namespace
 | 
|  
 | 
| -V8StackTraceImpl::Frame::Frame(v8::Local<v8::StackFrame> v8Frame)
 | 
| -    : m_functionName(toProtocolString(v8Frame->GetFunctionName())),
 | 
| -      m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
 | 
| -      m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())),
 | 
| -      m_lineNumber(v8Frame->GetLineNumber() - 1),
 | 
| -      m_columnNumber(v8Frame->GetColumn() - 1) {
 | 
| -  DCHECK(m_lineNumber + 1 != v8::Message::kNoLineNumberInfo);
 | 
| -  DCHECK(m_columnNumber + 1 != v8::Message::kNoColumnInfo);
 | 
| -}
 | 
| -
 | 
| -void V8StackTraceImpl::Frame::translate(WasmTranslation* wasmTranslation) {
 | 
| -  wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
 | 
| -      &m_scriptId, &m_lineNumber, &m_columnNumber);
 | 
| -}
 | 
| -
 | 
| -const String16& V8StackTraceImpl::Frame::functionName() const {
 | 
| -  return m_functionName;
 | 
| -}
 | 
| -
 | 
| -const String16& V8StackTraceImpl::Frame::scriptId() const { return m_scriptId; }
 | 
| -
 | 
| -const String16& V8StackTraceImpl::Frame::sourceURL() const {
 | 
| -  return m_sourceURL;
 | 
| -}
 | 
| -
 | 
| -int V8StackTraceImpl::Frame::lineNumber() const { return m_lineNumber; }
 | 
| -
 | 
| -int V8StackTraceImpl::Frame::columnNumber() const { return m_columnNumber; }
 | 
| -
 | 
| +V8StackTraceImpl::Frame::Frame(const String16& functionName,
 | 
| +                               const String16& scriptId,
 | 
| +                               const String16& scriptName, int lineNumber,
 | 
| +                               int column)
 | 
| +    : m_functionName(functionName),
 | 
| +      m_scriptId(scriptId),
 | 
| +      m_scriptName(scriptName),
 | 
| +      m_lineNumber(lineNumber),
 | 
| +      m_columnNumber(column) {
 | 
| +  DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo);
 | 
| +  DCHECK(m_columnNumber != v8::Message::kNoColumnInfo);
 | 
| +}
 | 
| +
 | 
| +// buildInspectorObject() and SourceLocation's toTracedValue() should set the
 | 
| +// same fields.
 | 
| +// If either of them is modified, the other should be also modified.
 | 
|  std::unique_ptr<protocol::Runtime::CallFrame>
 | 
|  V8StackTraceImpl::Frame::buildInspectorObject() const {
 | 
|    return protocol::Runtime::CallFrame::create()
 | 
|        .setFunctionName(m_functionName)
 | 
|        .setScriptId(m_scriptId)
 | 
| -      .setUrl(m_sourceURL)
 | 
| -      .setLineNumber(m_lineNumber)
 | 
| -      .setColumnNumber(m_columnNumber)
 | 
| +      .setUrl(m_scriptName)
 | 
| +      .setLineNumber(m_lineNumber - 1)
 | 
| +      .setColumnNumber(m_columnNumber - 1)
 | 
|        .build();
 | 
|  }
 | 
|  
 | 
| @@ -131,96 +95,196 @@
 | 
|  void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
 | 
|      v8::Isolate* isolate, bool capture) {
 | 
|    isolate->SetCaptureStackTraceForUncaughtExceptions(
 | 
| -      capture, V8StackTraceImpl::maxCallStackSizeToCapture);
 | 
| +      capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions);
 | 
|  }
 | 
|  
 | 
|  // static
 | 
|  std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
 | 
|      V8Debugger* debugger, int contextGroupId,
 | 
| -    v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
 | 
| +    v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize,
 | 
| +    const String16& description) {
 | 
|    DCHECK(debugger);
 | 
| -
 | 
| -  v8::Isolate* isolate = debugger->isolate();
 | 
| +  v8::Isolate* isolate = debugger->inspector()->isolate();
 | 
|    v8::HandleScope scope(isolate);
 | 
| -
 | 
|    std::vector<V8StackTraceImpl::Frame> frames;
 | 
| -  if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
 | 
| -    frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
 | 
| -  }
 | 
| -
 | 
| -  int maxAsyncDepth = 0;
 | 
| -  std::shared_ptr<AsyncStackTrace> asyncParent;
 | 
| -  std::shared_ptr<AsyncStackTrace> asyncCreation;
 | 
| -  calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
 | 
| -                      &maxAsyncDepth);
 | 
| -  if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
 | 
| -  return std::unique_ptr<V8StackTraceImpl>(
 | 
| -      new V8StackTraceImpl(frames, maxAsyncDepth, asyncParent, asyncCreation));
 | 
| +  if (!stackTrace.IsEmpty()) {
 | 
| +    toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger);
 | 
| +  }
 | 
| +
 | 
| +  int maxAsyncCallChainDepth = 1;
 | 
| +  V8StackTraceImpl* asyncCallChain = nullptr;
 | 
| +  V8StackTraceImpl* creationStack = nullptr;
 | 
| +  if (maxStackSize > 1) {
 | 
| +    asyncCallChain = debugger->currentAsyncCallChain();
 | 
| +    creationStack = debugger->currentAsyncTaskCreationStack();
 | 
| +    maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth();
 | 
| +  }
 | 
| +
 | 
| +  // Do not accidentally append async call chain from another group. This should
 | 
| +  // not happen if we have proper instrumentation, but let's double-check to be
 | 
| +  // safe.
 | 
| +  if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId &&
 | 
| +      asyncCallChain->m_contextGroupId != contextGroupId) {
 | 
| +    asyncCallChain = nullptr;
 | 
| +    creationStack = nullptr;
 | 
| +    maxAsyncCallChainDepth = 1;
 | 
| +  }
 | 
| +
 | 
| +  // Only the top stack in the chain may be empty and doesn't contain creation
 | 
| +  // stack , so ensure that second stack is non-empty (it's the top of appended
 | 
| +  // chain).
 | 
| +  if (asyncCallChain && !creationStack && !asyncCallChain->m_creation &&
 | 
| +      asyncCallChain->isEmpty()) {
 | 
| +    asyncCallChain = asyncCallChain->m_parent.get();
 | 
| +  }
 | 
| +
 | 
| +  if (frames.empty() && !creationStack && !asyncCallChain) return nullptr;
 | 
| +
 | 
| +  // When async call chain is empty but doesn't contain useful schedule stack
 | 
| +  // and parent async call chain contains creationg stack but doesn't
 | 
| +  // synchronous we can merge them together.
 | 
| +  // e.g. Promise ThenableJob.
 | 
| +  if (asyncCallChain && frames.empty() &&
 | 
| +      asyncCallChain->m_description == description && !creationStack) {
 | 
| +    return asyncCallChain->cloneImpl();
 | 
| +  }
 | 
| +
 | 
| +  std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(
 | 
| +      contextGroupId, description, frames,
 | 
| +      asyncCallChain ? asyncCallChain->cloneImpl() : nullptr,
 | 
| +      creationStack ? creationStack->cloneImpl() : nullptr));
 | 
| +
 | 
| +  // Crop to not exceed maxAsyncCallChainDepth.
 | 
| +  V8StackTraceImpl* deepest = result.get();
 | 
| +  while (deepest && maxAsyncCallChainDepth) {
 | 
| +    deepest = deepest->m_parent.get();
 | 
| +    maxAsyncCallChainDepth--;
 | 
| +  }
 | 
| +  if (deepest) deepest->m_parent.reset();
 | 
| +
 | 
| +  return result;
 | 
|  }
 | 
|  
 | 
|  // static
 | 
|  std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
 | 
| -    V8Debugger* debugger, int contextGroupId, int maxStackSize) {
 | 
| +    V8Debugger* debugger, int contextGroupId, size_t maxStackSize,
 | 
| +    const String16& description) {
 | 
|    DCHECK(debugger);
 | 
| -  v8::Isolate* isolate = debugger->isolate();
 | 
| +  v8::Isolate* isolate = debugger->inspector()->isolate();
 | 
|    v8::HandleScope handleScope(isolate);
 | 
| -  v8::Local<v8::StackTrace> v8StackTrace;
 | 
| +  v8::Local<v8::StackTrace> stackTrace;
 | 
|    if (isolate->InContext()) {
 | 
| -    v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
 | 
| -                                                     stackTraceOptions);
 | 
| -  }
 | 
| -  return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
 | 
| -                                  maxStackSize);
 | 
| -}
 | 
| -
 | 
| -V8StackTraceImpl::V8StackTraceImpl(
 | 
| -    const std::vector<Frame> frames, int maxAsyncDepth,
 | 
| -    std::shared_ptr<AsyncStackTrace> asyncParent,
 | 
| -    std::shared_ptr<AsyncStackTrace> asyncCreation)
 | 
| -    : m_frames(frames),
 | 
| -      m_maxAsyncDepth(maxAsyncDepth),
 | 
| -      m_asyncParent(asyncParent),
 | 
| -      m_asyncCreation(asyncCreation) {}
 | 
| +    stackTrace = v8::StackTrace::CurrentStackTrace(
 | 
| +        isolate, static_cast<int>(maxStackSize), stackTraceOptions);
 | 
| +  }
 | 
| +  return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace,
 | 
| +                                  maxStackSize, description);
 | 
| +}
 | 
| +
 | 
| +std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() {
 | 
| +  std::vector<Frame> framesCopy(m_frames);
 | 
| +  std::unique_ptr<V8StackTraceImpl> copy(
 | 
| +      new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy,
 | 
| +                           m_parent ? m_parent->cloneImpl() : nullptr,
 | 
| +                           m_creation ? m_creation->cloneImpl() : nullptr));
 | 
| +  return copy;
 | 
| +}
 | 
| +
 | 
| +std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
 | 
| +  std::vector<Frame> frames;
 | 
| +  for (size_t i = 0; i < m_frames.size(); i++) {
 | 
| +    frames.push_back(m_frames.at(i));
 | 
| +  }
 | 
| +  return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
 | 
| +      m_contextGroupId, m_description, frames, nullptr, nullptr));
 | 
| +}
 | 
| +
 | 
| +V8StackTraceImpl::V8StackTraceImpl(int contextGroupId,
 | 
| +                                   const String16& description,
 | 
| +                                   std::vector<Frame>& frames,
 | 
| +                                   std::unique_ptr<V8StackTraceImpl> parent,
 | 
| +                                   std::unique_ptr<V8StackTraceImpl> creation)
 | 
| +    : m_contextGroupId(contextGroupId),
 | 
| +      m_description(description),
 | 
| +      m_parent(std::move(parent)),
 | 
| +      m_creation(std::move(creation)) {
 | 
| +  m_frames.swap(frames);
 | 
| +}
 | 
|  
 | 
|  V8StackTraceImpl::~V8StackTraceImpl() {}
 | 
|  
 | 
| -std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
 | 
| -  return std::unique_ptr<V8StackTrace>(
 | 
| -      new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(),
 | 
| -                           std::shared_ptr<AsyncStackTrace>()));
 | 
| -}
 | 
| -
 | 
| -bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
 | 
| -
 | 
|  StringView V8StackTraceImpl::topSourceURL() const {
 | 
| -  return toStringView(m_frames[0].sourceURL());
 | 
| +  DCHECK(m_frames.size());
 | 
| +  return toStringView(m_frames[0].m_scriptName);
 | 
|  }
 | 
|  
 | 
|  int V8StackTraceImpl::topLineNumber() const {
 | 
| -  return m_frames[0].lineNumber() + 1;
 | 
| +  DCHECK(m_frames.size());
 | 
| +  return m_frames[0].m_lineNumber;
 | 
|  }
 | 
|  
 | 
|  int V8StackTraceImpl::topColumnNumber() const {
 | 
| -  return m_frames[0].columnNumber() + 1;
 | 
| +  DCHECK(m_frames.size());
 | 
| +  return m_frames[0].m_columnNumber;
 | 
| +}
 | 
| +
 | 
| +StringView V8StackTraceImpl::topFunctionName() const {
 | 
| +  DCHECK(m_frames.size());
 | 
| +  return toStringView(m_frames[0].m_functionName);
 | 
|  }
 | 
|  
 | 
|  StringView V8StackTraceImpl::topScriptId() const {
 | 
| -  return toStringView(m_frames[0].scriptId());
 | 
| -}
 | 
| -
 | 
| -StringView V8StackTraceImpl::topFunctionName() const {
 | 
| -  return toStringView(m_frames[0].functionName());
 | 
| +  DCHECK(m_frames.size());
 | 
| +  return toStringView(m_frames[0].m_scriptId);
 | 
|  }
 | 
|  
 | 
|  std::unique_ptr<protocol::Runtime::StackTrace>
 | 
|  V8StackTraceImpl::buildInspectorObjectImpl() const {
 | 
| -  return buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
 | 
| -                                    m_asyncCreation.lock(), m_maxAsyncDepth);
 | 
| +  return buildInspectorObjectImpl(nullptr);
 | 
| +}
 | 
| +
 | 
| +std::unique_ptr<protocol::Runtime::StackTrace>
 | 
| +V8StackTraceImpl::buildInspectorObjectImpl(V8StackTraceImpl* creation) const {
 | 
| +  std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames =
 | 
| +      protocol::Array<protocol::Runtime::CallFrame>::create();
 | 
| +  for (size_t i = 0; i < m_frames.size(); i++)
 | 
| +    frames->addItem(m_frames.at(i).buildInspectorObject());
 | 
| +
 | 
| +  std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
 | 
| +      protocol::Runtime::StackTrace::create()
 | 
| +          .setCallFrames(std::move(frames))
 | 
| +          .build();
 | 
| +  if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
 | 
| +  if (m_parent) {
 | 
| +    stackTrace->setParent(m_parent->buildInspectorObjectImpl(m_creation.get()));
 | 
| +  }
 | 
| +  if (creation && creation->m_frames.size()) {
 | 
| +    stackTrace->setPromiseCreationFrame(
 | 
| +        creation->m_frames[0].buildInspectorObject());
 | 
| +  }
 | 
| +  return stackTrace;
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +std::unique_ptr<protocol::Runtime::StackTrace>
 | 
| +V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) {
 | 
| +  DCHECK(debugger);
 | 
| +  v8::HandleScope handleScope(debugger->inspector()->isolate());
 | 
| +  // Next call collapses possible empty stack and ensures
 | 
| +  // maxAsyncCallChainDepth.
 | 
| +  V8StackTraceImpl* asyncChain = debugger->currentAsyncCallChain();
 | 
| +  if (!asyncChain) return nullptr;
 | 
| +  std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create(
 | 
| +      debugger, asyncChain->m_contextGroupId, v8::Local<v8::StackTrace>(),
 | 
| +      V8StackTraceImpl::maxCallStackSizeToCapture);
 | 
| +  if (!fullChain || !fullChain->m_parent) return nullptr;
 | 
| +  return fullChain->m_parent->buildInspectorObjectImpl(
 | 
| +      fullChain->m_creation.get());
 | 
|  }
 | 
|  
 | 
|  std::unique_ptr<protocol::Runtime::API::StackTrace>
 | 
|  V8StackTraceImpl::buildInspectorObject() const {
 | 
| -  return buildInspectorObjectImpl();
 | 
| +  return buildInspectorObjectImpl(nullptr);
 | 
|  }
 | 
|  
 | 
|  std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
 | 
| @@ -242,77 +306,4 @@
 | 
|    return StringBufferImpl::adopt(string);
 | 
|  }
 | 
|  
 | 
| -// static
 | 
| -std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
 | 
| -    V8Debugger* debugger, int contextGroupId, const String16& description,
 | 
| -    int maxStackSize) {
 | 
| -  DCHECK(debugger);
 | 
| -
 | 
| -  v8::Isolate* isolate = debugger->isolate();
 | 
| -  v8::HandleScope handleScope(isolate);
 | 
| -
 | 
| -  std::vector<V8StackTraceImpl::Frame> frames;
 | 
| -  if (isolate->InContext()) {
 | 
| -    v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
 | 
| -        isolate, maxStackSize, stackTraceOptions);
 | 
| -    frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
 | 
| -  }
 | 
| -
 | 
| -  std::shared_ptr<AsyncStackTrace> asyncParent;
 | 
| -  std::shared_ptr<AsyncStackTrace> asyncCreation;
 | 
| -  calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
 | 
| -                      nullptr);
 | 
| -
 | 
| -  if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
 | 
| -
 | 
| -  // When async call chain is empty but doesn't contain useful schedule stack
 | 
| -  // and parent async call chain contains creationg stack but doesn't
 | 
| -  // synchronous we can merge them together.
 | 
| -  // e.g. Promise ThenableJob.
 | 
| -  if (asyncParent && frames.empty() &&
 | 
| -      asyncParent->m_description == description && !asyncCreation) {
 | 
| -    return asyncParent;
 | 
| -  }
 | 
| -
 | 
| -  return std::shared_ptr<AsyncStackTrace>(new AsyncStackTrace(
 | 
| -      contextGroupId, description, frames, asyncParent, asyncCreation));
 | 
| -}
 | 
| -
 | 
| -AsyncStackTrace::AsyncStackTrace(
 | 
| -    int contextGroupId, const String16& description,
 | 
| -    const std::vector<V8StackTraceImpl::Frame>& frames,
 | 
| -    std::shared_ptr<AsyncStackTrace> asyncParent,
 | 
| -    std::shared_ptr<AsyncStackTrace> asyncCreation)
 | 
| -    : m_contextGroupId(contextGroupId),
 | 
| -      m_description(description),
 | 
| -      m_frames(frames),
 | 
| -      m_asyncParent(asyncParent),
 | 
| -      m_asyncCreation(asyncCreation) {}
 | 
| -
 | 
| -std::unique_ptr<protocol::Runtime::StackTrace>
 | 
| -AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation,
 | 
| -                                      int maxAsyncDepth) const {
 | 
| -  std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
 | 
| -      buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
 | 
| -                                 m_asyncCreation.lock(), maxAsyncDepth);
 | 
| -  if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
 | 
| -  if (asyncCreation && !asyncCreation->isEmpty()) {
 | 
| -    stackTrace->setPromiseCreationFrame(
 | 
| -        asyncCreation->m_frames[0].buildInspectorObject());
 | 
| -  }
 | 
| -  return stackTrace;
 | 
| -}
 | 
| -
 | 
| -int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
 | 
| -
 | 
| -std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
 | 
| -  return m_asyncParent;
 | 
| -}
 | 
| -
 | 
| -std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const {
 | 
| -  return m_asyncCreation;
 | 
| -}
 | 
| -
 | 
| -bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
 | 
| -
 | 
|  }  // namespace v8_inspector
 | 
| 
 |