class-debug-reader-generator.cc 25.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
// 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 "src/flags/flags.h"
#include "src/torque/implementation-visitor.h"
#include "src/torque/type-oracle.h"

namespace v8 {
namespace internal {
namespace torque {

13
constexpr char kTqObjectOverrideDecls[] =
14 15 16 17 18 19 20
    R"(  std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
      d::MemoryAccessor accessor) const override;
  const char* GetName() const override;
  void Visit(TqObjectVisitor* visitor) const override;
  bool IsSuperclassOf(const TqObject* other) const override;
)";

21 22 23 24 25 26 27
constexpr char kObjectClassListDefinition[] = R"(
const d::ClassList kObjectClassList {
  sizeof(kObjectClassNames) / sizeof(const char*),
  kObjectClassNames,
};
)";

28
namespace {
29 30 31 32 33
enum TypeStorage {
  kAsStoredInHeap,
  kUncompressed,
};

34 35 36 37 38 39 40 41 42 43 44 45 46
// An iterator for use in ValueTypeFieldsRange.
class ValueTypeFieldIterator {
 public:
  ValueTypeFieldIterator(const Type* type, size_t index)
      : type_(type), index_(index) {}
  struct Result {
    NameAndType name_and_type;
    SourcePosition pos;
    size_t offset_bytes;
    int num_bits;
    int shift_bits;
  };
  const Result operator*() const {
47 48
    if (auto struct_type = type_->StructSupertype()) {
      const auto& field = (*struct_type)->fields()[index_];
49 50
      return {field.name_and_type, field.pos, *field.offset, 0, 0};
    }
51 52 53 54 55
    const Type* type = type_;
    int bitfield_start_offset = 0;
    if (const auto type_wrapped_in_smi =
            Type::MatchUnaryGeneric(type_, TypeOracle::GetSmiTaggedGeneric())) {
      type = *type_wrapped_in_smi;
56
      bitfield_start_offset = TargetArchitecture::SmiTagAndShiftSize();
57
    }
58
    if (const BitFieldStructType* bit_field_struct_type =
59
            BitFieldStructType::DynamicCast(type)) {
60
      const auto& field = bit_field_struct_type->fields()[index_];
61 62
      return {field.name_and_type, field.pos, 0, field.num_bits,
              field.offset + bitfield_start_offset};
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    }
    UNREACHABLE();
  }
  ValueTypeFieldIterator& operator++() {
    ++index_;
    return *this;
  }
  bool operator==(const ValueTypeFieldIterator& other) const {
    return type_ == other.type_ && index_ == other.index_;
  }
  bool operator!=(const ValueTypeFieldIterator& other) const {
    return !(*this == other);
  }

 private:
  const Type* type_;
  size_t index_;
};

// A way to iterate over the fields of structs or bitfield structs. For other
// types, the iterators returned from begin() and end() are immediately equal.
class ValueTypeFieldsRange {
 public:
  explicit ValueTypeFieldsRange(const Type* type) : type_(type) {}
  ValueTypeFieldIterator begin() { return {type_, 0}; }
  ValueTypeFieldIterator end() {
    size_t index = 0;
90 91 92
    base::Optional<const StructType*> struct_type = type_->StructSupertype();
    if (struct_type && *struct_type != TypeOracle::GetFloat64OrHoleType()) {
      index = (*struct_type)->fields().size();
93
    }
94 95 96 97 98
    const Type* type = type_;
    if (const auto type_wrapped_in_smi =
            Type::MatchUnaryGeneric(type_, TypeOracle::GetSmiTaggedGeneric())) {
      type = *type_wrapped_in_smi;
    }
99
    if (const BitFieldStructType* bit_field_struct_type =
100
            BitFieldStructType::DynamicCast(type)) {
101 102 103 104 105 106 107 108 109
      index = bit_field_struct_type->fields().size();
    }
    return {type_, index};
  }

 private:
  const Type* type_;
};

110 111 112 113
// A convenient way to keep track of several different ways that we might need
// to represent a field's type in the generated C++.
class DebugFieldType {
 public:
114 115 116 117
  explicit DebugFieldType(const Field& field)
      : name_and_type_(field.name_and_type), pos_(field.pos) {}
  DebugFieldType(const NameAndType& name_and_type, const SourcePosition& pos)
      : name_and_type_(name_and_type), pos_(pos) {}
118 119

