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 "src/flags/flags.h"
6 #include "src/torque/implementation-visitor.h"
7 #include "src/torque/type-oracle.h"
8
9 namespace v8 {
10 namespace internal {
11 namespace torque {
12
13 constexpr char kTqObjectOverrideDecls[] =
14 R"( std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
15 d::MemoryAccessor accessor) const override;
16 const char* GetName() const override;
17 void Visit(TqObjectVisitor* visitor) const override;
18 bool IsSuperclassOf(const TqObject* other) const override;
19 )";
20
21 constexpr char kObjectClassListDefinition[] = R"(
22 const d::ClassList kObjectClassList {
23 sizeof(kObjectClassNames) / sizeof(const char*),
24 kObjectClassNames,
25 };
26 )";
27
28 namespace {
29 enum TypeStorage {
30 kAsStoredInHeap,
31 kUncompressed,
32 };
33
34 // An iterator for use in ValueTypeFieldsRange.
35 class ValueTypeFieldIterator {
36 public:
ValueTypeFieldIterator(const Type * type,size_t index)37 ValueTypeFieldIterator(const Type* type, size_t index)
38 : type_(type), index_(index) {}
39 struct Result {
40 NameAndType name_and_type;
41 SourcePosition pos;
42 size_t offset_bytes;
43 int num_bits;
44 int shift_bits;
45 };
operator *() const46 const Result operator*() const {
47 if (auto struct_type = type_->StructSupertype()) {
48 const auto& field = (*struct_type)->fields()[index_];
49 return {field.name_and_type, field.pos, *field.offset, 0, 0};
50 }
51 const Type* type = type_;
52 int bitfield_start_offset = 0;
53 if (const auto type_wrapped_in_smi =
54 Type::MatchUnaryGeneric(type_, TypeOracle::GetSmiTaggedGeneric())) {
55 type = *type_wrapped_in_smi;
56 bitfield_start_offset = kSmiTagSize + kSmiShiftSize;
57 }
58 if (const BitFieldStructType* bit_field_struct_type =
59 BitFieldStructType::DynamicCast(type)) {
60 const auto& field = bit_field_struct_type->fields()[index_];
61 return {field.name_and_type, field.pos, 0, field.num_bits,
62 field.offset + bitfield_start_offset};
63 }
64 UNREACHABLE();
65 }
operator ++()66 ValueTypeFieldIterator& operator++() {
67 ++index_;
68 return *this;
69 }
operator ==(const ValueTypeFieldIterator & other) const70 bool operator==(const ValueTypeFieldIterator& other) const {
71 return type_ == other.type_ && index_ == other.index_;
72 }
operator !=(const ValueTypeFieldIterator & other) const73 bool operator!=(const ValueTypeFieldIterator& other) const {
74 return !(*this == other);
75 }
76
77 private:
78 const Type* type_;
79 size_t index_;
80 };
81
82 // A way to iterate over the fields of structs or bitfield structs. For other
83 // types, the iterators returned from begin() and end() are immediately equal.
84 class ValueTypeFieldsRange {
85 public:
ValueTypeFieldsRange(const Type * type)86 explicit ValueTypeFieldsRange(const Type* type) : type_(type) {}
begin()87 ValueTypeFieldIterator begin() { return {type_, 0}; }
end()88 ValueTypeFieldIterator end() {
89 size_t index = 0;
90 base::Optional<const StructType*> struct_type = type_->StructSupertype();
91 if (struct_type && *struct_type != TypeOracle::GetFloat64OrHoleType()) {
92 index = (*struct_type)->fields().size();
93 }
94 const Type* type = type_;
95 if (const auto type_wrapped_in_smi =
96 Type::MatchUnaryGeneric(type_, TypeOracle::GetSmiTaggedGeneric())) {
97 type = *type_wrapped_in_smi;
98 }
99 if (const BitFieldStructType* bit_field_struct_type =
100 BitFieldStructType::DynamicCast(type)) {
101 index = bit_field_struct_type->fields().size();
102 }
103 return {type_, index};
104 }
105
106 private:
107 const Type* type_;
108 };
109
110 // A convenient way to keep track of several different ways that we might need
111 // to represent a field's type in the generated C++.
112 class DebugFieldType {
113 public:
DebugFieldType(const Field & field)114 explicit DebugFieldType(const Field& field)
115 : name_and_type_(field.name_and_type), pos_(field.pos) {}
DebugFieldType(const NameAndType & name_and_type,const SourcePosition & pos)116 DebugFieldType(const NameAndType& name_and_type, const SourcePosition& pos)
117 : name_and_type_(name_and_type), pos_(pos) {}
118
IsTagged() const119 bool IsTagged() const {
120 return name_and_type_.type->IsSubtypeOf(TypeOracle::GetTaggedType());
121 }
122
123 // Returns the type that should be used for this field's value within code
124 // that is compiled as part of the debug helper library. In particular, this
125 // simplifies any tagged type to a plain uintptr_t because the debug helper
126 // compiles without most of the V8 runtime code.
GetValueType(TypeStorage storage) const127 std::string GetValueType(TypeStorage storage) const {
128 if (IsTagged()) {
129 return storage == kAsStoredInHeap ? "i::Tagged_t" : "uintptr_t";
130 }
131
132 // We can't emit a useful error at this point if the constexpr type name is
133 // wrong, but we can include a comment that might be helpful.
134 return GetOriginalType(storage) +
135 " /*Failing? Ensure constexpr type name is correct, and the "
136 "necessary #include is in any .tq file*/";
137 }
138
139 // Returns the type that should be used to represent a field's type to
140 // debugging tools that have full V8 symbols. The types returned from this
141 // method are resolveable in the v8::internal namespace and may refer to
142 // object types that are not included in the compilation of the debug helper
143 // library.
GetOriginalType(TypeStorage storage) const144 std::string GetOriginalType(TypeStorage storage) const {
145 if (name_and_type_.type->StructSupertype()) {
146 // There's no meaningful type we could use here, because the V8 symbols
147 // don't have any definition of a C++ struct matching this struct type.
148 return "";
149 }
150 if (IsTagged()) {
151 if (storage == kAsStoredInHeap &&
152 TargetArchitecture::ArePointersCompressed()) {
153 return "v8::internal::TaggedValue";
154 }
155 base::Optional<const ClassType*> field_class_type =
156 name_and_type_.type->ClassSupertype();
157 return "v8::internal::" +
158 (field_class_type.has_value()
159 ? (*field_class_type)->GetGeneratedTNodeTypeName()
160 : "Object");
161 }
162 return name_and_type_.type->GetConstexprGeneratedTypeName();
163 }
164
165 // Returns a C++ expression that evaluates to a string (type `const char*`)
166 // containing the name of the field's type. The types returned from this
167 // method are resolveable in the v8::internal namespace and may refer to
168 // object types that are not included in the compilation of the debug helper
169 // library.
GetTypeString(TypeStorage storage) const170 std::string GetTypeString(TypeStorage storage) const {
171 if (IsTagged() || name_and_type_.type->IsStructType()) {
172 // Wrap up the original type in a string literal.
173 return "\"" + GetOriginalType(storage) + "\"";
174 }
175
176 // We require constexpr type names to be resolvable in the v8::internal
177 // namespace, according to the contract in debug-helper.h. In order to
178 // verify at compile time that constexpr type names are resolvable, we use
179 // the type name as a dummy template parameter to a function that just
180 // returns its parameter.
181 return "CheckTypeName<" + GetValueType(storage) + ">(\"" +
182 GetOriginalType(storage) + "\")";
183 }
184
185 // Returns the field's size in bytes.
GetSize() const186 size_t GetSize() const {
187 auto opt_size = SizeOf(name_and_type_.type);
188 if (!opt_size.has_value()) {
189 Error("Size required for type ", name_and_type_.type->ToString())
190 .Position(pos_);
191 return 0;
192 }
193 return std::get<0>(*opt_size);
194 }
195
196 // Returns the name of the function for getting this field's address.
GetAddressGetter()197 std::string GetAddressGetter() {
198 return "Get" + CamelifyString(name_and_type_.name) + "Address";
199 }
200
201 private:
202 NameAndType name_and_type_;
203 SourcePosition pos_;
204 };
205
206 // Emits a function to get the address of a field within a class, based on the
207 // member variable {address_}, which is a tagged pointer. Example
208 // implementation:
209 //
210 // uintptr_t TqFixedArray::GetObjectsAddress() const {
211 // return address_ - i::kHeapObjectTag + 16;
212 // }
GenerateFieldAddressAccessor(const Field & field,const std::string & class_name,std::ostream & h_contents,std::ostream & cc_contents)213 void GenerateFieldAddressAccessor(const Field& field,
214 const std::string& class_name,
215 std::ostream& h_contents,
216 std::ostream& cc_contents) {
217 DebugFieldType debug_field_type(field);
218
219 const std::string address_getter = debug_field_type.GetAddressGetter();
220
221 h_contents << " uintptr_t " << address_getter << "() const;\n";
222 cc_contents << "\nuintptr_t Tq" << class_name << "::" << address_getter
223 << "() const {\n";
224 cc_contents << " return address_ - i::kHeapObjectTag + " << *field.offset
225 << ";\n";
226 cc_contents << "}\n";
227 }
228
229 // Emits a function to get the value of a field, or the value from an indexed
230 // position within an array field, based on the member variable {address_},
231 // which is a tagged pointer, and the parameter {accessor}, a function pointer
232 // that allows for fetching memory from the debuggee. The returned result
233 // includes both a "validity", indicating whether the memory could be fetched,
234 // and the fetched value. If the field contains tagged data, then these
235 // functions call EnsureDecompressed to expand compressed data. Example:
236 //
237 // Value<uintptr_t> TqMap::GetPrototypeValue(d::MemoryAccessor accessor) const {
238 // i::Tagged_t value{};
239 // d::MemoryAccessResult validity = accessor(
240 // GetPrototypeAddress(),
241 // reinterpret_cast<uint8_t*>(&value),
242 // sizeof(value));
243 // return {validity, EnsureDecompressed(value, address_)};
244 // }
245 //
246 // For array fields, an offset parameter is included. Example:
247 //
248 // Value<uintptr_t> TqFixedArray::GetObjectsValue(d::MemoryAccessor accessor,
249 // size_t offset) const {
250 // i::Tagged_t value{};
251 // d::MemoryAccessResult validity = accessor(
252 // GetObjectsAddress() + offset * sizeof(value),
253 // reinterpret_cast<uint8_t*>(&value),
254 // sizeof(value));
255 // return {validity, EnsureDecompressed(value, address_)};
256 // }
GenerateFieldValueAccessor(const Field & field,const std::string & class_name,std::ostream & h_contents,std::ostream & cc_contents)257 void GenerateFieldValueAccessor(const Field& field,
258 const std::string& class_name,
259 std::ostream& h_contents,
260 std::ostream& cc_contents) {
261 // Currently not implemented for struct fields.
262 if (field.name_and_type.type->StructSupertype()) return;
263
264 DebugFieldType debug_field_type(field);
265
266 const std::string address_getter = debug_field_type.GetAddressGetter();
267 const std::string field_getter =
268 "Get" + CamelifyString(field.name_and_type.name) + "Value";
269
270 std::string index_param;
271 std::string index_offset;
272 if (field.index) {
273 index_param = ", size_t offset";
274 index_offset = " + offset * sizeof(value)";
275 }
276
277 std::string field_value_type = debug_field_type.GetValueType(kUncompressed);
278 h_contents << " Value<" << field_value_type << "> " << field_getter
279 << "(d::MemoryAccessor accessor " << index_param << ") const;\n";
280 cc_contents << "\nValue<" << field_value_type << "> Tq" << class_name
281 << "::" << field_getter << "(d::MemoryAccessor accessor"
282 << index_param << ") const {\n";
283 cc_contents << " " << debug_field_type.GetValueType(kAsStoredInHeap)
284 << " value{};\n";
285 cc_contents << " d::MemoryAccessResult validity = accessor("
286 << address_getter << "()" << index_offset
287 << ", reinterpret_cast<uint8_t*>(&value), sizeof(value));\n";
288 cc_contents << " return {validity, "
289 << (debug_field_type.IsTagged()
290 ? "EnsureDecompressed(value, address_)"
291 : "value")
292 << "};\n";
293 cc_contents << "}\n";
294 }
295
296 // Emits a portion of the member function GetProperties that is responsible for
297 // adding data about the current field to a result vector called "result".
298 // Example output:
299 //
300 // std::vector<std::unique_ptr<StructProperty>> prototype_struct_field_list;
301 // result.push_back(std::make_unique<ObjectProperty>(
302 // "prototype", // Field name
303 // "v8::internal::HeapObject", // Field type
304 // "v8::internal::HeapObject", // Decompressed type
305 // GetPrototypeAddress(), // Field address
306 // 1, // Number of values
307 // 8, // Size of value
308 // std::move(prototype_struct_field_list), // Struct fields
309 // d::PropertyKind::kSingle)); // Field kind
310 //
311 // In builds with pointer compression enabled, the field type for tagged values
312 // is "v8::internal::TaggedValue" (a four-byte class) and the decompressed type
313 // is a normal Object subclass that describes the expanded eight-byte type.
314 //
315 // If the field is an array, then its length is fetched from the debuggee. This
316 // could fail if the debuggee has incomplete memory, so the "validity" from that
317 // fetch is used to determine the result PropertyKind, which will say whether
318 // the array's length is known.
319 //
320 // If the field's type is a struct, then a local variable is created and filled
321 // with descriptions of each of the struct's fields. The type and decompressed
322 // type in the ObjectProperty are set to the empty string, to indicate to the
323 // caller that the struct fields vector should be used instead.
324 //
325 // The following example is an array of structs, so it uses both of the optional
326 // components described above:
327 //
328 // std::vector<std::unique_ptr<StructProperty>> descriptors_struct_field_list;
329 // descriptors_struct_field_list.push_back(std::make_unique<StructProperty>(
330 // "key", // Struct field name
331 // "v8::internal::PrimitiveHeapObject", // Struct field type
332 // "v8::internal::PrimitiveHeapObject", // Struct field decompressed type
333 // 0, // Byte offset within struct data
334 // 0, // Bitfield size (0=not a bitfield)
335 // 0)); // Bitfield shift
336 // // The line above is repeated for other struct fields. Omitted here.
337 // Value<uint16_t> indexed_field_count =
338 // GetNumberOfAllDescriptorsValue(accessor); // Fetch the array length.
339 // result.push_back(std::make_unique<ObjectProperty>(
340 // "descriptors", // Field name
341 // "", // Field type
342 // "", // Decompressed type
343 // GetDescriptorsAddress(), // Field address
344 // indexed_field_count.value, // Number of values
345 // 24, // Size of value
346 // std::move(descriptors_struct_field_list), // Struct fields
347 // GetArrayKind(indexed_field_count.validity))); // Field kind
GenerateGetPropsChunkForField(const Field & field,base::Optional<NameAndType> array_length,std::ostream & get_props_impl)348 void GenerateGetPropsChunkForField(const Field& field,
349 base::Optional<NameAndType> array_length,
350 std::ostream& get_props_impl) {
351 DebugFieldType debug_field_type(field);
352
353 // If the current field is a struct or bitfield struct, create a vector
354 // describing its fields. Otherwise this vector will be empty.
355 std::string struct_field_list =
356 field.name_and_type.name + "_struct_field_list";
357 get_props_impl << " std::vector<std::unique_ptr<StructProperty>> "
358 << struct_field_list << ";\n";
359 for (const auto& struct_field :
360 ValueTypeFieldsRange(field.name_and_type.type)) {
361 DebugFieldType struct_field_type(struct_field.name_and_type,
362 struct_field.pos);
363 get_props_impl << " " << struct_field_list
364 << ".push_back(std::make_unique<StructProperty>(\""
365 << struct_field.name_and_type.name << "\", "
366 << struct_field_type.GetTypeString(kAsStoredInHeap) << ", "
367 << struct_field_type.GetTypeString(kUncompressed) << ", "
368 << struct_field.offset_bytes << ", " << struct_field.num_bits
369 << ", " << struct_field.shift_bits << "));\n";
370 }
371 struct_field_list = "std::move(" + struct_field_list + ")";
372
373 // The number of values and property kind for non-indexed properties:
374 std::string count_value = "1";
375 std::string property_kind = "d::PropertyKind::kSingle";
376
377 // If the field is indexed, emit a fetch of the array length, and change
378 // count_value and property_kind to be the correct values for an array.
379 if (array_length) {
380 const Type* index_type = array_length->type;
381 std::string index_type_name;
382 if (index_type == TypeOracle::GetSmiType()) {
383 index_type_name = "uintptr_t";
384 count_value =
385 "i::PlatformSmiTagging::SmiToInt(indexed_field_count.value)";
386 } else if (!index_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
387 index_type_name = index_type->GetConstexprGeneratedTypeName();
388 count_value = "indexed_field_count.value";
389 } else {
390 Error("Unsupported index type: ", index_type);
391 return;
392 }
393 get_props_impl << " Value<" << index_type_name
394 << "> indexed_field_count = Get"
395 << CamelifyString(array_length->name)
396 << "Value(accessor);\n";
397 property_kind = "GetArrayKind(indexed_field_count.validity)";
398 }
399
400 get_props_impl << " result.push_back(std::make_unique<ObjectProperty>(\""
401 << field.name_and_type.name << "\", "
402 << debug_field_type.GetTypeString(kAsStoredInHeap) << ", "
403 << debug_field_type.GetTypeString(kUncompressed) << ", "
404 << debug_field_type.GetAddressGetter() << "(), " << count_value
405 << ", " << debug_field_type.GetSize() << ", "
406 << struct_field_list << ", " << property_kind << "));\n";
407 }
408
409 // For any Torque-defined class Foo, this function generates a class TqFoo which
410 // allows for convenient inspection of objects of type Foo in a crash dump or
411 // time travel session (where we can't just run the object printer). The
412 // generated class looks something like this:
413 //
414 // class TqFoo : public TqParentOfFoo {
415 // public:
416 // // {address} is an uncompressed tagged pointer.
417 // inline TqFoo(uintptr_t address) : TqParentOfFoo(address) {}
418 //
419 // // Creates and returns a list of this object's properties.
420 // std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
421 // d::MemoryAccessor accessor) const override;
422 //
423 // // Returns the name of this class, "v8::internal::Foo".
424 // const char* GetName() const override;
425 //
426 // // Visitor pattern; implementation just calls visitor->VisitFoo(this).
427 // void Visit(TqObjectVisitor* visitor) const override;
428 //
429 // // Returns whether Foo is a superclass of the other object's type.
430 // bool IsSuperclassOf(const TqObject* other) const override;
431 //
432 // // Field accessors omitted here (see other comments above).
433 // };
434 //
435 // Four output streams are written:
436 //
437 // h_contents: A header file which gets the class definition above.
438 // cc_contents: A cc file which gets implementations of that class's members.
439 // visitor: A stream that is accumulating the definition of the class
440 // TqObjectVisitor. Each class Foo gets its own virtual method
441 // VisitFoo in TqObjectVisitor.
442 // class_names: A stream that is accumulating a list of strings including fully-
443 // qualified names for every Torque-defined class type.
GenerateClassDebugReader(const ClassType & type,std::ostream & h_contents,std::ostream & cc_contents,std::ostream & visitor,std::ostream & class_names,std::unordered_set<const ClassType * > * done)444 void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
445 std::ostream& cc_contents, std::ostream& visitor,
446 std::ostream& class_names,
447 std::unordered_set<const ClassType*>* done) {
448 // Make sure each class only gets generated once.
449 if (!done->insert(&type).second) return;
450 const ClassType* super_type = type.GetSuperClass();
451
452 // We must emit the classes in dependency order. If the super class hasn't
453 // been emitted yet, go handle it first.
454 if (super_type != nullptr) {
455 GenerateClassDebugReader(*super_type, h_contents, cc_contents, visitor,
456 class_names, done);
457 }
458
459 // Classes with undefined layout don't grant any particular value here and may
460 // not correspond with actual C++ classes, so skip them.
461 if (type.HasUndefinedLayout()) return;
462
463 const std::string name = type.name();
464 const std::string super_name =
465 super_type == nullptr ? "Object" : super_type->name();
466 h_contents << "\nclass Tq" << name << " : public Tq" << super_name << " {\n";
467 h_contents << " public:\n";
468 h_contents << " inline Tq" << name << "(uintptr_t address) : Tq"
469 << super_name << "(address) {}\n";
470 h_contents << kTqObjectOverrideDecls;
471
472 cc_contents << "\nconst char* Tq" << name << "::GetName() const {\n";
473 cc_contents << " return \"v8::internal::" << name << "\";\n";
474 cc_contents << "}\n";
475
476 cc_contents << "\nvoid Tq" << name
477 << "::Visit(TqObjectVisitor* visitor) const {\n";
478 cc_contents << " visitor->Visit" << name << "(this);\n";
479 cc_contents << "}\n";
480
481 cc_contents << "\nbool Tq" << name
482 << "::IsSuperclassOf(const TqObject* other) const {\n";
483 cc_contents
484 << " return GetName() != other->GetName() && dynamic_cast<const Tq"
485 << name << "*>(other) != nullptr;\n";
486 cc_contents << "}\n";
487
488 // By default, the visitor method for this class just calls the visitor method
489 // for this class's parent. This allows custom visitors to only override a few
490 // classes they care about without needing to know about the entire hierarchy.
491 visitor << " virtual void Visit" << name << "(const Tq" << name
492 << "* object) {\n";
493 visitor << " Visit" << super_name << "(object);\n";
494 visitor << " }\n";
495
496 class_names << " \"v8::internal::" << name << "\",\n";
497
498 std::stringstream get_props_impl;
499
500 for (const Field& field : type.fields()) {
501 if (field.name_and_type.type == TypeOracle::GetVoidType()) continue;
502 if (!field.offset.has_value()) {
503 // Fields with dynamic offset are currently unsupported.
504 continue;
505 }
506 GenerateFieldAddressAccessor(field, name, h_contents, cc_contents);
507 GenerateFieldValueAccessor(field, name, h_contents, cc_contents);
508 base::Optional<NameAndType> array_length;
509 if (field.index) {
510 array_length = ExtractSimpleFieldArraySize(type, *field.index);
511 if (!array_length) {
512 // Unsupported complex array length, skipping this field.
513 continue;
514 }
515 }
516 GenerateGetPropsChunkForField(field, array_length, get_props_impl);
517 }
518
519 h_contents << "};\n";
520
521 cc_contents << "\nstd::vector<std::unique_ptr<ObjectProperty>> Tq" << name
522 << "::GetProperties(d::MemoryAccessor accessor) const {\n";
523 // Start by getting the fields from the parent class.
524 cc_contents << " std::vector<std::unique_ptr<ObjectProperty>> result = Tq"
525 << super_name << "::GetProperties(accessor);\n";
526 // Then add the fields from this class.
527 cc_contents << get_props_impl.str();
528 cc_contents << " return result;\n";
529 cc_contents << "}\n";
530 }
531 } // namespace
532
GenerateClassDebugReaders(const std::string & output_directory)533 void ImplementationVisitor::GenerateClassDebugReaders(
534 const std::string& output_directory) {
535 const std::string file_name = "class-debug-readers-tq";
536 std::stringstream h_contents;
537 std::stringstream cc_contents;
538 h_contents << "// Provides the ability to read object properties in\n";
539 h_contents << "// postmortem or remote scenarios, where the debuggee's\n";
540 h_contents << "// memory is not part of the current process's address\n";
541 h_contents << "// space and must be read using a callback function.\n\n";
542 {
543 IncludeGuardScope include_guard(h_contents, file_name + ".h");
544
545 h_contents << "#include <cstdint>\n";
546 h_contents << "#include <vector>\n";
547 h_contents
548 << "\n#include \"tools/debug_helper/debug-helper-internal.h\"\n\n";
549
550 h_contents << "// Unset a windgi.h macro that causes conflicts.\n";
551 h_contents << "#ifdef GetBValue\n";
552 h_contents << "#undef GetBValue\n";
553 h_contents << "#endif\n\n";
554
555 for (const std::string& include_path : GlobalContext::CppIncludes()) {
556 cc_contents << "#include " << StringLiteralQuote(include_path) << "\n";
557 }
558 cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n";
559 cc_contents << "#include \"include/v8-internal.h\"\n\n";
560 cc_contents << "namespace i = v8::internal;\n\n";
561
562 NamespaceScope h_namespaces(h_contents,
563 {"v8", "internal", "debug_helper_internal"});
564 NamespaceScope cc_namespaces(cc_contents,
565 {"v8", "internal", "debug_helper_internal"});
566
567 std::stringstream visitor;
568 visitor << "\nclass TqObjectVisitor {\n";
569 visitor << " public:\n";
570 visitor << " virtual void VisitObject(const TqObject* object) {}\n";
571
572 std::stringstream class_names;
573
574 std::unordered_set<const ClassType*> done;
575 for (const ClassType* type : TypeOracle::GetClasses()) {
576 GenerateClassDebugReader(*type, h_contents, cc_contents, visitor,
577 class_names, &done);
578 }
579
580 visitor << "};\n";
581 h_contents << visitor.str();
582
583 cc_contents << "\nconst char* kObjectClassNames[] {\n";
584 cc_contents << class_names.str();
585 cc_contents << "};\n";
586 cc_contents << kObjectClassListDefinition;
587 }
588 WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
589 WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
590 }
591
592 } // namespace torque
593 } // namespace internal
594 } // namespace v8
595