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-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/frames-inl.h"
13 #include "src/isolate.h"
14
15 namespace v8 {
16
Create(v8::Isolate * isolate,int index)17 std::unique_ptr<debug::StackTraceIterator> debug::StackTraceIterator::Create(
18 v8::Isolate* isolate, int index) {
19 return std::unique_ptr<debug::StackTraceIterator>(
20 new internal::DebugStackTraceIterator(
21 reinterpret_cast<internal::Isolate*>(isolate), index));
22 }
23
24 namespace internal {
25
DebugStackTraceIterator(Isolate * isolate,int index)26 DebugStackTraceIterator::DebugStackTraceIterator(Isolate* isolate, int index)
27 : isolate_(isolate),
28 iterator_(isolate, isolate->debug()->break_frame_id()),
29 is_top_frame_(true) {
30 if (iterator_.done()) return;
31 std::vector<FrameSummary> frames;
32 iterator_.frame()->Summarize(&frames);
33 inlined_frame_index_ = static_cast<int>(frames.size());
34 Advance();
35 for (; !Done() && index > 0; --index) Advance();
36 }
37
~DebugStackTraceIterator()38 DebugStackTraceIterator::~DebugStackTraceIterator() {}
39
Done() const40 bool DebugStackTraceIterator::Done() const { return iterator_.done(); }
41
Advance()42 void DebugStackTraceIterator::Advance() {
43 while (true) {
44 --inlined_frame_index_;
45 for (; inlined_frame_index_ >= 0; --inlined_frame_index_) {
46 // Omit functions from native and extension scripts.
47 if (FrameSummary::Get(iterator_.frame(), inlined_frame_index_)
48 .is_subject_to_debugging()) {
49 break;
50 }
51 is_top_frame_ = false;
52 }
53 if (inlined_frame_index_ >= 0) {
54 frame_inspector_.reset(new FrameInspector(
55 iterator_.frame(), inlined_frame_index_, isolate_));
56 break;
57 }
58 is_top_frame_ = false;
59 frame_inspector_.reset();
60 iterator_.Advance();
61 if (iterator_.done()) break;
62 std::vector<FrameSummary> frames;
63 iterator_.frame()->Summarize(&frames);
64 inlined_frame_index_ = static_cast<int>(frames.size());
65 }
66 }
67
GetContextId() const68 int DebugStackTraceIterator::GetContextId() const {
69 DCHECK(!Done());
70 Handle<Object> context = frame_inspector_->GetContext();
71 if (context->IsContext()) {
72 Object* value =
73 Context::cast(*context)->native_context()->debug_context_id();
74 if (value->IsSmi()) return Smi::ToInt(value);
75 }
76 return 0;
77 }
78
GetReceiver() const79 v8::MaybeLocal<v8::Value> DebugStackTraceIterator::GetReceiver() const {
80 DCHECK(!Done());
81 if (frame_inspector_->IsJavaScript() &&
82 frame_inspector_->GetFunction()->shared()->kind() == kArrowFunction) {
83 // FrameInspector is not able to get receiver for arrow function.
84 // So let's try to fetch it using same logic as is used to retrieve 'this'
85 // during DebugEvaluate::Local.
86 Handle<JSFunction> function = frame_inspector_->GetFunction();
87 Handle<Context> context(function->context(), isolate_);
88 // Arrow function defined in top level function without references to
89 // variables may have NativeContext as context.
90 if (!context->IsFunctionContext()) return v8::MaybeLocal<v8::Value>();
91 ScopeIterator scope_iterator(isolate_, frame_inspector_.get(),
92 ScopeIterator::COLLECT_NON_LOCALS);
93 // We lookup this variable in function context only when it is used in arrow
94 // function otherwise V8 can optimize it out.
95 if (!scope_iterator.GetNonLocals()->Has(isolate_,
96 isolate_->factory()->this_string()))
97 return v8::MaybeLocal<v8::Value>();
98
99 Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
100 VariableMode mode;
101 InitializationFlag flag;
102 MaybeAssignedFlag maybe_assigned_flag;
103 int slot_index = ScopeInfo::ContextSlotIndex(
104 scope_info, isolate_->factory()->this_string(), &mode, &flag,
105 &maybe_assigned_flag);
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 Handle<Object> value = frame_inspector_->GetReceiver();
112 if (value.is_null() || (value->IsSmi() || !value->IsTheHole(isolate_))) {
113 return Utils::ToLocal(value);
114 }
115 return v8::MaybeLocal<v8::Value>();
116 }
117
GetReturnValue() const118 v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const {
119 DCHECK(!Done());
120 if (frame_inspector_->IsWasm()) return v8::Local<v8::Value>();
121 bool is_optimized = iterator_.frame()->is_optimized();
122 if (is_optimized || !is_top_frame_ ||
123 !isolate_->debug()->IsBreakAtReturn(iterator_.javascript_frame())) {
124 return v8::Local<v8::Value>();
125 }
126 return Utils::ToLocal(isolate_->debug()->return_value_handle());
127 }
128
GetFunctionDebugName() const129 v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const {
130 DCHECK(!Done());
131 return Utils::ToLocal(frame_inspector_->GetFunctionName());
132 }
133
GetScript() const134 v8::Local<v8::debug::Script> DebugStackTraceIterator::GetScript() const {
135 DCHECK(!Done());
136 Handle<Object> value = frame_inspector_->GetScript();
137 if (!value->IsScript()) return v8::Local<v8::debug::Script>();
138 return ToApiHandle<debug::Script>(Handle<Script>::cast(value));
139 }
140
GetSourceLocation() const141 debug::Location DebugStackTraceIterator::GetSourceLocation() const {
142 DCHECK(!Done());
143 v8::Local<v8::debug::Script> script = GetScript();
144 if (script.IsEmpty()) return v8::debug::Location();
145 return script->GetSourceLocation(frame_inspector_->GetSourcePosition());
146 }
147
GetFunction() const148 v8::Local<v8::Function> DebugStackTraceIterator::GetFunction() const {
149 DCHECK(!Done());
150 if (!frame_inspector_->IsJavaScript()) return v8::Local<v8::Function>();
151 return Utils::ToLocal(frame_inspector_->GetFunction());
152 }
153
154 std::unique_ptr<v8::debug::ScopeIterator>
GetScopeIterator() const155 DebugStackTraceIterator::GetScopeIterator() const {
156 DCHECK(!Done());
157 StandardFrame* frame = iterator_.frame();
158 if (frame->is_wasm_interpreter_entry()) {
159 return std::unique_ptr<v8::debug::ScopeIterator>(new DebugWasmScopeIterator(
160 isolate_, iterator_.frame(), inlined_frame_index_));
161 }
162 return std::unique_ptr<v8::debug::ScopeIterator>(
163 new DebugScopeIterator(isolate_, frame_inspector_.get()));
164 }
165
Restart()166 bool DebugStackTraceIterator::Restart() {
167 DCHECK(!Done());
168 if (iterator_.is_wasm()) return false;
169 return !LiveEdit::RestartFrame(iterator_.javascript_frame());
170 }
171
Evaluate(v8::Local<v8::String> source,bool throw_on_side_effect)172 v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate(
173 v8::Local<v8::String> source, bool throw_on_side_effect) {
174 DCHECK(!Done());
175 Handle<Object> value;
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 } // namespace internal
187 } // namespace v8
188