  bool IsTagged() const {
120
    return name_and_type_.type->IsSubtypeOf(TypeOracle::GetTaggedType());
121 122 123 124 125 126 127 128 129 130
  }

  // Returns the type that should be used for this field's value within code
  // that is compiled as part of the debug helper library. In particular, this
  // simplifies any tagged type to a plain uintptr_t because the debug helper
  // compiles without most of the V8 runtime code.
  std::string GetValueType(TypeStorage storage) const {
    if (IsTagged()) {
      return storage == kAsStoredInHeap ? "i::Tagged_t" : "uintptr_t";
    }
131 132 133

    // We can't emit a useful error at this point if the constexpr type name is
    // wrong, but we can include a comment that might be helpful.
134
    return GetOriginalType(storage) +
135 136
           " /*Failing? Ensure constexpr type name is correct, and the "
           "necessary #include is in any .tq file*/";
137 138 139 140
  }

  // Returns the type that should be used to represent a field's type to
  // debugging tools that have full V8 symbols. The types returned from this
141 142 143
  // method are resolveable in the v8::internal namespace and may refer to
  // object types that are not included in the compilation of the debug helper
  // library.
144
  std::string GetOriginalType(TypeStorage storage) const {
145
    if (name_and_type_.type->StructSupertype()) {
146 147 148 149 150
      // There's no meaningful type we could use here, because the V8 symbols
      // don't have any definition of a C++ struct matching this struct type.
      return "";
    }
    if (IsTagged()) {
151 152
      if (storage == kAsStoredInHeap &&
          TargetArchitecture::ArePointersCompressed()) {
153 154 155
        return "v8::internal::TaggedValue";
      }
      base::Optional<const ClassType*> field_class_type =
156
          name_and_type_.type->ClassSupertype();
157 158 159 160 161
      return "v8::internal::" +
             (field_class_type.has_value()
                  ? (*field_class_type)->GetGeneratedTNodeTypeName()
                  : "Object");
    }
162
    return name_and_type_.type->GetConstexprGeneratedTypeName();
163 164
  }

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
  // Returns a C++ expression that evaluates to a string (type `const char*`)
  // containing the name of the field's type. The types returned from this
  // method are resolveable in the v8::internal namespace and may refer to
  // object types that are not included in the compilation of the debug helper
  // library.
  std::string GetTypeString(TypeStorage storage) const {
    if (IsTagged() || name_and_type_.type->IsStructType()) {
      // Wrap up the original type in a string literal.
      return "\"" + GetOriginalType(storage) + "\"";
    }

    // We require constexpr type names to be resolvable in the v8::internal
    // namespace, according to the contract in debug-helper.h. In order to
    // verify at compile time that constexpr type names are resolvable, we use
    // the type name as a dummy template parameter to a function that just
    // returns its parameter.
    return "CheckTypeName<" + GetValueType(storage) + ">(\"" +
           GetOriginalType(storage) + "\")";
  }

185 186
  // Returns the field's size in bytes.
  size_t GetSize() const {
187 188 189 190 191 192 193
    auto opt_size = SizeOf(name_and_type_.type);
    if (!opt_size.has_value()) {
      Error("Size required for type ", name_and_type_.type->ToString())
          .Position(pos_);
      return 0;
    }
    return std::get<0>(*opt_size);
194 195 196 197
  }

  // Returns the name of the function for getting this field's address.
  std::string GetAddressGetter() {
198
    return "Get" + CamelifyString(name_and_type_.name) + "Address";
199 200 201
  }

