blob: ea0f4d3fc96fd10b99ae9b9e1657d688cec54564 [file] [log] [blame]
// 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/api-inl.h"
#include "src/debug/debug-evaluate.h"
#include "src/debug/debug-interface.h"
#include "src/debug/debug-scope-iterator.h"
#include "src/debug/debug.h"
#include "src/debug/liveedit.h"
#include "src/execution/frames-inl.h"
#include "src/execution/frames.h"
#include "src/execution/isolate.h"
#include "src/wasm/wasm-debug-evaluate.h"
#include "src/wasm/wasm-debug.h"
namespace v8 {
bool debug::StackTraceIterator::SupportsWasmDebugEvaluate() {
return i::FLAG_wasm_expose_debug_eval;
}
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() = default;
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::ReparseStrategy::kFunctionLiteral);
// We lookup this variable in function context only when it is used in arrow
// function otherwise V8 can optimize it out.
if (!scope_iterator.ClosureScopeHasThisReference()) {
return v8::MaybeLocal<v8::Value>();
}
DisallowHeapAllocation no_gc;
VariableMode mode;
InitializationFlag flag;
MaybeAssignedFlag maybe_assigned_flag;
IsStaticFlag is_static_flag;
int slot_index = ScopeInfo::ContextSlotIndex(
context->scope_info(), ReadOnlyRoots(isolate_->heap()).this_string(),
&mode, &flag, &maybe_assigned_flag, &is_static_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 {
CHECK(!Done());
if (frame_inspector_ && frame_inspector_->IsWasm()) {
return v8::Local<v8::Value>();
}
CHECK_NOT_NULL(iterator_.frame());
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());
CommonFrame* frame = iterator_.frame();
if (frame->is_wasm()) {
return std::make_unique<DebugWasmScopeIterator>(isolate_,
WasmFrame::cast(frame));
}
return std::make_unique<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_);
bool success = false;
if (iterator_.is_wasm()) {
FrameSummary summary = FrameSummary::Get(iterator_.frame(), 0);
const FrameSummary::WasmFrameSummary& wasmSummary = summary.AsWasm();
Handle<WasmInstanceObject> instance = wasmSummary.wasm_instance();
success = DebugEvaluate::WebAssembly(instance, iterator_.frame()->id(),
Utils::OpenHandle(*source),
throw_on_side_effect)
.ToHandle(&value);
} else {
success = DebugEvaluate::Local(
isolate_, iterator_.frame()->id(), inlined_frame_index_,
Utils::OpenHandle(*source), throw_on_side_effect)
.ToHandle(&value);
}
if (!success) {
isolate_->OptionalRescheduleException(false);
return v8::MaybeLocal<v8::Value>();
}
return Utils::ToLocal(value);
}
v8::MaybeLocal<v8::String> DebugStackTraceIterator::EvaluateWasm(
internal::Vector<const internal::byte> source, int frame_index) {
DCHECK(!Done());
if (!i::FLAG_wasm_expose_debug_eval || !iterator_.is_wasm()) {
return v8::MaybeLocal<v8::String>();
}
Handle<String> value;
i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
FrameSummary summary = FrameSummary::Get(iterator_.frame(), 0);
const FrameSummary::WasmFrameSummary& wasmSummary = summary.AsWasm();
Handle<WasmInstanceObject> instance = wasmSummary.wasm_instance();
if (!v8::internal::wasm::DebugEvaluate(source, instance, iterator_.frame())
.ToHandle(&value)) {
isolate_->OptionalRescheduleException(false);
return v8::MaybeLocal<v8::String>();
}
return Utils::ToLocal(value);
}
} // namespace internal
} // namespace v8