// 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
|