get-object-properties.cc 24.1 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <sstream>

#include "debug-helper-internal.h"
#include "heap-constants.h"
#include "include/v8-internal.h"
10 11
#include "src/common/external-pointer.h"
#include "src/execution/isolate-utils.h"
12
#include "src/objects/string-inl.h"
13
#include "src/strings/unicode-inl.h"
14 15 16 17
#include "torque-generated/class-debug-readers-tq.h"

namespace i = v8::internal;

18 19 20
namespace v8 {
namespace internal {
namespace debug_helper_internal {
21

22 23 24 25 26 27 28 29 30 31
constexpr char kObject[] = "v8::internal::Object";
constexpr char kTaggedValue[] = "v8::internal::TaggedValue";
constexpr char kSmi[] = "v8::internal::Smi";
constexpr char kHeapObject[] = "v8::internal::HeapObject";
#ifdef V8_COMPRESS_POINTERS
constexpr char kObjectAsStoredInHeap[] = "v8::internal::TaggedValue";
#else
constexpr char kObjectAsStoredInHeap[] = "v8::internal::Object";
#endif

32 33 34 35 36 37 38 39
std::string AppendAddressAndType(const std::string& brief, uintptr_t address,
                                 const char* type) {
  std::stringstream brief_stream;
  brief_stream << "0x" << std::hex << address << " <" << type << ">";
  return brief.empty() ? brief_stream.str()
                       : brief + " (" + brief_stream.str() + ")";
}

40 41 42 43
std::string JoinWithSpace(const std::string& a, const std::string& b) {
  return a.empty() || b.empty() ? a + b : a + " " + b;
}

44 45 46 47
struct TypedObject {
  TypedObject(d::TypeCheckResult type_check_result,
              std::unique_ptr<TqObject> object)
      : type_check_result(type_check_result), object(std::move(object)) {}
48 49

  // How we discovered the object's type, or why we failed to do so.
50
  d::TypeCheckResult type_check_result;
51 52 53

  // Pointer to some TqObject subclass, representing the most specific known
  // type for the object.
54
  std::unique_ptr<TqObject> object;
55 56 57 58

