[fidlcat] Two steps decoding for types

- first step: lazy decoding of the libaries. Only the names of
  types are decoded (structs, enums,  unions, ...).

- then, if a type is used, all the information is decoded for this
  type and its dependencies (eager decoding of used types).

There is no new test because this cl doesn't add any
functionality.

fx run-test fidlcat_tests
[==========] 58 tests from 2 test suites ran. (74 ms total)
[  PASSED  ] 58 tests.

The tests are now 34% faster (113 ms to 74 ms)

Change-Id: I0815f5ef8c33ae73e9a1c57d93d40971c6a43016
diff --git a/tools/fidlcat/lib/library_loader.cc b/tools/fidlcat/lib/library_loader.cc
index e875e53..3b94c3b 100644
--- a/tools/fidlcat/lib/library_loader.cc
+++ b/tools/fidlcat/lib/library_loader.cc
@@ -14,45 +14,78 @@
 
 namespace fidlcat {
 
-const std::unique_ptr<Type> Enum::GetType() const {
-  // TODO Consider caching this.
-  return Type::ScalarTypeFromName(value_["type"].GetString(), 0);
+Enum::Enum(const rapidjson::Value& value) : value_(value) {}
+
+Enum::~Enum() {}
+
+void Enum::DecodeTypes() {
+  if (decoded_) {
+    return;
+  }
+  decoded_ = true;
+  name_ = value_["name"].GetString();
+  type_ = Type::ScalarTypeFromName(value_["type"].GetString(), 0);
+  size_ = type_->InlineSize();
 }
 
 std::string Enum::GetNameFromBytes(const uint8_t* bytes) const {
-  std::unique_ptr<Type> type = GetType();
-  size_t length = type->InlineSize();
   for (auto& member : value_["members"].GetArray()) {
-    if (type->ValueEquals(bytes, length, member["value"]["literal"])) {
+    if (type_->ValueEquals(bytes, size_, member["value"]["literal"])) {
       return member["name"].GetString();
     }
   }
   return "(Unknown enum member)";
 }
 
-uint32_t Enum::size() const { return GetType()->InlineSize(); }
-
-std::unique_ptr<Type> UnionMember::GetType() const {
-  if (!value_.HasMember("type")) {
+UnionMember::UnionMember(const Library& enclosing_library,
+                         const rapidjson::Value& value)
+    : name_(value["name"].GetString()),
+      offset_(std::strtoll(value["offset"].GetString(), nullptr, 10)),
+      size_(std::strtoll(value["size"].GetString(), nullptr, 10)),
+      ordinal_(value.HasMember("ordinal")
+                   ? std::strtoll(value["ordinal"].GetString(), nullptr, 10)
+                   : 0) {
+  if (!value.HasMember("type")) {
     FXL_LOG(ERROR) << "Type missing";
-    return std::make_unique<RawType>(size());
+    type_ = std::make_unique<RawType>(size_);
+    return;
   }
-  const rapidjson::Value& type = value_["type"];
-  return Type::GetType(enclosing_union().enclosing_library().enclosing_loader(),
-                       type, size());
+  type_ =
+      Type::GetType(enclosing_library.enclosing_loader(), value["type"], size_);
+}
+
+UnionMember::~UnionMember() {}
+
+Union::Union(const Library& enclosing_library, const rapidjson::Value& value)
+    : enclosing_library_(enclosing_library), value_(value) {}
+
+void Union::DecodeTypes() {
+  if (decoded_) {
+    return;
+  }
+  decoded_ = true;
+  name_ = value_["name"].GetString();
+  alignment_ = std::strtoll(value_["alignment"].GetString(), nullptr, 10);
+  size_ = std::strtoll(value_["size"].GetString(), nullptr, 10);
+  auto member_arr = value_["members"].GetArray();
+  members_.reserve(member_arr.Size());
+  for (auto& member : member_arr) {
+    members_.push_back(
+        std::make_unique<UnionMember>(enclosing_library_, member));
+  }
 }
 
 const UnionMember* Union::MemberWithTag(uint32_t tag) const {
   if (tag >= members_.size()) {
     return nullptr;
   }
-  return &members_[tag];
+  return members_[tag].get();
 }
 
 const UnionMember* Union::MemberWithOrdinal(Ordinal ordinal) const {
   for (const auto& member : members_) {
-    if (member.ordinal() == ordinal) {
-      return &member;
+    if (member->ordinal() == ordinal) {
+      return member.get();
     }
   }
   return nullptr;
@@ -72,14 +105,44 @@
   return result;
 }
 
-std::unique_ptr<Type> StructMember::GetType() const {
-  if (!value_.HasMember("type")) {
+StructMember::StructMember(const Library& enclosing_library,
+                           const rapidjson::Value& value)
+    : name_(value["name"].GetString()),
+      offset_(std::strtoll(value["offset"].GetString(), nullptr, 10)),
+      size_(std::strtoll(value["size"].GetString(), nullptr, 10)) {
+  if (!value.HasMember("type")) {
     FXL_LOG(ERROR) << "Type missing";
-    return std::make_unique<RawType>(size());
+    type_ = std::make_unique<RawType>(size());
+    return;
   }
-  const rapidjson::Value& type = value_["type"];
-  return Type::GetType(
-      enclosing_struct().enclosing_library().enclosing_loader(), type, size());
+  type_ =
+      Type::GetType(enclosing_library.enclosing_loader(), value["type"], size_);
+}
+
+StructMember::~StructMember() {}
+
+Struct::Struct(const Library& enclosing_library, const rapidjson::Value& value)
+    : enclosing_library_(enclosing_library), value_(value) {}
+
+void Struct::DecodeStructTypes() {
+  if (decoded_) {
+    return;
+  }
+  DecodeTypes("size", "members");
+}
+
+void Struct::DecodeRequestTypes() {
+  if (decoded_) {
+    return;
+  }
+  DecodeTypes("maybe_request_size", "maybe_request");
+}
+
+void Struct::DecodeResponseTypes() {
+  if (decoded_) {
+    return;
+  }
+  DecodeTypes("maybe_response_size", "maybe_response");
 }
 
 std::unique_ptr<Object> Struct::DecodeObject(MessageDecoder* decoder,
@@ -95,49 +158,78 @@
   return result;
 }
 
-std::unique_ptr<Type> TableMember::GetType() const {
-  if (!value_.HasMember("type")) {
-    FXL_LOG(ERROR) << "Type missing";
-    return std::make_unique<RawType>(size());
+void Struct::DecodeTypes(std::string size_name, std::string member_name) {
+  FXL_DCHECK(!decoded_);
+  decoded_ = true;
+  name_ = value_["name"].GetString();
+  size_ = std::strtoll(value_[size_name].GetString(), nullptr, 10);
+  auto member_arr = value_[member_name].GetArray();
+  members_.reserve(member_arr.Size());
+  for (auto& member : member_arr) {
+    members_.push_back(
+        std::make_unique<StructMember>(enclosing_library_, member));
   }
-  const rapidjson::Value& type = value_["type"];
-  return Type::GetType(enclosing_table().enclosing_library().enclosing_loader(),
-                       type, size());
 }
 
+TableMember::TableMember(const Library& enclosing_library,
+                         const rapidjson::Value& value)
+    : name_(value["name"].GetString()),
+      ordinal_(std::strtoll(value["ordinal"].GetString(), nullptr, 10)),
+      size_(std::strtoll(value["size"].GetString(), nullptr, 10)) {
+  if (!value.HasMember("type")) {
+    FXL_LOG(ERROR) << "Type missing";
+    type_ = std::make_unique<RawType>(size_);
+    return;
+  }
+  type_ =
+      Type::GetType(enclosing_library.enclosing_loader(), value["type"], size_);
+}
+
+TableMember::~TableMember() {}
+
 Table::Table(const Library& enclosing_library, const rapidjson::Value& value)
-    : enclosing_library_(enclosing_library), value_(value) {
-  auto member_arr = value["members"].GetArray();
+    : enclosing_library_(enclosing_library), value_(value) {}
+
+Table::~Table() {}
+
+void Table::DecodeTypes() {
+  if (decoded_) {
+    return;
+  }
+  decoded_ = true;
+  name_ = value_["name"].GetString();
+  size_ = std::strtoll(value_["size"].GetString(), nullptr, 10);
+  unknown_member_type_ = std::make_unique<RawType>(size_);
+  auto member_arr = value_["members"].GetArray();
   uint32_t max_ordinal = 0;
   for (auto& member : member_arr) {
-    Ordinal ordinal = std::strtoll(member["ordinal"].GetString(), nullptr, 10);
-    backing_members_.emplace_back(*this, member);
-    max_ordinal = std::max(max_ordinal, ordinal);
+    backing_members_.push_back(
+        std::make_unique<TableMember>(enclosing_library_, member));
+    max_ordinal = std::max(max_ordinal, backing_members_.back()->ordinal());
   }
 
   // There is one element in this array for each possible ordinal value.  The
   // array is dense, so there are unlikely to be gaps.
   members_.resize(max_ordinal + 1, nullptr);
-  for (auto& backing_member : backing_members_) {
-    members_[backing_member.ordinal()] = &backing_member;
+  for (const auto& backing_member : backing_members_) {
+    members_[backing_member->ordinal()] = backing_member.get();
   }
 }
 
 InterfaceMethod::InterfaceMethod(const Interface& interface,
                                  const rapidjson::Value& value)
     : enclosing_interface_(interface),
+      value_(value),
       ordinal_(std::strtoll(value["ordinal"].GetString(), nullptr, 10)),
-      name_(value["name"].GetString()),
-      value_(value) {
+      name_(value["name"].GetString()) {
   if (value_["has_request"].GetBool()) {
-    request_ = std::make_unique<Struct>(interface.enclosing_library(), value,
-                                        "maybe_request_size", "maybe_request");
+    request_ = std::unique_ptr<Struct>(
+        new Struct(interface.enclosing_library(), value));
   }
 
   if (value_["has_response"].GetBool()) {
-    response_ =
-        std::make_unique<Struct>(interface.enclosing_library(), value,
-                                 "maybe_response_size", "maybe_response");
+    response_ = std::unique_ptr<Struct>(
+        new Struct(interface.enclosing_library(), value));
   }
 }
 
@@ -151,78 +243,91 @@
 bool Interface::GetMethodByFullName(const std::string& name,
                                     const InterfaceMethod** method_ptr) const {
   for (const auto& method : methods()) {
-    if (method.fully_qualified_name() == name) {
-      *method_ptr = &method;
+    if (method->fully_qualified_name() == name) {
+      *method_ptr = method.get();
       return true;
     }
   }
   return false;
 }
 
-Library::Library(const LibraryLoader& enclosing, rapidjson::Document& document)
-    : enclosing_loader_(enclosing), backing_document_(std::move(document)) {
+Library::Library(LibraryLoader* enclosing_loader, rapidjson::Document& document,
+                 std::map<Ordinal, const InterfaceMethod*>& index)
+    : enclosing_loader_(enclosing_loader),
+      backing_document_(std::move(document)) {
   auto interfaces_array =
       backing_document_["interface_declarations"].GetArray();
   interfaces_.reserve(interfaces_array.Size());
 
   for (auto& decl : interfaces_array) {
-    interfaces_.emplace_back(*this, decl);
+    interfaces_.emplace_back(new Interface(*this, decl));
+    interfaces_.back()->AddMethodsToIndex(index);
   }
+}
+
+void Library::DecodeTypes() {
+  if (decoded_) {
+    return;
+  }
+  decoded_ = true;
+  name_ = backing_document_["name"].GetString();
   for (auto& enu : backing_document_["enum_declarations"].GetArray()) {
     enums_.emplace(std::piecewise_construct,
                    std::forward_as_tuple(enu["name"].GetString()),
-                   std::forward_as_tuple(*this, enu));
+                   std::forward_as_tuple(new Enum(enu)));
   }
   for (auto& str : backing_document_["struct_declarations"].GetArray()) {
     structs_.emplace(std::piecewise_construct,
                      std::forward_as_tuple(str["name"].GetString()),
-                     std::forward_as_tuple(*this, str, "size", "members"));
+                     std::forward_as_tuple(new Struct(*this, str)));
   }
   for (auto& tab : backing_document_["table_declarations"].GetArray()) {
     tables_.emplace(std::piecewise_construct,
                     std::forward_as_tuple(tab["name"].GetString()),
-                    std::forward_as_tuple(*this, tab));
+                    std::forward_as_tuple(new Table(*this, tab)));
   }
   for (auto& uni : backing_document_["union_declarations"].GetArray()) {
     unions_.emplace(std::piecewise_construct,
                     std::forward_as_tuple(uni["name"].GetString()),
-                    std::forward_as_tuple(*this, uni));
+                    std::forward_as_tuple(new Union(*this, uni)));
   }
   for (auto& xuni : backing_document_["xunion_declarations"].GetArray()) {
     xunions_.emplace(std::piecewise_construct,
                      std::forward_as_tuple(xuni["name"].GetString()),
-                     std::forward_as_tuple(*this, xuni));
+                     std::forward_as_tuple(new XUnion(*this, xuni)));
   }
 }
 
 std::unique_ptr<Type> Library::TypeFromIdentifier(bool is_nullable,
                                                   std::string& identifier,
-                                                  size_t inline_size) const {
+                                                  size_t inline_size) {
   auto str = structs_.find(identifier);
   if (str != structs_.end()) {
+    str->second->DecodeStructTypes();
     std::unique_ptr<Type> type(
-        new StructType(std::ref(str->second), is_nullable));
+        new StructType(std::ref(*str->second), is_nullable));
     return type;
   }
   auto enu = enums_.find(identifier);
   if (enu != enums_.end()) {
-    return std::make_unique<EnumType>(std::ref(enu->second));
+    enu->second->DecodeTypes();
+    return std::make_unique<EnumType>(std::ref(*enu->second));
   }
   auto tab = tables_.find(identifier);
   if (tab != tables_.end()) {
-    std::unique_ptr<Type> type(new TableType(std::ref(tab->second)));
-    return type;
+    tab->second->DecodeTypes();
+    return std::make_unique<TableType>(std::ref(*tab->second));
   }
   auto uni = unions_.find(identifier);
   if (uni != unions_.end()) {
-    std::unique_ptr<Type> type(
-        new UnionType(std::ref(uni->second), is_nullable));
-    return type;
+    uni->second->DecodeTypes();
+    return std::make_unique<UnionType>(std::ref(*uni->second), is_nullable);
   }
   auto xuni = xunions_.find(identifier);
   if (xuni != xunions_.end()) {
     // Note: XUnion and nullable XUnion are encoded in the same way
-    return std::make_unique<XUnionType>(std::ref(xuni->second), is_nullable);
+    xuni->second->DecodeTypes();
+    return std::make_unique<XUnionType>(std::ref(*xuni->second), is_nullable);
   }
   return std::make_unique<RawType>(inline_size);
 }
@@ -230,8 +335,8 @@
 bool Library::GetInterfaceByName(const std::string& name,
                                  const Interface** ptr) const {
   for (const auto& interface : interfaces()) {
-    if (interface.name() == name) {
-      *ptr = &interface;
+    if (interface->name() == name) {
+      *ptr = interface.get();
       return true;
     }
   }
@@ -241,7 +346,7 @@
 LibraryLoader::LibraryLoader(
     std::vector<std::unique_ptr<std::istream>>& library_streams,
     LibraryReadError* err) {
-  err->value = LibraryReadError ::kOk;
+  err->value = LibraryReadError::kOk;
   for (size_t i = 0; i < library_streams.size(); i++) {
     std::string ir(std::istreambuf_iterator<char>(*library_streams[i]), {});
     if (library_streams[i]->fail()) {
@@ -249,7 +354,7 @@
       return;
     }
     Add(ir, err);
-    if (err->value != LibraryReadError ::kOk) {
+    if (err->value != LibraryReadError::kOk) {
       FXL_LOG(ERROR) << "JSON parse error: "
                      << rapidjson::GetParseError_En(err->parse_result.Code())
                      << " at offset " << err->parse_result.Offset();
diff --git a/tools/fidlcat/lib/library_loader.h b/tools/fidlcat/lib/library_loader.h
index bcfaed5..59c91877 100644
--- a/tools/fidlcat/lib/library_loader.h
+++ b/tools/fidlcat/lib/library_loader.h
@@ -51,6 +51,7 @@
 };
 
 class Interface;
+class InterfaceMethod;
 class Library;
 class LibraryLoader;
 class MessageDecoder;
@@ -64,13 +65,13 @@
 
 class Enum {
  public:
-  Enum(const Library& enclosing_library, const rapidjson::Value& value)
-      : enclosing_library_(enclosing_library), value_(value) {
-    (void)value_;
-    (void)enclosing_library_;
-  }
+  friend class Library;
 
-  const std::unique_ptr<Type> GetType() const;
+  ~Enum();
+
+  const std::string& name() const { return name_; }
+  uint64_t size() const { return size_; }
+  const Type* type() const { return type_.get(); }
 
   // Gets the name of the enum member corresponding to the value pointed to by
   // |bytes| of length |length|.  For example, if we had the following
@@ -83,194 +84,169 @@
   // the member.
   std::string GetNameFromBytes(const uint8_t* bytes) const;
 
-  uint32_t size() const;
-
  private:
-  const Library& enclosing_library_;
+  explicit Enum(const rapidjson::Value& value);
+
+  // Decode all the values from the JSON definition.
+  void DecodeTypes();
+
   const rapidjson::Value& value_;
+  bool decoded_ = false;
+  std::string name_;
+  uint64_t size_;
+  std::unique_ptr<Type> type_;
 };
 
 // TODO: Consider whether this is duplicative of Struct / Table member.
 class UnionMember {
  public:
-  UnionMember(const Union& enclosing_union, const rapidjson::Value& value)
-      : enclosing_union_(enclosing_union),
-        value_(value),
-        name_(value["name"].GetString()),
-        offset_(std::strtoll(value["offset"].GetString(), nullptr, 10)),
-        size_(std::strtoll(value["size"].GetString(), nullptr, 10)),
-        ordinal_(value.HasMember("ordinal")
-                     ? std::strtoll(value["ordinal"].GetString(), nullptr, 10)
-                     : 0) {}
+  UnionMember(const Library& enclosing_library, const rapidjson::Value& value);
+  ~UnionMember();
 
-  const Union& enclosing_union() const { return enclosing_union_; }
   std::string_view name() const { return name_; }
   uint64_t size() const { return size_; }
   uint64_t offset() const { return offset_; }
   Ordinal ordinal() const { return ordinal_; }
-
-  std::unique_ptr<Type> GetType() const;
+  const Type* type() const { return type_.get(); }
 
  private:
-  const Union& enclosing_union_;
-  const rapidjson::Value& value_;
   const std::string name_;
   const uint64_t offset_;
   const uint64_t size_;
   const Ordinal ordinal_;
+  std::unique_ptr<Type> type_;
 };
 
 class Union {
-  friend class Library;
-
  public:
-  Union(const Library& enclosing_library, const rapidjson::Value& value)
-      : enclosing_library_(enclosing_library),
-        value_(value),
-        illegal_(nullptr) {
-    auto member_arr = value["members"].GetArray();
-    members_.reserve(member_arr.Size());
-    for (auto& member : member_arr) {
-      members_.emplace_back(*this, member);
-    }
-  }
-
-  Union(const Union& other) : Union(other.enclosing_library_, other.value_) {}
+  friend class Library;
+  friend class XUnion;
 
   const Library& enclosing_library() const { return enclosing_library_; }
-
-  const std::vector<UnionMember>& members() const { return members_; }
+  const std::string& name() const { return name_; }
+  uint64_t alignment() const { return alignment_; }
+  uint32_t size() const { return size_; }
+  const std::vector<std::unique_ptr<UnionMember>>& members() const {
+    return members_;
+  }
 
   const UnionMember* MemberWithTag(uint32_t tag) const;
 
   const UnionMember* MemberWithOrdinal(Ordinal ordinal) const;
 
-  uint64_t alignment() const {
-    return std::strtoll(value_["alignment"].GetString(), nullptr, 10);
-  }
-
-  uint32_t size() const {
-    return std::strtoll(value_["size"].GetString(), nullptr, 10);
-  }
-
   std::unique_ptr<UnionField> DecodeUnion(MessageDecoder* decoder,
                                           std::string_view name,
                                           uint64_t offset, bool nullable) const;
 
- protected:
+ private:
+  Union(const Library& enclosing_library, const rapidjson::Value& value);
+
+  // Decode all the values from the JSON definition.
+  void DecodeTypes();
+
   const Library& enclosing_library_;
   const rapidjson::Value& value_;
-
- private:
-  std::vector<UnionMember> members_;
-  mutable std::unique_ptr<UnionMember> illegal_;
+  bool decoded_ = false;
+  std::string name_;
+  uint64_t alignment_;
+  uint64_t size_;
+  std::vector<std::unique_ptr<UnionMember>> members_;
 };
 
 class XUnion : public Union {
+ public:
   friend class Library;
 
- public:
+ private:
   XUnion(const Library& enclosing_library, const rapidjson::Value& value)
       : Union(enclosing_library, value) {}
-
-  XUnion(const XUnion& other)
-      : XUnion(other.enclosing_library_, other.value_) {}
 };
 
 class StructMember {
  public:
-  StructMember(const Struct& enclosing_struct, const rapidjson::Value& value)
-      : enclosing_struct_(enclosing_struct), value_(value) {}
+  StructMember(const Library& enclosing_library, const rapidjson::Value& value);
+  ~StructMember();
 
-  std::unique_ptr<Type> GetType() const;
-
-  uint64_t size() const {
-    return std::strtoll(value_["size"].GetString(), nullptr, 10);
-  }
-
-  uint64_t offset() const {
-    return std::strtoll(value_["offset"].GetString(), nullptr, 10);
-  }
-
-  const std::string_view name() const { return value_["name"].GetString(); }
-
-  const Struct& enclosing_struct() const { return enclosing_struct_; }
+  std::string_view name() const { return name_; }
+  uint64_t offset() const { return offset_; }
+  uint64_t size() const { return size_; }
+  const Type* type() const { return type_.get(); }
 
  private:
-  const Struct& enclosing_struct_;
-  const rapidjson::Value& value_;
+  const std::string name_;
+  const uint64_t offset_;
+  const uint64_t size_;
+  std::unique_ptr<Type> type_;
 };
 
 class Struct {
-  friend class Library;
-
  public:
-  Struct(const Library& enclosing_library, const rapidjson::Value& value,
-         std::string size_name, std::string member_name)
-      : enclosing_library_(enclosing_library),
-        value_(value),
-        size_(std::strtoll(value_[size_name].GetString(), nullptr, 10)) {
-    auto member_arr = value[member_name].GetArray();
-    members_.reserve(member_arr.Size());
-    for (auto& member : member_arr) {
-      members_.emplace_back(*this, member);
-    }
-  }
+  friend class Library;
+  friend class InterfaceMethod;
 
   const Library& enclosing_library() const { return enclosing_library_; }
-
   uint32_t size() const { return size_; }
-
-  const std::vector<StructMember>& members() const { return members_; }
+  const std::vector<std::unique_ptr<StructMember>>& members() const {
+    return members_;
+  }
 
   std::unique_ptr<Object> DecodeObject(MessageDecoder* decoder,
                                        std::string_view name, uint64_t offset,
                                        bool nullable) const;
 
  private:
+  Struct(const Library& enclosing_library, const rapidjson::Value& value);
+
+  // Decode all the values from the JSON definition if the object represents a
+  // structure.
+  void DecodeStructTypes();
+
+  // Decode all the values from the JSON definition if the object represents a
+  // request message.
+  void DecodeRequestTypes();
+
+  // Decode all the values from the JSON definition if the object represents a
+  // response message.
+  void DecodeResponseTypes();
+
+  // Decode all the values from the JSON definition.
+  void DecodeTypes(std::string size_name, std::string member_name);
+
   const Library& enclosing_library_;
   const rapidjson::Value& value_;
-  const uint32_t size_;
-  std::vector<StructMember> members_;
+  bool decoded_ = false;
+  std::string name_;
+  uint32_t size_ = 0;
+  std::vector<std::unique_ptr<StructMember>> members_;
 };
 
 class TableMember {
  public:
-  TableMember(const Table& enclosing_table, const rapidjson::Value& value)
-      : enclosing_table_(enclosing_table), value_(value) {}
+  TableMember(const Library& enclosing_library, const rapidjson::Value& value);
+  ~TableMember();
 
-  std::unique_ptr<Type> GetType() const;
-
-  uint64_t size() const {
-    return std::strtoll(value_["size"].GetString(), nullptr, 10);
-  }
-
-  uint64_t offset() const {
-    return std::strtoll(value_["offset"].GetString(), nullptr, 10);
-  }
-
-  const std::string_view name() const { return value_["name"].GetString(); }
-
-  Ordinal ordinal() const {
-    return std::strtoll(value_["ordinal"].GetString(), nullptr, 10);
-  }
-
-  const Table& enclosing_table() const { return enclosing_table_; }
+  const std::string_view name() const { return name_; }
+  Ordinal ordinal() const { return ordinal_; }
+  uint64_t size() const { return size_; }
+  const Type* type() const { return type_.get(); }
 
  private:
-  const Table& enclosing_table_;
-  const rapidjson::Value& value_;
+  const std::string name_;
+  const Ordinal ordinal_;
+  const uint64_t size_;
+  std::unique_ptr<Type> type_;
 };
 
 class Table {
  public:
-  Table(const Library& enclosing_library, const rapidjson::Value& value);
+  friend class Library;
 
-  Table(const Table&) = delete;
-
-  Table(Table&& other) : Table(other.enclosing_library_, other.value_) {}
+  ~Table();
 
   const Library& enclosing_library() const { return enclosing_library_; }
+  const std::string& name() const { return name_; }
+  uint32_t size() const { return size_; }
+  const Type* unknown_member_type() const { return unknown_member_type_.get(); }
 
   // Returns a vector of pointers to the table's members.  The ordinal of each
   // member is its index in the vector.  Omitted ordinals are indicated by
@@ -278,77 +254,76 @@
   // nullptr.
   const std::vector<const TableMember*>& members() const { return members_; }
 
-  uint32_t size() const {
-    return std::strtoll(value_["size"].GetString(), nullptr, 10);
-  }
-
  private:
+  Table(const Library& enclosing_library, const rapidjson::Value& value);
+
+  // Decode all the values from the JSON definition.
+  void DecodeTypes();
+
   const Library& enclosing_library_;
   const rapidjson::Value& value_;
+  bool decoded_ = false;
+  std::string name_;
+  uint64_t size_;
+  std::unique_ptr<Type> unknown_member_type_;
 
   // This indirection - elements of members_ pointing to elements of
   // backing_members_ - is so that we can have empty members.  The author
   // thought that use sites would be more usable than a map.
   // These structures are not modified after the constructor.
   std::vector<const TableMember*> members_;
-  std::vector<TableMember> backing_members_;
+  std::vector<std::unique_ptr<TableMember>> backing_members_;
 };
 
 class InterfaceMethod {
  public:
   friend class Interface;
-  InterfaceMethod(const Interface& interface, const rapidjson::Value& value);
-  InterfaceMethod(InterfaceMethod&& other) = default;
 
   const Interface& enclosing_interface() const { return enclosing_interface_; }
-
   Ordinal ordinal() const { return ordinal_; }
-
   std::string name() const { return name_; }
-
-  const Struct* request() const { return request_.get(); }
-
-  const Struct* response() const { return response_.get(); }
+  Struct* request() const {
+    if (request_ != nullptr) {
+      request_->DecodeRequestTypes();
+    }
+    return request_.get();
+  }
+  Struct* response() const {
+    if (response_ != nullptr) {
+      response_->DecodeResponseTypes();
+    }
+    return response_.get();
+  }
 
   std::string fully_qualified_name() const;
 
-  const std::optional<uint64_t> request_size() const {
-    if (request_ == nullptr) {
-      return {};
-    }
-    return request_->size();
-  }
-
   InterfaceMethod(const InterfaceMethod& other) = delete;
   InterfaceMethod& operator=(const InterfaceMethod&) = delete;
 
  private:
+  InterfaceMethod(const Interface& interface, const rapidjson::Value& value);
+
   const Interface& enclosing_interface_;
-  Ordinal ordinal_;
-  std::string name_;
+  const rapidjson::Value& value_;
+  const Ordinal ordinal_;
+  const std::string name_;
   std::unique_ptr<Struct> request_;
   std::unique_ptr<Struct> response_;
-  const rapidjson::Value& value_;
 };
 
 class Interface {
  public:
-  Interface(const Library& library, const rapidjson::Value& value)
-      : value_(value),
-        enclosing_library_(library),
-        name_(value_["name"].GetString()) {
-    for (auto& method : value["methods"].GetArray()) {
-      interface_methods_.emplace_back(*this, method);
-    }
-  }
-  Interface(Interface&& other) = default;
+  friend class Library;
 
   Interface(const Interface& other) = delete;
   Interface& operator=(const Interface&) = delete;
 
+  const Library& enclosing_library() const { return enclosing_library_; }
+  std::string_view name() const { return name_; }
+
   void AddMethodsToIndex(std::map<Ordinal, const InterfaceMethod*>& index) {
     for (size_t i = 0; i < interface_methods_.size(); i++) {
-      const InterfaceMethod* method = &interface_methods_[i];
+      const InterfaceMethod* method = interface_methods_[i].get();
       index[method->ordinal()] = method;
     }
   }
@@ -357,49 +332,41 @@
   bool GetMethodByFullName(const std::string& name,
                            const InterfaceMethod** method) const;
 
-  const Library& enclosing_library() const { return enclosing_library_; }
-
-  std::string_view name() const { return name_; }
-
-  const std::vector<InterfaceMethod>& methods() const {
+  const std::vector<std::unique_ptr<InterfaceMethod>>& methods() const {
     return interface_methods_;
   }
 
  private:
-  const rapidjson::Value& value_;
+  Interface(const Library& library, const rapidjson::Value& value)
+      : enclosing_library_(library), name_(value["name"].GetString()) {
+    for (auto& method : value["methods"].GetArray()) {
+      interface_methods_.emplace_back(new InterfaceMethod(*this, method));
+    }
+  }
+
   const Library& enclosing_library_;
   std::string name_;
-  std::vector<InterfaceMethod> interface_methods_;
+  std::vector<std::unique_ptr<InterfaceMethod>> interface_methods_;
 };
 
 class Library {
  public:
-  Library(const LibraryLoader& enclosing, rapidjson::Document& document);
+  friend class LibraryLoader;
 
-  // Adds methods to this Library.  Pass it a std::map from ordinal value to the
-  // InterfaceMethod represented by that ordinal.
-  void AddMethodsToIndex(std::map<Ordinal, const InterfaceMethod*>& index) {
-    for (size_t i = 0; i < interfaces_.size(); i++) {
-      interfaces_[i].AddMethodsToIndex(index);
-    }
+  LibraryLoader* enclosing_loader() const { return enclosing_loader_; }
+  const std::string name() { return name_; }
+  const std::vector<std::unique_ptr<Interface>>& interfaces() const {
+    return interfaces_;
   }
 
-  const std::string_view name() {
-    return backing_document_["name"].GetString();
-  }
-
-  const LibraryLoader& enclosing_loader() const { return enclosing_loader_; }
-
   std::unique_ptr<Type> TypeFromIdentifier(bool is_nullable,
                                            std::string& identifier,
-                                           size_t inline_size) const;
+                                           size_t inline_size);
 
   // The size of the type with name |identifier| when it is inline (e.g.,
   // embedded in an array)
   size_t InlineSizeFromIdentifier(std::string& identifier) const;
 
-  const std::vector<Interface>& interfaces() const { return interfaces_; }
-
   // Set *ptr to the Interface called |name|
   bool GetInterfaceByName(const std::string& name, const Interface** ptr) const;
 
@@ -407,18 +374,28 @@
   Library(const Library&) = delete;
 
  private:
-  const LibraryLoader& enclosing_loader_;
-  rapidjson::Document backing_document_;
+  Library(LibraryLoader* enclosing_loader, rapidjson::Document& document,
+          std::map<Ordinal, const InterfaceMethod*>& index);
 
-  std::vector<Interface> interfaces_;
-  std::map<std::string, Enum> enums_;
-  std::map<std::string, Struct> structs_;
-  std::map<std::string, Table> tables_;
-  std::map<std::string, Union> unions_;
-  std::map<std::string, XUnion> xunions_;
+  // Decode all the values from the JSON definition.
+  void DecodeTypes();
+
+  LibraryLoader* enclosing_loader_;
+  rapidjson::Document backing_document_;
+  bool decoded_ = false;
+  std::string name_;
+  std::vector<std::unique_ptr<Interface>> interfaces_;
+  std::map<std::string, std::unique_ptr<Enum>> enums_;
+  std::map<std::string, std::unique_ptr<Struct>> structs_;
+  std::map<std::string, std::unique_ptr<Table>> tables_;
+  std::map<std::string, std::unique_ptr<Union>> unions_;
+  std::map<std::string, std::unique_ptr<XUnion>> xunions_;
 };
 
 // An indexed collection of libraries.
+// WARNING: All references on Enum, Struct, Table, ... and all references on
+//          types and fields must be destroyed before this class (LibraryLoader
+//          should be one of the last objects we destroy).
 class LibraryLoader {
  public:
   LibraryLoader(std::vector<std::unique_ptr<std::istream>>& library_streams,
@@ -438,18 +415,17 @@
     return false;
   }
 
-  // If the library with name |name| is present in this loader, sets *|library|
-  // to a pointer to that library and returns true.  Otherwise, returns false.
+  // If the library with name |name| is present in this loader, returns the
+  // library. Otherwise, returns null.
   // |name| is of the format "a.b.c"
-  bool GetLibraryFromName(const std::string& name,
-                          const Library** library) const {
+  Library* GetLibraryFromName(const std::string& name) {
     auto l = representations_.find(name);
     if (l != representations_.end()) {
-      const Library* lib = &(l->second);
-      *library = lib;
-      return true;
+      Library* library = l->second.get();
+      library->DecodeTypes();
+      return library;
     }
-    return false;
+    return nullptr;
   }
 
  private:
@@ -465,14 +441,12 @@
       return;
     }
     std::string library_name = document["name"].GetString();
-    representations_.emplace(std::piecewise_construct,
-                             std::forward_as_tuple(library_name),
-                             std::forward_as_tuple(*this, document));
-    auto i = representations_.find(library_name);
-    i->second.AddMethodsToIndex(ordinal_map_);
+    representations_.emplace(
+        std::piecewise_construct, std::forward_as_tuple(library_name),
+        std::forward_as_tuple(new Library(this, document, ordinal_map_)));
   }
 
-  std::map<std::string, Library> representations_;
+  std::map<std::string, std::unique_ptr<Library>> representations_;
   std::map<Ordinal, const InterfaceMethod*> ordinal_map_;
 };
 
diff --git a/tools/fidlcat/lib/library_loader_test.cc b/tools/fidlcat/lib/library_loader_test.cc
index cb7be89..f14d62f 100644
--- a/tools/fidlcat/lib/library_loader_test.cc
+++ b/tools/fidlcat/lib/library_loader_test.cc
@@ -27,8 +27,7 @@
   LibraryLoader loader = LibraryLoader(library_files, &err);
   ASSERT_EQ(LibraryReadError::kOk, err.value);
 
-  const Library* library_ptr;
-  loader.GetLibraryFromName("fidl.test.frobinator", &library_ptr);
+  Library* library_ptr = loader.GetLibraryFromName("fidl.test.frobinator");
 
   std::string kDesiredInterfaceName = "fidl.test.frobinator/Frobinator";
   const Interface* found_interface = nullptr;
@@ -59,8 +58,8 @@
   LibraryLoader loader = LibraryLoader(library_files, &err);
   ASSERT_EQ(LibraryReadError::kOk, err.value);
 
-  const Library* library_ptr = nullptr;
-  ASSERT_TRUE(loader.GetLibraryFromName("test.fidlcat.sys", &library_ptr));
+  Library* library_ptr = loader.GetLibraryFromName("test.fidlcat.sys");
+  ASSERT_NE(library_ptr, nullptr);
 
   std::string kDesiredInterfaceName = "test.fidlcat.sys/ComponentController";
   const Interface* found_interface = nullptr;
diff --git a/tools/fidlcat/lib/wire_object.cc b/tools/fidlcat/lib/wire_object.cc
index 5c820cd..5f4a678 100644
--- a/tools/fidlcat/lib/wire_object.cc
+++ b/tools/fidlcat/lib/wire_object.cc
@@ -74,8 +74,8 @@
 
 void Object::DecodeAt(MessageDecoder* decoder, uint64_t base_offset) {
   for (const auto& member : struct_definition_.members()) {
-    std::unique_ptr<Field> field = member.GetType()->Decode(
-        decoder, member.name(), base_offset + member.offset());
+    std::unique_ptr<Field> field = member->type()->Decode(
+        decoder, member->name(), base_offset + member->offset());
     if (field != nullptr) {
       fields_.push_back(std::move(field));
     }
@@ -97,12 +97,12 @@
   }
 }
 
-EnvelopeField::EnvelopeField(std::string_view name, std::unique_ptr<Type> type)
-    : NullableField(name), type_(std::move(type)) {}
+EnvelopeField::EnvelopeField(std::string_view name, const Type* type)
+    : NullableField(name), type_(type) {}
 
 void EnvelopeField::DecodeContent(MessageDecoder* decoder) {
   MessageDecoder envelope_decoder(decoder, num_bytes_, num_handles_);
-  field_ = envelope_decoder.DecodeField(name(), type_.get());
+  field_ = envelope_decoder.DecodeField(name(), type_);
   decoder->GotoNextObjectOffset(num_bytes_);
   decoder->SkipHandles(num_handles_);
 }
@@ -148,10 +148,10 @@
       std::string key_name =
           std::string("unknown$") + std::to_string(envelope_id + 1);
       envelope = std::make_unique<EnvelopeField>(
-          key_name, std::make_unique<RawType>(table_definition_.size()));
+          key_name, table_definition_.unknown_member_type());
     } else {
       envelope =
-          std::make_unique<EnvelopeField>(member->name(), member->GetType());
+          std::make_unique<EnvelopeField>(member->name(), member->type());
     }
     envelope->DecodeAt(decoder, offset);
     envelopes_.push_back(std::move(envelope));
@@ -186,9 +186,8 @@
     field_ = std::make_unique<RawField>(
         std::string("unknown$") + std::to_string(tag), nullptr, 0);
   } else {
-    std::unique_ptr<Type> member_type = member->GetType();
-    field_ = member_type->Decode(decoder, member->name(),
-                                 base_offset + member->offset());
+    field_ = member->type()->Decode(decoder, member->name(),
+                                    base_offset + member->offset());
   }
 }
 
diff --git a/tools/fidlcat/lib/wire_object.h b/tools/fidlcat/lib/wire_object.h
index 9249db3..a1fccaf 100644
--- a/tools/fidlcat/lib/wire_object.h
+++ b/tools/fidlcat/lib/wire_object.h
@@ -148,7 +148,7 @@
 // An envelope (used by TableField and XUnion).
 class EnvelopeField : public NullableField {
  public:
-  EnvelopeField(std::string_view name, std::unique_ptr<Type> type);
+  EnvelopeField(std::string_view name, const Type* type);
 
   uint32_t num_bytes() const { return num_bytes_; }
   uint32_t num_handles() const { return num_handles_; }
@@ -163,7 +163,7 @@
                    rapidjson::Value& result) const override;
 
  private:
-  std::unique_ptr<Type> type_;
+  const Type* const type_;
   uint32_t num_bytes_ = 0;
   uint32_t num_handles_ = 0;
   std::unique_ptr<Field> field_ = nullptr;
@@ -236,8 +236,7 @@
 // A vector.
 class VectorField : public NullableField {
  public:
-  VectorField(std::string_view name, uint64_t size,
-              std::shared_ptr<Type> component_type)
+  VectorField(std::string_view name, uint64_t size, const Type* component_type)
       : NullableField(name), size_(size), component_type_(component_type) {}
 
   void DecodeContent(MessageDecoder* decoder) override;
@@ -247,7 +246,7 @@
 
  private:
   const uint64_t size_;
-  std::shared_ptr<Type> component_type_;
+  const Type* const component_type_;
   std::vector<std::unique_ptr<Field>> fields_;
 };
 
diff --git a/tools/fidlcat/lib/wire_parser.cc b/tools/fidlcat/lib/wire_parser.cc
index af437ab2..65792d9 100644
--- a/tools/fidlcat/lib/wire_parser.cc
+++ b/tools/fidlcat/lib/wire_parser.cc
@@ -74,7 +74,6 @@
   if (method->response() == nullptr) {
     return false;
   }
-
   return MessageToJSON(*method->response(), message, response);
 }
 
diff --git a/tools/fidlcat/lib/wire_types.cc b/tools/fidlcat/lib/wire_types.cc
index 3af6a98..d3ede79 100644
--- a/tools/fidlcat/lib/wire_types.cc
+++ b/tools/fidlcat/lib/wire_types.cc
@@ -121,8 +121,7 @@
     std::string key_name = std::string("unknown$") + std::to_string(ordinal);
     envelope = std::make_unique<EnvelopeField>(key_name, nullptr);
   } else {
-    envelope =
-        std::make_unique<EnvelopeField>(member->name(), member->GetType());
+    envelope = std::make_unique<EnvelopeField>(member->name(), member->type());
   }
   envelope->DecodeAt(decoder, offset);
   result->set_field(std::move(envelope));
@@ -134,11 +133,6 @@
   FXL_DCHECK(component_type_.get() != nullptr);
 }
 
-ElementSequenceType::ElementSequenceType(std::shared_ptr<Type> component_type)
-    : component_type_(component_type) {
-  FXL_DCHECK(component_type_.get() != nullptr);
-}
-
 ArrayType::ArrayType(std::unique_ptr<Type>&& component_type, uint32_t count)
     : ElementSequenceType(std::move(component_type)), count_(count) {}
 
@@ -156,10 +150,6 @@
 VectorType::VectorType(std::unique_ptr<Type>&& component_type)
     : ElementSequenceType(std::move(component_type)) {}
 
-VectorType::VectorType(std::shared_ptr<Type> component_type,
-                       size_t element_size)
-    : ElementSequenceType(component_type) {}
-
 std::unique_ptr<Field> VectorType::Decode(MessageDecoder* decoder,
                                           std::string_view name,
                                           uint64_t offset) const {
@@ -167,7 +157,8 @@
   decoder->GetValueAt(offset, &size);
   offset += sizeof(size);
 
-  auto result = std::make_unique<VectorField>(name, size, component_type_);
+  auto result =
+      std::make_unique<VectorField>(name, size, component_type_.get());
   result->DecodeNullable(decoder, offset);
   return result;
 }
@@ -231,7 +222,7 @@
   return ScalarTypeFromName(subtype, inline_size);
 }
 
-std::unique_ptr<Type> Type::TypeFromIdentifier(const LibraryLoader& loader,
+std::unique_ptr<Type> Type::TypeFromIdentifier(LibraryLoader* loader,
                                                const rapidjson::Value& type,
                                                size_t inline_size) {
   if (!type.HasMember("identifier")) {
@@ -241,11 +232,10 @@
   std::string id = type["identifier"].GetString();
   size_t split_index = id.find('/');
   std::string library_name = id.substr(0, split_index);
-  const Library* library;
-  if (!loader.GetLibraryFromName(library_name, &library)) {
+  Library* library = loader->GetLibraryFromName(library_name);
+  if (library == nullptr) {
     FXL_LOG(ERROR) << "Unknown type for identifier: " << library_name;
-    // TODO: Something else here
-    return std::unique_ptr<Type>();
+    return std::make_unique<RawType>(inline_size);
   }
 
   bool is_nullable = false;
@@ -255,7 +245,7 @@
   return library->TypeFromIdentifier(is_nullable, id, inline_size);
 }
 
-std::unique_ptr<Type> Type::GetType(const LibraryLoader& loader,
+std::unique_ptr<Type> Type::GetType(LibraryLoader* loader,
                                     const rapidjson::Value& type,
                                     size_t inline_size) {
   // TODO: This is creating a new type every time we need one.  That's pretty
diff --git a/tools/fidlcat/lib/wire_types.h b/tools/fidlcat/lib/wire_types.h
index 0f6eafd..0c48460 100644
--- a/tools/fidlcat/lib/wire_types.h
+++ b/tools/fidlcat/lib/wire_types.h
@@ -31,8 +31,11 @@
   friend class Library;
 
  public:
-  Type() {}
-  virtual ~Type() {}
+  Type() = default;
+  virtual ~Type() = default;
+
+  // Return true if the type is a RawType.
+  virtual bool IsRaw() const { return false; }
 
   // Takes a pointer |bytes| and length of the data part |length|, and
   // returns whether that is equal to the Value represented by |value| according
@@ -53,7 +56,7 @@
   // field "kind" that states the type (e.g., "array", "vector", "foo.bar/Baz").
   // |loader| is the set of libraries to use to find types that need to be given
   // by identifier (e.g., "foo.bar/Baz").
-  static std::unique_ptr<Type> GetType(const LibraryLoader& loader,
+  static std::unique_ptr<Type> GetType(LibraryLoader* loader,
                                        const rapidjson::Value& type,
                                        size_t inline_size);
 
@@ -71,7 +74,7 @@
   // field "kind" that states the type.  "kind" is an identifier
   // (e.g.,"foo.bar/Baz").  |loader| is the set of libraries to use to lookup
   // that identifier.
-  static std::unique_ptr<Type> TypeFromIdentifier(const LibraryLoader& loader,
+  static std::unique_ptr<Type> TypeFromIdentifier(LibraryLoader* loader,
                                                   const rapidjson::Value& type,
                                                   size_t inline_size);
 
@@ -85,6 +88,8 @@
  public:
   RawType(size_t inline_size) : inline_size_(inline_size) {}
 
+  bool IsRaw() const override { return true; }
+
   virtual size_t InlineSize() const override { return inline_size_; }
 
   std::unique_ptr<Field> Decode(MessageDecoder* decoder, std::string_view name,
@@ -202,11 +207,7 @@
   explicit ElementSequenceType(std::unique_ptr<Type>&& component_type);
 
  protected:
-  explicit ElementSequenceType(std::shared_ptr<Type> component_type);
-
-  // The unique_ptr is converted to a shared_ptr so that it can be used by
-  // VectorField even when ElementSequenceType is destroyed.
-  std::shared_ptr<Type> component_type_;
+  std::unique_ptr<Type> component_type_;
 };
 
 class ArrayType : public ElementSequenceType {
@@ -234,9 +235,6 @@
 
   std::unique_ptr<Field> Decode(MessageDecoder* decoder, std::string_view name,
                                 uint64_t offset) const override;
-
- private:
-  VectorType(std::shared_ptr<Type> component_type, size_t element_size);
 };
 
 class EnumType : public Type {