debug-stack-trace-iterator.cc 7.89 KB
Newer Older
1 2 3 4 5 6
// 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"

7
#include "src/api/api-inl.h"
8
#include "src/debug/debug-evaluate.h"
9
#include "src/debug/debug-interface.h"
10 11 12
#include "src/debug/debug-scope-iterator.h"
#include "src/debug/debug.h"
#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 17
#include "src/wasm/wasm-debug-evaluate.h"
#include "src/wasm/wasm-debug.h"
18 19 20

namespace v8 {

21 22 23 24
bool debug::StackTraceIterator::SupportsWasmDebugEvaluate() {
  return i::FLAG_wasm_expose_debug_eval;
}

25 26 27 28 29 30 31 32 33 34 35 36 37 38
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;
39
  std::vector<FrameSummary> frames;
40
  iterator_.frame()->Summarize(&frames);
41
  inlined_frame_index_ = static_cast<int>(frames.size());
42 43 44 45
  Advance();
  for (; !Done() && index > 0; --index) Advance();
}

46
DebugStackTraceIterator::~DebugStackTraceIterator() = default;
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

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;
70
    std::vector<FrameSummary> frames;
71
    iterator_.frame()->Summarize(&frames);
72
    inlined_frame_index_ = static_cast<int>(frames.size());
73 74 75 76 77
  }
}

int DebugStackTraceIterator::GetContextId() const {
  DCHECK(!Done());
78 79
  Handle<Object> context = frame_inspector_->GetContext();
  if (context->IsContext()) {
80 81
    Object value = Context::cast(*context).native_context().debug_context_id();
    if (value.IsSmi()) return Smi::ToInt(value);
82 83
  }
  return 0;
84 85
}

86
v8::MaybeLocal<v8::Value> DebugStackTraceIterator::GetReceiver() const {
87
  DCHECK(!Done());
88
  if (frame_inspector_->IsJavaScript() &&
89
      frame_inspector_->GetFunction()->shared().kind() == kArrowFunction) {
90 91 92 93
    // 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();
94
    Handle<Context> context(function->context(), isolate_);
95 96 97
    // Arrow function defined in top level function without references to
    // variables may have NativeContext as context.
    if (!context->IsFunctionContext()) return v8::MaybeLocal<v8::Value>();
98 99 100
    ScopeIterator scope_iterator(
        isolate_, frame_inspector_.get(),
        ScopeIterator::ReparseStrategy::kFunctionLiteral);
101 102
    // We lookup this variable in function context only when it is used in arrow
    // function otherwise V8 can optimize it out.
103
    if (!scope_iterator.ClosureScopeHasThisReference()) {
104
      return v8::MaybeLocal<v8::Value>();
105
    }
106
    DisallowHeapAllocation no_gc;
107 108 109
    VariableMode mode;
    InitializationFlag flag;
    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 115 116 117 118
    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);
  }
119

120
  Handle<Object> value = frame_inspector_->GetReceiver();
121 122 123
  if (value.is_null() || (value->IsSmi() || !value->IsTheHole(isolate_))) {
    return Utils::ToLocal(value);
  }
124
  return v8::MaybeLocal<v8::Value>();
125 126 127
}

v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const {
128
  CHECK(!Done());
129 130 131
  if (frame_inspector_ && frame_inspector_->IsWasm()) {
    return v8::Local<v8::Value>();
  }
132
  CHECK_NOT_NULL(iterator_.frame());
133 134 135 136 137 138 139 140
  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());
}

141
v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const {
142
  DCHECK(!Done());
143
  return Utils::ToLocal(frame_inspector_->GetFunctionName());
144 145 146 147
}

v8::Local<v8::debug::Script> DebugStackTraceIterator::GetScript() const {
  DCHECK(!Done());
148
  Handle<Object> value = frame_inspector_->GetScript();
149 150 151 152 153 154 155 156
  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();
157
  return script->GetSourceLocation(frame_inspector_->GetSourcePosition());
158 159 160 161
}

v8::Local<v8::Function> DebugStackTraceIterator::GetFunction() const {
  DCHECK(!Done());
162 163
  if (!frame_inspector_->IsJavaScript()) return v8::Local<v8::Function>();
  return Utils::ToLocal(frame_inspector_->GetFunction());
164 165 166 167 168 169
}

std::unique_ptr<v8::debug::ScopeIterator>
DebugStackTraceIterator::GetScopeIterator() const {
  DCHECK(!Done());
  StandardFrame* 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 177 178 179
}

bool DebugStackTraceIterator::Restart() {
  DCHECK(!Done());
  if (iterator_.is_wasm()) return false;
180
  return LiveEdit::RestartFrame(iterator_.javascript_frame());
181 182 183 184 185 186
}

v8::MaybeLocal<v8::Value> DebugStackTraceIterator::Evaluate(
    v8::Local<v8::String> source, bool throw_on_side_effect) {
  DCHECK(!Done());
  Handle<Object> value;
187
  i::SafeForInterruptsScope safe_for_interrupt_scope(isolate_);
188 189 190 191 192 193 194 195 196
  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);
}
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217

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);
}
218 219
}  // namespace internal
}  // namespace v8