• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-interface.h"
10 #include "src/debug/debug-scope-iterator.h"
11 #include "src/debug/debug.h"
12 #include "src/debug/liveedit.h"
13 #include "src/execution/frames-inl.h"
14 #include "src/execution/frames.h"
15 #include "src/execution/isolate.h"
16 #include "src/wasm/wasm-debug-evaluate.h"
17 #include "src/wasm/wasm-debug.h"
18 
19 namespace v8 {
20 
SupportsWasmDebugEvaluate()21 bool debug::StackTraceIterator::SupportsWasmDebugEvaluate() {
22   return i::FLAG_wasm_expose_debug_eval;
23 }
24 
Create(v8::Isolate * isolate,int index)25 std::unique_ptr<debug::StackTraceIterator> debug::StackTraceIterator::Create(
26     v8::Isolate* isolate, int index) {
27   return std::unique_ptr<debug::StackTraceIterator>(
28       new internal::DebugStackTraceIterator(
29           reinterpret_cast<internal::Isolate*>(isolate), index));
30 }
31 
32 namespace internal {
33 
DebugStackTraceIterator(Isolate * isolate,int index)34 DebugStackTraceIterator::DebugStackTraceIterator(Isolate* isolate, int index)
35     : isolate_(isolate),
36       iterator_(isolate, isolate->debug()->break_frame_id()),
37       is_top_frame_(true) {
38   if (iterator_.done()) return;
39   std::vector<FrameSummary> frames;
40   iterator_.frame()->Summarize(&frames);
41   inlined_frame_index_ = static_cast<int>(frames.size());
42   Advance();
43   for (; !Done() && index > 0; --index) Advance();
44 }
45 
46 DebugStackTraceIterator::~DebugStackTraceIterator() = default;
47 
Done() const48 bool DebugStackTraceIterator::Done() const { return iterator_.done(); }
49 
Advance()50 void DebugStackTraceIterator::Advance() {
51   while (true) {
52     --inlined_frame_index_;
53     for (; inlined_frame_index_ >= 0; --inlined_frame_index_) {
54       // Omit functions from native and extension scripts.
55       if (FrameSummary::Get(iterator_.frame(), inlined_frame_index_)
56               .is_subject_to_debugging()) {
57         break;
58       }
59       is_top_frame_ = false;
60     }
61     if (inlined_frame_index_ >= 0) {
62       frame_inspector_.reset(new FrameInspector(
63           iterator_.frame(), inlined_frame_index_, isolate_));
64       break;
65     }
66     is_top_frame_ = false;
67     frame_inspector_.reset();
68     iterator_.Advance();
69     if (iterator_.done()) break;
70     std::vector<FrameSummary> frames;
71     iterator_.frame()->Summarize(&frames);
72     inlined_frame_index_ = static_cast<int>(frames.size());
73   }
74 }
75 
GetContextId() const76 int DebugStackTraceIterator::GetContextId() const {
77   DCHECK(!Done());
78   Handle<Object> context = frame_inspector_->GetContext();
79   if (context->IsContext()) {
80     Object value = Context::cast(*context).native_context().debug_context_id();
81     if (value.IsSmi()) return Smi::ToInt(value);
82   }
83   return 0;
84 }
85 
GetReceiver() const86 v8::MaybeLocal<v8::Value> DebugStackTraceIterator::GetReceiver() const {
87   DCHECK(!Done());
88   if (frame_inspector_->IsJavaScript() &&
89       frame_inspector_->GetFunction()->shared().kind() == kArrowFunction) {
90     // FrameInspector is not able to get receiver for arrow function.
91     // So let's try to fetch it using same logic as is used to retrieve 'this'
92     // during DebugEvaluate::Local.
93     Handle<JSFunction> function = frame_inspector_->GetFunction();
94     Handle<Context> context(function->context(), isolate_);
95     // Arrow function defined in top level function without references to
96     // variables may have NativeContext as context.
97     if (!context->IsFunctionContext()) return v8::MaybeLocal<v8::Value>();
98     ScopeIterator scope_iterator(
99         isolate_, frame_inspector_.get(),
100         ScopeIterator::ReparseStrategy::kFunctionLiteral);
101     // We lookup this variable in function context only when it is used in arrow
102     // function otherwise V8 can optimize it out.
103     if (!scope_iterator.ClosureScopeHasThisReference()) {
104       return v8::MaybeLocal<v8::Value>();
105     }
106     DisallowHeapAllocation no_gc;
107     VariableMode mode;
108     InitializationFlag flag;
109     MaybeAssignedFlag maybe_assigned_flag;
110     IsStaticFlag is_static_flag;
111     int slot_index = ScopeInfo::ContextSlotIndex(
112         context->scope_info(), ReadOnlyRoots(isolate_->heap()).this_string(),
113         &mode, &flag, &maybe_assigned_flag, &is_static_flag);
114     if (slot_index < 0) return v8::MaybeLocal<v8::Value>();
115     Handle<Object> value = handle(context->get(slot_index), isolate_);
116     if (value->IsTheHole(isolate_)) return v8::MaybeLocal<v8::Value>();
117     return Utils::ToLocal(value);
118   }
119 
120   Handle<Object> value = frame_inspector_->GetReceiver();
121   if (value.is_null() || (value->IsSmi() || !value->IsTheHole(isolate_))) {
122     return Utils::ToLocal(value);
123   }
124   return v8::MaybeLocal<v8::Value>();
125 }
126 
GetReturnValue() const127 v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const {
128   CHECK(!Done());
129   if (frame_inspector_ && frame_inspector_->IsWasm()) {
130     return v8::Local<v8::Value>();
131   }
132   CHECK_NOT_NULL(iterator_.frame());
133   bool is_optimized = iterator_.frame()->is_optimized();
134   if (is_optimized || !is_top_frame_ ||
135       !isolate_->debug()->IsBreakAtReturn(iterator_.javascript_frame())) {
136     return v8::Local<v8::Value>();
137   }
138   return Utils::ToLocal(isolate_->debug()->return_value_handle());
139 }
140 
GetFunctionDebugName() const141 v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const {
142   DCHECK(!Done());
143   return Utils::ToLocal(frame_inspector_->GetFunctionName());
144 }
145 
GetScript() const146 v8::Local<v8::debug::Script> DebugStackTraceIterator::GetScript() const {
147   DCHECK(!Done());
148   Handle<Object> value = frame_inspector_->GetScript();
149   if (!value->IsScript()) return v8::Local<v8::debug::Script>();
150   return ToApiHandle<debug::Script>(Handle<Script>::cast(value));
151 }
152 
GetSourceLocation() const153 debug::Location DebugStackTraceIterator::GetSourceLocation() const {
154   DCHECK(!Done());
155   v8::Local<v8::debug::Script> script = GetScript();
156   if (script.IsEmpty()) return v8::debug::Location();
157   return script->GetSourceLocation(frame_inspector_->GetSourcePosition());
158 }
159 
GetFunction() const160 v8::Local<v8::Function> DebugStackTraceIterator::GetFunction() const {
161   DCHECK(!Done());
162   if (!frame_inspector_->IsJavaScript()) return v8::Local<v8::Function>();
163   return Utils::ToLocal(frame_inspector_->GetFunction());
164 }
165 
166 std::unique_ptr<v8::debug::ScopeIterator>
GetScopeIterator() const167 DebugStackTraceIterator::GetScopeIterator() const {
168   DCHECK(!Done());
169   CommonFrame* frame = iterator_.frame();
170   if (frame->is_wasm()) {
171     return std::make_unique<DebugWasmScopeIterator>(isolate_,
172                                                     WasmFrame::cast(frame));
173   }
174   return std::make_unique<DebugScopeIterator>(isolate_, frame_inspector_.get());
175 }
176 
Restart()177 bool DebugStackTraceIterator::Restart() {
178   DCHECK(!Done());
179   if (iterator_.is_wasm()) return false;
180   return LiveEdit::RestartFrame(iterator_.javascript_frame());
181 }
182 
Evaluate(v8::Local<v8::String> source,bool throw_on_side_effect)183 v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate(
184     v8::Local<v8::String> source, bool throw_on_side_effect) {
185   DCHECK(!Done());
186   Handle<Object> value;
187 
188   i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
189   bool success = false;
190   if (iterator_.is_wasm()) {
191     FrameSummary summary = FrameSummary::Get(iterator_.frame(), 0);
192     const FrameSummary::WasmFrameSummary& wasmSummary = summary.AsWasm();
193     Handle<WasmInstanceObject> instance = wasmSummary.wasm_instance();
194 
195     success = DebugEvaluate::WebAssembly(instance, iterator_.frame()->id(),
196                                          Utils::OpenHandle(*source),
197                                          throw_on_side_effect)
198                   .ToHandle(&value);
199   } else {
200     success = DebugEvaluate::Local(
201                   isolate_, iterator_.frame()->id(), inlined_frame_index_,
202                   Utils::OpenHandle(*source), throw_on_side_effect)
203                   .ToHandle(&value);
204   }
205   if (!success) {
206     isolate_->OptionalRescheduleException(false);
207     return v8::MaybeLocal<v8::Value>();
208   }
209   return Utils::ToLocal(value);
210 }
211 
EvaluateWasm(internal::Vector<const internal::byte> source,int frame_index)212 v8::MaybeLocal<v8::String> DebugStackTraceIterator::EvaluateWasm(
213     internal::Vector<const internal::byte> source, int frame_index) {
214   DCHECK(!Done());
215   if (!i::FLAG_wasm_expose_debug_eval || !iterator_.is_wasm()) {
216     return v8::MaybeLocal<v8::String>();
217   }
218   Handle<String> value;
219   i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
220 
221   FrameSummary summary = FrameSummary::Get(iterator_.frame(), 0);
222   const FrameSummary::WasmFrameSummary& wasmSummary = summary.AsWasm();
223   Handle<WasmInstanceObject> instance = wasmSummary.wasm_instance();
224 
225   if (!v8::internal::wasm::DebugEvaluate(source, instance, iterator_.frame())
226            .ToHandle(&value)) {
227     isolate_->OptionalRescheduleException(false);
228     return v8::MaybeLocal<v8::String>();
229   }
230   return Utils::ToLocal(value);
231 }
232 }  // namespace internal
233 }  // namespace v8
234