1// Copyright 2017 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/debug/debug-stack-trace-iterator.h"
6
7#include "src/api/api-inl.h"
8#include "src/debug/debug-evaluate.h"
9#include "src/debug/debug-scope-iterator.h"
10#include "src/debug/debug.h"
11#include "src/debug/liveedit.h"
12#include "src/execution/frames-inl.h"
13#include "src/execution/isolate.h"
14
15#if V8_ENABLE_WEBASSEMBLY
16#include "src/debug/debug-wasm-objects.h"
17#endif  // V8_ENABLE_WEBASSEMBLY
18
19namespace v8 {
20
21std::unique_ptr<debug::StackTraceIterator> debug::StackTraceIterator::Create(
22    v8::Isolate* isolate, int index) {
23  return std::unique_ptr<debug::StackTraceIterator>(
24      new internal::DebugStackTraceIterator(
25          reinterpret_cast<internal::Isolate*>(isolate), index));
26}
27
28namespace internal {
29
30DebugStackTraceIterator::DebugStackTraceIterator(Isolate* isolate, int index)
31    : isolate_(isolate),
32      iterator_(isolate, isolate->debug()->break_frame_id()),
33      is_top_frame_(true) {
34  if (iterator_.done()) return;
35  std::vector<FrameSummary> frames;
36  iterator_.frame()->Summarize(&frames);
37  inlined_frame_index_ = static_cast<int>(frames.size());
38  Advance();
39  for (; !Done() && index > 0; --index) Advance();
40}
41
42DebugStackTraceIterator::~DebugStackTraceIterator() = default;
43
44bool DebugStackTraceIterator::Done() const { return iterator_.done(); }
45
46void DebugStackTraceIterator::Advance() {
47  while (true) {
48    --inlined_frame_index_;
49    for (; inlined_frame_index_ >= 0; --inlined_frame_index_) {
50      // Omit functions from native and extension scripts.
51      if (FrameSummary::Get(iterator_.frame(), inlined_frame_index_)
52              .is_subject_to_debugging()) {
53        break;
54      }
55      is_top_frame_ = false;
56    }
57    if (inlined_frame_index_ >= 0) {
58      frame_inspector_.reset(new FrameInspector(
59          iterator_.frame(), inlined_frame_index_, isolate_));
60      break;
61    }
62    is_top_frame_ = false;
63    frame_inspector_.reset();
64    iterator_.Advance();
65    if (iterator_.done()) break;
66    std::vector<FrameSummary> frames;
67    iterator_.frame()->Summarize(&frames);
68    inlined_frame_index_ = static_cast<int>(frames.size());
69  }
70}
71
72int DebugStackTraceIterator::GetContextId() const {
73  DCHECK(!Done());
74  Handle<Object> context = frame_inspector_->GetContext();
75  if (context->IsContext()) {
76    Object value = Context::cast(*context).native_context().debug_context_id();
77    if (value.IsSmi()) return Smi::ToInt(value);
78  }
79  return 0;
80}
81
82v8::MaybeLocal<v8::Value> DebugStackTraceIterator::GetReceiver() const {
83  DCHECK(!Done());
84  if (frame_inspector_->IsJavaScript() &&
85      frame_inspector_->GetFunction()->shared().kind() ==
86          FunctionKind::kArrowFunction) {
87    // FrameInspector is not able to get receiver for arrow function.
88    // So let's try to fetch it using same logic as is used to retrieve 'this'
89    // during DebugEvaluate::Local.
90    Handle<JSFunction> function = frame_inspector_->GetFunction();
91    Handle<Context> context(function->context(), isolate_);
92    // Arrow function defined in top level function without references to
93    // variables may have NativeContext as context.
94    if (!context->IsFunctionContext()) return v8::MaybeLocal<v8::Value>();
95    ScopeIterator scope_iterator(
96        isolate_, frame_inspector_.get(),
97        ScopeIterator::ReparseStrategy::kFunctionLiteral);
98    // We lookup this variable in function context only when it is used in arrow
99    // function otherwise V8 can optimize it out.
100    if (!scope_iterator.ClosureScopeHasThisReference()) {
101      return v8::MaybeLocal<v8::Value>();
102    }
103    DisallowGarbageCollection no_gc;
104    int slot_index = context->scope_info().ContextSlotIndex(
105        ReadOnlyRoots(isolate_).this_string_handle());
106    if (slot_index < 0) return v8::MaybeLocal<v8::Value>();
107    Handle<Object> value = handle(context->get(slot_index), isolate_);
108    if (value->IsTheHole(isolate_)) return v8::MaybeLocal<v8::Value>();
109    return Utils::ToLocal(value);
110  }
111
112  Handle<Object> value = frame_inspector_->GetReceiver();
113  if (value.is_null() || (value->IsSmi() || !value->IsTheHole(isolate_))) {
114    return Utils::ToLocal(value);
115  }
116  return v8::MaybeLocal<v8::Value>();
117}
118
119v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const {
120  CHECK(!Done());
121#if V8_ENABLE_WEBASSEMBLY
122  if (frame_inspector_ && frame_inspector_->IsWasm()) {
123    return v8::Local<v8::Value>();
124  }
125#endif  // V8_ENABLE_WEBASSEMBLY
126  CHECK_NOT_NULL(iterator_.frame());
127  bool is_optimized = iterator_.frame()->is_optimized();
128  if (is_optimized || !is_top_frame_ ||
129      !isolate_->debug()->IsBreakAtReturn(iterator_.javascript_frame())) {
130    return v8::Local<v8::Value>();
131  }
132  return Utils::ToLocal(isolate_->debug()->return_value_handle());
133}
134
135v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const {
136  DCHECK(!Done());
137  return Utils::ToLocal(frame_inspector_->GetFunctionName());
138}
139
140v8::Local<v8::debug::Script> DebugStackTraceIterator::GetScript() const {
141  DCHECK(!Done());
142  Handle<Object> value = frame_inspector_->GetScript();
143  if (!value->IsScript()) return v8::Local<v8::debug::Script>();
144  return ToApiHandle<debug::Script>(Handle<Script>::cast(value));
145}
146
147debug::Location DebugStackTraceIterator::GetSourceLocation() const {
148  DCHECK(!Done());
149  v8::Local<v8::debug::Script> script = GetScript();
150  if (script.IsEmpty()) return v8::debug::Location();
151  return script->GetSourceLocation(frame_inspector_->GetSourcePosition());
152}
153
154v8::Local<v8::Function> DebugStackTraceIterator::GetFunction() const {
155  DCHECK(!Done());
156  if (!frame_inspector_->IsJavaScript()) return v8::Local<v8::Function>();
157  return Utils::ToLocal(frame_inspector_->GetFunction());
158}
159
160std::unique_ptr<v8::debug::ScopeIterator>
161DebugStackTraceIterator::GetScopeIterator() const {
162  DCHECK(!Done());
163#if V8_ENABLE_WEBASSEMBLY
164  if (iterator_.frame()->is_wasm()) {
165    return GetWasmScopeIterator(WasmFrame::cast(iterator_.frame()));
166  }
167#endif  // V8_ENABLE_WEBASSEMBLY
168  return std::make_unique<DebugScopeIterator>(isolate_, frame_inspector_.get());
169}
170
171v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate(
172    v8::Local<v8::String> source, bool throw_on_side_effect) {
173  DCHECK(!Done());
174  Handle<Object> value;
175
176  i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
177  if (!DebugEvaluate::Local(isolate_, iterator_.frame()->id(),
178                            inlined_frame_index_, Utils::OpenHandle(*source),
179                            throw_on_side_effect)
180           .ToHandle(&value)) {
181    isolate_->OptionalRescheduleException(false);
182    return v8::MaybeLocal<v8::Value>();
183  }
184  return Utils::ToLocal(value);
185}
186
187}  // namespace internal
188}  // namespace v8
189