  // Collection of other guesses at more specific types than the one represented
  // by |object|.
  std::vector<TypedObject> possible_types;
59 60
};

61 62
TypedObject GetTypedObjectByHint(uintptr_t address,
                                 std::string type_hint_string) {
63 64 65 66
#define TYPE_NAME_CASE(ClassName, ...)                   \
  if (type_hint_string == "v8::internal::" #ClassName) { \
    return {d::TypeCheckResult::kUsedTypeHint,           \
            std::make_unique<Tq##ClassName>(address)};   \
67 68
  }

69 70
  TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(TYPE_NAME_CASE)
  TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(TYPE_NAME_CASE)
71
  STRING_CLASS_TYPES(TYPE_NAME_CASE)
72 73 74

#undef TYPE_NAME_CASE

75
  return {d::TypeCheckResult::kUnknownTypeHint,
76
          std::make_unique<TqHeapObject>(address)};
77 78
}

79 80
TypedObject GetTypedObjectForString(uintptr_t address, i::InstanceType type,
                                    d::TypeCheckResult type_source) {
81 82
  class StringGetDispatcher : public i::AllStatic {
   public:
83 84 85 86
#define DEFINE_METHOD(ClassName)                                    \
  static inline TypedObject Handle##ClassName(                      \
      uintptr_t address, d::TypeCheckResult type_source) {          \
    return {type_source, std::make_unique<Tq##ClassName>(address)}; \
87 88 89
  }
    STRING_CLASS_TYPES(DEFINE_METHOD)
#undef DEFINE_METHOD
90 91
    static inline TypedObject HandleInvalidString(
        uintptr_t address, d::TypeCheckResult type_source) {
92
      return {d::TypeCheckResult::kUnknownInstanceType,
93
              std::make_unique<TqString>(address)};
94 95 96 97
    }
  };

  return i::StringShape(type)
98
      .DispatchToSpecificTypeWithoutCast<StringGetDispatcher, TypedObject>(
99 100 101 102 103 104 105 106 107 108
          address, type_source);
}

TypedObject GetTypedObjectByInstanceType(uintptr_t address,
                                         i::InstanceType type,
                                         d::TypeCheckResult type_source) {
  switch (type) {
#define INSTANCE_TYPE_CASE(ClassName, INSTANCE_TYPE) \
  case i::INSTANCE_TYPE:                             \
    return {type_source, std::make_unique<Tq##ClassName>(address)};
109
    TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(INSTANCE_TYPE_CASE)
110
    TORQUE_INSTANCE_CHECKERS_MULTIPLE_FULLY_DEFINED(INSTANCE_TYPE_CASE)
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
#undef INSTANCE_TYPE_CASE

    default:

      // Special case: concrete subtypes of String are not included in the
      // main instance type list because they use the low bits of the instance
      // type enum as flags.
      if (type <= i::LAST_STRING_TYPE) {
        return GetTypedObjectForString(address, type, type_source);
      }

#define INSTANCE_RANGE_CASE(ClassName, FIRST_TYPE, LAST_TYPE)       \
  if (type >= i::FIRST_TYPE && type <= i::LAST_TYPE) {              \
    return {type_source, std::make_unique<Tq##ClassName>(address)}; \
  }
126
      TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(INSTANCE_RANGE_CASE)
127 128 129 130 131
#undef INSTANCE_RANGE_CASE

      return {d::TypeCheckResult::kUnknownInstanceType,
              std::make_unique<TqHeapObject>(address)};
  }
132 133
}

134
TypedObject GetTypedHeapObject(uintptr_t address, d::MemoryAccessor accessor,
135 136
                               const char* type_hint,
                               const d::HeapAddresses& heap_addresses) {
137
  auto heap_object = std::make_unique<TqHeapObject>(address);
138 139 140
  Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor);

  if (map_ptr.validity != d::MemoryAccessResult::kOk) {
141 142 143
    // If we can't read the Map pointer from the object, then we likely can't
    // read anything else, so there's not any point in attempting to use the
    // type hint. Just return a failure.
144 145 146 147 148
    return {map_ptr.validity == d::MemoryAccessResult::kAddressNotValid
                ? d::TypeCheckResult::kObjectPointerInvalid
                : d::TypeCheckResult::kObjectPointerValidButInaccessible,
            std::move(heap_object)};
  }
149

150 151
  Value<i::InstanceType> type =
      TqMap(map_ptr.value).GetInstanceTypeValue(accessor);
152
  if (type.validity == d::MemoryAccessResult::kOk) {
153 154 155
    return GetTypedObjectByInstanceType(address, type.value,
                                        d::TypeCheckResult::kUsedMap);
  }
156

157 158 159
  // We can't read the Map, so check whether it is in the list of known Maps,
  // as another way to get its instance type.
  KnownInstanceType known_map_type =
160
      FindKnownMapInstanceTypes(map_ptr.value, heap_addresses);
161 162 163 164 165
  if (known_map_type.confidence == KnownInstanceType::Confidence::kHigh) {
    DCHECK_EQ(known_map_type.types.size(), 1);
    return GetTypedObjectByInstanceType(address, known_map_type.types[0],
                                        d::TypeCheckResult::kKnownMapPointer);
  }
166

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
  // Create a basic result that says that the object is a HeapObject and we
  // couldn't read its Map.
  TypedObject result = {
      type.validity == d::MemoryAccessResult::kAddressNotValid
          ? d::TypeCheckResult::kMapPointerInvalid
          : d::TypeCheckResult::kMapPointerValidButInaccessible,
      std::move(heap_object)};

  // If a type hint is available, it may give us something more specific than
  // HeapObject. However, a type hint of Object would be even less specific, so
  // we'll only use the type hint if it's a subclass of HeapObject.
  if (type_hint != nullptr) {
    TypedObject hint_result = GetTypedObjectByHint(address, type_hint);
    if (result.object->IsSuperclassOf(hint_result.object.get())) {
      result = std::move(hint_result);
    }
183 184
  }

185 186 187 188 189 190 191 192
  // If low-confidence results are available from known Maps, include them only
  // if they don't contradict the primary type and would provide some additional
  // specificity.
  for (const i::InstanceType type_guess : known_map_type.types) {
    TypedObject guess_result = GetTypedObjectByInstanceType(
        address, type_guess, d::TypeCheckResult::kKnownMapPointer);
    if (result.object->IsSuperclassOf(guess_result.object.get())) {
      result.possible_types.push_back(std::move(guess_result));
193
    }
194
  }
195 196

  return result;
197 198 199 200 201
}

// An object visitor that accumulates the first few characters of a string.
class ReadStringVisitor : public TqObjectVisitor {
 public:
202 203 204 205 206 207 208
  static v8::base::Optional<std::string> Visit(
      d::MemoryAccessor accessor, const d::HeapAddresses& heap_addresses,
      const TqString* object) {
    ReadStringVisitor visitor(accessor, heap_addresses);
    object->Visit(&visitor);
    return visitor.GetString();
  }
209 210

  // Returns the result as UTF-8 once visiting is complete.
211 212
  v8::base::Optional<std::string> GetString() {
    if (failed_) return {};
213 214 215 216 217 218 219 220 221 222 223
    std::vector<char> result(
        string_.size() * unibrow::Utf16::kMaxExtraUtf8BytesForOneUtf16CodeUnit);
    unsigned write_index = 0;
    int prev_char = unibrow::Utf16::kNoPreviousCharacter;
    for (size_t read_index = 0; read_index < string_.size(); ++read_index) {
      uint16_t character = string_[read_index];
      write_index +=
          unibrow::Utf8::Encode(result.data() + write_index, character,
                                prev_char, /*replace_invalid=*/true);
      prev_char = character;
    }
224
    return std::string(result.data(), write_index);
225 226
  }

227 228 229 230 231 232 233 234 235 236 237
  template <typename TChar>
  Value<TChar> ReadCharacter(uintptr_t data_address, int32_t index) {
    TChar value{};
    d::MemoryAccessResult validity =
        accessor_(data_address + index * sizeof(TChar),
                  reinterpret_cast<uint8_t*>(&value), sizeof(value));
    return {validity, value};
  }

  template <typename TChar>
  void ReadStringCharacters(const TqString* object, uintptr_t data_address) {
238 239
    int32_t length = GetOrFinish(object->GetLengthValue(accessor_));
    for (; index_ < length && index_ < limit_ && !done_; ++index_) {
240
      STATIC_ASSERT(sizeof(TChar) <= sizeof(char16_t));
241
      char16_t c = static_cast<char16_t>(
242
          GetOrFinish(ReadCharacter<TChar>(data_address, index_)));
243 244 245 246
      if (!done_) AddCharacter(c);
    }
  }

247 248 249 250 251
  template <typename TChar, typename TString>
  void ReadSeqString(const TString* object) {
    ReadStringCharacters<TChar>(object, object->GetCharsAddress());
  }

252
  void VisitSeqOneByteString(const TqSeqOneByteString* object) override {
253
    ReadSeqString<char>(object);
254 255 256
  }

  void VisitSeqTwoByteString(const TqSeqTwoByteString* object) override {
257
    ReadSeqString<char16_t>(object);
258 259 260 261 262
  }

  void VisitConsString(const TqConsString* object) override {
    uintptr_t first_address = GetOrFinish(object->GetFirstValue(accessor_));
    if (done_) return;
263 264 265
    auto first =
        GetTypedHeapObject(first_address, accessor_, nullptr, heap_addresses_)
            .object;
266 267 268 269 270 271 272
    first->Visit(this);
    if (done_) return;
    int32_t first_length = GetOrFinish(
        static_cast<TqString*>(first.get())->GetLengthValue(accessor_));
    uintptr_t second = GetOrFinish(object->GetSecondValue(accessor_));
    if (done_) return;
    IndexModifier modifier(this, -first_length, -first_length);
273 274
    GetTypedHeapObject(second, accessor_, nullptr, heap_addresses_)
        .object->Visit(this);
275 276 277 278 279 280 281 282 283 284
  }

  void VisitSlicedString(const TqSlicedString* object) override {
    uintptr_t parent = GetOrFinish(object->GetParentValue(accessor_));
    int32_t length = GetOrFinish(object->GetLengthValue(accessor_));
    int32_t offset = i::PlatformSmiTagging::SmiToInt(
        GetOrFinish(object->GetOffsetValue(accessor_)));
    if (done_) return;
    int32_t limit_adjust = offset + length - limit_;
    IndexModifier modifier(this, offset, limit_adjust < 0 ? limit_adjust : 0);
285 286
    GetTypedHeapObject(parent, accessor_, nullptr, heap_addresses_)
        .object->Visit(this);
287 288 289 290 291
  }

  void VisitThinString(const TqThinString* object) override {
    uintptr_t actual = GetOrFinish(object->GetActualValue(accessor_));
    if (done_) return;
292 293
    GetTypedHeapObject(actual, accessor_, nullptr, heap_addresses_)
        .object->Visit(this);
294 295
  }

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
  bool IsExternalStringCached(const TqExternalString* object) {
    // The safest way to get the instance type is to use known map pointers, in
    // case the map data is not available.
    uintptr_t map = GetOrFinish(object->GetMapValue(accessor_));
    if (done_) return false;
    auto instance_types = FindKnownMapInstanceTypes(map, heap_addresses_);
    // Exactly one of the matched instance types should be a string type,
    // because all maps for string types are in the same space (read-only
    // space). The "uncached" flag on that instance type tells us whether it's
    // safe to read the cached data.
    for (const auto& type : instance_types.types) {
      if ((type & i::kIsNotStringMask) == i::kStringTag &&
          (type & i::kStringRepresentationMask) == i::kExternalStringTag) {
        return (type & i::kUncachedExternalStringMask) !=
               i::kUncachedExternalStringTag;
      }
    }

    // If for some reason we can't find an external string type here (maybe the
    // caller provided an external string type as the type hint, but it doesn't
    // actually match the in-memory map pointer), then we can't safely use the
    // cached data.
    return false;
  }

  template <typename TChar>
  void ReadExternalString(const TqExternalString* object) {
    // Cached external strings are easy to read; uncached external strings
    // require knowledge of the embedder. For now, we only read cached external
    // strings.
    if (IsExternalStringCached(object)) {
327 328 329 330 331 332 333 334 335
      ExternalPointer_t resource_data =
          GetOrFinish(object->GetResourceDataValue(accessor_));
#ifdef V8_COMPRESS_POINTERS
      uintptr_t data_address = static_cast<uintptr_t>(DecodeExternalPointer(
          Isolate::FromRoot(GetIsolateRoot(heap_addresses_.any_heap_pointer)),
          resource_data));
#else
      uintptr_t data_address = reinterpret_cast<uintptr_t>(resource_data);
#endif  // V8_COMPRESS_POINTERS
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
      if (done_) return;
      ReadStringCharacters<TChar>(object, data_address);
    } else {
      // TODO(v8:9376): Come up with some way that a caller with full knowledge
      // of a particular embedder could provide a callback function for getting
      // uncached string data.
      AddEllipsisAndFinish();
    }
  }

  void VisitExternalOneByteString(
      const TqExternalOneByteString* object) override {
    ReadExternalString<char>(object);
  }

  void VisitExternalTwoByteString(
      const TqExternalTwoByteString* object) override {
    ReadExternalString<char16_t>(object);
354 355 356 357 358 359 360 361 362
  }

  void VisitObject(const TqObject* object) override {
    // If we fail to find a specific type for a sub-object within a cons string,
    // sliced string, or thin string, we will end up here.
    AddEllipsisAndFinish();
  }

 private:
363 364 365 366 367 368 369 370 371
  ReadStringVisitor(d::MemoryAccessor accessor,
                    const d::HeapAddresses& heap_addresses)
      : accessor_(accessor),
        heap_addresses_(heap_addresses),
        index_(0),
        limit_(INT32_MAX),
        done_(false),
        failed_(false) {}

372 373 374 375 376 377 378 379
  // Unpacks a value that was fetched from the debuggee. If the value indicates
  // that it couldn't successfully fetch memory, then prevents further work.
  template <typename T>
  T GetOrFinish(Value<T> value) {
    if (value.validity != d::MemoryAccessResult::kOk) {
      AddEllipsisAndFinish();
    }
    return value.value;
380 381
  }

382 383 384
  void AddEllipsisAndFinish() {
    if (!done_) {
      done_ = true;
385 386 387 388 389
      if (string_.empty()) {
        failed_ = true;
      } else {
        string_ += u"...";
      }
390 391 392 393 394 395 396 397 398
    }
  }

  void AddCharacter(char16_t c) {
    if (string_.size() >= kMaxCharacters) {
      AddEllipsisAndFinish();
    } else {
      string_.push_back(c);
    }
399 400
  }

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
  // Temporarily adds offsets to both index_ and limit_, to handle ConsString
  // and SlicedString.
  class IndexModifier {
   public:
    IndexModifier(ReadStringVisitor* that, int32_t index_adjust,
                  int32_t limit_adjust)
        : that_(that),
          index_adjust_(index_adjust),
          limit_adjust_(limit_adjust) {
      that_->index_ += index_adjust_;
      that_->limit_ += limit_adjust_;
    }
    ~IndexModifier() {
      that_->index_ -= index_adjust_;
      that_->limit_ -= limit_adjust_;
    }

   private:
    ReadStringVisitor* that_;
    int32_t index_adjust_;
    int32_t limit_adjust_;
    DISALLOW_COPY_AND_ASSIGN(IndexModifier);
  };

  static constexpr int kMaxCharacters = 80;  // How many characters to print.

  std::u16string string_;  // Result string.
  d::MemoryAccessor accessor_;
429
  const d::HeapAddresses& heap_addresses_;
430 431 432
  int32_t index_;  // Index of next char to read.
  int32_t limit_;  // Don't read past this index (set by SlicedString).
  bool done_;      // Whether to stop further work.
433
  bool failed_;    // Whether an error was encountered before any valid data.
434 435
};

436
// An object visitor that supplies extra information for some types.
437 438
class AddInfoVisitor : public TqObjectVisitor {
 public:
439 440 441 442 443 444 445 446 447 448
  // Returns a descriptive string and a list of properties for the given object.
  // Both may be empty, and are meant as an addition to, not a replacement for,
  // the Torque-generated data about the object.
  static std::pair<std::string, std::vector<std::unique_ptr<ObjectProperty>>>
  Visit(const TqObject* object, d::MemoryAccessor accessor,
        const d::HeapAddresses& heap_addresses) {
    AddInfoVisitor visitor(accessor, heap_addresses);
    object->Visit(&visitor);
    return {std::move(visitor.brief_), std::move(visitor.properties_)};
  }
449 450

  void VisitString(const TqString* object) override {
451 452 453 454
    auto str = ReadStringVisitor::Visit(accessor_, heap_addresses_, object);
    if (str.has_value()) {
      brief_ = "\"" + *str + "\"";
    }
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
  }

  void VisitJSObject(const TqJSObject* object) override {
    // JSObject and its subclasses can be followed directly by an array of
    // property values. The start and end offsets of those values are described
    // by a pair of values in its Map.
    auto map_ptr = object->GetMapValue(accessor_);
    if (map_ptr.validity != d::MemoryAccessResult::kOk) {
      return;  // Can't read the JSObject. Nothing useful to do.
    }
    TqMap map(map_ptr.value);

    // On JSObject instances, this value is the start of in-object properties.
    // The constructor function index option is only for primitives.
    auto start_offset =
        map.GetInObjectPropertiesStartOrConstructorFunctionIndexValue(
            accessor_);

    // The total size of the object in memory. This may include over-allocated
    // expansion space that doesn't correspond to any user-accessible property.
    auto instance_size = map.GetInstanceSizeInWordsValue(accessor_);

    if (start_offset.validity != d::MemoryAccessResult::kOk ||
        instance_size.validity != d::MemoryAccessResult::kOk) {
      return;  // Can't read the Map. Nothing useful to do.
    }
    int num_properties = instance_size.value - start_offset.value;
    if (num_properties > 0) {
      properties_.push_back(std::make_unique<ObjectProperty>(
          "in-object properties", kObjectAsStoredInHeap, kObject,
          object->GetMapAddress() + start_offset.value * i::kTaggedSize,
          num_properties, i::kTaggedSize,
          std::vector<std::unique_ptr<StructProperty>>(),
          d::PropertyKind::kArrayOfKnownSize));
    }
490 491 492
  }

 private:
493 494 495 496 497 498
  AddInfoVisitor(d::MemoryAccessor accessor,
                 const d::HeapAddresses& heap_addresses)
      : accessor_(accessor), heap_addresses_(heap_addresses) {}

  // Inputs used by this visitor:

499
  d::MemoryAccessor accessor_;
500
  const d::HeapAddresses& heap_addresses_;
501 502 503 504 505 506 507 508

  // Outputs generated by this visitor:

  // A brief description of the object.
  std::string brief_;
  // A list of extra properties to append after the automatic ones that are
  // created for all Torque-defined class fields.
  std::vector<std::unique_ptr<ObjectProperty>> properties_;
509 510
};

511
std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesNotCompressed(
512
    uintptr_t address, d::MemoryAccessor accessor, const char* type_hint,
513 514 515 516 517 518 519
    const d::HeapAddresses& heap_addresses) {
  // Regardless of whether we can read the object itself, maybe we can find its
  // pointer in the list of known objects.
  std::string brief = FindKnownObject(address, heap_addresses);

  TypedObject typed =
      GetTypedHeapObject(address, accessor, type_hint, heap_addresses);
520
  auto props = typed.object->GetProperties(accessor);
521

522 523 524 525 526 527 528
  // Use the AddInfoVisitor to get any extra properties or descriptive text that
  // can't be directly derived from Torque class definitions.
  auto extra_info =
      AddInfoVisitor::Visit(typed.object.get(), accessor, heap_addresses);
  brief = JoinWithSpace(brief, extra_info.first);
  props.insert(props.end(), std::make_move_iterator(extra_info.second.begin()),
               std::make_move_iterator(extra_info.second.end()));
529

530
  brief = AppendAddressAndType(brief, address, typed.object->GetName());
531

532 533 534 535 536 537 538
  // Convert the low-confidence guessed types to a list of strings as expected
  // for the response.
  std::vector<std::string> guessed_types;
  for (const auto& guess : typed.possible_types) {
    guessed_types.push_back(guess.object->GetName());
  }

539
  return std::make_unique<ObjectPropertiesResult>(
540 541
      typed.type_check_result, brief, typed.object->GetName(), std::move(props),
      std::move(guessed_types));
542 543
}

544
std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesMaybeCompressed(
545 546
    uintptr_t address, d::MemoryAccessor memory_accessor,
    d::HeapAddresses heap_addresses, const char* type_hint) {
547 548 549 550
  // Try to figure out the heap range, for pointer compression (this is unused
  // if pointer compression is disabled).
  uintptr_t any_uncompressed_ptr = 0;
  if (!IsPointerCompressed(address)) any_uncompressed_ptr = address;
551 552 553 554 555 556 557 558 559
  if (any_uncompressed_ptr == 0)
    any_uncompressed_ptr = heap_addresses.any_heap_pointer;
  if (any_uncompressed_ptr == 0)
    any_uncompressed_ptr = heap_addresses.map_space_first_page;
  if (any_uncompressed_ptr == 0)
    any_uncompressed_ptr = heap_addresses.old_space_first_page;
  if (any_uncompressed_ptr == 0)
    any_uncompressed_ptr = heap_addresses.read_only_space_first_page;
  FillInUnknownHeapAddresses(&heap_addresses, any_uncompressed_ptr);
560 561
  if (any_uncompressed_ptr == 0) {
    // We can't figure out the heap range. Just check for known objects.
562
    std::string brief = FindKnownObject(address, heap_addresses);
563
    brief = AppendAddressAndType(brief, address, kTaggedValue);
564
    return std::make_unique<ObjectPropertiesResult>(
565
        d::TypeCheckResult::kUnableToDecompress, brief, kTaggedValue);
566 567
  }

568
  address = EnsureDecompressed(address, any_uncompressed_ptr);
569

570 571
  return GetHeapObjectPropertiesNotCompressed(address, memory_accessor,
                                              type_hint, heap_addresses);
572 573
}

574
std::unique_ptr<ObjectPropertiesResult> GetObjectProperties(
575 576
    uintptr_t address, d::MemoryAccessor memory_accessor,
    const d::HeapAddresses& heap_addresses, const char* type_hint) {
577
  if (static_cast<uint32_t>(address) == i::kClearedWeakHeapObjectLower32) {
578
    return std::make_unique<ObjectPropertiesResult>(
579
        d::TypeCheckResult::kWeakRef, "cleared weak ref", kHeapObject);
580 581 582 583 584 585
  }
  bool is_weak = (address & i::kHeapObjectTagMask) == i::kWeakHeapObjectTag;
  if (is_weak) {
    address &= ~i::kWeakHeapObjectMask;
  }
  if (i::Internals::HasHeapObjectTag(address)) {
586 587 588
    std::unique_ptr<ObjectPropertiesResult> result =
        GetHeapObjectPropertiesMaybeCompressed(address, memory_accessor,
                                               heap_addresses, type_hint);
589 590 591 592 593 594 595 596 597 598 599
    if (is_weak) {
      result->Prepend("weak ref to ");
    }
    return result;
  }

  // For smi values, construct a response with a description representing the
  // untagged value.
  int32_t value = i::PlatformSmiTagging::SmiToInt(address);
  std::stringstream stream;
  stream << value << " (0x" << std::hex << value << ")";
600 601
  return std::make_unique<ObjectPropertiesResult>(d::TypeCheckResult::kSmi,
                                                  stream.str(), kSmi);
602 603
}

604 605 606
}  // namespace debug_helper_internal
}  // namespace internal
}  // namespace v8
607

608
namespace di = v8::internal::debug_helper_internal;
609 610 611 612 613

extern "C" {
V8_DEBUG_HELPER_EXPORT d::ObjectPropertiesResult*
_v8_debug_helper_GetObjectProperties(uintptr_t object,
                                     d::MemoryAccessor memory_accessor,
614
                                     const d::HeapAddresses& heap_addresses,
615
                                     const char* type_hint) {
616 617
  return di::GetObjectProperties(object, memory_accessor, heap_addresses,
                                 type_hint)
618 619 620 621 622 623 624 625 626
      .release()
      ->GetPublicView();
}
V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult(
    d::ObjectPropertiesResult* result) {
  std::unique_ptr<di::ObjectPropertiesResult> ptr(
      static_cast<di::ObjectPropertiesResultExtended*>(result)->base);
}
}