| // 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. |
| |
| #include "fidl/flat_ast.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #include <algorithm> |
| #include <sstream> |
| |
| #include "fidl/ast.h" |
| #include "fidl/lexer.h" |
| #include "fidl/parser.h" |
| |
| namespace fidl { |
| namespace flat { |
| |
| namespace { |
| |
| template <typename T> |
| class Scope { |
| public: |
| bool Insert(const T& t) { |
| auto iter = scope_.insert(t); |
| return iter.second; |
| } |
| |
| private: |
| std::set<T> scope_; |
| }; |
| |
| constexpr TypeShape kHandleTypeShape = TypeShape(4u, 4u); |
| constexpr TypeShape kInt8TypeShape = TypeShape(1u, 1u); |
| constexpr TypeShape kInt16TypeShape = TypeShape(2u, 2u); |
| constexpr TypeShape kInt32TypeShape = TypeShape(4u, 4u); |
| constexpr TypeShape kInt64TypeShape = TypeShape(8u, 8u); |
| constexpr TypeShape kUint8TypeShape = TypeShape(1u, 1u); |
| constexpr TypeShape kUint16TypeShape = TypeShape(2u, 2u); |
| constexpr TypeShape kUint32TypeShape = TypeShape(4u, 4u); |
| constexpr TypeShape kUint64TypeShape = TypeShape(8u, 8u); |
| constexpr TypeShape kBoolTypeShape = TypeShape(1u, 1u); |
| constexpr TypeShape kStatusTypeShape = TypeShape(4u, 4u); |
| constexpr TypeShape kFloat32TypeShape = TypeShape(4u, 4u); |
| constexpr TypeShape kFloat64TypeShape = TypeShape(8u, 8u); |
| constexpr TypeShape kPointerTypeShape = TypeShape(8u, 8u); |
| |
| size_t AlignTo(size_t size, size_t alignment) { |
| auto mask = alignment - 1; |
| size += mask; |
| size &= ~mask; |
| return size; |
| } |
| |
| TypeShape CStructTypeShape(std::vector<TypeShape> member_typeshapes) { |
| size_t size = 0u; |
| size_t alignment = 1u; |
| |
| for (const auto& type_shape : member_typeshapes) { |
| alignment = std::max(alignment, type_shape.Alignment()); |
| size = AlignTo(size, type_shape.Alignment()); |
| size += type_shape.Size(); |
| } |
| |
| return TypeShape(size, alignment); |
| } |
| |
| TypeShape FidlStructTypeShape(std::vector<TypeShape> member_typeshapes) { |
| // TODO(kulakowski) Fit-sort members. |
| return CStructTypeShape(std::move(member_typeshapes)); |
| } |
| |
| TypeShape CUnionTypeShape(std::vector<TypeShape> member_typeshapes) { |
| size_t size = 0u; |
| size_t alignment = 1u; |
| for (const auto& type_shape : member_typeshapes) { |
| size = std::max(size, type_shape.Size()); |
| alignment = std::max(alignment, type_shape.Alignment()); |
| } |
| size = AlignTo(size, alignment); |
| return TypeShape(size, alignment); |
| } |
| |
| TypeShape FidlUnionTypeShape(std::vector<TypeShape> member_typeshapes) { |
| std::vector<TypeShape> fidl_union; |
| fidl_union.push_back(kUint32TypeShape); |
| fidl_union.push_back(CUnionTypeShape(std::move(member_typeshapes))); |
| return CStructTypeShape(std::move(fidl_union)); |
| } |
| |
| TypeShape ArrayTypeShape(TypeShape element, uint64_t count) { |
| return TypeShape(element.Size() * count, element.Alignment()); |
| } |
| |
| TypeShape VectorTypeShape(TypeShape element, uint64_t count) { |
| auto header_shape = |
| CStructTypeShape(std::vector<TypeShape>({kUint64TypeShape, kPointerTypeShape})); |
| return header_shape; |
| } |
| |
| TypeShape StringTypeShape(uint64_t count) { |
| auto header_shape = |
| CStructTypeShape(std::vector<TypeShape>({kUint64TypeShape, kPointerTypeShape})); |
| return header_shape; |
| } |
| |
| } // namespace |
| |
| // Consuming the AST is primarily concerned with walking the tree and |
| // flattening the representation. The AST's declaration nodes are |
| // converted into the Library's foo_declaration structures. This means pulling |
| // a struct declaration inside an interface out to the top level and |
| // so on. |
| |
| bool Library::ConsumeConstDeclaration(std::unique_ptr<ast::ConstDeclaration> const_declaration) { |
| auto name = Name(std::move(const_declaration->identifier)); |
| |
| if (!RegisterTypeName(name)) |
| return false; |
| const_declarations_.emplace_back(std::move(name), std::move(const_declaration->type), |
| std::move(const_declaration->constant)); |
| return true; |
| } |
| |
| bool Library::ConsumeEnumDeclaration(std::unique_ptr<ast::EnumDeclaration> enum_declaration) { |
| std::vector<Enum::Member> members; |
| for (auto& member : enum_declaration->members) { |
| auto name = Name(std::move(member->identifier)); |
| auto value = std::move(member->value); |
| members.emplace_back(std::move(name), std::move(value)); |
| } |
| std::unique_ptr<ast::PrimitiveType> type = std::move(enum_declaration->maybe_subtype); |
| if (!type) |
| type = std::make_unique<ast::PrimitiveType>(types::PrimitiveSubtype::Uint32); |
| auto name = Name(std::move(enum_declaration->identifier)); |
| |
| if (!RegisterTypeName(name)) |
| return false; |
| enum_declarations_.emplace_back(std::move(name), std::move(type), std::move(members)); |
| return true; |
| } |
| |
| bool Library::ConsumeInterfaceDeclaration( |
| std::unique_ptr<ast::InterfaceDeclaration> interface_declaration) { |
| auto name = Name(std::move(interface_declaration->identifier)); |
| |
| for (auto& const_member : interface_declaration->const_members) |
| if (!ConsumeConstDeclaration(std::move(const_member))) |
| return false; |
| for (auto& enum_member : interface_declaration->enum_members) |
| if (!ConsumeEnumDeclaration(std::move(enum_member))) |
| return false; |
| |
| std::vector<Interface::Method> methods; |
| for (auto& method : interface_declaration->method_members) { |
| auto ordinal_literal = std::move(method->ordinal); |
| uint32_t value; |
| if (!ParseIntegerLiteral<decltype(value)>(ordinal_literal.get(), &value)) |
| return false; |
| if (value == 0u) |
| return false; |
| Ordinal ordinal(std::move(ordinal_literal), value); |
| |
| auto method_name = std::move(method->identifier); |
| |
| bool has_request = static_cast<bool>(method->maybe_request); |
| std::vector<Interface::Method::Parameter> maybe_request; |
| if (has_request) { |
| for (auto& parameter : method->maybe_request->parameter_list) { |
| auto parameter_name = std::move(parameter->identifier); |
| maybe_request.emplace_back(std::move(parameter->type), std::move(parameter_name)); |
| } |
| } |
| |
| bool has_response = static_cast<bool>(method->maybe_response); |
| std::vector<Interface::Method::Parameter> maybe_response; |
| if (has_response) { |
| for (auto& parameter : method->maybe_response->parameter_list) { |
| auto response_paramater_name = std::move(parameter->identifier); |
| maybe_response.emplace_back(std::move(parameter->type), |
| std::move(response_paramater_name)); |
| } |
| } |
| |
| assert(has_request || has_response); |
| |
| methods.emplace_back(std::move(ordinal), std::move(method_name), |
| has_request, std::move(maybe_request), |
| has_response, std::move(maybe_response)); |
| } |
| |
| if (!RegisterTypeName(name)) |
| return false; |
| interface_declarations_.emplace_back(std::move(name), std::move(methods)); |
| return true; |
| } |
| |
| bool Library::ConsumeStructDeclaration(std::unique_ptr<ast::StructDeclaration> struct_declaration) { |
| auto name = Name(std::move(struct_declaration->identifier)); |
| |
| for (auto& const_member : struct_declaration->const_members) |
| if (!ConsumeConstDeclaration(std::move(const_member))) |
| return false; |
| for (auto& enum_member : struct_declaration->enum_members) |
| if (!ConsumeEnumDeclaration(std::move(enum_member))) |
| return false; |
| |
| std::vector<Struct::Member> members; |
| for (auto& member : struct_declaration->members) { |
| auto name = std::move(member->identifier); |
| members.emplace_back(std::move(member->type), std::move(name), |
| std::move(member->maybe_default_value)); |
| } |
| |
| if (!RegisterTypeName(name)) |
| return false; |
| struct_declarations_.emplace_back(std::move(name), std::move(members)); |
| return true; |
| } |
| |
| bool Library::ConsumeUnionDeclaration(std::unique_ptr<ast::UnionDeclaration> union_declaration) { |
| std::vector<Union::Member> members; |
| for (auto& member : union_declaration->members) { |
| auto name = std::move(member->identifier); |
| members.emplace_back(std::move(member->type), std::move(name)); |
| } |
| auto name = Name(std::move(union_declaration->identifier)); |
| |
| if (!RegisterTypeName(name)) |
| return false; |
| union_declarations_.emplace_back(std::move(name), std::move(members)); |
| return true; |
| } |
| |
| bool Library::ConsumeFile(std::unique_ptr<ast::File> file) { |
| // All fidl files in a library should agree on the library name. |
| if (file->identifier->components.size() != 1) { |
| return false; |
| } |
| auto library_name = std::move(file->identifier->components[0]); |
| |
| if (library_name_ == nullptr) { |
| library_name_ = std::move(library_name); |
| } else { |
| StringView current_name = library_name_->location.data(); |
| StringView new_name = library_name->location.data(); |
| if (current_name != new_name) { |
| return false; |
| } |
| } |
| |
| auto using_list = std::move(file->using_list); |
| |
| auto const_declaration_list = std::move(file->const_declaration_list); |
| for (auto& const_declaration : const_declaration_list) { |
| if (!ConsumeConstDeclaration(std::move(const_declaration))) { |
| return false; |
| } |
| } |
| |
| auto enum_declaration_list = std::move(file->enum_declaration_list); |
| for (auto& enum_declaration : enum_declaration_list) { |
| if (!ConsumeEnumDeclaration(std::move(enum_declaration))) { |
| return false; |
| } |
| } |
| |
| auto interface_declaration_list = std::move(file->interface_declaration_list); |
| for (auto& interface_declaration : interface_declaration_list) { |
| if (!ConsumeInterfaceDeclaration(std::move(interface_declaration))) { |
| return false; |
| } |
| } |
| |
| auto struct_declaration_list = std::move(file->struct_declaration_list); |
| for (auto& struct_declaration : struct_declaration_list) { |
| if (!ConsumeStructDeclaration(std::move(struct_declaration))) { |
| return false; |
| } |
| } |
| |
| auto union_declaration_list = std::move(file->union_declaration_list); |
| for (auto& union_declaration : union_declaration_list) { |
| if (!ConsumeUnionDeclaration(std::move(union_declaration))) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Library::RegisterTypeName(const Name& name) { |
| // TODO(TO-701) Should this copy the Name? |
| // auto iter = registered_types_.insert(name); |
| // return iter.second; |
| return true; |
| } |
| |
| bool Library::RegisterResolvedType(const Name& name, TypeShape typeshape) { |
| // TODO(TO-701) Should this copy the Name? |
| // auto key_value = std::make_pair(name, typeshape); |
| // auto iter = resolved_types_.insert(std::move(key_value)); |
| // return iter.second; |
| return true; |
| } |
| |
| bool Library::LookupTypeShape(const Name& name, TypeShape* out_typeshape) { |
| auto iter = resolved_types_.find(name); |
| if (iter == resolved_types_.end()) { |
| return false; |
| } |
| *out_typeshape = iter->second; |
| return true; |
| } |
| |
| // Library resolution is concerned with resolving identifiers to their |
| // declarations, and with computing type sizes and alignments. |
| |
| bool Library::ResolveConst(const Const& const_declaration) { |
| if (!ResolveType(const_declaration.type.get())) { |
| return false; |
| } |
| // TODO(TO-702) Resolve const declarations. |
| return true; |
| } |
| |
| bool Library::ResolveEnum(const Enum& enum_declaration) { |
| TypeShape typeshape; |
| |
| switch (enum_declaration.type->subtype) { |
| case types::PrimitiveSubtype::Int8: |
| case types::PrimitiveSubtype::Int16: |
| case types::PrimitiveSubtype::Int32: |
| case types::PrimitiveSubtype::Int64: |
| case types::PrimitiveSubtype::Uint8: |
| case types::PrimitiveSubtype::Uint16: |
| case types::PrimitiveSubtype::Uint32: |
| case types::PrimitiveSubtype::Uint64: |
| // These are allowed as enum subtypes. Resolve the size and alignment. |
| if (!ResolveType(enum_declaration.type.get(), &typeshape)) |
| return false; |
| break; |
| |
| case types::PrimitiveSubtype::Bool: |
| case types::PrimitiveSubtype::Status: |
| case types::PrimitiveSubtype::Float32: |
| case types::PrimitiveSubtype::Float64: |
| // These are not allowed as enum subtypes. |
| return false; |
| } |
| |
| if (!RegisterResolvedType(enum_declaration.name, typeshape)) { |
| return false; |
| } |
| |
| // TODO(TO-702) Validate values. |
| return true; |
| } |
| |
| bool Library::ResolveInterface(const Interface& interface_declaration) { |
| // TODO(TO-703) Add subinterfaces here. |
| Scope<StringView> name_scope; |
| Scope<uint32_t> ordinal_scope; |
| for (const auto& method : interface_declaration.methods) { |
| if (!name_scope.Insert(method.name->location.data())) |
| return false; |
| if (!ordinal_scope.Insert(method.ordinal.Value())) |
| return false; |
| if (method.has_request) { |
| Scope<StringView> request_scope; |
| for (const auto& param : method.maybe_request) { |
| if (!request_scope.Insert(param.name->location.data())) |
| return false; |
| if (!ResolveType(param.type.get())) |
| return false; |
| } |
| } |
| if (method.has_response) { |
| Scope<StringView> response_scope; |
| for (const auto& response_param : method.maybe_response) { |
| if (!response_scope.Insert(response_param.name->location.data())) |
| return false; |
| if (!ResolveType(response_param.type.get())) |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool Library::ResolveStruct(const Struct& struct_declaration) { |
| Scope<StringView> scope; |
| std::vector<TypeShape> member_typeshapes; |
| for (const auto& member : struct_declaration.members) { |
| if (!scope.Insert(member.name->location.data())) |
| return false; |
| TypeShape member_typeshape; |
| if (!ResolveType(member.type.get(), &member_typeshape)) |
| return false; |
| member_typeshapes.push_back(member_typeshape); |
| } |
| |
| auto type_shape = FidlStructTypeShape(std::move(member_typeshapes)); |
| if (!RegisterResolvedType(struct_declaration.name, type_shape)) |
| return false; |
| |
| return true; |
| } |
| |
| bool Library::ResolveUnion(const Union& union_declaration) { |
| Scope<StringView> scope; |
| std::vector<TypeShape> member_typeshapes; |
| for (const auto& member : union_declaration.members) { |
| if (!scope.Insert(member.name->location.data())) |
| return false; |
| TypeShape member_typeshape; |
| if (!ResolveType(member.type.get(), &member_typeshape)) |
| return false; |
| member_typeshapes.push_back(member_typeshape); |
| } |
| |
| auto typeshape = FidlUnionTypeShape(std::move(member_typeshapes)); |
| if (!RegisterResolvedType(union_declaration.name, typeshape)) |
| return false; |
| |
| return true; |
| } |
| |
| bool Library::Resolve() { |
| for (const auto& const_declaration : const_declarations_) { |
| if (!ResolveConst(const_declaration)) { |
| return false; |
| } |
| } |
| |
| for (const auto& enum_declaration : enum_declarations_) { |
| if (!ResolveEnum(enum_declaration)) { |
| return false; |
| } |
| } |
| |
| for (const auto& interface_declaration : interface_declarations_) { |
| if (!ResolveInterface(interface_declaration)) { |
| return false; |
| } |
| } |
| |
| for (const auto& struct_declaration : struct_declarations_) { |
| if (!ResolveStruct(struct_declaration)) { |
| return false; |
| } |
| } |
| |
| for (const auto& union_declaration : union_declarations_) { |
| if (!ResolveUnion(union_declaration)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool Library::ResolveArrayType(const ast::ArrayType& array_type, TypeShape* out_typeshape) { |
| TypeShape element_typeshape; |
| if (!ResolveType(array_type.element_type.get(), &element_typeshape)) |
| return false; |
| uint64_t element_count; |
| if (!ParseIntegerConstant<decltype(element_count)>(array_type.element_count.get(), |
| &element_count)) |
| return false; |
| if (element_count == 0) { |
| return false; |
| } |
| *out_typeshape = ArrayTypeShape(element_typeshape, element_count); |
| return true; |
| } |
| |
| bool Library::ResolveVectorType(const ast::VectorType& vector_type, TypeShape* out_typeshape) { |
| TypeShape element_typeshape; |
| if (!ResolveType(vector_type.element_type.get(), &element_typeshape)) { |
| return false; |
| } |
| auto element_count = std::numeric_limits<uint64_t>::max(); |
| if (vector_type.maybe_element_count) { |
| if (!ParseIntegerConstant(vector_type.maybe_element_count.get(), &element_count)) { |
| return false; |
| } |
| if (element_count == 0u) { |
| return false; |
| } |
| } |
| *out_typeshape = VectorTypeShape(element_typeshape, element_count); |
| return true; |
| } |
| |
| bool Library::ResolveStringType(const ast::StringType& string_type, TypeShape* out_typeshape) { |
| auto byte_count = std::numeric_limits<uint64_t>::max(); |
| if (string_type.maybe_element_count) { |
| if (!ParseIntegerConstant(string_type.maybe_element_count.get(), &byte_count)) { |
| return false; |
| } |
| if (byte_count == 0u) { |
| return false; |
| } |
| } |
| *out_typeshape = StringTypeShape(byte_count); |
| return true; |
| } |
| |
| bool Library::ResolveHandleType(const ast::HandleType& handle_type, TypeShape* out_typeshape) { |
| // Nothing to check. |
| *out_typeshape = kHandleTypeShape; |
| return true; |
| } |
| |
| bool Library::ResolveRequestType(const ast::RequestType& request_type, TypeShape* out_typeshape) { |
| if (!ResolveTypeName(request_type.subtype.get())) { |
| return false; |
| } |
| *out_typeshape = kHandleTypeShape; |
| return true; |
| } |
| |
| bool Library::ResolvePrimitiveType(const ast::PrimitiveType& primitive_type, |
| TypeShape* out_typeshape) { |
| switch (primitive_type.subtype) { |
| case types::PrimitiveSubtype::Int8: |
| *out_typeshape = kInt8TypeShape; |
| break; |
| case types::PrimitiveSubtype::Int16: |
| *out_typeshape = kInt16TypeShape; |
| break; |
| case types::PrimitiveSubtype::Int32: |
| *out_typeshape = kInt32TypeShape; |
| break; |
| case types::PrimitiveSubtype::Int64: |
| *out_typeshape = kInt64TypeShape; |
| break; |
| case types::PrimitiveSubtype::Uint8: |
| *out_typeshape = kUint8TypeShape; |
| break; |
| case types::PrimitiveSubtype::Uint16: |
| *out_typeshape = kUint16TypeShape; |
| break; |
| case types::PrimitiveSubtype::Uint32: |
| *out_typeshape = kUint32TypeShape; |
| break; |
| case types::PrimitiveSubtype::Uint64: |
| *out_typeshape = kUint64TypeShape; |
| break; |
| case types::PrimitiveSubtype::Bool: |
| *out_typeshape = kBoolTypeShape; |
| break; |
| case types::PrimitiveSubtype::Status: |
| *out_typeshape = kStatusTypeShape; |
| break; |
| case types::PrimitiveSubtype::Float32: |
| *out_typeshape = kFloat32TypeShape; |
| break; |
| case types::PrimitiveSubtype::Float64: |
| *out_typeshape = kFloat64TypeShape; |
| break; |
| } |
| return true; |
| } |
| |
| bool Library::ResolveIdentifierType(const ast::IdentifierType& identifier_type, |
| TypeShape* out_typeshape) { |
| if (!ResolveTypeName(identifier_type.identifier.get())) |
| return false; |
| // TODO(TO-702) identifier type shape |
| *out_typeshape = TypeShape(184u, 8u); |
| return true; |
| } |
| |
| bool Library::ResolveType(const ast::Type* type, TypeShape* out_typeshape) { |
| switch (type->kind) { |
| case ast::Type::Kind::Array: { |
| auto array_type = static_cast<const ast::ArrayType*>(type); |
| return ResolveArrayType(*array_type, out_typeshape); |
| } |
| |
| case ast::Type::Kind::Vector: { |
| auto vector_type = static_cast<const ast::VectorType*>(type); |
| return ResolveVectorType(*vector_type, out_typeshape); |
| } |
| |
| case ast::Type::Kind::String: { |
| auto string_type = static_cast<const ast::StringType*>(type); |
| return ResolveStringType(*string_type, out_typeshape); |
| } |
| |
| case ast::Type::Kind::Handle: { |
| auto handle_type = static_cast<const ast::HandleType*>(type); |
| return ResolveHandleType(*handle_type, out_typeshape); |
| } |
| |
| case ast::Type::Kind::Request: { |
| auto request_type = static_cast<const ast::RequestType*>(type); |
| return ResolveRequestType(*request_type, out_typeshape); |
| } |
| |
| case ast::Type::Kind::Primitive: { |
| auto primitive_type = static_cast<const ast::PrimitiveType*>(type); |
| return ResolvePrimitiveType(*primitive_type, out_typeshape); |
| } |
| |
| case ast::Type::Kind::Identifier: { |
| auto identifier_type = static_cast<const ast::IdentifierType*>(type); |
| return ResolveIdentifierType(*identifier_type, out_typeshape); |
| } |
| } |
| } |
| |
| bool Library::ResolveTypeName(const ast::CompoundIdentifier* name) { |
| // TODO(TO-701) Make this use Names. |
| // assert(name->components.size() == 1); |
| // StringView identifier = name->components[0]->identifier.data(); |
| // return registered_types_.find(identifier) != registered_types_.end(); |
| return true; |
| } |
| |
| } // namespace flat |
| } // namespace fidl |