 private:
202 203
  NameAndType name_and_type_;
  SourcePosition pos_;
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
};

// Emits a function to get the address of a field within a class, based on the
// member variable {address_}, which is a tagged pointer. Example
// implementation:
//
// uintptr_t TqFixedArray::GetObjectsAddress() const {
//   return address_ - i::kHeapObjectTag + 16;
// }
void GenerateFieldAddressAccessor(const Field& field,
                                  const std::string& class_name,
                                  std::ostream& h_contents,
                                  std::ostream& cc_contents) {
  DebugFieldType debug_field_type(field);

  const std::string address_getter = debug_field_type.GetAddressGetter();

  h_contents << "  uintptr_t " << address_getter << "() const;\n";
  cc_contents << "\nuintptr_t Tq" << class_name << "::" << address_getter
              << "() const {\n";
224
  cc_contents << "  return address_ - i::kHeapObjectTag + " << *field.offset
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
              << ";\n";
  cc_contents << "}\n";
}

// Emits a function to get the value of a field, or the value from an indexed
// position within an array field, based on the member variable {address_},
// which is a tagged pointer, and the parameter {accessor}, a function pointer
// that allows for fetching memory from the debuggee. The returned result
// includes both a "validity", indicating whether the memory could be fetched,
// and the fetched value. If the field contains tagged data, then these
// functions call EnsureDecompressed to expand compressed data. Example:
//
// Value<uintptr_t> TqMap::GetPrototypeValue(d::MemoryAccessor accessor) const {
//   i::Tagged_t value{};
//   d::MemoryAccessResult validity = accessor(
//       GetPrototypeAddress(),
//       reinterpret_cast<uint8_t*>(&value),
//       sizeof(value));
//   return {validity, EnsureDecompressed(value, address_)};
// }
//
// For array fields, an offset parameter is included. Example:
//
// Value<uintptr_t> TqFixedArray::GetObjectsValue(d::MemoryAccessor accessor,
//                                                size_t offset) const {
//   i::Tagged_t value{};
//   d::MemoryAccessResult validity = accessor(
//       GetObjectsAddress() + offset * sizeof(value),
//       reinterpret_cast<uint8_t*>(&value),
//       sizeof(value));
//   return {validity, EnsureDecompressed(value, address_)};
// }
void GenerateFieldValueAccessor(const Field& field,
                                const std::string& class_name,
                                std::ostream& h_contents,
                                std::ostream& cc_contents) {
  // Currently not implemented for struct fields.
262
  if (field.name_and_type.type->StructSupertype()) return;
263 264 265 266 267 268 269 270 271 272 273 274 275 276

  DebugFieldType debug_field_type(field);

  const std::string address_getter = debug_field_type.GetAddressGetter();
  const std::string field_getter =
      "Get" + CamelifyString(field.name_and_type.name) + "Value";

  std::string index_param;
  std::string index_offset;
  if (field.index) {
    index_param = ", size_t offset";
    index_offset = " + offset * sizeof(value)";
  }

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
  std::string field_value_type = debug_field_type.GetValueType(kUncompressed);
  h_contents << "  Value<" << field_value_type << "> " << field_getter
             << "(d::MemoryAccessor accessor " << index_param << ") const;\n";
  cc_contents << "\nValue<" << field_value_type << "> Tq" << class_name
              << "::" << field_getter << "(d::MemoryAccessor accessor"
              << index_param << ") const {\n";
  cc_contents << "  " << debug_field_type.GetValueType(kAsStoredInHeap)
              << " value{};\n";
  cc_contents << "  d::MemoryAccessResult validity = accessor("
              << address_getter << "()" << index_offset
              << ", reinterpret_cast<uint8_t*>(&value), sizeof(value));\n";
  cc_contents << "  return {validity, "
              << (debug_field_type.IsTagged()
                      ? "EnsureDecompressed(value, address_)"
                      : "value")
              << "};\n";
  cc_contents << "}\n";
294 295 296 297 298 299
}

// Emits a portion of the member function GetProperties that is responsible for
// adding data about the current field to a result vector called "result".
// Example output:
//
300
// std::vector<std::unique_ptr<StructProperty>> prototype_struct_field_list;
301 302 303 304 305 306 307
// result.push_back(std::make_unique<ObjectProperty>(
//     "prototype",                                     // Field name
//     "v8::internal::HeapObject",                      // Field type
//     "v8::internal::HeapObject",                      // Decompressed type
//     GetPrototypeAddress(),                           // Field address
//     1,                                               // Number of values
//     8,                                               // Size of value
308
//     std::move(prototype_struct_field_list),          // Struct fields
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
//     d::PropertyKind::kSingle));                      // Field kind
//
// In builds with pointer compression enabled, the field type for tagged values
// is "v8::internal::TaggedValue" (a four-byte class) and the decompressed type
// is a normal Object subclass that describes the expanded eight-byte type.
//
// If the field is an array, then its length is fetched from the debuggee. This
// could fail if the debuggee has incomplete memory, so the "validity" from that
// fetch is used to determine the result PropertyKind, which will say whether
// the array's length is known.
//
// If the field's type is a struct, then a local variable is created and filled
// with descriptions of each of the struct's fields. The type and decompressed
// type in the ObjectProperty are set to the empty string, to indicate to the
// caller that the struct fields vector should be used instead.
//
// The following example is an array of structs, so it uses both of the optional
// components described above:
//
// std::vector<std::unique_ptr<StructProperty>> descriptors_struct_field_list;
// descriptors_struct_field_list.push_back(std::make_unique<StructProperty>(
//     "key",                                // Struct field name
//     "v8::internal::PrimitiveHeapObject",  // Struct field type
//     "v8::internal::PrimitiveHeapObject",  // Struct field decompressed type
333 334 335
//     0,                                    // Byte offset within struct data
//     0,                                    // Bitfield size (0=not a bitfield)
//     0));                                  // Bitfield shift
336
// // The line above is repeated for other struct fields. Omitted here.
337 338 339 340 341
// // Fetch the slice.
// auto indexed_field_slice_descriptors =
//     TqDebugFieldSliceDescriptorArrayDescriptors(accessor, address_);
// if (indexed_field_slice_descriptors.validity == d::MemoryAccessResult::kOk) {
//   result.push_back(std::make_unique<ObjectProperty>(
342 343 344
//     "descriptors",                                 // Field name
//     "",                                            // Field type
//     "",                                            // Decompressed type
345 346 347 348
//     address_ - i::kHeapObjectTag +
//     std::get<1>(indexed_field_slice_descriptors.value), // Field address
//     std::get<2>(indexed_field_slice_descriptors.value), // Number of values
//     12,                                            // Size of value
349
//     std::move(descriptors_struct_field_list),      // Struct fields
350 351
//     GetArrayKind(indexed_field_slice_descriptors.validity)));  // Field kind
// }
352
void GenerateGetPropsChunkForField(const Field& field,
353 354
                                   std::ostream& get_props_impl,
                                   std::string class_name) {
355 356
  DebugFieldType debug_field_type(field);

357 358
  // If the current field is a struct or bitfield struct, create a vector
  // describing its fields. Otherwise this vector will be empty.
359
  std::string struct_field_list =
360 361 362 363 364 365 366 367 368
      field.name_and_type.name + "_struct_field_list";
  get_props_impl << "  std::vector<std::unique_ptr<StructProperty>> "
                 << struct_field_list << ";\n";
  for (const auto& struct_field :
       ValueTypeFieldsRange(field.name_and_type.type)) {
    DebugFieldType struct_field_type(struct_field.name_and_type,
                                     struct_field.pos);
    get_props_impl << "  " << struct_field_list
                   << ".push_back(std::make_unique<StructProperty>(\""
369 370 371
                   << struct_field.name_and_type.name << "\", "
                   << struct_field_type.GetTypeString(kAsStoredInHeap) << ", "
                   << struct_field_type.GetTypeString(kUncompressed) << ", "
372 373
                   << struct_field.offset_bytes << ", " << struct_field.num_bits
                   << ", " << struct_field.shift_bits << "));\n";
374
  }
375
  struct_field_list = "std::move(" + struct_field_list + ")";
376 377 378 379 380 381 382

  // The number of values and property kind for non-indexed properties:
  std::string count_value = "1";
  std::string property_kind = "d::PropertyKind::kSingle";

  // If the field is indexed, emit a fetch of the array length, and change
  // count_value and property_kind to be the correct values for an array.
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  if (field.index) {
    std::string indexed_field_slice =
        "indexed_field_slice_" + field.name_and_type.name;
    get_props_impl << "  auto " << indexed_field_slice << " = "
                   << "TqDebugFieldSlice" << class_name
                   << CamelifyString(field.name_and_type.name)
                   << "(accessor, address_);\n";
    std::string validity = indexed_field_slice + ".validity";
    std::string value = indexed_field_slice + ".value";
    property_kind = "GetArrayKind(" + validity + ")";

    get_props_impl << "  if (" << validity
                   << " == d::MemoryAccessResult::kOk) {\n"
                   << "    result.push_back(std::make_unique<ObjectProperty>(\""
                   << field.name_and_type.name << "\", "
                   << debug_field_type.GetTypeString(kAsStoredInHeap) << ", "
                   << debug_field_type.GetTypeString(kUncompressed) << ", "
                   << "address_ - i::kHeapObjectTag + std::get<1>(" << value
                   << "), "
                   << "std::get<2>(" << value << ")"
                   << ", " << debug_field_type.GetSize() << ", "
                   << struct_field_list << ", " << property_kind << "));\n"
                   << "  }\n";
    return;
407 408
  }
  get_props_impl << "  result.push_back(std::make_unique<ObjectProperty>(\""
409 410 411 412 413
                 << field.name_and_type.name << "\", "
                 << debug_field_type.GetTypeString(kAsStoredInHeap) << ", "
                 << debug_field_type.GetTypeString(kUncompressed) << ", "
                 << debug_field_type.GetAddressGetter() << "(), " << count_value
                 << ", " << debug_field_type.GetSize() << ", "
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
                 << struct_field_list << ", " << property_kind << "));\n";
}

// For any Torque-defined class Foo, this function generates a class TqFoo which
// allows for convenient inspection of objects of type Foo in a crash dump or
// time travel session (where we can't just run the object printer). The
// generated class looks something like this:
//
// class TqFoo : public TqParentOfFoo {
//  public:
//   // {address} is an uncompressed tagged pointer.
//   inline TqFoo(uintptr_t address) : TqParentOfFoo(address) {}
//
//   // Creates and returns a list of this object's properties.
//   std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
//       d::MemoryAccessor accessor) const override;
//
//   // Returns the name of this class, "v8::internal::Foo".
//   const char* GetName() const override;
//
//   // Visitor pattern; implementation just calls visitor->VisitFoo(this).
//   void Visit(TqObjectVisitor* visitor) const override;
//
//   // Returns whether Foo is a superclass of the other object's type.
//   bool IsSuperclassOf(const TqObject* other) const override;
//
//   // Field accessors omitted here (see other comments above).
// };
//
// Four output streams are written:
//
// h_contents:  A header file which gets the class definition above.
// cc_contents: A cc file which gets implementations of that class's members.
// visitor:     A stream that is accumulating the definition of the class
//              TqObjectVisitor. Each class Foo gets its own virtual method
//              VisitFoo in TqObjectVisitor.
// class_names: A stream that is accumulating a list of strings including fully-
//              qualified names for every Torque-defined class type.
452
void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
453
                              std::ostream& cc_contents, std::ostream& visitor,
454
                              std::ostream& class_names,
455 456
                              std::unordered_set<const ClassType*>* done) {
  // Make sure each class only gets generated once.
457
  if (!done->insert(&type).second) return;
458 459 460 461 462
  const ClassType* super_type = type.GetSuperClass();

  // We must emit the classes in dependency order. If the super class hasn't
  // been emitted yet, go handle it first.
  if (super_type != nullptr) {
463
    GenerateClassDebugReader(*super_type, h_contents, cc_contents, visitor,
464
                             class_names, done);
465 466
  }

467 468 469 470
  // Classes with undefined layout don't grant any particular value here and may
  // not correspond with actual C++ classes, so skip them.
  if (type.HasUndefinedLayout()) return;

471 472 473 474 475 476 477
  const std::string name = type.name();
  const std::string super_name =
      super_type == nullptr ? "Object" : super_type->name();
  h_contents << "\nclass Tq" << name << " : public Tq" << super_name << " {\n";
  h_contents << " public:\n";
  h_contents << "  inline Tq" << name << "(uintptr_t address) : Tq"
             << super_name << "(address) {}\n";
478
  h_contents << kTqObjectOverrideDecls;
479 480 481 482 483 484 485 486 487 488

  cc_contents << "\nconst char* Tq" << name << "::GetName() const {\n";
  cc_contents << "  return \"v8::internal::" << name << "\";\n";
  cc_contents << "}\n";

  cc_contents << "\nvoid Tq" << name
              << "::Visit(TqObjectVisitor* visitor) const {\n";
  cc_contents << "  visitor->Visit" << name << "(this);\n";
  cc_contents << "}\n";

489 490 491 492 493 494 495
  cc_contents << "\nbool Tq" << name
              << "::IsSuperclassOf(const TqObject* other) const {\n";
  cc_contents
      << "  return GetName() != other->GetName() && dynamic_cast<const Tq"
      << name << "*>(other) != nullptr;\n";
  cc_contents << "}\n";

496 497 498
  // By default, the visitor method for this class just calls the visitor method
  // for this class's parent. This allows custom visitors to only override a few
  // classes they care about without needing to know about the entire hierarchy.
499 500 501 502 503
  visitor << "  virtual void Visit" << name << "(const Tq" << name
          << "* object) {\n";
  visitor << "    Visit" << super_name << "(object);\n";
  visitor << "  }\n";

504 505
  class_names << "  \"v8::internal::" << name << "\",\n";

506 507 508
  std::stringstream get_props_impl;

  for (const Field& field : type.fields()) {
509
    if (field.name_and_type.type == TypeOracle::GetVoidType()) continue;
510 511 512
    if (field.offset.has_value()) {
      GenerateFieldAddressAccessor(field, name, h_contents, cc_contents);
      GenerateFieldValueAccessor(field, name, h_contents, cc_contents);
513
    }
514
    GenerateGetPropsChunkForField(field, get_props_impl, name);
515 516 517 518 519
  }

  h_contents << "};\n";

  cc_contents << "\nstd::vector<std::unique_ptr<ObjectProperty>> Tq" << name
520
              << "::GetProperties(d::MemoryAccessor accessor) const {\n";
521
  // Start by getting the fields from the parent class.
522 523
  cc_contents << "  std::vector<std::unique_ptr<ObjectProperty>> result = Tq"
              << super_name << "::GetProperties(accessor);\n";
524
  // Then add the fields from this class.
525 526 527 528 529 530 531 532
  cc_contents << get_props_impl.str();
  cc_contents << "  return result;\n";
  cc_contents << "}\n";
}
}  // namespace

