blob: 75b73c21e9bb70e98b941cf71c733903dc208428 [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 SRC_LIB_FIDL_CODEC_LIBRARY_LOADER_H_
#define SRC_LIB_FIDL_CODEC_LIBRARY_LOADER_H_
#include <zircon/assert.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <rapidjson/document.h>
#include "src/lib/fidl_codec/semantic.h"
// This file contains a programmatic representation of a FIDL schema. A
// LibraryLoader loads a set of Libraries. The libraries contain structs,
// enums, protocols, 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 protocol
// 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 std::vector<ProtocolMethod*>* methods = loader_->GetByOrdinal(header.ordinal);
// rapidjson::Document actual;
// fidl_codec::RequestToJSON(methods->at(0), message, actual);
//
// |actual| will then contain the contents of the message in JSON
// (human-readable) format.
//
// These libraries are currently thread-unsafe.
namespace fidl_codec {
constexpr int kDecimalBase = 10;
using Ordinal32 = uint32_t;
using Ordinal64 = uint64_t;
enum class WireVersion : uint8_t { kWireV2 };
struct LibraryReadError {
enum ErrorValue : uint8_t {
kOk,
kIoError,
kParseError,
};
ErrorValue value;
// Unset unless `value` == kIoError.
int errno_value;
rapidjson::ParseResult parse_result;
};
class Protocol;
class ProtocolMethod;
class Payload;
class Library;
class LibraryLoader;
class MessageDecoder;
class Struct;
class StructValue;
class Table;
class TypeVisitor;
class Union;
class Int32Type;
class EnumOrBitsMember {
friend class Enum;
friend class Bits;
public:
EnumOrBitsMember(const std::string_view& name, uint64_t absolute_value, bool negative)
: name_(name), absolute_value_(absolute_value), negative_(negative) {}
const std::string& name() const { return name_; }
uint64_t absolute_value() const { return absolute_value_; }
bool negative() const { return negative_; }
private:
const std::string name_;
const uint64_t absolute_value_;
const bool negative_;
};
class EnumOrBits {
public:
friend class Library;
~EnumOrBits();
const std::string& name() const { return name_; }
uint64_t size() const { return size_; }
const Type* type() const { return type_.get(); }
// Get a list of Enum members.
const std::vector<EnumOrBitsMember>& members() const { return members_; }
uint64_t Size(WireVersion version) const { return size_; }
protected:
EnumOrBits(std::string name, uint64_t size_v2, std::unique_ptr<Type> type,
std::vector<EnumOrBitsMember> members);
explicit EnumOrBits(const rapidjson::Value* json_definition);
// Decode all the values from the JSON definition.
void DecodeTypes(bool is_scalar, const std::string& supertype_name, Library* enclosing_library);
private:
const rapidjson::Value* json_definition_;
std::string name_;
uint64_t size_;
std::unique_ptr<Type> type_;
std::vector<EnumOrBitsMember> members_;
};
class Enum : public EnumOrBits {
public:
friend class Library;
// 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 GetName(uint64_t absolute_value, bool negative) const;
static const Enum& FrameworkErrorEnum();
private:
Enum(std::string name, uint64_t size, std::unique_ptr<Type> type,
std::vector<EnumOrBitsMember> members);
explicit Enum(const rapidjson::Value* json_definition) : EnumOrBits(json_definition) {}
void DecodeTypes(Library* enclosing_library) {
return EnumOrBits::DecodeTypes(true, "enum", enclosing_library);
}
};
class Bits : public EnumOrBits {
public:
friend class Library;
std::string GetName(uint64_t absolute_value, bool negative) const;
private:
explicit Bits(const rapidjson::Value* json_definition) : EnumOrBits(json_definition) {}
void DecodeTypes(Library* enclosing_library) {
return EnumOrBits::DecodeTypes(false, "bits", enclosing_library);
}
};
class UnionMember {
public:
UnionMember(const Union& union_definition, Library* enclosing_library,
const rapidjson::Value* json_definition);
const Union& union_definition() const { return union_definition_; }
const std::string& name() const { return name_; }
Ordinal64 ordinal() const { return ordinal_; }
const Type* type() const { return type_.get(); }
private:
const Union& union_definition_;
const std::string name_;
const Ordinal64 ordinal_;
std::unique_ptr<Type> type_;
};
class Union final {
public:
friend class Library;
Library* enclosing_library() const { return enclosing_library_; }
const std::string& name() const { return name_; }
const std::vector<std::unique_ptr<UnionMember>>& members() const { return members_; }
const UnionMember* MemberFromOrdinal(Ordinal64 ordinal) const;
UnionMember* SearchMember(std::string_view name) const;
std::string ToString(bool expand) const;
private:
Union(Library* enclosing_library, const rapidjson::Value* json_definition);
// Decode all the values from the JSON definition.
void DecodeTypes();
Library* enclosing_library_;
const rapidjson::Value* json_definition_;
std::string name_;
std::vector<std::unique_ptr<UnionMember>> members_;
};
class StructMember {
public:
StructMember(Library* enclosing_library, const rapidjson::Value* json_definition);
StructMember(std::string_view name, std::unique_ptr<Type> type);
StructMember(std::string_view name, std::unique_ptr<Type> type, uint8_t id);
const std::string& name() const { return name_; }
Type* type() const { return type_.get(); }
void reset_type();
uint8_t id() const { return id_; }
uint64_t Offset(WireVersion version) const { return offset_; }
private:
const std::string name_;
uint64_t offset_ = 0;
std::unique_ptr<Type> type_;
uint8_t id_ = 0;
};
class Struct final {
public:
friend class Library;
explicit Struct(std::string_view name);
Library* enclosing_library() const { return enclosing_library_; }
const std::string& name() const { return name_; }
const std::vector<std::unique_ptr<StructMember>>& members() const { return members_; }
void AddMember(std::string_view name, std::unique_ptr<Type> type, uint32_t id = 0);
StructMember* SearchMember(std::string_view name, uint32_t id = 0) const;
uint32_t Size(WireVersion version) const;
std::string ToString(bool expand) const;
private:
Struct(Library* enclosing_library, const rapidjson::Value* json_definition);
// Decode all the values from the JSON definition.
void DecodeTypes();
Library* enclosing_library_;
const rapidjson::Value* json_definition_;
std::string name_;
uint32_t size_ = 0;
std::vector<std::unique_ptr<StructMember>> members_;
};
class TableMember {
public:
TableMember(Library* enclosing_library, const rapidjson::Value* json_definition);
const std::string& name() const { return name_; }
Ordinal32 ordinal() const { return ordinal_; }
const Type* type() const { return type_.get(); }
private:
const std::string name_;
const Ordinal32 ordinal_;
std::unique_ptr<Type> type_;
};
class Table final {
public:
friend class Library;
Library* enclosing_library() const { return enclosing_library_; }
const std::string& name() const { return name_; }
const std::vector<std::unique_ptr<TableMember>>& members() const { return members_; }
const TableMember* MemberFromOrdinal(Ordinal64 ordinal) const;
const TableMember* SearchMember(std::string_view name) const;
std::string ToString(bool expand) const;
private:
Table(Library* enclosing_library, const rapidjson::Value* json_definition);
// Decode all the values from the JSON definition.
void DecodeTypes();
Library* enclosing_library_;
const rapidjson::Value* json_definition_;
std::string name_;
std::vector<std::unique_ptr<TableMember>> members_;
};
class ProtocolMethod {
public:
friend class Protocol;
ProtocolMethod() = default;
~ProtocolMethod();
const Protocol& enclosing_protocol() const { return *enclosing_protocol_; }
const std::string& name() const { return name_; }
Ordinal64 ordinal() const { return ordinal_; }
bool is_composed() const { return is_composed_; }
bool has_request() const { return has_request_; }
bool has_response() const { return has_response_; }
const Type* request() const { return request_.get(); }
const Type* response() const { return response_.get(); }
semantic::MethodSemantic* semantic() { return semantic_.get(); }
const semantic::MethodSemantic* semantic() const { return semantic_.get(); }
void set_semantic(std::unique_ptr<semantic::MethodSemantic> semantic) {
semantic_ = std::move(semantic);
}
semantic::MethodDisplay* short_display() { return short_display_.get(); }
const semantic::MethodDisplay* short_display() const { return short_display_.get(); }
void set_short_display(std::unique_ptr<semantic::MethodDisplay> short_display) {
short_display_ = std::move(short_display);
}
std::string fully_qualified_name() const;
void DecodeTypes();
ProtocolMethod(const ProtocolMethod& other) = delete;
ProtocolMethod& operator=(const ProtocolMethod&) = delete;
private:
ProtocolMethod(Library* enclosing_library, const Protocol& protocol,
const rapidjson::Value* json_definition);
const rapidjson::Value* json_definition_ = nullptr;
const Protocol* const enclosing_protocol_ = nullptr;
const std::string name_;
const Ordinal64 ordinal_ = 0;
const bool is_composed_ = false;
bool has_request_ = false;
std::unique_ptr<Type> request_ = nullptr;
bool has_response_ = false;
std::unique_ptr<Type> response_ = nullptr;
std::unique_ptr<semantic::MethodSemantic> semantic_;
std::unique_ptr<semantic::MethodDisplay> short_display_;
};
class Protocol {
public:
friend class Library;
Protocol(const Protocol& other) = delete;
Protocol& operator=(const Protocol&) = delete;
Library* enclosing_library() const { return enclosing_library_; }
const std::string& name() const { return name_; }
void AddMethodsToIndex(LibraryLoader* library_loader);
// Sets *|method| to the fully qualified |name|'s ProtocolMethod (protocol.method).
bool GetMethodByFullName(const std::string& name, const ProtocolMethod** method) const;
ProtocolMethod* GetMethodByName(std::string_view name) const;
const std::vector<std::unique_ptr<ProtocolMethod>>& methods() const { return protocol_methods_; }
private:
Protocol(Library* enclosing_library, const rapidjson::Value& json_definition)
: enclosing_library_(enclosing_library), name_(json_definition["name"].GetString()) {
for (auto& method : json_definition["methods"].GetArray()) {
protocol_methods_.emplace_back(new ProtocolMethod(enclosing_library, *this, &method));
}
}
Library* enclosing_library_;
std::string name_;
std::vector<std::unique_ptr<ProtocolMethod>> protocol_methods_;
};
class Library {
public:
friend class LibraryLoader;
LibraryLoader* enclosing_loader() const { return enclosing_loader_; }
const std::string& name() const { return name_; }
// The source file path from which this library has been loaded. Will be returned as it was
// entered when `AddPath` or `AddContent` was called, so if it is a relative path or absolute path
// it will remain as such.
const std::string& source() const { return source_; }
const std::vector<std::unique_ptr<Protocol>>& protocols() const { return protocols_; }
// Decode all the values from the JSON definition.
void DecodeTypes();
// Decode all the content of this FIDL file.
bool DecodeAll();
std::unique_ptr<Type> TypeFromIdentifier(bool is_nullable, const std::string& identifier);
// 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;
// Set *ptr to the Protocol called |name|
bool GetProtocolByName(std::string_view name, Protocol** ptr) const;
// Extract a boolean field from a JSON value.
bool ExtractBool(const rapidjson::Value* json_definition, std::string_view container_type,
std::string_view container_name, const char* field_name);
// Extract a string field from a JSON value.
std::string ExtractString(const rapidjson::Value* json_definition,
std::string_view container_type, std::string_view container_name,
const char* field_name);
// Extract a uint64_t field from a JSON value.
uint64_t ExtractUint64(const rapidjson::Value* json_definition, std::string_view container_type,
std::string_view container_name, const char* field_name);
// Extract a uint32_t field from a JSON value.
uint32_t ExtractUint32(const rapidjson::Value* json_definition, std::string_view container_type,
std::string_view container_name, const char* field_name);
// Extract a scalar type from a JSON value.
std::unique_ptr<Type> ExtractScalarType(const rapidjson::Value* json_definition,
std::string_view container_type,
std::string_view container_name, const char* field_name);
// Extract a type from a JSON value.
std::unique_ptr<Type> ExtractType(const rapidjson::Value* json_definition,
std::string_view container_type,
std::string_view container_name, const char* field_name);
// Extract field offset.
uint64_t ExtractFieldOffset(const rapidjson::Value* json_definition,
std::string_view container_type, std::string_view container_name,
const char* field_name);
// Display an error when a field is not found.
void FieldNotFound(std::string_view container_type, std::string_view container_name,
const char* field_name);
const Table* GetTable(const std::string& table_name) const {
auto result = tables_.find(table_name);
if (result == tables_.end()) {
return nullptr;
}
return result->second.get();
}
Library& operator=(const Library&) = delete;
Library(const Library&) = delete;
~Library();
private:
Library(std::string source, LibraryLoader* enclosing_loader,
rapidjson::Document& json_definition);
LibraryLoader* enclosing_loader_;
rapidjson::Document json_definition_;
bool decoded_ = false;
bool has_errors_ = false;
std::string name_;
std::string source_;
std::vector<std::unique_ptr<Protocol>> protocols_;
std::map<std::string, std::unique_ptr<Enum>> enums_;
std::map<std::string, std::unique_ptr<Bits>> bits_;
std::map<std::string, std::unique_ptr<Union>> unions_;
std::map<std::string, std::unique_ptr<Struct>> structs_;
std::map<std::string, std::unique_ptr<Table>> tables_;
};
// 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:
friend class Library;
// Creates a LibraryLoader populated by the given library paths.
LibraryLoader(const std::vector<std::string>& library_paths, LibraryReadError* err);
// Creates a LibraryLoader with no libraries
LibraryLoader() = default;
LibraryLoader& operator=(const LibraryLoader&) = delete;
LibraryLoader(const LibraryLoader&) = delete;
// Add the libraries for all the paths.
bool AddAll(const std::vector<std::string>& library_paths, LibraryReadError* err);
// Decode all the FIDL files.
bool DecodeAll();
// Adds a single library to this Loader given its path. Sets err as appropriate.
void AddPath(const std::string& path, LibraryReadError* err);
// Adds a single library to this Loader given its content (the JSON text).
// Sets err as appropriate. The optional `source` field denotes the location from which the
// file was loaded, and is expected to be formatted as a file system path.
void AddContent(const std::string& content, LibraryReadError* err, std::string source = "");
// Adds a method ordinal to the ordinal map.
void AddMethod(ProtocolMethod* method);
void ParseBuiltinSemantic();
// Returns a pointer to a set of methods that have this ordinal. There may be
// more than one if the method was composed into multiple protocols. For
// convenience, the methods that are not composed are at the front of the
// vector. Returns |nullptr| if there is no such method. The returned
// pointer continues to be owned by the LibraryLoader, and should not be
// deleted.
const std::vector<ProtocolMethod*>* GetByOrdinal(Ordinal64 ordinal) {
auto m = ordinal_map_.find(ordinal);
if (m != ordinal_map_.end()) {
auto methods_for_ordinal = m->second.get();
for (auto& method : *methods_for_ordinal) {
method->DecodeTypes();
}
return methods_for_ordinal;
}
return nullptr;
}
// 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"
Library* GetLibraryFromName(const std::string& name) {
auto l = representations_.find(name);
if (l != representations_.end()) {
Library* library = l->second.get();
library->DecodeTypes();
return library;
}
return nullptr;
}
private:
void Delete(const Library* library) {
// The only way to delete a library is to remove it from representations_, so we don't need to
// do that explicitly. However...
for (const auto& iface : library->protocols()) {
for (const auto& method : iface->methods()) {
ordinal_map_.erase(method->ordinal());
}
}
}
// Because Delete() above is run whenever a Library is destructed, we want ordinal_map_ to be
// intact when a Library is destructed. Therefore, ordinal_map_ has to come first.
std::map<Ordinal64, std::unique_ptr<std::vector<ProtocolMethod*>>> ordinal_map_;
std::map<std::string, std::unique_ptr<Library>> representations_;
};
} // namespace fidl_codec
#endif // SRC_LIB_FIDL_CODEC_LIBRARY_LOADER_H_