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 
GetJSStackFrames(WRL::ComPtr<IModelObject>& sp_result)7 HRESULT 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
26 IFACEMETHODIMP 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 
55 FrameData::FrameData() = default;
56 FrameData::~FrameData() = default;
57 FrameData::FrameData(const FrameData&) = default;
58 FrameData::FrameData(FrameData&&) = default;
59 FrameData& FrameData::operator=(const FrameData&) = default;
60 FrameData& FrameData::operator=(FrameData&&) = default;
61 
StackFrameIterator( WRL::ComPtr<IDebugHostContext>& host_context)62 StackFrameIterator::StackFrameIterator(
63     WRL::ComPtr<IDebugHostContext>& host_context)
64     : sp_ctx_(host_context) {}
65 StackFrameIterator::~StackFrameIterator() = default;
66 
PopulateFrameData()67 HRESULT 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 
129 IFACEMETHODIMP StackFrameIterator::Reset() noexcept {
130   position_ = 0;
131   return S_OK;
132 }
133 
134 IFACEMETHODIMP 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 
GetAt(uint64_t index, IModelObject** result) const164 HRESULT 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 
187 StackFrames::StackFrames() = default;
188 StackFrames::~StackFrames() = default;
189 
190 IFACEMETHODIMP StackFrames::GetDimensionality(
191     IModelObject* context_object, ULONG64* dimensionality) noexcept {
192   *dimensionality = 1;
193   return S_OK;
194 }
195 
196 IFACEMETHODIMP 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 
220 IFACEMETHODIMP StackFrames::SetAt(IModelObject* context_object,
221                                   ULONG64 indexer_count,
222                                   IModelObject** indexers,
223                                   IModelObject* value) noexcept {
224   return E_NOTIMPL;
225 }
226 
227 IFACEMETHODIMP StackFrames::GetDefaultIndexDimensionality(
228     IModelObject* context_object, ULONG64* dimensionality) noexcept {
229   *dimensionality = 1;
230   return S_OK;
231 }
232 
233 IFACEMETHODIMP 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