blob: bcfaed59b1e1dd28bc3ca5c6e21e8546d56bae58 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_FIDLCAT_LIB_LIBRARY_LOADER_H_
#define TOOLS_FIDLCAT_LIB_LIBRARY_LOADER_H_
#include <iostream>
#include <map>
#include <optional>
#include <vector>
#include "rapidjson/document.h"
// This file contains a programmatic representation of a FIDL schema. A
// LibraryLoader loads a set of Libraries. The libraries contain structs,
// enums, interfaces, and so on. Each element has the logic necessary to take
// wire-encoded bits of that type, and transform it to a representation of that
// type.
// A LibraryLoader object can be used to fetch a particular library or interface
// method, which can then be used for debug purposes.
// An example of building a LibraryLoader can be found in
// library_loader_test.cc:LoadSimple. Callers can then do something like the
// following, if they have a fidl::Message:
//
// fidl_message_header_t header = message.header();
// const InterfaceMethod* method;
// loader_->GetByOrdinal(header.ordinal, &method);
// rapidjson::Document actual;
// fidlcat::RequestToJSON(method, message, actual);
//
// |actual| will then contain the contents of the message in JSON
// (human-readable) format.
//
// These libraries are currently thread-unsafe.
namespace fidlcat {
typedef uint32_t Ordinal;
struct LibraryReadError {
enum ErrorValue {
kOk,
kIoError,
kParseError,
};
ErrorValue value;
rapidjson::ParseResult parse_result;
};
class Interface;
class Library;
class LibraryLoader;
class MessageDecoder;
class Object;
class Struct;
class Table;
class Type;
class Union;
class UnionField;
class XUnion;
class Enum {
public:
Enum(const Library& enclosing_library, const rapidjson::Value& value)
: enclosing_library_(enclosing_library), value_(value) {
(void)value_;
(void)enclosing_library_;
}
const std::unique_ptr<Type> GetType() const;
// 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
// definition:
// enum i16_enum : int16 {
// x = -23;
// };
// and you pass |bytes| a 2-byte representation of -23, and |length| 2, this
// function will return "x". Returns "(Unknown enum member)" if it can't find
// the member.
std::string GetNameFromBytes(const uint8_t* bytes) const;
uint32_t size() const;
private:
const Library& enclosing_library_;
const rapidjson::Value& value_;
};
// 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) {}
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;
private:
const Union& enclosing_union_;
const rapidjson::Value& value_;
const std::string name_;
const uint64_t offset_;
const uint64_t size_;
const Ordinal ordinal_;
};
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_) {}
const Library& enclosing_library() const { return enclosing_library_; }
const std::vector<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:
const Library& enclosing_library_;
const rapidjson::Value& value_;
private:
std::vector<UnionMember> members_;
mutable std::unique_ptr<UnionMember> illegal_;
};
class XUnion : public Union {
friend class Library;
public:
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) {}
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_; }
private:
const Struct& enclosing_struct_;
const rapidjson::Value& value_;
};
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);
}
}
const Library& enclosing_library() const { return enclosing_library_; }
uint32_t size() const { return size_; }
const std::vector<StructMember>& members() const { return members_; }
std::unique_ptr<Object> DecodeObject(MessageDecoder* decoder,
std::string_view name, uint64_t offset,
bool nullable) const;
private:
const Library& enclosing_library_;
const rapidjson::Value& value_;
const uint32_t size_;
std::vector<StructMember> members_;
};
class TableMember {
public:
TableMember(const Table& enclosing_table, const rapidjson::Value& value)
: enclosing_table_(enclosing_table), value_(value) {}
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_; }
private:
const Table& enclosing_table_;
const rapidjson::Value& value_;
};
class Table {
public:
Table(const Library& enclosing_library, const rapidjson::Value& value);
Table(const Table&) = delete;
Table(Table&& other) : Table(other.enclosing_library_, other.value_) {}
const Library& enclosing_library() const { return enclosing_library_; }
// 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
// nullptr. Also, note that ordinal 0 is disallowed, so element 0 is always
// nullptr.
const std::vector<const TableMember*>& members() const { return members_; }
uint32_t size() const {
return std::strtoll(value_["size"].GetString(), nullptr, 10);
}
private:
const Library& enclosing_library_;
const rapidjson::Value& value_;
// 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_;
};
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(); }
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:
const Interface& enclosing_interface_;
Ordinal ordinal_;
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;
Interface(const Interface& other) = delete;
Interface& operator=(const Interface&) = delete;
void AddMethodsToIndex(std::map<Ordinal, const InterfaceMethod*>& index) {
for (size_t i = 0; i < interface_methods_.size(); i++) {
const InterfaceMethod* method = &interface_methods_[i];
index[method->ordinal()] = method;
}
}
// Sets *|method| to the fully qualified |name|'s InterfaceMethod
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 {
return interface_methods_;
}
private:
const rapidjson::Value& value_;
const Library& enclosing_library_;
std::string name_;
std::vector<InterfaceMethod> interface_methods_;
};
class Library {
public:
Library(const LibraryLoader& enclosing, rapidjson::Document& document);
// 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);
}
}
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;
// 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;
Library& operator=(const Library&) = delete;
Library(const Library&) = delete;
private:
const LibraryLoader& enclosing_loader_;
rapidjson::Document backing_document_;
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_;
};
// An indexed collection of libraries.
class LibraryLoader {
public:
LibraryLoader(std::vector<std::unique_ptr<std::istream>>& library_streams,
LibraryReadError* err);
LibraryLoader& operator=(const LibraryLoader&) = delete;
LibraryLoader(const LibraryLoader&) = delete;
// Returns true and sets **method if the ordinal was present in the map, and
// false otherwise.
bool GetByOrdinal(Ordinal ordinal, const InterfaceMethod** method) {
auto m = ordinal_map_.find(ordinal);
if (m != ordinal_map_.end()) {
*method = m->second;
return true;
}
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.
// |name| is of the format "a.b.c"
bool GetLibraryFromName(const std::string& name,
const Library** library) const {
auto l = representations_.find(name);
if (l != representations_.end()) {
const Library* lib = &(l->second);
*library = lib;
return true;
}
return false;
}
private:
void Add(std::string& ir, LibraryReadError* err) {
rapidjson::Document document;
err->parse_result =
document.Parse<rapidjson::kParseNumbersAsStringsFlag>(ir.c_str());
// TODO: This would be a good place to validate that the resulting JSON
// matches the schema in zircon/system/host/fidl/schema.json. If there are
// errors, we will currently get mysterious crashes.
if (document.HasParseError()) {
err->value = LibraryReadError::kParseError;
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_);
}
std::map<std::string, Library> representations_;
std::map<Ordinal, const InterfaceMethod*> ordinal_map_;
};
} // namespace fidlcat
#endif // TOOLS_FIDLCAT_LIB_LIBRARY_LOADER_H_