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 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 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 38 DebugStackTraceIterator::~DebugStackTraceIterator() {} 39 40 bool DebugStackTraceIterator::Done() const { return iterator_.done(); } 41 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 68 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 79 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 118 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 129 v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const { 130 DCHECK(!Done()); 131 return Utils::ToLocal(frame_inspector_->GetFunctionName()); 132 } 133 134 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 141 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 148 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> 155 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 166 bool DebugStackTraceIterator::Restart() { 167 DCHECK(!Done()); 168 if (iterator_.is_wasm()) return false; 169 return !LiveEdit::RestartFrame(iterator_.javascript_frame()); 170 } 171 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