• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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/object-inspection.h"
6 
7 #include "src/flags/flags.h"
8 #include "tools/v8windbg/base/utilities.h"
9 #include "tools/v8windbg/src/v8-debug-helper-interop.h"
10 #include "tools/v8windbg/src/v8windbg-extension.h"
11 
V8CachedObject(Location location,std::string uncompressed_type_name,WRL::ComPtr<IDebugHostContext> context,bool is_compressed)12 V8CachedObject::V8CachedObject(Location location,
13                                std::string uncompressed_type_name,
14                                WRL::ComPtr<IDebugHostContext> context,
15                                bool is_compressed)
16     : location_(std::move(location)),
17       uncompressed_type_name_(std::move(uncompressed_type_name)),
18       context_(std::move(context)),
19       is_compressed_(is_compressed) {}
20 
Create(IModelObject * p_v8_object_instance,IV8CachedObject ** result)21 HRESULT V8CachedObject::Create(IModelObject* p_v8_object_instance,
22                                IV8CachedObject** result) {
23   Location location;
24   RETURN_IF_FAIL(p_v8_object_instance->GetLocation(&location));
25 
26   WRL::ComPtr<IDebugHostContext> context;
27   RETURN_IF_FAIL(p_v8_object_instance->GetContext(&context));
28 
29   WRL::ComPtr<IDebugHostType> sp_type;
30   _bstr_t type_name;
31   RETURN_IF_FAIL(p_v8_object_instance->GetTypeInfo(&sp_type));
32   RETURN_IF_FAIL(sp_type->GetName(type_name.GetAddress()));
33 
34   // If the object is of type v8::internal::TaggedValue, and this build uses
35   // compressed pointers, then the value is compressed. Other types such as
36   // v8::internal::Object represent uncompressed tagged values.
37   bool is_compressed =
38       COMPRESS_POINTERS_BOOL &&
39       static_cast<const char*>(type_name) == std::string(kTaggedValue);
40 
41   const char* uncompressed_type_name =
42       is_compressed ? kObject : static_cast<const char*>(type_name);
43 
44   *result = WRL::Make<V8CachedObject>(location, uncompressed_type_name, context,
45                                       is_compressed)
46                 .Detach();
47   return S_OK;
48 }
49 
V8CachedObject(V8HeapObject heap_object)50 V8CachedObject::V8CachedObject(V8HeapObject heap_object)
51     : heap_object_(std::move(heap_object)), heap_object_initialized_(true) {}
52 
53 V8CachedObject::~V8CachedObject() = default;
54 
GetCachedV8HeapObject(V8HeapObject ** pp_heap_object)55 IFACEMETHODIMP V8CachedObject::GetCachedV8HeapObject(
56     V8HeapObject** pp_heap_object) noexcept {
57   if (!heap_object_initialized_) {
58     heap_object_initialized_ = true;
59     uint64_t tagged_ptr = 0;
60     uint64_t bytes_read;
61     HRESULT hr = sp_debug_host_memory->ReadBytes(
62         context_.Get(), location_, reinterpret_cast<void*>(&tagged_ptr),
63         is_compressed_ ? i::kTaggedSize : sizeof(void*), &bytes_read);
64     // S_FALSE can be returned if fewer bytes were read than were requested. We
65     // need all of the bytes, so check for S_OK.
66     if (hr != S_OK) {
67       std::stringstream message;
68       message << "Unable to read memory";
69       if (location_.IsVirtualAddress()) {
70         message << " at 0x" << std::hex << location_.GetOffset();
71       }
72       heap_object_.friendly_name = ConvertToU16String(message.str());
73     } else {
74       if (is_compressed_)
75         tagged_ptr = ExpandCompressedPointer(static_cast<uint32_t>(tagged_ptr));
76       heap_object_ =
77           ::GetHeapObject(context_, tagged_ptr, location_.GetOffset(),
78                           uncompressed_type_name_.c_str(), is_compressed_);
79     }
80   }
81   *pp_heap_object = &this->heap_object_;
82   return S_OK;
83 }
84 
IndexedFieldData(Property property)85 IndexedFieldData::IndexedFieldData(Property property)
86     : property_(std::move(property)) {}
87 
88 IndexedFieldData::~IndexedFieldData() = default;
89 
GetProperty(Property ** property)90 IFACEMETHODIMP IndexedFieldData::GetProperty(Property** property) noexcept {
91   if (!property) return E_POINTER;
92   *property = &this->property_;
93   return S_OK;
94 }
95 
V8ObjectKeyEnumerator(WRL::ComPtr<IV8CachedObject> & v8_cached_object)96 V8ObjectKeyEnumerator::V8ObjectKeyEnumerator(
97     WRL::ComPtr<IV8CachedObject>& v8_cached_object)
98     : sp_v8_cached_object_{v8_cached_object} {}
99 V8ObjectKeyEnumerator::~V8ObjectKeyEnumerator() = default;
100 
Reset()101 IFACEMETHODIMP V8ObjectKeyEnumerator::Reset() noexcept {
102   index_ = 0;
103   return S_OK;
104 }
105 
GetNext(BSTR * key,IModelObject ** value,IKeyStore ** metadata)106 IFACEMETHODIMP V8ObjectKeyEnumerator::GetNext(BSTR* key, IModelObject** value,
107                                               IKeyStore** metadata) noexcept {
108   V8HeapObject* p_v8_heap_object;
109   sp_v8_cached_object_->GetCachedV8HeapObject(&p_v8_heap_object);
110 
111   if (static_cast<size_t>(index_) >= p_v8_heap_object->properties.size())
112     return E_BOUNDS;
113 
114   auto* name_ptr = p_v8_heap_object->properties[index_].name.c_str();
115   *key = ::SysAllocString(U16ToWChar(name_ptr));
116   ++index_;
117   return S_OK;
118 }
119 
InitializeObject(IModelObject * model_object,IDebugHostTypeSignature * matching_type_signature,IDebugHostSymbolEnumerator * wildcard_matches)120 IFACEMETHODIMP V8LocalDataModel::InitializeObject(
121     IModelObject* model_object,
122     IDebugHostTypeSignature* matching_type_signature,
123     IDebugHostSymbolEnumerator* wildcard_matches) noexcept {
124   return S_OK;
125 }
126 
GetName(BSTR * model_name)127 IFACEMETHODIMP V8LocalDataModel::GetName(BSTR* model_name) noexcept {
128   return E_NOTIMPL;
129 }
130 
InitializeObject(IModelObject * model_object,IDebugHostTypeSignature * matching_type_signature,IDebugHostSymbolEnumerator * wildcard_matches)131 IFACEMETHODIMP V8ObjectDataModel::InitializeObject(
132     IModelObject* model_object,
133     IDebugHostTypeSignature* matching_type_signature,
134     IDebugHostSymbolEnumerator* wildcard_matches) noexcept {
135   return S_OK;
136 }
137 
GetName(BSTR * model_name)138 IFACEMETHODIMP V8ObjectDataModel::GetName(BSTR* model_name) noexcept {
139   return E_NOTIMPL;
140 }
141 
ToDisplayString(IModelObject * context_object,IKeyStore * metadata,BSTR * display_string)142 IFACEMETHODIMP V8ObjectDataModel::ToDisplayString(
143     IModelObject* context_object, IKeyStore* metadata,
144     BSTR* display_string) noexcept {
145   WRL::ComPtr<IV8CachedObject> sp_v8_cached_object;
146   RETURN_IF_FAIL(GetCachedObject(context_object, &sp_v8_cached_object));
147   V8HeapObject* p_v8_heap_object;
148   RETURN_IF_FAIL(sp_v8_cached_object->GetCachedV8HeapObject(&p_v8_heap_object));
149   *display_string = ::SysAllocString(
150       reinterpret_cast<const wchar_t*>(p_v8_heap_object->friendly_name.data()));
151   return S_OK;
152 }
153 
154 namespace {
155 
156 // Creates a synthetic object, attaches a parent model, and sets the context
157 // object for that parent data model. Caller is responsible for ensuring that
158 // the parent model's Concepts have been initialized correctly and that the
159 // data model context is of an appropriate type for the parent model.
CreateSyntheticObjectWithParentAndDataContext(IDebugHostContext * ctx,IModelObject * parent_model,IUnknown * data_context,IModelObject ** result)160 HRESULT CreateSyntheticObjectWithParentAndDataContext(
161     IDebugHostContext* ctx, IModelObject* parent_model, IUnknown* data_context,
162     IModelObject** result) {
163   WRL::ComPtr<IModelObject> value;
164   RETURN_IF_FAIL(sp_data_model_manager->CreateSyntheticObject(ctx, &value));
165   RETURN_IF_FAIL(
166       value->AddParentModel(parent_model, nullptr, true /*override*/));
167   RETURN_IF_FAIL(value->SetContextForDataModel(parent_model, data_context));
168   *result = value.Detach();
169   return S_OK;
170 }
171 
172 // Creates an IModelObject for a V8 object whose value is represented by the
173 // data in cached_object. This is an alternative to  CreateTypedObject for
174 // particularly complex cases (compressed values and those that don't exist
175 // anywhere in memory).
CreateSyntheticObjectForV8Object(IDebugHostContext * ctx,V8CachedObject * cached_object,IModelObject ** result)176 HRESULT CreateSyntheticObjectForV8Object(IDebugHostContext* ctx,
177                                          V8CachedObject* cached_object,
178                                          IModelObject** result) {
179   // Explicitly add the parent model and data context. On a plain typed object,
180   // the parent model would be attached automatically because we registered for
181   // a matching type signature, and the data context would be set during
182   // V8ObjectDataModel::GetCachedObject.
183   return CreateSyntheticObjectWithParentAndDataContext(
184       ctx, Extension::Current()->GetObjectDataModel(), cached_object, result);
185 }
186 
187 // Creates an IModelObject to represent a field that is not a struct or array.
GetModelForBasicField(const uint64_t address,const std::u16string & type_name,const std::string & uncompressed_type_name,WRL::ComPtr<IDebugHostContext> & sp_ctx,IModelObject ** result)188 HRESULT GetModelForBasicField(const uint64_t address,
189                               const std::u16string& type_name,
190                               const std::string& uncompressed_type_name,
191                               WRL::ComPtr<IDebugHostContext>& sp_ctx,
192                               IModelObject** result) {
193   if (type_name == ConvertToU16String(uncompressed_type_name)) {
194     // For untagged and uncompressed tagged fields, create an IModelObject
195     // representing a normal native data type.
196     WRL::ComPtr<IDebugHostType> type =
197         Extension::Current()->GetTypeFromV8Module(sp_ctx, type_name.c_str());
198     if (type == nullptr) return E_FAIL;
199     return sp_data_model_manager->CreateTypedObject(
200         sp_ctx.Get(), Location{address}, type.Get(), result);
201   }
202 
203   // For compressed tagged fields, we need to do something a little more
204   // complicated. We could just use CreateTypedObject with the type
205   // v8::internal::TaggedValue, but then we'd sacrifice any other data
206   // that we've learned about the field's specific type. So instead we
207   // create a synthetic object.
208   WRL::ComPtr<V8CachedObject> cached_object = WRL::Make<V8CachedObject>(
209       Location(address), uncompressed_type_name, sp_ctx,
210       /*is_compressed=*/true);
211   return CreateSyntheticObjectForV8Object(sp_ctx.Get(), cached_object.Get(),
212                                           result);
213 }
214 
215 // Creates an IModelObject representing the value of a bitfield.
GetModelForBitField(uint64_t address,const uint8_t num_bits,uint8_t shift_bits,const std::u16string & type_name,WRL::ComPtr<IDebugHostContext> & sp_ctx,IModelObject ** result)216 HRESULT GetModelForBitField(uint64_t address, const uint8_t num_bits,
217                             uint8_t shift_bits, const std::u16string& type_name,
218                             WRL::ComPtr<IDebugHostContext>& sp_ctx,
219                             IModelObject** result) {
220   // Look up the type by name.
221   WRL::ComPtr<IDebugHostType> type =
222       Extension::Current()->GetTypeFromV8Module(sp_ctx, type_name.c_str());
223   if (type == nullptr) return E_FAIL;
224 
225   // Figure out exactly which bytes contain the bitfield's data. This depends on
226   // platform byte order (little-endian for Windows).
227   constexpr int kBitsPerByte = 8;
228   uint8_t shift_bytes = shift_bits / kBitsPerByte;
229   address += shift_bytes;
230   shift_bits -= shift_bytes * kBitsPerByte;
231   size_t bits_to_read = shift_bits + num_bits;
232   size_t bytes_to_read = (bits_to_read + kBitsPerByte - 1) / kBitsPerByte;
233 
234   uintptr_t value = 0;
235 
236   // V8 guarantees that bitfield structs are no bigger than a single pointer.
237   if (bytes_to_read > sizeof(value)) {
238     std::stringstream message;
239     message << "Fatal v8windbg error: found bitfield struct of "
240             << bytes_to_read << "bytes, which exceeds the supported size of "
241             << sizeof(value);
242     return CreateString(ConvertToU16String(message.str()), result);
243   }
244 
245   uint64_t bytes_read;
246   HRESULT hr = sp_debug_host_memory->ReadBytes(sp_ctx.Get(), address,
247                                                reinterpret_cast<void*>(&value),
248                                                bytes_to_read, &bytes_read);
249 
250   // S_FALSE can be returned if fewer bytes were read than were requested. We
251   // need all of the bytes, so check for S_OK.
252   if (hr != S_OK) {
253     std::stringstream message;
254     message << "Unable to read memory at 0x" << std::hex << address;
255     return CreateString(ConvertToU16String(message.str()), result);
256   }
257 
258   // Decode the bitfield.
259   value = (value >> shift_bits) & ((1 << num_bits) - 1);
260 
261   return CreateTypedIntrinsic(value, type.Get(), result);
262 }
263 
264 // Creates an IModelObject to represent the packed fields in a Torque struct.
265 // Note that Torque structs are not C++ structs and do not have any type
266 // definitions in the V8 symbols.
GetModelForStruct(const uint64_t address,const std::vector<StructField> & fields,WRL::ComPtr<IDebugHostContext> & sp_ctx,IModelObject ** result)267 HRESULT GetModelForStruct(const uint64_t address,
268                           const std::vector<StructField>& fields,
269                           WRL::ComPtr<IDebugHostContext>& sp_ctx,
270                           IModelObject** result) {
271   WRL::ComPtr<IModelObject> sp_value;
272   RETURN_IF_FAIL(
273       sp_data_model_manager->CreateSyntheticObject(sp_ctx.Get(), &sp_value));
274 
275   // There's no need for any fancy Concepts here; just add key-value pairs for
276   // each field.
277   for (const StructField& field : fields) {
278     WRL::ComPtr<IModelObject> field_model;
279     if (field.num_bits == 0) {
280       RETURN_IF_FAIL(GetModelForBasicField(
281           address + field.offset, field.type_name, field.uncompressed_type_name,
282           sp_ctx, &field_model));
283     } else {
284       RETURN_IF_FAIL(GetModelForBitField(address + field.offset, field.num_bits,
285                                          field.shift_bits, field.type_name,
286                                          sp_ctx, &field_model));
287     }
288     RETURN_IF_FAIL(
289         sp_value->SetKey(reinterpret_cast<const wchar_t*>(field.name.c_str()),
290                          field_model.Get(), nullptr));
291   }
292 
293   *result = sp_value.Detach();
294   return S_OK;
295 }
296 
297 // Creates an IModelObject representing an array of some type that we expect to
298 // be defined in the V8 symbols.
GetModelForNativeArray(const uint64_t address,const std::u16string & type_name,size_t count,WRL::ComPtr<IDebugHostContext> & sp_ctx,IModelObject ** result)299 HRESULT GetModelForNativeArray(const uint64_t address,
300                                const std::u16string& type_name, size_t count,
301                                WRL::ComPtr<IDebugHostContext>& sp_ctx,
302                                IModelObject** result) {
303   WRL::ComPtr<IDebugHostType> type =
304       Extension::Current()->GetTypeFromV8Module(sp_ctx, type_name.c_str());
305   if (type == nullptr) return E_FAIL;
306 
307   ULONG64 object_size{};
308   RETURN_IF_FAIL(type->GetSize(&object_size));
309 
310   ArrayDimension dimensions[] = {
311       {/*start=*/0, /*length=*/count, /*stride=*/object_size}};
312   WRL::ComPtr<IDebugHostType> array_type;
313   RETURN_IF_FAIL(
314       type->CreateArrayOf(/*dimensions=*/1, dimensions, &array_type));
315 
316   return sp_data_model_manager->CreateTypedObject(
317       sp_ctx.Get(), Location{address}, array_type.Get(), result);
318 }
319 
320 // Creates an IModelObject that represents an array of structs or compressed
321 // tagged values.
GetModelForCustomArray(const Property & prop,WRL::ComPtr<IDebugHostContext> & sp_ctx,IModelObject ** result)322 HRESULT GetModelForCustomArray(const Property& prop,
323                                WRL::ComPtr<IDebugHostContext>& sp_ctx,
324                                IModelObject** result) {
325   // Create the context which should be provided to the indexing and iterating
326   // functionality provided by the parent model. This is instance-specific data,
327   // whereas the parent model object could be shared among many custom arrays.
328   WRL::ComPtr<IndexedFieldData> context_data =
329       WRL::Make<IndexedFieldData>(prop);
330 
331   return CreateSyntheticObjectWithParentAndDataContext(
332       sp_ctx.Get(), Extension::Current()->GetIndexedFieldDataModel(),
333       context_data.Get(), result);
334 }
335 
336 
337 // Creates an IModelObject representing the data in an array at the given index.
338 // context_object is expected to be an object of the form created by
339 // GetModelForCustomArray, meaning its context for the IndexedFieldParent data
340 // model is an IIndexedFieldData containing the description of the array.
GetModelForCustomArrayElement(IModelObject * context_object,size_t index,IModelObject ** object)341 HRESULT GetModelForCustomArrayElement(IModelObject* context_object,
342                                       size_t index, IModelObject** object) {
343   // Open a few layers of wrapper objects to get to the Property object that
344   // describes the array.
345   WRL::ComPtr<IUnknown> data_model_context;
346   RETURN_IF_FAIL(context_object->GetContextForDataModel(
347       Extension::Current()->GetIndexedFieldDataModel(), &data_model_context));
348   WRL::ComPtr<IIndexedFieldData> indexed_field_data;
349   RETURN_IF_FAIL(data_model_context.As(&indexed_field_data));
350   Property* prop;
351   RETURN_IF_FAIL(indexed_field_data->GetProperty(&prop));
352 
353   if (index >= prop->length) {
354     return E_BOUNDS;
355   }
356 
357   WRL::ComPtr<IDebugHostContext> sp_ctx;
358   RETURN_IF_FAIL(context_object->GetContext(&sp_ctx));
359 
360   ULONG64 address = prop->addr_value + index * prop->item_size;
361 
362   switch (prop->type) {
363     case PropertyType::kArray:
364       return GetModelForBasicField(address, prop->type_name,
365                                    prop->uncompressed_type_name, sp_ctx,
366                                    object);
367     case PropertyType::kStructArray:
368       return GetModelForStruct(address, prop->fields, sp_ctx, object);
369     default:
370       return E_FAIL;  // Only array properties should be possible here.
371   }
372 }
373 
374 }  // namespace
375 
InitializeObject(IModelObject * model_object,IDebugHostTypeSignature * matching_type_signature,IDebugHostSymbolEnumerator * wildcard_matches)376 IFACEMETHODIMP IndexedFieldParent::InitializeObject(
377     IModelObject* model_object,
378     IDebugHostTypeSignature* matching_type_signature,
379     IDebugHostSymbolEnumerator* wildcard_matches) noexcept {
380   return S_OK;
381 }
382 
GetName(BSTR * model_name)383 IFACEMETHODIMP IndexedFieldParent::GetName(BSTR* model_name) noexcept {
384   return E_NOTIMPL;
385 }
386 
GetDimensionality(IModelObject * context_object,ULONG64 * dimensionality)387 IFACEMETHODIMP IndexedFieldParent::GetDimensionality(
388     IModelObject* context_object, ULONG64* dimensionality) noexcept {
389   *dimensionality = 1;
390   return S_OK;
391 }
392 
GetAt(IModelObject * context_object,ULONG64 indexer_count,IModelObject ** indexers,IModelObject ** object,IKeyStore ** metadata)393 IFACEMETHODIMP IndexedFieldParent::GetAt(IModelObject* context_object,
394                                          ULONG64 indexer_count,
395                                          IModelObject** indexers,
396                                          IModelObject** object,
397                                          IKeyStore** metadata) noexcept {
398   if (indexer_count != 1) return E_INVALIDARG;
399   if (metadata != nullptr) *metadata = nullptr;
400 
401   ULONG64 index;
402   RETURN_IF_FAIL(UnboxULong64(indexers[0], &index, /*convert=*/true));
403 
404   return GetModelForCustomArrayElement(context_object, index, object);
405 }
406 
SetAt(IModelObject * context_object,ULONG64 indexer_count,IModelObject ** indexers,IModelObject * value)407 IFACEMETHODIMP IndexedFieldParent::SetAt(IModelObject* context_object,
408                                          ULONG64 indexer_count,
409                                          IModelObject** indexers,
410                                          IModelObject* value) noexcept {
411   return E_NOTIMPL;
412 }
413 
GetDefaultIndexDimensionality(IModelObject * context_object,ULONG64 * dimensionality)414 IFACEMETHODIMP IndexedFieldParent::GetDefaultIndexDimensionality(
415     IModelObject* context_object, ULONG64* dimensionality) noexcept {
416   *dimensionality = 1;
417   return S_OK;
418 }
419 
GetIterator(IModelObject * context_object,IModelIterator ** iterator)420 IFACEMETHODIMP IndexedFieldParent::GetIterator(
421     IModelObject* context_object, IModelIterator** iterator) noexcept {
422   auto indexed_field_iterator{WRL::Make<IndexedFieldIterator>(context_object)};
423   *iterator = indexed_field_iterator.Detach();
424   return S_OK;
425 }
426 
IndexedFieldIterator(IModelObject * context_object)427 IndexedFieldIterator::IndexedFieldIterator(IModelObject* context_object)
428     : context_object_(context_object) {}
429 IndexedFieldIterator::~IndexedFieldIterator() = default;
430 
Reset()431 IFACEMETHODIMP IndexedFieldIterator::Reset() noexcept {
432   next_ = 0;
433   return S_OK;
434 }
435 
GetNext(IModelObject ** object,ULONG64 dimensions,IModelObject ** indexers,IKeyStore ** metadata)436 IFACEMETHODIMP IndexedFieldIterator::GetNext(IModelObject** object,
437                                              ULONG64 dimensions,
438                                              IModelObject** indexers,
439                                              IKeyStore** metadata) noexcept {
440   if (dimensions > 1) return E_INVALIDARG;
441 
442   WRL::ComPtr<IModelObject> sp_index, sp_value;
443   RETURN_IF_FAIL(
444       GetModelForCustomArrayElement(context_object_.Get(), next_, &sp_value));
445   RETURN_IF_FAIL(CreateULong64(next_, &sp_index));
446 
447   // Everything that could fail (including the bounds check) has succeeded, so
448   // increment the index.
449   ++next_;
450 
451   // Write results (none of these steps can fail, which is important because we
452   // transfer ownership of two separate objects).
453   if (dimensions == 1) {
454     indexers[0] = sp_index.Detach();
455   }
456   *object = sp_value.Detach();
457   if (metadata != nullptr) *metadata = nullptr;
458   return S_OK;
459 }
460 
GetKey(IModelObject * context_object,PCWSTR key,IModelObject ** key_value,IKeyStore ** metadata,bool * has_key)461 IFACEMETHODIMP V8ObjectDataModel::GetKey(IModelObject* context_object,
462                                          PCWSTR key, IModelObject** key_value,
463                                          IKeyStore** metadata,
464                                          bool* has_key) noexcept {
465   if (metadata != nullptr) *metadata = nullptr;
466 
467   WRL::ComPtr<IV8CachedObject> sp_v8_cached_object;
468   RETURN_IF_FAIL(GetCachedObject(context_object, &sp_v8_cached_object));
469   V8HeapObject* p_v8_heap_object;
470   RETURN_IF_FAIL(sp_v8_cached_object->GetCachedV8HeapObject(&p_v8_heap_object));
471 
472   *has_key = false;
473   for (const auto& prop : p_v8_heap_object->properties) {
474     const char16_t* p_key = reinterpret_cast<const char16_t*>(key);
475     if (prop.name.compare(p_key) == 0) {
476       *has_key = true;
477       if (key_value != nullptr) {
478         WRL::ComPtr<IDebugHostContext> sp_ctx;
479         RETURN_IF_FAIL(context_object->GetContext(&sp_ctx));
480         RETURN_IF_FAIL(GetModelForProperty(prop, sp_ctx, key_value));
481       }
482       return S_OK;
483     }
484   }
485 
486   return S_OK;
487 }
488 
SetKey(IModelObject * context_object,PCWSTR key,IModelObject * key_value,IKeyStore * metadata)489 IFACEMETHODIMP V8ObjectDataModel::SetKey(IModelObject* context_object,
490                                          PCWSTR key, IModelObject* key_value,
491                                          IKeyStore* metadata) noexcept {
492   return E_NOTIMPL;
493 }
494 
EnumerateKeys(IModelObject * context_object,IKeyEnumerator ** pp_enumerator)495 IFACEMETHODIMP V8ObjectDataModel::EnumerateKeys(
496     IModelObject* context_object, IKeyEnumerator** pp_enumerator) noexcept {
497   WRL::ComPtr<IV8CachedObject> sp_v8_cached_object;
498   RETURN_IF_FAIL(GetCachedObject(context_object, &sp_v8_cached_object));
499 
500   auto enumerator{WRL::Make<V8ObjectKeyEnumerator>(sp_v8_cached_object)};
501   *pp_enumerator = enumerator.Detach();
502   return S_OK;
503 }
504 
GetValue(PCWSTR pwsz_key,IModelObject * p_v8_local_instance,IModelObject ** pp_value)505 IFACEMETHODIMP V8LocalValueProperty::GetValue(
506     PCWSTR pwsz_key, IModelObject* p_v8_local_instance,
507     IModelObject** pp_value) noexcept {
508   // Get the parametric type within v8::Local<*>
509   // Set value to a pointer to an instance of this type.
510 
511   WRL::ComPtr<IDebugHostType> sp_type;
512   RETURN_IF_FAIL(p_v8_local_instance->GetTypeInfo(&sp_type));
513 
514   bool is_generic;
515   RETURN_IF_FAIL(sp_type->IsGeneric(&is_generic));
516   if (!is_generic) return E_FAIL;
517 
518   WRL::ComPtr<IDebugHostSymbol> sp_generic_arg;
519   RETURN_IF_FAIL(sp_type->GetGenericArgumentAt(0, &sp_generic_arg));
520 
521   _bstr_t generic_name;
522   RETURN_IF_FAIL(sp_generic_arg->GetName(generic_name.GetAddress()));
523 
524   WRL::ComPtr<IDebugHostContext> sp_ctx;
525   RETURN_IF_FAIL(p_v8_local_instance->GetContext(&sp_ctx));
526 
527   WRL::ComPtr<IDebugHostType> sp_value_type =
528       Extension::Current()->GetTypeFromV8Module(
529           sp_ctx, reinterpret_cast<const char16_t*>(
530                       static_cast<const wchar_t*>(generic_name)));
531   if (sp_value_type == nullptr ||
532       !Extension::Current()->DoesTypeDeriveFromObject(sp_value_type)) {
533     // The value type doesn't derive from v8::internal::Object (probably a
534     // public API type), so just use plain v8::internal::Object. We could
535     // consider mapping some public API types to their corresponding internal
536     // types here, at the possible cost of increased maintenance.
537     sp_value_type = Extension::Current()->GetV8ObjectType(sp_ctx);
538   }
539 
540   Location loc;
541   RETURN_IF_FAIL(p_v8_local_instance->GetLocation(&loc));
542 
543   // Read the pointer at the Object location
544   ULONG64 obj_address;
545   RETURN_IF_FAIL(
546       sp_debug_host_memory->ReadPointers(sp_ctx.Get(), loc, 1, &obj_address));
547 
548   // If the val_ is a nullptr, then there is no value in the Local.
549   if (obj_address == 0) {
550     RETURN_IF_FAIL(CreateString(std::u16string{u"<empty>"}, pp_value));
551   } else {
552     // Should be a v8::internal::Object at the address
553     RETURN_IF_FAIL(sp_data_model_manager->CreateTypedObject(
554         sp_ctx.Get(), obj_address, sp_value_type.Get(), pp_value));
555   }
556 
557   return S_OK;
558 }
559 
SetValue(PCWSTR,IModelObject *,IModelObject *)560 IFACEMETHODIMP V8LocalValueProperty::SetValue(
561     PCWSTR /*pwsz_key*/, IModelObject* /*p_process_instance*/,
562     IModelObject* /*p_value*/) noexcept {
563   return E_NOTIMPL;
564 }
565 
GetValue(PCWSTR pwsz_key,IModelObject * p_v8_compiler_node_instance,IModelObject ** pp_value)566 IFACEMETHODIMP V8InternalCompilerNodeIdProperty::GetValue(
567     PCWSTR pwsz_key, IModelObject* p_v8_compiler_node_instance,
568     IModelObject** pp_value) noexcept {
569   WRL::ComPtr<IModelObject> sp_bit_field;
570   RETURN_IF_FAIL(p_v8_compiler_node_instance->GetRawValue(
571       SymbolKind::SymbolField, L"bit_field_", RawSearchNone, &sp_bit_field));
572 
573   uint64_t bit_field_value;
574   RETURN_IF_FAIL(
575       UnboxULong64(sp_bit_field.Get(), &bit_field_value, true /*convert*/));
576 
577   WRL::ComPtr<IDebugHostContext> sp_host_context;
578   RETURN_IF_FAIL(p_v8_compiler_node_instance->GetContext(&sp_host_context));
579 
580   WRL::ComPtr<IDebugHostType> sp_id_field_type;
581   RETURN_IF_FAIL(Extension::Current()
582                      ->GetV8Module(sp_host_context)
583                      ->FindTypeByName(L"v8::internal::compiler::Node::IdField",
584                                       &sp_id_field_type));
585 
586   // Get 2nd template parameter as 24 in class.
587   // v8::base::BitField<v8::internal::compiler::NodeId, 0, 24>.
588   bool is_generic;
589   RETURN_IF_FAIL(sp_id_field_type->IsGeneric(&is_generic));
590   if (!is_generic) return E_FAIL;
591 
592   WRL::ComPtr<IDebugHostSymbol> sp_k_size_arg;
593   RETURN_IF_FAIL(sp_id_field_type->GetGenericArgumentAt(2, &sp_k_size_arg));
594 
595   WRL::ComPtr<IDebugHostConstant> sp_k_size_constant;
596   RETURN_IF_FAIL(sp_k_size_arg.As(&sp_k_size_constant));
597 
598   int k_size;
599   RETURN_IF_FAIL(GetInt32(sp_k_size_constant.Get(), &k_size));
600 
601   // Compute node_id.
602   uint32_t node_id = bit_field_value & (0xFFFFFFFF >> k_size);
603   RETURN_IF_FAIL(CreateUInt32(node_id, pp_value));
604 
605   return S_OK;
606 }
607 
SetValue(PCWSTR,IModelObject *,IModelObject *)608 IFACEMETHODIMP V8InternalCompilerNodeIdProperty::SetValue(
609     PCWSTR /*pwsz_key*/, IModelObject* /*p_process_instance*/,
610     IModelObject* /*p_value*/) noexcept {
611   return E_NOTIMPL;
612 }
613 
GetValue(PCWSTR pwsz_key,IModelObject * p_v8_compiler_type_instance,IModelObject ** pp_value)614 IFACEMETHODIMP V8InternalCompilerBitsetNameProperty::GetValue(
615     PCWSTR pwsz_key, IModelObject* p_v8_compiler_type_instance,
616     IModelObject** pp_value) noexcept {
617   WRL::ComPtr<IModelObject> sp_payload;
618   RETURN_IF_FAIL(p_v8_compiler_type_instance->GetRawValue(
619       SymbolKind::SymbolField, L"payload_", RawSearchNone, &sp_payload));
620 
621   uint64_t payload_value;
622   RETURN_IF_FAIL(
623       UnboxULong64(sp_payload.Get(), &payload_value, true /*convert*/));
624 
625   const char* bitset_name = ::BitsetName(payload_value);
626   if (!bitset_name) return E_FAIL;
627   std::string name(bitset_name);
628   RETURN_IF_FAIL(CreateString(ConvertToU16String(name), pp_value));
629 
630   return S_OK;
631 }
632 
SetValue(PCWSTR,IModelObject *,IModelObject *)633 IFACEMETHODIMP V8InternalCompilerBitsetNameProperty::SetValue(
634     PCWSTR /*pwsz_key*/, IModelObject* /*p_process_instance*/,
635     IModelObject* /*p_value*/) noexcept {
636   return E_NOTIMPL;
637 }
638 
639 constexpr wchar_t usage[] =
640     LR"(Invalid arguments.
641 First argument should be a uint64 representing the tagged value to investigate.
642 Second argument is optional, and may be a fully-qualified type name such as
643 v8::internal::String.)";
644 
Call(IModelObject * p_context_object,ULONG64 arg_count,_In_reads_ (arg_count)IModelObject ** pp_arguments,IModelObject ** pp_result,IKeyStore ** pp_metadata)645 IFACEMETHODIMP InspectV8ObjectMethod::Call(IModelObject* p_context_object,
646                                            ULONG64 arg_count,
647                                            _In_reads_(arg_count)
648                                                IModelObject** pp_arguments,
649                                            IModelObject** pp_result,
650                                            IKeyStore** pp_metadata) noexcept {
651   // Read the arguments.
652   ULONG64 tagged_value;
653   _bstr_t type_name;
654   if (arg_count < 1 ||
655       FAILED(UnboxULong64(pp_arguments[0], &tagged_value, /*convert=*/true)) ||
656       (arg_count >= 2 &&
657        FAILED(UnboxString(pp_arguments[1], type_name.GetAddress())))) {
658     sp_data_model_manager->CreateErrorObject(E_INVALIDARG, usage, pp_result);
659     return E_INVALIDARG;
660   }
661 
662   WRL::ComPtr<IDebugHostContext> sp_ctx;
663   RETURN_IF_FAIL(sp_debug_host->GetCurrentContext(&sp_ctx));
664 
665   // We can't use CreateTypedObject for a value which may not actually reside
666   // anywhere in memory, so create a synthetic object.
667   WRL::ComPtr<V8CachedObject> cached_object =
668       WRL::Make<V8CachedObject>(::GetHeapObject(
669           sp_ctx, tagged_value, 0, static_cast<const char*>(type_name),
670           /*is_compressed=*/false));
671   return CreateSyntheticObjectForV8Object(sp_ctx.Get(), cached_object.Get(),
672                                           pp_result);
673 }
674 
675 // Creates an IModelObject representing the data in the given property.
GetModelForProperty(const Property & prop,WRL::ComPtr<IDebugHostContext> & sp_ctx,IModelObject ** result)676 HRESULT GetModelForProperty(const Property& prop,
677                             WRL::ComPtr<IDebugHostContext>& sp_ctx,
678                             IModelObject** result) {
679   switch (prop.type) {
680     case PropertyType::kPointer:
681       return GetModelForBasicField(prop.addr_value, prop.type_name,
682                                    prop.uncompressed_type_name, sp_ctx, result);
683     case PropertyType::kStruct:
684       return GetModelForStruct(prop.addr_value, prop.fields, sp_ctx, result);
685     case PropertyType::kArray:
686     case PropertyType::kStructArray:
687       if (prop.type == PropertyType::kArray &&
688           prop.type_name == ConvertToU16String(prop.uncompressed_type_name)) {
689         // An array of things that are not structs or compressed tagged values
690         // is most cleanly represented by a native array.
691         return GetModelForNativeArray(prop.addr_value, prop.type_name,
692                                       prop.length, sp_ctx, result);
693       }
694       // Otherwise, we must construct a custom iterable object.
695       return GetModelForCustomArray(prop, sp_ctx, result);
696     default:
697       return E_FAIL;
698   }
699 }
700