| // Copyright 2017 the V8 project authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/debug/debug-stack-trace-iterator.h" | |
| #include "src/api-inl.h" | |
| #include "src/debug/debug-evaluate.h" | |
| #include "src/debug/debug-scope-iterator.h" | |
| #include "src/debug/debug.h" | |
| #include "src/debug/liveedit.h" | |
| #include "src/frames-inl.h" | |
| #include "src/isolate.h" | |
| namespace v8 { | |
| std::unique_ptr<debug::StackTraceIterator> debug::StackTraceIterator::Create( | |
| v8::Isolate* isolate, int index) { | |
| return std::unique_ptr<debug::StackTraceIterator>( | |
| new internal::DebugStackTraceIterator( | |
| reinterpret_cast<internal::Isolate*>(isolate), index)); | |
| } | |
| namespace internal { | |
| DebugStackTraceIterator::DebugStackTraceIterator(Isolate* isolate, int index) | |
| : isolate_(isolate), | |
| iterator_(isolate, isolate->debug()->break_frame_id()), | |
| is_top_frame_(true) { | |
| if (iterator_.done()) return; | |
| std::vector<FrameSummary> frames; | |
| iterator_.frame()->Summarize(&frames); | |
| inlined_frame_index_ = static_cast<int>(frames.size()); | |
| Advance(); | |
| for (; !Done() && index > 0; --index) Advance(); | |
| } | |
| DebugStackTraceIterator::~DebugStackTraceIterator() {} | |
| bool DebugStackTraceIterator::Done() const { return iterator_.done(); } | |
| void DebugStackTraceIterator::Advance() { | |
| while (true) { | |
| --inlined_frame_index_; | |
| for (; inlined_frame_index_ >= 0; --inlined_frame_index_) { | |
| // Omit functions from native and extension scripts. | |
| if (FrameSummary::Get(iterator_.frame(), inlined_frame_index_) | |
| .is_subject_to_debugging()) { | |
| break; | |
| } | |
| is_top_frame_ = false; | |
| } | |
| if (inlined_frame_index_ >= 0) { | |
| frame_inspector_.reset(new FrameInspector( | |
| iterator_.frame(), inlined_frame_index_, isolate_)); | |
| break; | |
| } | |
| is_top_frame_ = false; | |
| frame_inspector_.reset(); | |
| iterator_.Advance(); | |
| if (iterator_.done()) break; | |
| std::vector<FrameSummary> frames; | |
| iterator_.frame()->Summarize(&frames); | |
| inlined_frame_index_ = static_cast<int>(frames.size()); | |
| } | |
| } | |
| int DebugStackTraceIterator::GetContextId() const { | |
| DCHECK(!Done()); | |
| Handle<Object> context = frame_inspector_->GetContext(); | |
| if (context->IsContext()) { | |
| Object* value = | |
| Context::cast(*context)->native_context()->debug_context_id(); | |
| if (value->IsSmi()) return Smi::ToInt(value); | |
| } | |
| return 0; | |
| } | |
| v8::MaybeLocal<v8::Value> DebugStackTraceIterator::GetReceiver() const { | |
| DCHECK(!Done()); | |
| if (frame_inspector_->IsJavaScript() && | |
| frame_inspector_->GetFunction()->shared()->kind() == kArrowFunction) { | |
| // FrameInspector is not able to get receiver for arrow function. | |
| // So let's try to fetch it using same logic as is used to retrieve 'this' | |
| // during DebugEvaluate::Local. | |
| Handle<JSFunction> function = frame_inspector_->GetFunction(); | |
| Handle<Context> context(function->context(), isolate_); | |
| // Arrow function defined in top level function without references to | |
| // variables may have NativeContext as context. | |
| if (!context->IsFunctionContext()) return v8::MaybeLocal<v8::Value>(); | |
| ScopeIterator scope_iterator(isolate_, frame_inspector_.get(), | |
| ScopeIterator::COLLECT_NON_LOCALS); | |
| // We lookup this variable in function context only when it is used in arrow | |
| // function otherwise V8 can optimize it out. | |
| if (!scope_iterator.GetNonLocals()->Has(isolate_, | |
| isolate_->factory()->this_string())) | |
| return v8::MaybeLocal<v8::Value>(); | |
| Handle<ScopeInfo> scope_info(context->scope_info(), isolate_); | |
| VariableMode mode; | |
| InitializationFlag flag; | |
| MaybeAssignedFlag maybe_assigned_flag; | |
| int slot_index = ScopeInfo::ContextSlotIndex( | |
| scope_info, isolate_->factory()->this_string(), &mode, &flag, | |
| &maybe_assigned_flag); | |
| if (slot_index < 0) return v8::MaybeLocal<v8::Value>(); | |
| Handle<Object> value = handle(context->get(slot_index), isolate_); | |
| if (value->IsTheHole(isolate_)) return v8::MaybeLocal<v8::Value>(); | |
| return Utils::ToLocal(value); | |
| } | |
| Handle<Object> value = frame_inspector_->GetReceiver(); | |
| if (value.is_null() || (value->IsSmi() || !value->IsTheHole(isolate_))) { | |
| return Utils::ToLocal(value); | |
| } | |
| return v8::MaybeLocal<v8::Value>(); | |
| } | |
| v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const { | |
| DCHECK(!Done()); | |
| if (frame_inspector_->IsWasm()) return v8::Local<v8::Value>(); | |
| bool is_optimized = iterator_.frame()->is_optimized(); | |
| if (is_optimized || !is_top_frame_ || | |
| !isolate_->debug()->IsBreakAtReturn(iterator_.javascript_frame())) { | |
| return v8::Local<v8::Value>(); | |
| } | |
| return Utils::ToLocal(isolate_->debug()->return_value_handle()); | |
| } | |
| v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const { | |
| DCHECK(!Done()); | |
| return Utils::ToLocal(frame_inspector_->GetFunctionName()); | |
| } | |
| v8::Local<v8::debug::Script> DebugStackTraceIterator::GetScript() const { | |
| DCHECK(!Done()); | |
| Handle<Object> value = frame_inspector_->GetScript(); | |
| if (!value->IsScript()) return v8::Local<v8::debug::Script>(); | |
| return ToApiHandle<debug::Script>(Handle<Script>::cast(value)); | |
| } | |
| debug::Location DebugStackTraceIterator::GetSourceLocation() const { | |
| DCHECK(!Done()); | |
| v8::Local<v8::debug::Script> script = GetScript(); | |
| if (script.IsEmpty()) return v8::debug::Location(); | |
| return script->GetSourceLocation(frame_inspector_->GetSourcePosition()); | |
| } | |
| v8::Local<v8::Function> DebugStackTraceIterator::GetFunction() const { | |
| DCHECK(!Done()); | |
| if (!frame_inspector_->IsJavaScript()) return v8::Local<v8::Function>(); | |
| return Utils::ToLocal(frame_inspector_->GetFunction()); | |
| } | |
| std::unique_ptr<v8::debug::ScopeIterator> | |
| DebugStackTraceIterator::GetScopeIterator() const { | |
| DCHECK(!Done()); | |
| StandardFrame* frame = iterator_.frame(); | |
| if (frame->is_wasm_interpreter_entry()) { | |
| return std::unique_ptr<v8::debug::ScopeIterator>(new DebugWasmScopeIterator( | |
| isolate_, iterator_.frame(), inlined_frame_index_)); | |
| } | |
| return std::unique_ptr<v8::debug::ScopeIterator>( | |
| new DebugScopeIterator(isolate_, frame_inspector_.get())); | |
| } | |
| bool DebugStackTraceIterator::Restart() { | |
| DCHECK(!Done()); | |
| if (iterator_.is_wasm()) return false; | |
| return !LiveEdit::RestartFrame(iterator_.javascript_frame()); | |
| } | |
| v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate( | |
| v8::Local<v8::String> source, bool throw_on_side_effect) { | |
| DCHECK(!Done()); | |
| Handle<Object> value; | |
| i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_); | |
| if (!DebugEvaluate::Local(isolate_, iterator_.frame()->id(), | |
| inlined_frame_index_, Utils::OpenHandle(*source), | |
| throw_on_side_effect) | |
| .ToHandle(&value)) { | |
| isolate_->OptionalRescheduleException(false); | |
| return v8::MaybeLocal<v8::Value>(); | |
| } | |
| return Utils::ToLocal(value); | |
| } | |
| } // namespace internal | |
| } // namespace v8 |