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