| // 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-scope-iterator.h" |
| |
| #include "src/api.h" |
| #include "src/debug/debug.h" |
| #include "src/debug/liveedit.h" |
| #include "src/frames-inl.h" |
| #include "src/isolate.h" |
| #include "src/wasm/wasm-objects-inl.h" |
| |
| namespace v8 { |
| |
| std::unique_ptr<debug::ScopeIterator> debug::ScopeIterator::CreateForFunction( |
| v8::Isolate* v8_isolate, v8::Local<v8::Function> v8_func) { |
| internal::Handle<internal::JSReceiver> receiver = |
| internal::Handle<internal::JSReceiver>::cast(Utils::OpenHandle(*v8_func)); |
| |
| // Besides JSFunction and JSBoundFunction, {v8_func} could be an |
| // ObjectTemplate with a CallAsFunctionHandler. We only handle plain |
| // JSFunctions. |
| if (!receiver->IsJSFunction()) return nullptr; |
| |
| internal::Handle<internal::JSFunction> function = |
| internal::Handle<internal::JSFunction>::cast(receiver); |
| |
| // Blink has function objects with callable map, JS_SPECIAL_API_OBJECT_TYPE |
| // but without context on heap. |
| if (!function->has_context()) return nullptr; |
| return std::unique_ptr<debug::ScopeIterator>(new internal::DebugScopeIterator( |
| reinterpret_cast<internal::Isolate*>(v8_isolate), function)); |
| } |
| |
| std::unique_ptr<debug::ScopeIterator> |
| debug::ScopeIterator::CreateForGeneratorObject( |
| v8::Isolate* v8_isolate, v8::Local<v8::Object> v8_generator) { |
| internal::Handle<internal::Object> generator = |
| Utils::OpenHandle(*v8_generator); |
| DCHECK(generator->IsJSGeneratorObject()); |
| return std::unique_ptr<debug::ScopeIterator>(new internal::DebugScopeIterator( |
| reinterpret_cast<internal::Isolate*>(v8_isolate), |
| internal::Handle<internal::JSGeneratorObject>::cast(generator))); |
| } |
| |
| namespace internal { |
| |
| DebugScopeIterator::DebugScopeIterator(Isolate* isolate, |
| FrameInspector* frame_inspector) |
| : iterator_(isolate, frame_inspector) { |
| if (!Done() && ShouldIgnore()) Advance(); |
| } |
| |
| DebugScopeIterator::DebugScopeIterator(Isolate* isolate, |
| Handle<JSFunction> function) |
| : iterator_(isolate, function) { |
| if (!Done() && ShouldIgnore()) Advance(); |
| } |
| |
| DebugScopeIterator::DebugScopeIterator(Isolate* isolate, |
| Handle<JSGeneratorObject> generator) |
| : iterator_(isolate, generator) { |
| if (!Done() && ShouldIgnore()) Advance(); |
| } |
| |
| bool DebugScopeIterator::Done() { return iterator_.Done(); } |
| |
| void DebugScopeIterator::Advance() { |
| DCHECK(!Done()); |
| iterator_.Next(); |
| while (!Done() && ShouldIgnore()) { |
| iterator_.Next(); |
| } |
| } |
| |
| bool DebugScopeIterator::ShouldIgnore() { |
| // Almost always Script scope will be empty, so just filter out that noise. |
| // Also drop empty Block, Eval and Script scopes, should we get any. |
| DCHECK(!Done()); |
| debug::ScopeIterator::ScopeType type = GetType(); |
| if (type != debug::ScopeIterator::ScopeTypeBlock && |
| type != debug::ScopeIterator::ScopeTypeScript && |
| type != debug::ScopeIterator::ScopeTypeEval && |
| type != debug::ScopeIterator::ScopeTypeModule) { |
| return false; |
| } |
| |
| // TODO(kozyatinskiy): make this function faster. |
| Handle<JSObject> value; |
| if (!iterator_.ScopeObject().ToHandle(&value)) return false; |
| Handle<FixedArray> keys = |
| KeyAccumulator::GetKeys(value, KeyCollectionMode::kOwnOnly, |
| ENUMERABLE_STRINGS, |
| GetKeysConversion::kConvertToString) |
| .ToHandleChecked(); |
| return keys->length() == 0; |
| } |
| |
| v8::debug::ScopeIterator::ScopeType DebugScopeIterator::GetType() { |
| DCHECK(!Done()); |
| return static_cast<v8::debug::ScopeIterator::ScopeType>(iterator_.Type()); |
| } |
| |
| v8::Local<v8::Object> DebugScopeIterator::GetObject() { |
| DCHECK(!Done()); |
| Handle<JSObject> value; |
| if (iterator_.ScopeObject().ToHandle(&value)) { |
| return Utils::ToLocal(value); |
| } |
| return v8::Local<v8::Object>(); |
| } |
| |
| v8::Local<v8::Function> DebugScopeIterator::GetFunction() { |
| DCHECK(!Done()); |
| Handle<JSFunction> closure = iterator_.GetClosure(); |
| if (closure.is_null()) return v8::Local<v8::Function>(); |
| return Utils::ToLocal(closure); |
| } |
| |
| debug::Location DebugScopeIterator::GetStartLocation() { |
| DCHECK(!Done()); |
| Handle<JSFunction> closure = iterator_.GetClosure(); |
| if (closure.is_null()) return debug::Location(); |
| Object* obj = closure->shared()->script(); |
| if (!obj->IsScript()) return debug::Location(); |
| return ToApiHandle<v8::debug::Script>(handle(Script::cast(obj))) |
| ->GetSourceLocation(iterator_.start_position()); |
| } |
| |
| debug::Location DebugScopeIterator::GetEndLocation() { |
| DCHECK(!Done()); |
| Handle<JSFunction> closure = iterator_.GetClosure(); |
| if (closure.is_null()) return debug::Location(); |
| Object* obj = closure->shared()->script(); |
| if (!obj->IsScript()) return debug::Location(); |
| return ToApiHandle<v8::debug::Script>(handle(Script::cast(obj))) |
| ->GetSourceLocation(iterator_.end_position()); |
| } |
| |
| bool DebugScopeIterator::SetVariableValue(v8::Local<v8::String> name, |
| v8::Local<v8::Value> value) { |
| DCHECK(!Done()); |
| return iterator_.SetVariableValue(Utils::OpenHandle(*name), |
| Utils::OpenHandle(*value)); |
| } |
| |
| DebugWasmScopeIterator::DebugWasmScopeIterator(Isolate* isolate, |
| StandardFrame* frame, |
| int inlined_frame_index) |
| : isolate_(isolate), |
| frame_(frame), |
| inlined_frame_index_(inlined_frame_index), |
| type_(debug::ScopeIterator::ScopeTypeGlobal) {} |
| |
| bool DebugWasmScopeIterator::Done() { |
| return type_ != debug::ScopeIterator::ScopeTypeGlobal && |
| type_ != debug::ScopeIterator::ScopeTypeLocal; |
| } |
| |
| void DebugWasmScopeIterator::Advance() { |
| DCHECK(!Done()); |
| if (type_ == debug::ScopeIterator::ScopeTypeGlobal) { |
| type_ = debug::ScopeIterator::ScopeTypeLocal; |
| } else { |
| // We use ScopeTypeWith type as marker for done. |
| type_ = debug::ScopeIterator::ScopeTypeWith; |
| } |
| } |
| |
| v8::debug::ScopeIterator::ScopeType DebugWasmScopeIterator::GetType() { |
| DCHECK(!Done()); |
| return type_; |
| } |
| |
| v8::Local<v8::Object> DebugWasmScopeIterator::GetObject() { |
| DCHECK(!Done()); |
| Handle<WasmDebugInfo> debug_info( |
| WasmInterpreterEntryFrame::cast(frame_)->wasm_instance()->debug_info(), |
| isolate_); |
| switch (type_) { |
| case debug::ScopeIterator::ScopeTypeGlobal: |
| return Utils::ToLocal(WasmDebugInfo::GetGlobalScopeObject( |
| debug_info, frame_->fp(), inlined_frame_index_)); |
| case debug::ScopeIterator::ScopeTypeLocal: |
| return Utils::ToLocal(WasmDebugInfo::GetLocalScopeObject( |
| debug_info, frame_->fp(), inlined_frame_index_)); |
| default: |
| return v8::Local<v8::Object>(); |
| } |
| return v8::Local<v8::Object>(); |
| } |
| |
| v8::Local<v8::Function> DebugWasmScopeIterator::GetFunction() { |
| DCHECK(!Done()); |
| return v8::Local<v8::Function>(); |
| } |
| |
| debug::Location DebugWasmScopeIterator::GetStartLocation() { |
| DCHECK(!Done()); |
| return debug::Location(); |
| } |
| |
| debug::Location DebugWasmScopeIterator::GetEndLocation() { |
| DCHECK(!Done()); |
| return debug::Location(); |
| } |
| |
| bool DebugWasmScopeIterator::SetVariableValue(v8::Local<v8::String> name, |
| v8::Local<v8::Value> value) { |
| DCHECK(!Done()); |
| return false; |
| } |
| } // namespace internal |
| } // namespace v8 |