class-debug-reader-generator.cc 9.47 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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 {

namespace {
void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
15
                              std::ostream& cc_contents, std::ostream& visitor,
16 17 18 19 20 21 22 23
                              std::unordered_set<const ClassType*>* done) {
  // Make sure each class only gets generated once.
  if (!type.IsExtern() || !done->insert(&type).second) return;
  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) {
24 25
    GenerateClassDebugReader(*super_type, h_contents, cc_contents, visitor,
                             done);
26 27 28 29 30 31 32 33 34 35
  }

  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";
  h_contents << "  std::vector<std::unique_ptr<ObjectProperty>> "
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
                "GetProperties(d::MemoryAccessor accessor) const override;\n";
  h_contents << "  const char* GetName() const override;\n";
  h_contents << "  void Visit(TqObjectVisitor* visitor) const override;\n";

  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";

  visitor << "  virtual void Visit" << name << "(const Tq" << name
          << "* object) {\n";
  visitor << "    Visit" << super_name << "(object);\n";
  visitor << "  }\n";

54 55 56 57 58 59 60 61 62 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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
  std::stringstream get_props_impl;

  for (const Field& field : type.fields()) {
    const Type* field_type = field.name_and_type.type;
    if (field_type == TypeOracle::GetVoidType()) continue;
    const std::string& field_name = field.name_and_type.name;
    bool is_field_tagged = field_type->IsSubtypeOf(TypeOracle::GetTaggedType());
    base::Optional<const ClassType*> field_class_type =
        field_type->ClassSupertype();
    size_t field_size = 0;
    std::string field_size_string;
    std::tie(field_size, field_size_string) = field.GetFieldSizeInformation();

    std::string field_value_type;
    std::string field_value_type_compressed;
    std::string field_cc_type;
    std::string field_cc_type_compressed;
    if (is_field_tagged) {
      field_value_type = "uintptr_t";
      field_value_type_compressed = "i::Tagged_t";
      field_cc_type = "v8::internal::" + (field_class_type.has_value()
                                              ? (*field_class_type)->name()
                                              : "Object");
      field_cc_type_compressed =
          COMPRESS_POINTERS_BOOL ? "v8::internal::TaggedValue" : field_cc_type;
    } else {
      const Type* constexpr_version = field_type->ConstexprVersion();
      if (constexpr_version == nullptr) {
        Error("Type '", field_type->ToString(),
              "' requires a constexpr representation");
        continue;
      }
      field_cc_type = constexpr_version->GetGeneratedTypeName();
      field_cc_type_compressed = field_cc_type;
      // Note that we need constexpr names to resolve correctly in the global
      // namespace, because we're passing them as strings to a debugging
      // extension. We can verify this during build of the debug helper, because
      // we use this type for a local variable below, and generate this code in
      // a disjoint namespace. However, we can't emit a useful error at this
      // point. Instead we'll emit a comment that might be helpful.
      field_value_type =
          field_cc_type +
          " /*Failing? Ensure constexpr type name is fully qualified and "
          "necessary #includes are in debug-helper-internal.h*/";
      field_value_type_compressed = field_value_type;
    }

    const std::string field_getter =
        "Get" + CamelifyString(field_name) + "Value";
    const std::string address_getter =
        "Get" + CamelifyString(field_name) + "Address";

    std::string indexed_field_info;
107 108
    std::string index_param;
    std::string index_offset;
109
    if (field.index) {
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
      const Type* index_type = (*field.index)->name_and_type.type;
      std::string index_type_name;
      std::string index_value;
      if (index_type == TypeOracle::GetSmiType()) {
        index_type_name = "uintptr_t";
        index_value =
            "i::PlatformSmiTagging::SmiToInt(indexed_field_count.value)";
      } else if (!index_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
        const Type* constexpr_index = index_type->ConstexprVersion();
        if (constexpr_index == nullptr) {
          Error("Type '", index_type->ToString(),
                "' requires a constexpr representation");
          continue;
        }
        index_type_name = constexpr_index->GetGeneratedTypeName();
        index_value = "indexed_field_count.value";
      } else {
        Error("Unsupported index type: ", index_type);
128 129
        continue;
      }
130 131
      get_props_impl << "  Value<" << index_type_name
                     << "> indexed_field_count = Get"
132 133 134
                     << CamelifyString((*field.index)->name_and_type.name)
                     << "Value(accessor);\n";
      indexed_field_info =
135
          ", " + index_value + ", GetArrayKind(indexed_field_count.validity)";
136 137
      index_param = ", size_t offset";
      index_offset = " + offset * sizeof(value)";
138
    }
139 140 141 142
    get_props_impl << "  result.push_back(std::make_unique<ObjectProperty>(\""
                   << field_name << "\", \"" << field_cc_type_compressed
                   << "\", \"" << field_cc_type << "\", " << address_getter
                   << "()" << indexed_field_info << "));\n";
143

144
    h_contents << "  uintptr_t " << address_getter << "() const;\n";
145
    h_contents << "  Value<" << field_value_type << "> " << field_getter
146
               << "(d::MemoryAccessor accessor " << index_param << ") const;\n";
147
    cc_contents << "\nuintptr_t Tq" << name << "::" << address_getter
148
                << "() const {\n";
149 150 151 152
    cc_contents << "  return address_ - i::kHeapObjectTag + " << field.offset
                << ";\n";
    cc_contents << "}\n";
    cc_contents << "\nValue<" << field_value_type << "> Tq" << name
153 154
                << "::" << field_getter << "(d::MemoryAccessor accessor"
                << index_param << ") const {\n";
155 156
    cc_contents << "  " << field_value_type_compressed << " value{};\n";
    cc_contents << "  d::MemoryAccessResult validity = accessor("
157 158
                << address_getter << "()" << index_offset
                << ", reinterpret_cast<uint8_t*>(&value), sizeof(value));\n";
159 160 161 162 163 164 165 166 167
    cc_contents << "  return {validity, "
                << (is_field_tagged ? "Decompress(value, address_)" : "value")
                << "};\n";
    cc_contents << "}\n";
  }

