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