| // Copyright 2018 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 ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_FLAT_AST_H_ |
| #define ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_FLAT_AST_H_ |
| |
| #include <errno.h> |
| #include <stdint.h> |
| |
| #include <limits> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <vector> |
| |
| #include "raw_ast.h" |
| #include "type_shape.h" |
| |
| namespace fidl { |
| namespace flat { |
| |
| template <typename T> |
| struct PtrCompare { |
| bool operator()(const T* left, const T* right) const { |
| return *left < *right; |
| } |
| }; |
| |
| struct Ordinal { |
| Ordinal(std::unique_ptr<raw::NumericLiteral> literal, uint32_t value) |
| : literal_(std::move(literal)), value_(value) {} |
| |
| uint32_t Value() const { return value_; } |
| |
| private: |
| std::unique_ptr<raw::NumericLiteral> literal_; |
| uint32_t value_; |
| }; |
| |
| template <typename IntType> |
| struct IntConstant { |
| IntConstant(std::unique_ptr<raw::Constant> raw_constant, IntType value) |
| : raw_constant_(std::move(raw_constant)), value_(value) {} |
| |
| explicit IntConstant(IntType value) |
| : value_(value) {} |
| |
| IntConstant() |
| : value_(0) {} |
| |
| IntType Value() const { return value_; } |
| |
| static IntConstant Max() { return IntConstant(std::numeric_limits<IntType>::max()); } |
| |
| private: |
| std::unique_ptr<raw::Constant> raw_constant_; |
| IntType value_; |
| }; |
| |
| using Size = IntConstant<uint32_t>; |
| |
| struct Decl; |
| class Library; |
| |
| // TODO(TO-701) Handle multipart names. |
| struct Name { |
| Name() |
| : name_(SourceLocation()) {} |
| |
| explicit Name(SourceLocation name) |
| : name_(name) {} |
| |
| Name(Name&&) = default; |
| Name& operator=(Name&&) = default; |
| |
| const Library* library() const { return library_; } |
| const std::vector<SourceLocation>& nested_decls() const { return nested_decls_; } |
| SourceLocation name() const { return name_; } |
| |
| bool operator==(const Name& other) const { |
| return name_.data() == other.name_.data(); |
| } |
| bool operator!=(const Name& other) const { |
| return name_.data() != other.name_.data(); |
| } |
| |
| bool operator<(const Name& other) const { |
| return name_.data() < other.name_.data(); |
| } |
| |
| private: |
| const Library* library_ = nullptr; |
| std::vector<SourceLocation> nested_decls_; |
| SourceLocation name_; |
| }; |
| |
| struct Decl { |
| virtual ~Decl() {} |
| |
| enum struct Kind { |
| kConst, |
| kEnum, |
| kInterface, |
| kStruct, |
| kUnion, |
| }; |
| |
| Decl(Kind kind, std::unique_ptr<raw::AttributeList> attributes, Name name) |
| : kind(kind), attributes(std::move(attributes)), name(std::move(name)) {} |
| |
| Decl(Decl&&) = default; |
| Decl& operator=(Decl&&) = default; |
| |
| const Kind kind; |
| std::unique_ptr<raw::AttributeList> attributes; |
| const Name name; |
| }; |
| |
| struct Type { |
| virtual ~Type() {} |
| |
| enum struct Kind { |
| Array, |
| Vector, |
| String, |
| Handle, |
| RequestHandle, |
| Primitive, |
| Identifier, |
| }; |
| |
| explicit Type(Kind kind, uint32_t size) |
| : kind(kind), size(size) {} |
| |
| const Kind kind; |
| // Set at construction time for most Types. Identifier types get |
| // this set later, during compilation. |
| uint32_t size; |
| |
| bool operator<(const Type& other) const; |
| }; |
| |
| struct ArrayType : public Type { |
| ArrayType(uint32_t size, std::unique_ptr<Type> element_type, Size element_count) |
| : Type(Kind::Array, size), |
| element_type(std::move(element_type)), |
| element_count(std::move(element_count)) {} |
| |
| std::unique_ptr<Type> element_type; |
| Size element_count; |
| |
| bool operator<(const ArrayType& other) const { |
| if (element_count.Value() != other.element_count.Value()) |
| return element_count.Value() < other.element_count.Value(); |
| return *element_type < *other.element_type; |
| } |
| }; |
| |
| struct VectorType : public Type { |
| VectorType(std::unique_ptr<Type> element_type, Size element_count, |
| types::Nullability nullability) |
| : Type(Kind::Vector, 16u), |
| element_type(std::move(element_type)), |
| element_count(std::move(element_count)), nullability(nullability) {} |
| |
| std::unique_ptr<Type> element_type; |
| Size element_count; |
| types::Nullability nullability; |
| |
| bool operator<(const VectorType& other) const { |
| if (element_count.Value() != other.element_count.Value()) |
| return element_count.Value() < other.element_count.Value(); |
| if (nullability != other.nullability) |
| return nullability < other.nullability; |
| return *element_type < *other.element_type; |
| } |
| }; |
| |
| struct StringType : public Type { |
| StringType(Size max_size, types::Nullability nullability) |
| : Type(Kind::String, 16u), max_size(std::move(max_size)), |
| nullability(nullability) {} |
| |
| Size max_size; |
| types::Nullability nullability; |
| |
| bool operator<(const StringType& other) const { |
| if (max_size.Value() != other.max_size.Value()) |
| return max_size.Value() < other.max_size.Value(); |
| return nullability < other.nullability; |
| } |
| }; |
| |
| struct HandleType : public Type { |
| HandleType(types::HandleSubtype subtype, types::Nullability nullability) |
| : Type(Kind::Handle, 4u), subtype(subtype), nullability(nullability) {} |
| |
| types::HandleSubtype subtype; |
| types::Nullability nullability; |
| |
| bool operator<(const HandleType& other) const { |
| if (subtype != other.subtype) |
| return subtype < other.subtype; |
| return nullability < other.nullability; |
| } |
| }; |
| |
| struct RequestHandleType : public Type { |
| RequestHandleType(Name name, types::Nullability nullability) |
| : Type(Kind::RequestHandle, 4u), name(std::move(name)), nullability(nullability) {} |
| |
| Name name; |
| types::Nullability nullability; |
| |
| bool operator<(const RequestHandleType& other) const { |
| if (name != other.name) |
| return name < other.name; |
| return nullability < other.nullability; |
| } |
| }; |
| |
| struct PrimitiveType : public Type { |
| static uint32_t SubtypeSize(types::PrimitiveSubtype subtype) { |
| switch (subtype) { |
| case types::PrimitiveSubtype::Bool: |
| case types::PrimitiveSubtype::Int8: |
| case types::PrimitiveSubtype::Uint8: |
| return 1u; |
| |
| case types::PrimitiveSubtype::Int16: |
| case types::PrimitiveSubtype::Uint16: |
| return 2u; |
| |
| case types::PrimitiveSubtype::Float32: |
| case types::PrimitiveSubtype::Status: |
| case types::PrimitiveSubtype::Int32: |
| case types::PrimitiveSubtype::Uint32: |
| return 4u; |
| |
| case types::PrimitiveSubtype::Float64: |
| case types::PrimitiveSubtype::Int64: |
| case types::PrimitiveSubtype::Uint64: |
| return 8u; |
| } |
| } |
| |
| explicit PrimitiveType(types::PrimitiveSubtype subtype) |
| : Type(Kind::Primitive, SubtypeSize(subtype)), subtype(subtype) {} |
| |
| types::PrimitiveSubtype subtype; |
| |
| bool operator<(const PrimitiveType& other) const { |
| return subtype < other.subtype; |
| } |
| }; |
| |
| struct IdentifierType : public Type { |
| IdentifierType(Name name, types::Nullability nullability) |
| : Type(Kind::Identifier, 0u), name(std::move(name)), nullability(nullability) {} |
| |
| Name name; |
| types::Nullability nullability; |
| |
| bool operator<(const IdentifierType& other) const { |
| if (name != other.name) |
| return name < other.name; |
| return nullability < other.nullability; |
| } |
| }; |
| |
| inline bool Type::operator<(const Type& other) const { |
| if (kind != other.kind) |
| return kind < other.kind; |
| switch (kind) { |
| case Type::Kind::Array: { |
| auto left_array = static_cast<const ArrayType*>(this); |
| auto right_array = static_cast<const ArrayType*>(&other); |
| return *left_array < *right_array; |
| } |
| case Type::Kind::Vector: { |
| auto left_vector = static_cast<const VectorType*>(this); |
| auto right_vector = static_cast<const VectorType*>(&other); |
| return *left_vector < *right_vector; |
| } |
| case Type::Kind::String: { |
| auto left_string = static_cast<const StringType*>(this); |
| auto right_string = static_cast<const StringType*>(&other); |
| return *left_string < *right_string; |
| } |
| case Type::Kind::Handle: { |
| auto left_handle = static_cast<const HandleType*>(this); |
| auto right_handle = static_cast<const HandleType*>(&other); |
| return *left_handle < *right_handle; |
| } |
| case Type::Kind::RequestHandle: { |
| auto left_request = static_cast<const RequestHandleType*>(this); |
| auto right_request = static_cast<const RequestHandleType*>(&other); |
| return *left_request < *right_request; |
| } |
| case Type::Kind::Primitive: { |
| auto left_primitive = static_cast<const PrimitiveType*>(this); |
| auto right_primitive = static_cast<const PrimitiveType*>(&other); |
| return *left_primitive < *right_primitive; |
| } |
| case Type::Kind::Identifier: { |
| auto left_identifier = static_cast<const IdentifierType*>(this); |
| auto right_identifier = static_cast<const IdentifierType*>(&other); |
| return *left_identifier < *right_identifier; |
| } |
| } |
| } |
| |
| struct Const : public Decl { |
| Const(std::unique_ptr<raw::AttributeList> attributes, Name name, std::unique_ptr<Type> type, std::unique_ptr<raw::Constant> value) |
| : Decl(Kind::kConst, std::move(attributes), std::move(name)), type(std::move(type)), value(std::move(value)) {} |
| std::unique_ptr<Type> type; |
| std::unique_ptr<raw::Constant> value; |
| }; |
| |
| struct Enum : public Decl { |
| struct Member { |
| Member(SourceLocation name, std::unique_ptr<raw::Constant> value) |
| : name(name), value(std::move(value)) {} |
| SourceLocation name; |
| std::unique_ptr<raw::Constant> value; |
| }; |
| |
| Enum(std::unique_ptr<raw::AttributeList> attributes, Name name, types::PrimitiveSubtype type, std::vector<Member> members) |
| : Decl(Kind::kEnum, std::move(attributes), std::move(name)), type(type), members(std::move(members)) {} |
| |
| types::PrimitiveSubtype type; |
| std::vector<Member> members; |
| TypeShape typeshape; |
| }; |
| |
| struct Interface : public Decl { |
| struct Method { |
| struct Parameter { |
| Parameter(std::unique_ptr<Type> type, SourceLocation name) |
| : type(std::move(type)), name(std::move(name)) {} |
| std::unique_ptr<Type> type; |
| SourceLocation name; |
| // TODO(TO-758) Compute these. |
| FieldShape fieldshape; |
| }; |
| |
| struct Message { |
| std::vector<Parameter> parameters; |
| TypeShape typeshape; |
| }; |
| |
| Method(Method&&) = default; |
| Method& operator=(Method&&) = default; |
| |
| Method(Ordinal ordinal, SourceLocation name, |
| std::unique_ptr<Message> maybe_request, |
| std::unique_ptr<Message> maybe_response) |
| : ordinal(std::move(ordinal)), name(std::move(name)), |
| maybe_request(std::move(maybe_request)), |
| maybe_response(std::move(maybe_response)) { |
| assert(this->maybe_request != nullptr || this->maybe_response != nullptr); |
| } |
| |
| Ordinal ordinal; |
| SourceLocation name; |
| std::unique_ptr<Message> maybe_request; |
| std::unique_ptr<Message> maybe_response; |
| }; |
| |
| Interface(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Method> methods) |
| : Decl(Kind::kInterface, std::move(attributes), std::move(name)), methods(std::move(methods)) {} |
| |
| std::vector<Method> methods; |
| }; |
| |
| struct Struct : public Decl { |
| struct Member { |
| Member(std::unique_ptr<Type> type, SourceLocation name, |
| std::unique_ptr<raw::Constant> maybe_default_value) |
| : type(std::move(type)), name(std::move(name)), |
| maybe_default_value(std::move(maybe_default_value)) {} |
| std::unique_ptr<Type> type; |
| SourceLocation name; |
| std::unique_ptr<raw::Constant> maybe_default_value; |
| // TODO(TO-758) Compute these. |
| FieldShape fieldshape; |
| }; |
| |
| Struct(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members) |
| : Decl(Kind::kStruct, std::move(attributes), std::move(name)), members(std::move(members)) {} |
| |
| std::vector<Member> members; |
| TypeShape typeshape; |
| }; |
| |
| struct Union : public Decl { |
| struct Member { |
| Member(std::unique_ptr<Type> type, SourceLocation name) |
| : type(std::move(type)), name(std::move(name)) {} |
| std::unique_ptr<Type> type; |
| SourceLocation name; |
| // TODO(TO-758) Compute these. |
| FieldShape fieldshape; |
| }; |
| |
| Union(std::unique_ptr<raw::AttributeList> attributes, Name name, std::vector<Member> members) |
| : Decl(Kind::kUnion, std::move(attributes), std::move(name)), members(std::move(members)) {} |
| |
| std::vector<Member> members; |
| // The offset of each of the union members is the same, so store |
| // it here as well. |
| FieldShape fieldshape; |
| }; |
| |
| class Library { |
| public: |
| bool ConsumeFile(std::unique_ptr<raw::File> file); |
| bool Resolve(); |
| |
| StringView name() const { return library_name_.data(); } |
| |
| private: |
| bool ParseSize(std::unique_ptr<raw::Constant> raw_constant, Size* out_size); |
| |
| bool RegisterDecl(Decl* decl); |
| |
| bool ConsumeType(std::unique_ptr<raw::Type> type, std::unique_ptr<Type>* out_type); |
| |
| bool ConsumeConstDeclaration(std::unique_ptr<raw::ConstDeclaration> const_declaration); |
| bool ConsumeEnumDeclaration(std::unique_ptr<raw::EnumDeclaration> enum_declaration); |
| bool ConsumeInterfaceDeclaration(std::unique_ptr<raw::InterfaceDeclaration> interface_declaration); |
| bool ConsumeStructDeclaration(std::unique_ptr<raw::StructDeclaration> struct_declaration); |
| bool ConsumeUnionDeclaration(std::unique_ptr<raw::UnionDeclaration> union_declaration); |
| |
| // Returns nullptr when |type| does not correspond directly to a |
| // declaration. For example, if |type| refers to int32 or if it is |
| // a struct pointer, this will return null. If it is a struct, it |
| // will return a pointer to the declaration of the type. |
| Decl* LookupType(const flat::Type* type); |
| |
| // Returns nullptr when the |name| cannot be resolved to a |
| // Name. Otherwise it returns the declaration. |
| Decl* LookupType(const Name& name); |
| |
| std::set<Decl*> DeclDependencies(Decl* decl); |
| |
| bool SortDeclarations(); |
| |
| bool ResolveConst(Const* const_declaration); |
| bool ResolveEnum(Enum* enum_declaration); |
| bool ResolveInterface(Interface* interface_declaration); |
| bool ResolveStruct(Struct* struct_declaration); |
| bool ResolveUnion(Union* union_declaration); |
| |
| bool ResolveArrayType(ArrayType* array_type, TypeShape* out_type_metadata); |
| bool ResolveVectorType(VectorType* vector_type, TypeShape* out_type_metadata); |
| bool ResolveStringType(StringType* string_type, TypeShape* out_type_metadata); |
| bool ResolveHandleType(HandleType* handle_type, TypeShape* out_type_metadata); |
| bool ResolveRequestHandleType(RequestHandleType* request_type, TypeShape* out_type_metadata); |
| bool ResolvePrimitiveType(PrimitiveType* primitive_type, TypeShape* out_type_metadata); |
| bool ResolveIdentifierType(IdentifierType* identifier_type, TypeShape* out_type_metadata); |
| bool ResolveType(Type* type, TypeShape* out_type_metadata); |
| |
| public: |
| // TODO(TO-702) Add a validate literal function. Some things |
| // (e.g. array indexes) want to check the value but print the |
| // constant, say. |
| template <typename IntType> |
| bool ParseIntegerLiteral(const raw::NumericLiteral* literal, IntType* out_value) { |
| if (!literal) { |
| return false; |
| } |
| auto data = literal->location.data(); |
| std::string string_data(data.data(), data.data() + data.size()); |
| if (std::is_unsigned<IntType>::value) { |
| errno = 0; |
| unsigned long long value = strtoull(string_data.data(), nullptr, 0); |
| if (errno != 0) |
| return false; |
| if (value > std::numeric_limits<IntType>::max()) |
| return false; |
| *out_value = static_cast<IntType>(value); |
| } else { |
| errno = 0; |
| long long value = strtoll(string_data.data(), nullptr, 0); |
| if (errno != 0) { |
| return false; |
| } |
| if (value > std::numeric_limits<IntType>::max()) { |
| return false; |
| } |
| if (value < std::numeric_limits<IntType>::min()) { |
| return false; |
| } |
| *out_value = static_cast<IntType>(value); |
| } |
| return true; |
| } |
| |
| template <typename IntType> |
| bool ParseIntegerConstant(const raw::Constant* constant, IntType* out_value) { |
| if (!constant) { |
| return false; |
| } |
| switch (constant->kind) { |
| case raw::Constant::Kind::Identifier: { |
| auto identifier_constant = static_cast<const raw::IdentifierConstant*>(constant); |
| auto identifier = identifier_constant->identifier.get(); |
| // TODO(TO-701) Support more parts of names. |
| Name name(identifier->components[0]->location); |
| auto decl = LookupType(name); |
| if (!decl || decl->kind != Decl::Kind::kConst) |
| return false; |
| return ParseIntegerConstant(static_cast<Const*>(decl)->value.get(), out_value); |
| } |
| case raw::Constant::Kind::Literal: { |
| auto literal_constant = static_cast<const raw::LiteralConstant*>(constant); |
| switch (literal_constant->literal->kind) { |
| case raw::Literal::Kind::String: |
| case raw::Literal::Kind::True: |
| case raw::Literal::Kind::False: |
| case raw::Literal::Kind::Default: { |
| return false; |
| } |
| |
| case raw::Literal::Kind::Numeric: { |
| auto numeric_literal = |
| static_cast<const raw::NumericLiteral*>(literal_constant->literal.get()); |
| return ParseIntegerLiteral<IntType>(numeric_literal, out_value); |
| } |
| } |
| } |
| } |
| } |
| |
| SourceLocation library_name_; |
| |
| std::vector<std::unique_ptr<Const>> const_declarations_; |
| std::vector<std::unique_ptr<Enum>> enum_declarations_; |
| std::vector<std::unique_ptr<Interface>> interface_declarations_; |
| std::vector<std::unique_ptr<Struct>> struct_declarations_; |
| std::vector<std::unique_ptr<Union>> union_declarations_; |
| |
| // All Decl pointers here are non-null and are owned by the |
| // various foo_declarations_. |
| std::vector<Decl*> declaration_order_; |
| |
| private: |
| // All Name and Decl pointers here are non-null and are owned by the |
| // various foo_declarations_. |
| std::map<const Name*, Decl*, PtrCompare<Name>> declarations_; |
| }; |
| |
| } // namespace flat |
| } // namespace fidl |
| |
| #endif // ZIRCON_SYSTEM_HOST_FIDL_INCLUDE_FIDL_FLAT_AST_H_ |