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