Home | History | Annotate | Download | only in debug
      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