1// Copyright 2019 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 <sstream>
6
7#include "debug-helper-internal.h"
8#include "heap-constants.h"
9#include "include/v8-internal.h"
10#include "src/execution/frame-constants.h"
11#include "src/execution/frames.h"
12#include "src/execution/isolate-utils.h"
13#include "src/objects/string-inl.h"
14#include "src/sandbox/external-pointer.h"
15#include "src/strings/unicode-inl.h"
16#include "torque-generated/class-debug-readers.h"
17#include "torque-generated/debug-macros.h"
18
19namespace i = v8::internal;
20
21namespace v8 {
22namespace internal {
23namespace debug_helper_internal {
24
25constexpr char kObject[] = "v8::internal::Object";
26constexpr char kTaggedValue[] = "v8::internal::TaggedValue";
27constexpr char kSmi[] = "v8::internal::Smi";
28constexpr char kHeapObject[] = "v8::internal::HeapObject";
29#ifdef V8_COMPRESS_POINTERS
30constexpr char kObjectAsStoredInHeap[] = "v8::internal::TaggedValue";
31#else
32constexpr char kObjectAsStoredInHeap[] = "v8::internal::Object";
33#endif
34
35std::string AppendAddressAndType(const std::string& brief, uintptr_t address,
36                                 const char* type) {
37  std::stringstream brief_stream;
38  brief_stream << "0x" << std::hex << address << " <" << type << ">";
39  return brief.empty() ? brief_stream.str()
40                       : brief + " (" + brief_stream.str() + ")";
41}
42
43std::string JoinWithSpace(const std::string& a, const std::string& b) {
44  return a.empty() || b.empty() ? a + b : a + " " + b;
45}
46
47struct TypedObject {
48  TypedObject(d::TypeCheckResult type_check_result,
49              std::unique_ptr<TqObject> object)
50      : type_check_result(type_check_result), object(std::move(object)) {}
51
52  // How we discovered the object's type, or why we failed to do so.
53  d::TypeCheckResult type_check_result;
54
55  // Pointer to some TqObject subclass, representing the most specific known
56  // type for the object.
57  std::unique_ptr<TqObject> object;
58
59  // Collection of other guesses at more specific types than the one represented
60  // by |object|.
61  std::vector<TypedObject> possible_types;
62};
63
64TypedObject GetTypedObjectByHint(uintptr_t address,
65                                 std::string type_hint_string) {
66#define TYPE_NAME_CASE(ClassName, ...)                   \
67  if (type_hint_string == "v8::internal::" #ClassName) { \
68    return {d::TypeCheckResult::kUsedTypeHint,           \
69            std::make_unique<Tq##ClassName>(address)};   \
70  }
71
72  TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(TYPE_NAME_CASE)
73  TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(TYPE_NAME_CASE)
74  STRING_CLASS_TYPES(TYPE_NAME_CASE)
75
76#undef TYPE_NAME_CASE
77
78  return {d::TypeCheckResult::kUnknownTypeHint,
79          std::make_unique<TqHeapObject>(address)};
80}
81
82TypedObject GetTypedObjectForString(uintptr_t address, i::InstanceType type,
83                                    d::TypeCheckResult type_source) {
84  class StringGetDispatcher : public i::AllStatic {
85   public:
86#define DEFINE_METHOD(ClassName)                                    \
87  static inline TypedObject Handle##ClassName(                      \
88      uintptr_t address, d::TypeCheckResult type_source) {          \
89    return {type_source, std::make_unique<Tq##ClassName>(address)}; \
90  }
91    STRING_CLASS_TYPES(DEFINE_METHOD)
92#undef DEFINE_METHOD
93    static inline TypedObject HandleInvalidString(
94        uintptr_t address, d::TypeCheckResult type_source) {
95      return {d::TypeCheckResult::kUnknownInstanceType,
96              std::make_unique<TqString>(address)};
97    }
98  };
99
100  return i::StringShape(type)
101      .DispatchToSpecificTypeWithoutCast<StringGetDispatcher, TypedObject>(
102          address, type_source);
103}
104
105TypedObject GetTypedObjectByInstanceType(uintptr_t address,
106                                         i::InstanceType type,
107                                         d::TypeCheckResult type_source) {
108  switch (type) {
109#define INSTANCE_TYPE_CASE(ClassName, INSTANCE_TYPE) \
110  case i::INSTANCE_TYPE:                             \
111    return {type_source, std::make_unique<Tq##ClassName>(address)};
112    TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(INSTANCE_TYPE_CASE)
113    TORQUE_INSTANCE_CHECKERS_MULTIPLE_FULLY_DEFINED(INSTANCE_TYPE_CASE)
114#undef INSTANCE_TYPE_CASE
115
116    default:
117
118      // Special case: concrete subtypes of String are not included in the
119      // main instance type list because they use the low bits of the instance
120      // type enum as flags.
121      if (type <= i::LAST_STRING_TYPE) {
122        return GetTypedObjectForString(address, type, type_source);
123      }
124
125#define INSTANCE_RANGE_CASE(ClassName, FIRST_TYPE, LAST_TYPE)       \
126  if (type >= i::FIRST_TYPE && type <= i::LAST_TYPE) {              \
127    return {type_source, std::make_unique<Tq##ClassName>(address)}; \
128  }
129      TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(INSTANCE_RANGE_CASE)
130#undef INSTANCE_RANGE_CASE
131
132      return {d::TypeCheckResult::kUnknownInstanceType,
133              std::make_unique<TqHeapObject>(address)};
134  }
135}
136
137bool IsTypedHeapObjectInstanceTypeOf(uintptr_t address,
138                                     d::MemoryAccessor accessor,
139                                     i::InstanceType instance_type) {
140  auto heap_object = std::make_unique<TqHeapObject>(address);
141  Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor);
142
143  if (map_ptr.validity == d::MemoryAccessResult::kOk) {
144    Value<i::InstanceType> type =
145        TqMap(map_ptr.value).GetInstanceTypeValue(accessor);
146    if (type.validity == d::MemoryAccessResult::kOk) {
147      return instance_type == type.value;
148    }
149  }
150
151  return false;
152}
153
154TypedObject GetTypedHeapObject(uintptr_t address, d::MemoryAccessor accessor,
155                               const char* type_hint,
156                               const d::HeapAddresses& heap_addresses) {
157  auto heap_object = std::make_unique<TqHeapObject>(address);
158  Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor);
159
160  if (map_ptr.validity != d::MemoryAccessResult::kOk) {
161    // If we can't read the Map pointer from the object, then we likely can't
162    // read anything else, so there's not any point in attempting to use the
163    // type hint. Just return a failure.
164    return {map_ptr.validity == d::MemoryAccessResult::kAddressNotValid
165                ? d::TypeCheckResult::kObjectPointerInvalid
166                : d::TypeCheckResult::kObjectPointerValidButInaccessible,
167            std::move(heap_object)};
168  }
169
170  Value<i::InstanceType> type =
171      TqMap(map_ptr.value).GetInstanceTypeValue(accessor);
172  if (type.validity == d::MemoryAccessResult::kOk) {
173    return GetTypedObjectByInstanceType(address, type.value,
174                                        d::TypeCheckResult::kUsedMap);
175  }
176
177  // We can't read the Map, so check whether it is in the list of known Maps,
178  // as another way to get its instance type.
179  KnownInstanceType known_map_type =
180      FindKnownMapInstanceTypes(map_ptr.value, heap_addresses);
181  if (known_map_type.confidence == KnownInstanceType::Confidence::kHigh) {
182    DCHECK_EQ(known_map_type.types.size(), 1);
183    return GetTypedObjectByInstanceType(address, known_map_type.types[0],
184                                        d::TypeCheckResult::kKnownMapPointer);
185  }
186
187  // Create a basic result that says that the object is a HeapObject and we
188  // couldn't read its Map.
189  TypedObject result = {
190      type.validity == d::MemoryAccessResult::kAddressNotValid
191          ? d::TypeCheckResult::kMapPointerInvalid
192          : d::TypeCheckResult::kMapPointerValidButInaccessible,
193      std::move(heap_object)};
194
195  // If a type hint is available, it may give us something more specific than
196  // HeapObject. However, a type hint of Object would be even less specific, so
197  // we'll only use the type hint if it's a subclass of HeapObject.
198  if (type_hint != nullptr) {
199    TypedObject hint_result = GetTypedObjectByHint(address, type_hint);
200    if (result.object->IsSuperclassOf(hint_result.object.get())) {
201      result = std::move(hint_result);
202    }
203  }
204
205  // If low-confidence results are available from known Maps, include them only
206  // if they don't contradict the primary type and would provide some additional
207  // specificity.
208  for (const i::InstanceType type_guess : known_map_type.types) {
209    TypedObject guess_result = GetTypedObjectByInstanceType(
210        address, type_guess, d::TypeCheckResult::kKnownMapPointer);
211    if (result.object->IsSuperclassOf(guess_result.object.get())) {
212      result.possible_types.push_back(std::move(guess_result));
213    }
214  }
215
216  return result;
217}
218
219// An object visitor that accumulates the first few characters of a string.
220class ReadStringVisitor : public TqObjectVisitor {
221 public:
222  static v8::base::Optional<std::string> Visit(
223      d::MemoryAccessor accessor, const d::HeapAddresses& heap_addresses,
224      const TqString* object) {
225    ReadStringVisitor visitor(accessor, heap_addresses);
226    object->Visit(&visitor);
227    return visitor.GetString();
228  }
229
230  // Returns the result as UTF-8 once visiting is complete.
231  v8::base::Optional<std::string> GetString() {
232    if (failed_) return {};
233    std::vector<char> result(
234        string_.size() * unibrow::Utf16::kMaxExtraUtf8BytesForOneUtf16CodeUnit);
235    unsigned write_index = 0;
236    int prev_char = unibrow::Utf16::kNoPreviousCharacter;
237    for (size_t read_index = 0; read_index < string_.size(); ++read_index) {
238      uint16_t character = string_[read_index];
239      write_index +=
240          unibrow::Utf8::Encode(result.data() + write_index, character,
241                                prev_char, /*replace_invalid=*/true);
242      prev_char = character;
243    }
244    return std::string(result.data(), write_index);
245  }
246
247  template <typename TChar>
248  Value<TChar> ReadCharacter(uintptr_t data_address, int32_t index) {
249    TChar value{};
250    d::MemoryAccessResult validity =
251        accessor_(data_address + index * sizeof(TChar),
252                  reinterpret_cast<uint8_t*>(&value), sizeof(value));
253    return {validity, value};
254  }
255
256  template <typename TChar>
257  void ReadStringCharacters(const TqString* object, uintptr_t data_address) {
258    int32_t length = GetOrFinish(object->GetLengthValue(accessor_));
259    for (; index_ < length && index_ < limit_ && !done_; ++index_) {
260      STATIC_ASSERT(sizeof(TChar) <= sizeof(char16_t));
261      char16_t c = static_cast<char16_t>(
262          GetOrFinish(ReadCharacter<TChar>(data_address, index_)));
263      if (!done_) AddCharacter(c);
264    }
265  }
266
267  template <typename TChar, typename TString>
268  void ReadSeqString(const TString* object) {
269    ReadStringCharacters<TChar>(object, object->GetCharsAddress());
270  }
271
272  void VisitSeqOneByteString(const TqSeqOneByteString* object) override {
273    ReadSeqString<char>(object);
274  }
275
276  void VisitSeqTwoByteString(const TqSeqTwoByteString* object) override {
277    ReadSeqString<char16_t>(object);
278  }
279
280  void VisitConsString(const TqConsString* object) override {
281    uintptr_t first_address = GetOrFinish(object->GetFirstValue(accessor_));
282    if (done_) return;
283    auto first =
284        GetTypedHeapObject(first_address, accessor_, nullptr, heap_addresses_)
285            .object;
286    first->Visit(this);
287    if (done_) return;
288    int32_t first_length = GetOrFinish(
289        static_cast<TqString*>(first.get())->GetLengthValue(accessor_));
290    uintptr_t second = GetOrFinish(object->GetSecondValue(accessor_));
291    if (done_) return;
292    IndexModifier modifier(this, -first_length, -first_length);
293    GetTypedHeapObject(second, accessor_, nullptr, heap_addresses_)
294        .object->Visit(this);
295  }
296
297  void VisitSlicedString(const TqSlicedString* object) override {
298    uintptr_t parent = GetOrFinish(object->GetParentValue(accessor_));
299    int32_t length = GetOrFinish(object->GetLengthValue(accessor_));
300    int32_t offset = i::PlatformSmiTagging::SmiToInt(
301        GetOrFinish(object->GetOffsetValue(accessor_)));
302    if (done_) return;
303    int32_t limit_adjust = offset + length - limit_;
304    IndexModifier modifier(this, offset, limit_adjust < 0 ? limit_adjust : 0);
305    GetTypedHeapObject(parent, accessor_, nullptr, heap_addresses_)
306        .object->Visit(this);
307  }
308
309  void VisitThinString(const TqThinString* object) override {
310    uintptr_t actual = GetOrFinish(object->GetActualValue(accessor_));
311    if (done_) return;
312    GetTypedHeapObject(actual, accessor_, nullptr, heap_addresses_)
313        .object->Visit(this);
314  }
315
316  bool IsExternalStringCached(const TqExternalString* object) {
317    // The safest way to get the instance type is to use known map pointers, in
318    // case the map data is not available.
319    Value<uintptr_t> map_ptr = object->GetMapValue(accessor_);
320    DCHECK_IMPLIES(map_ptr.validity == d::MemoryAccessResult::kOk,
321                   !v8::internal::MapWord::IsPacked(map_ptr.value));
322    uintptr_t map = GetOrFinish(map_ptr);
323    if (done_) return false;
324    auto instance_types = FindKnownMapInstanceTypes(map, heap_addresses_);
325    // Exactly one of the matched instance types should be a string type,
326    // because all maps for string types are in the same space (read-only
327    // space). The "uncached" flag on that instance type tells us whether it's
328    // safe to read the cached data.
329    for (const auto& type : instance_types.types) {
330      if ((type & i::kIsNotStringMask) == i::kStringTag &&
331          (type & i::kStringRepresentationMask) == i::kExternalStringTag) {
332        return (type & i::kUncachedExternalStringMask) !=
333               i::kUncachedExternalStringTag;
334      }
335    }
336
337    // If for some reason we can't find an external string type here (maybe the
338    // caller provided an external string type as the type hint, but it doesn't
339    // actually match the in-memory map pointer), then we can't safely use the
340    // cached data.
341    return false;
342  }
343
344  template <typename TChar>
345  void ReadExternalString(const TqExternalString* object) {
346    // Cached external strings are easy to read; uncached external strings
347    // require knowledge of the embedder. For now, we only read cached external
348    // strings.
349    if (IsExternalStringCached(object)) {
350      ExternalPointer_t resource_data =
351          GetOrFinish(object->GetResourceDataValue(accessor_));
352#ifdef V8_COMPRESS_POINTERS
353      Isolate* isolate = GetIsolateForSandbox(
354          HeapObject::unchecked_cast(Object(heap_addresses_.any_heap_pointer)));
355      uintptr_t data_address = static_cast<uintptr_t>(DecodeExternalPointer(
356          isolate, resource_data, kExternalStringResourceDataTag));
357#else
358      uintptr_t data_address = static_cast<uintptr_t>(resource_data);
359#endif  // V8_COMPRESS_POINTERS
360      if (done_) return;
361      ReadStringCharacters<TChar>(object, data_address);
362    } else {
363      // TODO(v8:9376): Come up with some way that a caller with full knowledge
364      // of a particular embedder could provide a callback function for getting
365      // uncached string data.
366      AddEllipsisAndFinish();
367    }
368  }
369
370  void VisitExternalOneByteString(
371      const TqExternalOneByteString* object) override {
372    ReadExternalString<char>(object);
373  }
374
375  void VisitExternalTwoByteString(
376      const TqExternalTwoByteString* object) override {
377    ReadExternalString<char16_t>(object);
378  }
379
380  void VisitObject(const TqObject* object) override {
381    // If we fail to find a specific type for a sub-object within a cons string,
382    // sliced string, or thin string, we will end up here.
383    AddEllipsisAndFinish();
384  }
385
386 private:
387  ReadStringVisitor(d::MemoryAccessor accessor,
388                    const d::HeapAddresses& heap_addresses)
389      : accessor_(accessor),
390        heap_addresses_(heap_addresses),
391        index_(0),
392        limit_(INT32_MAX),
393        done_(false),
394        failed_(false) {}
395
396  // Unpacks a value that was fetched from the debuggee. If the value indicates
397  // that it couldn't successfully fetch memory, then prevents further work.
398  template <typename T>
399  T GetOrFinish(Value<T> value) {
400    if (value.validity != d::MemoryAccessResult::kOk) {
401      AddEllipsisAndFinish();
402    }
403    return value.value;
404  }
405
406  void AddEllipsisAndFinish() {
407    if (!done_) {
408      done_ = true;
409      if (string_.empty()) {
410        failed_ = true;
411      } else {
412        string_ += u"...";
413      }
414    }
415  }
416
417  void AddCharacter(char16_t c) {
418    if (string_.size() >= kMaxCharacters) {
419      AddEllipsisAndFinish();
420    } else {
421      string_.push_back(c);
422    }
423  }
424
425  // Temporarily adds offsets to both index_ and limit_, to handle ConsString
426  // and SlicedString.
427  class IndexModifier {
428   public:
429    IndexModifier(ReadStringVisitor* that, int32_t index_adjust,
430                  int32_t limit_adjust)
431        : that_(that),
432          index_adjust_(index_adjust),
433          limit_adjust_(limit_adjust) {
434      that_->index_ += index_adjust_;
435      that_->limit_ += limit_adjust_;
436    }
437    IndexModifier(const IndexModifier&) = delete;
438    IndexModifier& operator=(const IndexModifier&) = delete;
439    ~IndexModifier() {
440      that_->index_ -= index_adjust_;
441      that_->limit_ -= limit_adjust_;
442    }
443
444   private:
445    ReadStringVisitor* that_;
446    int32_t index_adjust_;
447    int32_t limit_adjust_;
448  };
449
450  static constexpr int kMaxCharacters = 80;  // How many characters to print.
451
452  std::u16string string_;  // Result string.
453  d::MemoryAccessor accessor_;
454  const d::HeapAddresses& heap_addresses_;
455  int32_t index_;  // Index of next char to read.
456  int32_t limit_;  // Don't read past this index (set by SlicedString).
457  bool done_;      // Whether to stop further work.
458  bool failed_;    // Whether an error was encountered before any valid data.
459};
460
461// An object visitor that supplies extra information for some types.
462class AddInfoVisitor : public TqObjectVisitor {
463 public:
464  // Returns a descriptive string and a list of properties for the given object.
465  // Both may be empty, and are meant as an addition or a replacement for,
466  // the Torque-generated data about the object.
467  static std::pair<std::string, std::vector<std::unique_ptr<ObjectProperty>>>
468  Visit(const TqObject* object, d::MemoryAccessor accessor,
469        const d::HeapAddresses& heap_addresses) {
470    AddInfoVisitor visitor(accessor, heap_addresses);
471    object->Visit(&visitor);
472    return {std::move(visitor.brief_), std::move(visitor.properties_)};
473  }
474
475  void VisitString(const TqString* object) override {
476    auto str = ReadStringVisitor::Visit(accessor_, heap_addresses_, object);
477    if (str.has_value()) {
478      brief_ = "\"" + *str + "\"";
479    }
480  }
481
482  void VisitExternalString(const TqExternalString* object) override {
483    VisitString(object);
484    // Cast resource field to v8::String::ExternalStringResourceBase* would add
485    // more info.
486    properties_.push_back(std::make_unique<ObjectProperty>(
487        "resource",
488        CheckTypeName<v8::String::ExternalStringResourceBase*>(
489            "v8::String::ExternalStringResourceBase*"),
490        CheckTypeName<v8::String::ExternalStringResourceBase*>(
491            "v8::String::ExternalStringResourceBase*"),
492        object->GetResourceAddress(), 1,
493        sizeof(v8::String::ExternalStringResourceBase*),
494        std::vector<std::unique_ptr<StructProperty>>(),
495        d::PropertyKind::kSingle));
496  }
497
498  void VisitJSObject(const TqJSObject* object) override {
499    // JSObject and its subclasses can be followed directly by an array of
500    // property values. The start and end offsets of those values are described
501    // by a pair of values in its Map.
502    auto map_ptr = object->GetMapValue(accessor_);
503    if (map_ptr.validity != d::MemoryAccessResult::kOk) {
504      return;  // Can't read the JSObject. Nothing useful to do.
505    }
506    DCHECK(!v8::internal::MapWord::IsPacked(map_ptr.value));
507    TqMap map(map_ptr.value);
508
509    // On JSObject instances, this value is the start of in-object properties.
510    // The constructor function index option is only for primitives.
511    auto start_offset =
512        map.GetInobjectPropertiesStartOrConstructorFunctionIndexValue(
513            accessor_);
514
515    // The total size of the object in memory. This may include over-allocated
516    // expansion space that doesn't correspond to any user-accessible property.
517    auto instance_size = map.GetInstanceSizeInWordsValue(accessor_);
518
519    if (start_offset.validity != d::MemoryAccessResult::kOk ||
520        instance_size.validity != d::MemoryAccessResult::kOk) {
521      return;  // Can't read the Map. Nothing useful to do.
522    }
523    int num_properties = instance_size.value - start_offset.value;
524    if (num_properties > 0) {
525      properties_.push_back(std::make_unique<ObjectProperty>(
526          "in-object properties", kObjectAsStoredInHeap, kObject,
527          object->GetMapAddress() + start_offset.value * i::kTaggedSize,
528          num_properties, i::kTaggedSize,
529          std::vector<std::unique_ptr<StructProperty>>(),
530          d::PropertyKind::kArrayOfKnownSize));
531    }
532  }
533
534 private:
535  AddInfoVisitor(d::MemoryAccessor accessor,
536                 const d::HeapAddresses& heap_addresses)
537      : accessor_(accessor), heap_addresses_(heap_addresses) {}
538
539  // Inputs used by this visitor:
540
541  d::MemoryAccessor accessor_;
542  const d::HeapAddresses& heap_addresses_;
543
544  // Outputs generated by this visitor:
545
546  // A brief description of the object.
547  std::string brief_;
548  // A list of extra properties to append after the automatic ones that are
549  // created for all Torque-defined class fields.
550  std::vector<std::unique_ptr<ObjectProperty>> properties_;
551};
552
553std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesNotCompressed(
554    uintptr_t address, d::MemoryAccessor accessor, const char* type_hint,
555    const d::HeapAddresses& heap_addresses) {
556  // Regardless of whether we can read the object itself, maybe we can find its
557  // pointer in the list of known objects.
558  std::string brief = FindKnownObject(address, heap_addresses);
559
560  TypedObject typed =
561      GetTypedHeapObject(address, accessor, type_hint, heap_addresses);
562  auto props = typed.object->GetProperties(accessor);
563
564  // Use the AddInfoVisitor to get any extra properties or descriptive text that
565  // can't be directly derived from Torque class definitions.
566  auto extra_info =
567      AddInfoVisitor::Visit(typed.object.get(), accessor, heap_addresses);
568  brief = JoinWithSpace(brief, extra_info.first);
569
570  // Overwrite existing properties if they have the same name.
571  for (size_t i = 0; i < extra_info.second.size(); i++) {
572    bool overwrite = false;
573    for (size_t j = 0; j < props.size(); j++) {
574      if (strcmp(props[j]->GetPublicView()->name,
575                 extra_info.second[i]->GetPublicView()->name) == 0) {
576        props[j] = std::move(extra_info.second[i]);
577        overwrite = true;
578        break;
579      }
580    }
581    if (overwrite) continue;
582    props.push_back(std::move(extra_info.second[i]));
583  }
584
585  brief = AppendAddressAndType(brief, address, typed.object->GetName());
586
587  // Convert the low-confidence guessed types to a list of strings as expected
588  // for the response.
589  std::vector<std::string> guessed_types;
590  for (const auto& guess : typed.possible_types) {
591    guessed_types.push_back(guess.object->GetName());
592  }
593
594  return std::make_unique<ObjectPropertiesResult>(
595      typed.type_check_result, brief, typed.object->GetName(), std::move(props),
596      std::move(guessed_types));
597}
598
599std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesMaybeCompressed(
600    uintptr_t address, d::MemoryAccessor memory_accessor,
601    d::HeapAddresses heap_addresses, const char* type_hint) {
602  // Try to figure out the heap range, for pointer compression (this is unused
603  // if pointer compression is disabled).
604  uintptr_t any_uncompressed_ptr = 0;
605  if (!IsPointerCompressed(address)) any_uncompressed_ptr = address;
606  if (any_uncompressed_ptr == 0)
607    any_uncompressed_ptr = heap_addresses.any_heap_pointer;
608  if (any_uncompressed_ptr == 0)
609    any_uncompressed_ptr = heap_addresses.map_space_first_page;
610  if (any_uncompressed_ptr == 0)
611    any_uncompressed_ptr = heap_addresses.old_space_first_page;
612  if (any_uncompressed_ptr == 0)
613    any_uncompressed_ptr = heap_addresses.read_only_space_first_page;
614  FillInUnknownHeapAddresses(&heap_addresses, any_uncompressed_ptr);
615  if (any_uncompressed_ptr == 0) {
616    // We can't figure out the heap range. Just check for known objects.
617    std::string brief = FindKnownObject(address, heap_addresses);
618    brief = AppendAddressAndType(brief, address, kTaggedValue);
619    return std::make_unique<ObjectPropertiesResult>(
620        d::TypeCheckResult::kUnableToDecompress, brief, kTaggedValue);
621  }
622
623  address = EnsureDecompressed(address, any_uncompressed_ptr);
624
625  return GetHeapObjectPropertiesNotCompressed(address, memory_accessor,
626                                              type_hint, heap_addresses);
627}
628
629std::unique_ptr<ObjectPropertiesResult> GetObjectProperties(
630    uintptr_t address, d::MemoryAccessor memory_accessor,
631    const d::HeapAddresses& heap_addresses, const char* type_hint) {
632  if (static_cast<uint32_t>(address) == i::kClearedWeakHeapObjectLower32) {
633    return std::make_unique<ObjectPropertiesResult>(
634        d::TypeCheckResult::kWeakRef, "cleared weak ref", kHeapObject);
635  }
636  bool is_weak = (address & i::kHeapObjectTagMask) == i::kWeakHeapObjectTag;
637  if (is_weak) {
638    address &= ~i::kWeakHeapObjectMask;
639  }
640  if (i::Internals::HasHeapObjectTag(address)) {
641    std::unique_ptr<ObjectPropertiesResult> result =
642        GetHeapObjectPropertiesMaybeCompressed(address, memory_accessor,
643                                               heap_addresses, type_hint);
644    if (is_weak) {
645      result->Prepend("weak ref to ");
646    }
647    return result;
648  }
649
650  // For smi values, construct a response with a description representing the
651  // untagged value.
652  int32_t value = i::PlatformSmiTagging::SmiToInt(address);
653  std::stringstream stream;
654  stream << value << " (0x" << std::hex << value << ")";
655  return std::make_unique<ObjectPropertiesResult>(d::TypeCheckResult::kSmi,
656                                                  stream.str(), kSmi);
657}
658
659std::unique_ptr<StackFrameResult> GetStackFrame(
660    uintptr_t frame_pointer, d::MemoryAccessor memory_accessor) {
661  // Read the data at frame_pointer + kContextOrFrameTypeOffset.
662  intptr_t context_or_frame_type = 0;
663  d::MemoryAccessResult validity = memory_accessor(
664      frame_pointer + CommonFrameConstants::kContextOrFrameTypeOffset,
665      reinterpret_cast<void*>(&context_or_frame_type), sizeof(intptr_t));
666  auto props = std::vector<std::unique_ptr<ObjectProperty>>();
667  if (validity == d::MemoryAccessResult::kOk) {
668    // If it is context, not frame marker then add new property
669    // "currently_executing_function".
670    if (!StackFrame::IsTypeMarker(context_or_frame_type)) {
671      props.push_back(std::make_unique<ObjectProperty>(
672          "currently_executing_jsfunction",
673          CheckTypeName<v8::internal::JSFunction>("v8::internal::JSFunction"),
674          CheckTypeName<v8::internal::JSFunction*>("v8::internal::JSFunction"),
675          frame_pointer + StandardFrameConstants::kFunctionOffset, 1,
676          sizeof(v8::internal::JSFunction),
677          std::vector<std::unique_ptr<StructProperty>>(),
678          d::PropertyKind::kSingle));
679      // Add more items in the Locals pane representing the JS function name,
680      // source file name, and line & column numbers within the source file, so
681      // that the user doesn’t need to dig through the shared_function_info to
682      // find them.
683      intptr_t js_function_ptr = 0;
684      validity = memory_accessor(
685          frame_pointer + StandardFrameConstants::kFunctionOffset,
686          reinterpret_cast<void*>(&js_function_ptr), sizeof(intptr_t));
687      if (validity == d::MemoryAccessResult::kOk) {
688        TqJSFunction js_function(js_function_ptr);
689        auto shared_function_info_ptr =
690            js_function.GetSharedFunctionInfoValue(memory_accessor);
691        if (shared_function_info_ptr.validity == d::MemoryAccessResult::kOk) {
692          TqSharedFunctionInfo shared_function_info(
693              shared_function_info_ptr.value);
694          auto script_or_debug_info_ptr =
695              shared_function_info.GetScriptOrDebugInfoValue(memory_accessor);
696          if (script_or_debug_info_ptr.validity == d::MemoryAccessResult::kOk) {
697            // Make sure script_or_debug_info_ptr is script.
698            auto address = script_or_debug_info_ptr.value;
699            if (IsTypedHeapObjectInstanceTypeOf(address, memory_accessor,
700                                                i::InstanceType::SCRIPT_TYPE)) {
701              TqScript script(script_or_debug_info_ptr.value);
702              props.push_back(std::make_unique<ObjectProperty>(
703                  "script_name", kObjectAsStoredInHeap, kObject,
704                  script.GetNameAddress(), 1, i::kTaggedSize,
705                  std::vector<std::unique_ptr<StructProperty>>(),
706                  d::PropertyKind::kSingle));
707              props.push_back(std::make_unique<ObjectProperty>(
708                  "script_source", kObjectAsStoredInHeap, kObject,
709                  script.GetSourceAddress(), 1, i::kTaggedSize,
710                  std::vector<std::unique_ptr<StructProperty>>(),
711                  d::PropertyKind::kSingle));
712            }
713          }
714          auto name_or_scope_info_ptr =
715              shared_function_info.GetNameOrScopeInfoValue(memory_accessor);
716          if (name_or_scope_info_ptr.validity == d::MemoryAccessResult::kOk) {
717            auto scope_info_address = name_or_scope_info_ptr.value;
718            // Make sure name_or_scope_info_ptr is scope info.
719            if (IsTypedHeapObjectInstanceTypeOf(
720                    scope_info_address, memory_accessor,
721                    i::InstanceType::SCOPE_INFO_TYPE)) {
722              auto indexed_field_slice_function_variable_info =
723                  TqDebugFieldSliceScopeInfoFunctionVariableInfo(
724                      memory_accessor, scope_info_address);
725              if (indexed_field_slice_function_variable_info.validity ==
726                  d::MemoryAccessResult::kOk) {
727                props.push_back(std::make_unique<ObjectProperty>(
728                    "function_name", kObjectAsStoredInHeap, kObject,
729                    scope_info_address - i::kHeapObjectTag +
730                        std::get<1>(
731                            indexed_field_slice_function_variable_info.value),
732                    std::get<2>(
733                        indexed_field_slice_function_variable_info.value),
734                    i::kTaggedSize,
735                    std::vector<std::unique_ptr<StructProperty>>(),
736                    d::PropertyKind::kSingle));
737              }
738              std::vector<std::unique_ptr<StructProperty>>
739                  position_info_struct_field_list;
740              position_info_struct_field_list.push_back(
741                  std::make_unique<StructProperty>(
742                      "start", kObjectAsStoredInHeap, kObject, 0, 0, 0));
743              position_info_struct_field_list.push_back(
744                  std::make_unique<StructProperty>("end", kObjectAsStoredInHeap,
745                                                   kObject, 4, 0, 0));
746              auto indexed_field_slice_position_info =
747                  TqDebugFieldSliceScopeInfoPositionInfo(memory_accessor,
748                                                         scope_info_address);
749              if (indexed_field_slice_position_info.validity ==
750                  d::MemoryAccessResult::kOk) {
751                props.push_back(std::make_unique<ObjectProperty>(
752                    "function_character_offset", "", "",
753                    scope_info_address - i::kHeapObjectTag +
754                        std::get<1>(indexed_field_slice_position_info.value),
755                    std::get<2>(indexed_field_slice_position_info.value),
756                    i::kTaggedSize, std::move(position_info_struct_field_list),
757                    d::PropertyKind::kSingle));
758              }
759            }
760          }
761        }
762      }
763    }
764  }
765
766  return std::make_unique<StackFrameResult>(std::move(props));
767}
768
769}  // namespace debug_helper_internal
770}  // namespace internal
771}  // namespace v8
772
773namespace di = v8::internal::debug_helper_internal;
774
775extern "C" {
776V8_DEBUG_HELPER_EXPORT d::ObjectPropertiesResult*
777_v8_debug_helper_GetObjectProperties(uintptr_t object,
778                                     d::MemoryAccessor memory_accessor,
779                                     const d::HeapAddresses& heap_addresses,
780                                     const char* type_hint) {
781  return di::GetObjectProperties(object, memory_accessor, heap_addresses,
782                                 type_hint)
783      .release()
784      ->GetPublicView();
785}
786V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult(
787    d::ObjectPropertiesResult* result) {
788  std::unique_ptr<di::ObjectPropertiesResult> ptr(
789      static_cast<di::ObjectPropertiesResultExtended*>(result)->base);
790}
791
792V8_DEBUG_HELPER_EXPORT d::StackFrameResult* _v8_debug_helper_GetStackFrame(
793    uintptr_t frame_pointer, d::MemoryAccessor memory_accessor) {
794  return di::GetStackFrame(frame_pointer, memory_accessor)
795      .release()
796      ->GetPublicView();
797}
798V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_StackFrameResult(
799    d::StackFrameResult* result) {
800  std::unique_ptr<di::StackFrameResult> ptr(
801      static_cast<di::StackFrameResultExtended*>(result)->base);
802}
803}
804