void ImplementationVisitor::GenerateClassDebugReaders(
    const std::string& output_directory) {
533
  const std::string file_name = "class-debug-readers";
534 535 536 537 538 539 540 541 542 543 544 545 546 547
  std::stringstream h_contents;
  std::stringstream cc_contents;
  h_contents << "// Provides the ability to read object properties in\n";
  h_contents << "// postmortem or remote scenarios, where the debuggee's\n";
  h_contents << "// memory is not part of the current process's address\n";
  h_contents << "// space and must be read using a callback function.\n\n";
  {
    IncludeGuardScope include_guard(h_contents, file_name + ".h");

    h_contents << "#include <cstdint>\n";
    h_contents << "#include <vector>\n";
    h_contents
        << "\n#include \"tools/debug_helper/debug-helper-internal.h\"\n\n";

548 549 550 551 552
    h_contents << "// Unset a windgi.h macro that causes conflicts.\n";
    h_contents << "#ifdef GetBValue\n";
    h_contents << "#undef GetBValue\n";
    h_contents << "#endif\n\n";

553 554 555
    for (const std::string& include_path : GlobalContext::CppIncludes()) {
      cc_contents << "#include " << StringLiteralQuote(include_path) << "\n";
    }
556
    cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n";
557 558 559
    cc_contents << "#include \"torque-generated/"
                << "debug-macros"
                << ".h\"\n";
560 561 562
    cc_contents << "#include \"include/v8-internal.h\"\n\n";
    cc_contents << "namespace i = v8::internal;\n\n";

563 564 565 566
    NamespaceScope h_namespaces(h_contents,
                                {"v8", "internal", "debug_helper_internal"});
    NamespaceScope cc_namespaces(cc_contents,
                                 {"v8", "internal", "debug_helper_internal"});
567

568 569 570 571 572
    std::stringstream visitor;
    visitor << "\nclass TqObjectVisitor {\n";
    visitor << " public:\n";
    visitor << "  virtual void VisitObject(const TqObject* object) {}\n";

573 574
    std::stringstream class_names;

575
    std::unordered_set<const ClassType*> done;
576
    for (const ClassType* type : TypeOracle::GetClasses()) {
577 578
      GenerateClassDebugReader(*type, h_contents, cc_contents, visitor,
                               class_names, &done);
579
    }
580 581 582

    visitor << "};\n";
    h_contents << visitor.str();
583 584 585 586 587

    cc_contents << "\nconst char* kObjectClassNames[] {\n";
    cc_contents << class_names.str();
    cc_contents << "};\n";
    cc_contents << kObjectClassListDefinition;
588 589 590 591 592 593 594 595
  }
  WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
  WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
}

}  // namespace torque
}  // namespace internal
}  // namespace v8