1// Copyright 2021 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 "tools/v8windbg/src/js-stack.h" 6 7HRESULT GetJSStackFrames(WRL::ComPtr<IModelObject>& sp_result) { 8 sp_result = nullptr; 9 10 // Get the current context 11 WRL::ComPtr<IDebugHostContext> sp_host_context; 12 RETURN_IF_FAIL(sp_debug_host->GetCurrentContext(&sp_host_context)); 13 14 WRL::ComPtr<IModelObject> sp_curr_thread; 15 RETURN_IF_FAIL(GetCurrentThread(sp_host_context, &sp_curr_thread)); 16 17 WRL::ComPtr<IModelObject> sp_stack; 18 RETURN_IF_FAIL(sp_curr_thread->GetKeyValue(L"Stack", &sp_stack, nullptr)); 19 20 RETURN_IF_FAIL(sp_stack->GetKeyValue(L"Frames", &sp_result, nullptr)); 21 22 return S_OK; 23} 24 25// v8windbg!JSStackAlias::Call 26IFACEMETHODIMP JSStackAlias::Call(IModelObject* p_context_object, 27 ULONG64 arg_count, 28 _In_reads_(arg_count) 29 IModelObject** pp_arguments, 30 IModelObject** pp_result, 31 IKeyStore** pp_metadata) noexcept { 32 WRL::ComPtr<IDebugHostContext> sp_ctx; 33 RETURN_IF_FAIL(sp_debug_host->GetCurrentContext(&sp_ctx)); 34 35 WRL::ComPtr<IModelObject> result; 36 RETURN_IF_FAIL( 37 sp_data_model_manager->CreateSyntheticObject(sp_ctx.Get(), &result)); 38 39 auto sp_iterator{WRL::Make<StackFrames>()}; 40 41 RETURN_IF_FAIL(result->SetConcept( 42 __uuidof(IIndexableConcept), 43 static_cast<IIndexableConcept*>(sp_iterator.Get()), nullptr)); 44 RETURN_IF_FAIL(result->SetConcept( 45 __uuidof(IIterableConcept), 46 static_cast<IIterableConcept*>(sp_iterator.Get()), nullptr)); 47 48 *pp_result = result.Detach(); 49 if (pp_metadata) { 50 *pp_metadata = nullptr; 51 } 52 return S_OK; 53} 54 55FrameData::FrameData() = default; 56FrameData::~FrameData() = default; 57FrameData::FrameData(const FrameData&) = default; 58FrameData::FrameData(FrameData&&) = default; 59FrameData& FrameData::operator=(const FrameData&) = default; 60FrameData& FrameData::operator=(FrameData&&) = default; 61 62StackFrameIterator::StackFrameIterator( 63 WRL::ComPtr<IDebugHostContext>& host_context) 64 : sp_ctx_(host_context) {} 65StackFrameIterator::~StackFrameIterator() = default; 66 67HRESULT StackFrameIterator::PopulateFrameData() { 68 frames_.clear(); 69 WRL::ComPtr<IModelObject> sp_frames; 70 71 RETURN_IF_FAIL(GetJSStackFrames(sp_frames)); 72 73 // Iterate over the array of frames. 74 WRL::ComPtr<IIterableConcept> sp_iterable; 75 RETURN_IF_FAIL( 76 sp_frames->GetConcept(__uuidof(IIterableConcept), &sp_iterable, nullptr)); 77 78 WRL::ComPtr<IModelIterator> sp_frame_iterator; 79 RETURN_IF_FAIL(sp_iterable->GetIterator(sp_frames.Get(), &sp_frame_iterator)); 80 81 // Loop through all the frames in the array. 82 WRL::ComPtr<IModelObject> sp_frame; 83 while (sp_frame_iterator->GetNext(&sp_frame, 0, nullptr, nullptr) != 84 E_BOUNDS) { 85 // Skip non-JS frame (frame that doesn't have a function_name). 86 WRL::ComPtr<IModelObject> sp_local_variables; 87 HRESULT hr = 88 sp_frame->GetKeyValue(L"LocalVariables", &sp_local_variables, nullptr); 89 if (FAILED(hr)) continue; 90 91 WRL::ComPtr<IModelObject> sp_currently_executing_jsfunction; 92 hr = sp_local_variables->GetKeyValue(L"currently_executing_jsfunction", 93 &sp_currently_executing_jsfunction, 94 nullptr); 95 if (FAILED(hr)) continue; 96 97 // At this point, it is safe to add frame entry even though some fields 98 // might not be available. 99 WRL::ComPtr<IModelObject> sp_function_name, sp_script_name, 100 sp_script_source, sp_function_character_offset; 101 FrameData frame_entry; 102 hr = sp_local_variables->GetKeyValue(L"script_name", &sp_script_name, 103 nullptr); 104 if (SUCCEEDED(hr)) { 105 frame_entry.script_name = sp_script_name; 106 } 107 hr = sp_local_variables->GetKeyValue(L"script_source", &sp_script_source, 108 nullptr); 109 if (SUCCEEDED(hr)) { 110 frame_entry.script_source = sp_script_source; 111 } 112 hr = sp_local_variables->GetKeyValue(L"function_name", &sp_function_name, 113 nullptr); 114 if (SUCCEEDED(hr)) { 115 frame_entry.function_name = sp_function_name; 116 } 117 hr = sp_local_variables->GetKeyValue( 118 L"function_character_offset", &sp_function_character_offset, nullptr); 119 if (SUCCEEDED(hr)) { 120 frame_entry.function_character_offset = sp_function_character_offset; 121 } 122 123 frames_.push_back(frame_entry); 124 } 125 126 return S_OK; 127} 128 129IFACEMETHODIMP StackFrameIterator::Reset() noexcept { 130 position_ = 0; 131 return S_OK; 132} 133 134IFACEMETHODIMP StackFrameIterator::GetNext(IModelObject** object, 135 ULONG64 dimensions, 136 IModelObject** indexers, 137 IKeyStore** metadata) noexcept { 138 if (dimensions > 1) return E_INVALIDARG; 139 140 if (position_ == 0) { 141 RETURN_IF_FAIL(PopulateFrameData()); 142 } 143 144 if (metadata != nullptr) *metadata = nullptr; 145 146 WRL::ComPtr<IModelObject> sp_index, sp_value; 147 148 if (dimensions == 1) { 149 RETURN_IF_FAIL(CreateULong64(position_, &sp_index)); 150 } 151 152 RETURN_IF_FAIL(GetAt(position_, &sp_value)); 153 154 // Now update counter and transfer ownership of results, because nothing can 155 // fail from this point onward. 156 ++position_; 157 if (dimensions == 1) { 158 *indexers = sp_index.Detach(); 159 } 160 *object = sp_value.Detach(); 161 return S_OK; 162} 163 164HRESULT StackFrameIterator::GetAt(uint64_t index, IModelObject** result) const { 165 if (index >= frames_.size()) return E_BOUNDS; 166 167 // Create the synthetic object representing the frame here. 168 const FrameData& curr_frame = frames_.at(index); 169 WRL::ComPtr<IModelObject> sp_value; 170 RETURN_IF_FAIL( 171 sp_data_model_manager->CreateSyntheticObject(sp_ctx_.Get(), &sp_value)); 172 RETURN_IF_FAIL( 173 sp_value->SetKey(L"script_name", curr_frame.script_name.Get(), 174 nullptr)); 175 RETURN_IF_FAIL(sp_value->SetKey(L"script_source", 176 curr_frame.script_source.Get(), nullptr)); 177 RETURN_IF_FAIL(sp_value->SetKey(L"function_name", 178 curr_frame.function_name.Get(), nullptr)); 179 RETURN_IF_FAIL(sp_value->SetKey(L"function_character_offset", 180 curr_frame.function_character_offset.Get(), 181 nullptr)); 182 183 *result = sp_value.Detach(); 184 return S_OK; 185} 186 187StackFrames::StackFrames() = default; 188StackFrames::~StackFrames() = default; 189 190IFACEMETHODIMP StackFrames::GetDimensionality( 191 IModelObject* context_object, ULONG64* dimensionality) noexcept { 192 *dimensionality = 1; 193 return S_OK; 194} 195 196IFACEMETHODIMP StackFrames::GetAt(IModelObject* context_object, 197 ULONG64 indexer_count, 198 IModelObject** indexers, 199 IModelObject** object, 200 IKeyStore** metadata) noexcept { 201 if (indexer_count != 1) return E_INVALIDARG; 202 if (metadata != nullptr) *metadata = nullptr; 203 WRL::ComPtr<IDebugHostContext> sp_ctx; 204 RETURN_IF_FAIL(context_object->GetContext(&sp_ctx)); 205 206 // This should be instantiated once for each synthetic object returned, 207 // so should be able to cache/reuse an iterator. 208 if (opt_frames_ == nullptr) { 209 opt_frames_ = WRL::Make<StackFrameIterator>(sp_ctx); 210 _ASSERT(opt_frames_ != nullptr); 211 RETURN_IF_FAIL(opt_frames_->PopulateFrameData()); 212 } 213 214 uint64_t index; 215 RETURN_IF_FAIL(UnboxULong64(indexers[0], &index, true /*convert*/)); 216 217 return opt_frames_->GetAt(index, object); 218} 219 220IFACEMETHODIMP StackFrames::SetAt(IModelObject* context_object, 221 ULONG64 indexer_count, 222 IModelObject** indexers, 223 IModelObject* value) noexcept { 224 return E_NOTIMPL; 225} 226 227IFACEMETHODIMP StackFrames::GetDefaultIndexDimensionality( 228 IModelObject* context_object, ULONG64* dimensionality) noexcept { 229 *dimensionality = 1; 230 return S_OK; 231} 232 233IFACEMETHODIMP StackFrames::GetIterator(IModelObject* context_object, 234 IModelIterator** iterator) noexcept { 235 WRL::ComPtr<IDebugHostContext> sp_ctx; 236 RETURN_IF_FAIL(context_object->GetContext(&sp_ctx)); 237 auto sp_memory_iterator{WRL::Make<StackFrameIterator>(sp_ctx)}; 238 *iterator = sp_memory_iterator.Detach(); 239 return S_OK; 240} 241