  h_contents << "};\n";

  cc_contents << "\nstd::vector<std::unique_ptr<ObjectProperty>> Tq" << name
168
              << "::GetProperties(d::MemoryAccessor accessor) const {\n";
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
  cc_contents << "  std::vector<std::unique_ptr<ObjectProperty>> result = Tq"
              << super_name << "::GetProperties(accessor);\n";
  cc_contents << get_props_impl.str();
  cc_contents << "  return result;\n";
  cc_contents << "}\n";
}
}  // namespace

void ImplementationVisitor::GenerateClassDebugReaders(
    const std::string& output_directory) {
  const std::string file_name = "class-debug-readers-tq";
  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";

    cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n";
    cc_contents << "#include \"include/v8-internal.h\"\n\n";
    cc_contents << "namespace i = v8::internal;\n\n";

    NamespaceScope h_namespaces(h_contents, {"v8_debug_helper_internal"});
    NamespaceScope cc_namespaces(cc_contents, {"v8_debug_helper_internal"});

201 202 203 204 205
    std::stringstream visitor;
    visitor << "\nclass TqObjectVisitor {\n";
    visitor << " public:\n";
    visitor << "  virtual void VisitObject(const TqObject* object) {}\n";

206 207 208
    std::unordered_set<const ClassType*> done;
    for (const TypeAlias* alias : GlobalContext::GetClasses()) {
      const ClassType* type = ClassType::DynamicCast(alias->type());
209
      GenerateClassDebugReader(*type, h_contents, cc_contents, visitor, &done);
210
    }
211 212 213

    visitor << "};\n";
    h_contents << visitor.str();
214 215 216 217 218 219 220 221
  }
  WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
  WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
}

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