| // 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/c_generator.h" |
| |
| #include "fidl/attributes.h" |
| #include "fidl/names.h" |
| |
| namespace fidl { |
| |
| namespace { |
| |
| // Various string values are looked up or computed in these |
| // functions. Nothing else should be dealing in string literals, or |
| // computing strings from these or AST values. |
| |
| constexpr const char* kIndent = " "; |
| |
| CGenerator::Member MessageHeader() { |
| return {flat::Type::Kind::kIdentifier, "fidl_message_header_t", "hdr", {}, {}}; |
| } |
| |
| // Functions named "Emit..." are called to actually emit to an std::ostream |
| // is here. No other functions should directly emit to the streams. |
| |
| std::ostream& operator<<(std::ostream& stream, StringView view) { |
| stream.rdbuf()->sputn(view.data(), view.size()); |
| return stream; |
| } |
| |
| void EmitFileComment(std::ostream* file) { |
| *file << "// WARNING: This file is machine generated by fidlc.\n\n"; |
| } |
| |
| void EmitHeaderGuard(std::ostream* file) { |
| // TODO(704) Generate an appropriate header guard name. |
| *file << "#pragma once\n"; |
| } |
| |
| void EmitIncludeHeader(std::ostream* file, StringView header) { |
| *file << "#include " << header << "\n"; |
| } |
| |
| void EmitBeginExternC(std::ostream* file) { |
| *file << "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n"; |
| } |
| |
| void EmitEndExternC(std::ostream* file) { |
| *file << "#if defined(__cplusplus)\n}\n#endif\n"; |
| } |
| |
| void EmitBlank(std::ostream* file) { |
| *file << "\n"; |
| } |
| |
| void EmitMemberDecl(std::ostream* file, const CGenerator::Member& member) { |
| *file << member.type << " " << member.name; |
| for (uint32_t array_count : member.array_counts) { |
| *file << "[" << array_count << "]"; |
| } |
| } |
| |
| void EmitMethodInParamDecl(std::ostream* file, const CGenerator::Member& member) { |
| switch (member.kind) { |
| case flat::Type::Kind::kArray: |
| *file << "const " << member.type << " " << member.name; |
| for (uint32_t array_count : member.array_counts) { |
| *file << "[" << array_count << "]"; |
| } |
| break; |
| case flat::Type::Kind::kVector: |
| *file << "const " << member.element_type << "* " << member.name << "_data, " |
| << "size_t " << member.name << "_count"; |
| break; |
| case flat::Type::Kind::kString: |
| *file << "const char* " << member.name << "_data, " |
| << "size_t " << member.name << "_size"; |
| break; |
| case flat::Type::Kind::kHandle: |
| case flat::Type::Kind::kRequestHandle: |
| case flat::Type::Kind::kPrimitive: |
| *file << member.type << " " << member.name; |
| break; |
| case flat::Type::Kind::kIdentifier: |
| *file << "const " << member.type << "* " << member.name; |
| break; |
| } |
| } |
| |
| void EmitMethodOutParamDecl(std::ostream* file, const CGenerator::Member& member) { |
| switch (member.kind) { |
| case flat::Type::Kind::kArray: |
| *file << member.type << " out_" << member.name; |
| for (uint32_t array_count : member.array_counts) { |
| *file << "[" << array_count << "]"; |
| } |
| case flat::Type::Kind::kVector: |
| *file << member.element_type << "* " << member.name << "_data, " |
| << "size_t " << member.name << "_capacity, " |
| << "size_t* out_" << member.name << "_count"; |
| break; |
| case flat::Type::Kind::kString: |
| *file << "char* " << member.name << "_data, " |
| << "size_t " << member.name << "_capacity, " |
| << "size_t* out_" << member.name << "_size"; |
| break; |
| case flat::Type::Kind::kHandle: |
| case flat::Type::Kind::kRequestHandle: |
| case flat::Type::Kind::kPrimitive: |
| case flat::Type::Kind::kIdentifier: |
| *file << member.type << "* " << member.name; |
| break; |
| } |
| } |
| |
| void EmitClientMethodDecl(std::ostream* file, StringView method_name, |
| const std::vector<CGenerator::Member>& request, |
| const std::vector<CGenerator::Member>& response) { |
| *file << "zx_status_t " << method_name << "(zx_handle_t _channel"; |
| for (const auto& member : request) { |
| *file << ", "; |
| EmitMethodInParamDecl(file, member); |
| } |
| for (auto member : response) { |
| *file << ", "; |
| EmitMethodOutParamDecl(file, member); |
| } |
| *file << ")"; |
| } |
| |
| void EmitServerMethodDecl(std::ostream* file, StringView method_name, |
| const std::vector<CGenerator::Member>& request, |
| bool has_response) { |
| *file << "zx_status_t (*" << method_name << ")(void* ctx"; |
| for (const auto& member : request) { |
| *file << ", "; |
| EmitMethodInParamDecl(file, member); |
| } |
| if (has_response) { |
| *file << ", fidl_txn_t* txn"; |
| } |
| *file << ")"; |
| } |
| |
| void EmitServerDispatchDecl(std::ostream* file, StringView interface_name) { |
| *file << "zx_status_t " << interface_name |
| << "_dispatch(void* ctx, fidl_txn_t* txn, fidl_msg_t* msg, const " |
| << interface_name << "_ops_t* ops)"; |
| } |
| |
| void EmitServerReplyDecl(std::ostream* file, StringView method_name, |
| const std::vector<CGenerator::Member>& response) { |
| *file << "zx_status_t " << method_name << "_reply(fidl_txn_t* _txn"; |
| for (const auto& member : response) { |
| *file << ", "; |
| EmitMethodInParamDecl(file, member); |
| } |
| *file << ")"; |
| } |
| |
| void EmitMeasureParams(std::ostream* file, |
| const std::vector<CGenerator::Member>& params, |
| StringView vector_suffix, |
| StringView string_suffix) { |
| for (const auto& member : params) { |
| if (member.kind == flat::Type::Kind::kVector) |
| *file << " + FIDL_ALIGN(sizeof(*" << member.name << "_data) * " << member.name << vector_suffix << ")"; |
| else if (member.kind == flat::Type::Kind::kString) |
| *file << " + FIDL_ALIGN(" << member.name << string_suffix << ")"; |
| } |
| } |
| |
| // This function assumes the |params| are part of a [Layout="Simple"] interface. |
| // In particular, simple interfaces don't have nullable structs or nested |
| // vectors. The only secondary objects they contain are top-level vectors and |
| // strings. |
| size_t CountSecondaryObjects(const std::vector<CGenerator::Member>& params) { |
| size_t count = 0u; |
| for (const auto& member : params) { |
| if (member.kind == flat::Type::Kind::kVector || |
| member.kind == flat::Type::Kind::kString) |
| ++count; |
| } |
| return count; |
| } |
| |
| void EmitLinearizeMessage(std::ostream* file, |
| StringView receiver, |
| StringView bytes, |
| const std::vector<CGenerator::Member>& request) { |
| if (CountSecondaryObjects(request) > 0) |
| *file << kIndent << "uint32_t _next = sizeof(*" << receiver << ");\n"; |
| for (const auto& member : request) { |
| const auto& name = member.name; |
| switch (member.kind) { |
| case flat::Type::Kind::kArray: |
| *file << kIndent << "memcpy(" << receiver << "->" << name << ", " |
| << name << ", sizeof(" << receiver << "->" << name << "));\n"; |
| break; |
| case flat::Type::Kind::kVector: |
| *file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n"; |
| *file << kIndent << receiver << "->" << name << ".count = " << name << "_count;\n"; |
| *file << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name << "_data, sizeof(*" << name << "_data) * " << name << "_count);\n"; |
| *file << kIndent << "_next += FIDL_ALIGN(sizeof(*" << name << "_data) * " << name << "_count);\n"; |
| break; |
| case flat::Type::Kind::kString: |
| *file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n"; |
| *file << kIndent << receiver << "->" << name << ".size = " << name << "_size;\n"; |
| *file << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name << "_data, " << name << "_size);\n"; |
| *file << kIndent << "_next += FIDL_ALIGN(" << name << "_size);\n"; |
| break; |
| case flat::Type::Kind::kHandle: |
| case flat::Type::Kind::kRequestHandle: |
| case flat::Type::Kind::kPrimitive: |
| *file << kIndent << receiver << "->" << name << " = " << name << ";\n"; |
| break; |
| case flat::Type::Kind::kIdentifier: |
| *file << kIndent << receiver << "->" << name << " = *" << name << ";\n"; |
| break; |
| } |
| } |
| } |
| |
| // Various computational helper routines. |
| |
| void EnumValue(types::PrimitiveSubtype type, const flat::Constant* constant, |
| const flat::Library* library, std::string* out_value) { |
| // TODO(kulakowski) Move this into library resolution. |
| |
| std::ostringstream member_value; |
| |
| switch (type) { |
| case types::PrimitiveSubtype::kInt8: { |
| int8_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| // The char-sized overloads of operator<< here print |
| // the character value, not the numeric value, so cast up. |
| member_value << static_cast<int>(value); |
| break; |
| } |
| case types::PrimitiveSubtype::kInt16: { |
| int16_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| member_value << value; |
| break; |
| } |
| case types::PrimitiveSubtype::kInt32: { |
| int32_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| member_value << value; |
| break; |
| } |
| case types::PrimitiveSubtype::kInt64: { |
| int64_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| member_value << value; |
| break; |
| } |
| case types::PrimitiveSubtype::kUint8: { |
| uint8_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| // The char-sized overloads of operator<< here print |
| // the character value, not the numeric value, so cast up. |
| member_value << static_cast<unsigned int>(value); |
| break; |
| } |
| case types::PrimitiveSubtype::kUint16: { |
| uint16_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| member_value << value; |
| break; |
| } |
| case types::PrimitiveSubtype::kUint32: { |
| uint32_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| member_value << value; |
| break; |
| } |
| case types::PrimitiveSubtype::kUint64: { |
| uint64_t value; |
| bool success = library->ParseIntegerConstant(constant, &value); |
| if (!success) { |
| __builtin_trap(); |
| } |
| member_value << value; |
| break; |
| } |
| case types::PrimitiveSubtype::kBool: |
| case types::PrimitiveSubtype::kStatus: |
| case types::PrimitiveSubtype::kFloat32: |
| case types::PrimitiveSubtype::kFloat64: |
| assert(false && "bad primitive type for an enum"); |
| break; |
| } |
| |
| *out_value = member_value.str(); |
| } |
| |
| std::vector<uint32_t> ArrayCounts(const flat::Library* library, const flat::Type* type) { |
| std::vector<uint32_t> array_counts; |
| for (;;) { |
| switch (type->kind) { |
| default: { return array_counts; } |
| case flat::Type::Kind::kArray: { |
| auto array_type = static_cast<const flat::ArrayType*>(type); |
| uint32_t element_count = array_type->element_count.Value(); |
| array_counts.push_back(element_count); |
| type = array_type->element_type.get(); |
| continue; |
| } |
| } |
| } |
| } |
| |
| template <typename T> |
| CGenerator::Member CreateMember(const flat::Library* library, const T& decl) { |
| std::string name = NameIdentifier(decl.name); |
| const flat::Type* type = decl.type.get(); |
| auto type_name = NameFlatCType(library, type); |
| std::vector<uint32_t> array_counts = ArrayCounts(library, type); |
| std::string element_type; |
| if (type->kind == flat::Type::Kind::kVector) { |
| auto vector_type = static_cast<const flat::VectorType*>(type); |
| element_type = NameFlatCType(library, vector_type->element_type.get()); |
| } |
| return CGenerator::Member{ |
| type->kind, |
| std::move(type_name), |
| std::move(name), |
| std::move(element_type), |
| std::move(array_counts), |
| }; |
| } |
| |
| std::vector<CGenerator::Member> |
| GenerateMembers(const flat::Library* library, |
| const std::vector<flat::Union::Member>& union_members) { |
| std::vector<CGenerator::Member> members; |
| members.reserve(union_members.size()); |
| for (const auto& union_member : union_members) { |
| members.push_back(CreateMember(library, union_member)); |
| } |
| return members; |
| } |
| |
| void GetMethodParameters(const flat::Library* library, |
| const CGenerator::NamedMethod& method_info, |
| std::vector<CGenerator::Member>* request, |
| std::vector<CGenerator::Member>* response) { |
| if (request) { |
| request->reserve(method_info.request->parameters.size()); |
| for (const auto& parameter : method_info.request->parameters) { |
| request->push_back(CreateMember(library, parameter)); |
| } |
| } |
| |
| if (response && method_info.response) { |
| response->reserve(method_info.request->parameters.size()); |
| for (const auto& parameter : method_info.response->parameters) { |
| response->push_back(CreateMember(library, parameter)); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| void CGenerator::GeneratePrologues() { |
| EmitFileComment(&file_); |
| EmitHeaderGuard(&file_); |
| EmitBlank(&file_); |
| EmitIncludeHeader(&file_, "<stdalign.h>"); |
| EmitIncludeHeader(&file_, "<stdbool.h>"); |
| EmitIncludeHeader(&file_, "<stdint.h>"); |
| EmitIncludeHeader(&file_, "<zircon/fidl.h>"); |
| EmitIncludeHeader(&file_, "<zircon/syscalls/object.h>"); |
| EmitIncludeHeader(&file_, "<zircon/types.h>"); |
| for (const auto& pair : *library_->dependencies_) { |
| if (pair.second.get() == library_) |
| continue; |
| EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(pair.first) + ">"); |
| } |
| EmitBlank(&file_); |
| EmitBeginExternC(&file_); |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::GenerateEpilogues() { |
| EmitEndExternC(&file_); |
| } |
| |
| void CGenerator::GenerateIntegerDefine(StringView name, types::PrimitiveSubtype subtype, |
| StringView value) { |
| std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype); |
| file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n"; |
| } |
| |
| void CGenerator::GenerateIntegerTypedef(types::PrimitiveSubtype subtype, StringView name) { |
| std::string underlying_type = NamePrimitiveCType(subtype); |
| file_ << "typedef " << underlying_type << " " << name << ";\n"; |
| } |
| |
| void CGenerator::GenerateStructTypedef(StringView name) { |
| file_ << "typedef struct " << name << " " << name << ";\n"; |
| } |
| |
| void CGenerator::GenerateStructDeclaration(StringView name, const std::vector<Member>& members) { |
| file_ << "struct " << name << " {\n"; |
| file_ << kIndent << "FIDL_ALIGNDECL\n"; |
| for (const auto& member : members) { |
| file_ << kIndent; |
| EmitMemberDecl(&file_, member); |
| file_ << ";\n"; |
| } |
| file_ << "};\n"; |
| } |
| |
| void CGenerator::GenerateTaggedUnionDeclaration(StringView name, |
| const std::vector<Member>& members) { |
| file_ << "struct " << name << " {\n"; |
| file_ << kIndent << "FIDL_ALIGNDECL\n"; |
| file_ << kIndent << "fidl_union_tag_t tag;\n"; |
| file_ << kIndent << "union {\n"; |
| for (const auto& member : members) { |
| file_ << kIndent << kIndent; |
| EmitMemberDecl(&file_, member); |
| file_ << ";\n"; |
| } |
| file_ << kIndent << "};\n"; |
| file_ << "};\n"; |
| } |
| |
| // TODO(TO-702) These should maybe check for global name |
| // collisions? Otherwise, is there some other way they should fail? |
| std::map<const flat::Decl*, CGenerator::NamedConst> |
| CGenerator::NameConsts(const std::vector<std::unique_ptr<flat::Const>>& const_infos) { |
| std::map<const flat::Decl*, NamedConst> named_consts; |
| for (const auto& const_info : const_infos) { |
| named_consts.emplace(const_info.get(), NamedConst{"", *const_info}); |
| } |
| return named_consts; |
| } |
| |
| std::map<const flat::Decl*, CGenerator::NamedEnum> |
| CGenerator::NameEnums(const std::vector<std::unique_ptr<flat::Enum>>& enum_infos) { |
| std::map<const flat::Decl*, NamedEnum> named_enums; |
| for (const auto& enum_info : enum_infos) { |
| std::string enum_name = NameName(enum_info->name, "_", "_"); |
| named_enums.emplace(enum_info.get(), NamedEnum{std::move(enum_name), *enum_info}); |
| } |
| return named_enums; |
| } |
| |
| std::map<const flat::Decl*, CGenerator::NamedInterface> |
| CGenerator::NameInterfaces(const std::vector<std::unique_ptr<flat::Interface>>& interface_infos) { |
| std::map<const flat::Decl*, NamedInterface> named_interfaces; |
| for (const auto& interface_info : interface_infos) { |
| NamedInterface named_interface; |
| named_interface.c_name = NameInterface(*interface_info); |
| if (interface_info->HasAttribute("Discoverable")) { |
| named_interface.discoverable_name = NameDiscoverable(*interface_info); |
| } |
| for (const auto& method : interface_info->methods) { |
| NamedMethod named_method; |
| std::string method_name = NameMethod(named_interface.c_name, method); |
| named_method.ordinal = method.ordinal.Value(); |
| named_method.ordinal_name = NameOrdinal(method_name); |
| named_method.identifier = NameIdentifier(method.name); |
| named_method.c_name = method_name; |
| if (method.maybe_request != nullptr) { |
| std::string c_name = NameMessage(method_name, types::MessageKind::kRequest); |
| std::string coded_name = NameTable(c_name); |
| named_method.request = std::make_unique<NamedMessage>(NamedMessage{ |
| std::move(c_name), std::move(coded_name), method.maybe_request->parameters}); |
| } |
| if (method.maybe_response != nullptr) { |
| if (method.maybe_request == nullptr) { |
| std::string c_name = |
| NameMessage(method_name, types::MessageKind::kEvent); |
| std::string coded_name = NameTable(c_name); |
| named_method.response = std::make_unique<NamedMessage>( |
| NamedMessage{std::move(c_name), std::move(coded_name), |
| method.maybe_response->parameters}); |
| } else { |
| std::string c_name = NameMessage(method_name, types::MessageKind::kResponse); |
| std::string coded_name = NameTable(c_name); |
| named_method.response = std::make_unique<NamedMessage>( |
| NamedMessage{std::move(c_name), std::move(coded_name), |
| method.maybe_response->parameters}); |
| } |
| } |
| named_interface.methods.push_back(std::move(named_method)); |
| } |
| named_interfaces.emplace(interface_info.get(), std::move(named_interface)); |
| } |
| return named_interfaces; |
| } |
| |
| std::map<const flat::Decl*, CGenerator::NamedStruct> |
| CGenerator::NameStructs(const std::vector<std::unique_ptr<flat::Struct>>& struct_infos) { |
| std::map<const flat::Decl*, NamedStruct> named_structs; |
| for (const auto& struct_info : struct_infos) { |
| std::string c_name = NameName(struct_info->name, "_", "_"); |
| std::string coded_name = c_name + "Coded"; |
| named_structs.emplace(struct_info.get(), |
| NamedStruct{std::move(c_name), std::move(coded_name), *struct_info}); |
| } |
| return named_structs; |
| } |
| |
| std::map<const flat::Decl*, CGenerator::NamedUnion> |
| CGenerator::NameUnions(const std::vector<std::unique_ptr<flat::Union>>& union_infos) { |
| std::map<const flat::Decl*, NamedUnion> named_unions; |
| for (const auto& union_info : union_infos) { |
| std::string union_name = NameName(union_info->name, "_", "_"); |
| named_unions.emplace(union_info.get(), NamedUnion{std::move(union_name), *union_info}); |
| } |
| return named_unions; |
| } |
| |
| void CGenerator::ProduceConstForwardDeclaration(const NamedConst& named_const) { |
| // TODO(TO-702) |
| } |
| |
| void CGenerator::ProduceEnumForwardDeclaration(const NamedEnum& named_enum) { |
| types::PrimitiveSubtype subtype = named_enum.enum_info.type; |
| GenerateIntegerTypedef(subtype, named_enum.name); |
| for (const auto& member : named_enum.enum_info.members) { |
| std::string member_name = named_enum.name + "_" + NameIdentifier(member.name); |
| std::string member_value; |
| EnumValue(named_enum.enum_info.type, member.value.get(), library_, &member_value); |
| GenerateIntegerDefine(member_name, subtype, std::move(member_value)); |
| } |
| |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::ProduceInterfaceForwardDeclaration(const NamedInterface& named_interface) { |
| if (!named_interface.discoverable_name.empty()) { |
| file_ << "#define " << named_interface.c_name << "_Name \"" << named_interface.discoverable_name << "\"\n"; |
| } |
| for (const auto& method_info : named_interface.methods) { |
| file_ << "#define " << method_info.ordinal_name << " ((uint32_t)" |
| << method_info.ordinal << ")\n"; |
| if (method_info.request) |
| GenerateStructTypedef(method_info.request->c_name); |
| if (method_info.response) |
| GenerateStructTypedef(method_info.response->c_name); |
| } |
| } |
| |
| void CGenerator::ProduceStructForwardDeclaration(const NamedStruct& named_struct) { |
| GenerateStructTypedef(named_struct.c_name); |
| } |
| |
| void CGenerator::ProduceUnionForwardDeclaration(const NamedUnion& named_union) { |
| GenerateStructTypedef(named_union.name); |
| } |
| |
| void CGenerator::ProduceInterfaceExternDeclaration(const NamedInterface& named_interface) { |
| for (const auto& method_info : named_interface.methods) { |
| if (method_info.request) |
| file_ << "extern const fidl_type_t " << method_info.request->coded_name << ";\n"; |
| if (method_info.response) |
| file_ << "extern const fidl_type_t " << method_info.response->coded_name |
| << ";\n"; |
| } |
| } |
| |
| void CGenerator::ProduceConstDeclaration(const NamedConst& named_const) { |
| // TODO(TO-702) |
| static_cast<void>(named_const); |
| |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::ProduceMessageDeclaration(const NamedMessage& named_message) { |
| std::vector<CGenerator::Member> members; |
| members.reserve(1 + named_message.parameters.size()); |
| members.push_back(MessageHeader()); |
| for (const auto& parameter : named_message.parameters) { |
| members.push_back(CreateMember(library_, parameter)); |
| } |
| |
| GenerateStructDeclaration(named_message.c_name, members); |
| |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::ProduceInterfaceDeclaration(const NamedInterface& named_interface) { |
| for (const auto& method_info : named_interface.methods) { |
| if (method_info.request) |
| ProduceMessageDeclaration(*method_info.request); |
| if (method_info.response) |
| ProduceMessageDeclaration(*method_info.response); |
| } |
| } |
| |
| void CGenerator::ProduceStructDeclaration(const NamedStruct& named_struct) { |
| std::vector<CGenerator::Member> members; |
| members.reserve(named_struct.struct_info.members.size()); |
| for (const auto& struct_member : named_struct.struct_info.members) { |
| members.push_back(CreateMember(library_, struct_member)); |
| } |
| |
| GenerateStructDeclaration(named_struct.c_name, members); |
| |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::ProduceUnionDeclaration(const NamedUnion& named_union) { |
| std::vector<CGenerator::Member> members = |
| GenerateMembers(library_, named_union.union_info.members); |
| GenerateTaggedUnionDeclaration(named_union.name, members); |
| |
| uint32_t tag = 0u; |
| for (const auto& member : named_union.union_info.members) { |
| std::string tag_name = NameUnionTag(named_union.name, member); |
| auto union_tag_type = types::PrimitiveSubtype::kUint32; |
| std::ostringstream value; |
| value << tag; |
| GenerateIntegerDefine(std::move(tag_name), union_tag_type, value.str()); |
| ++tag; |
| } |
| |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::ProduceInterfaceClientDeclaration(const NamedInterface& named_interface) { |
| for (const auto& method_info : named_interface.methods) { |
| if (!method_info.request) |
| continue; |
| std::vector<Member> request; |
| std::vector<Member> response; |
| GetMethodParameters(library_, method_info, &request, &response); |
| EmitClientMethodDecl(&file_, method_info.c_name, request, response); |
| file_ << ";\n"; |
| } |
| |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::ProduceInterfaceClientImplementation(const NamedInterface& named_interface) { |
| for (const auto& method_info : named_interface.methods) { |
| if (!method_info.request) |
| continue; |
| std::vector<Member> request; |
| std::vector<Member> response; |
| GetMethodParameters(library_, method_info, &request, &response); |
| EmitClientMethodDecl(&file_, method_info.c_name, request, response); |
| file_ << " {\n"; |
| file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.request->c_name << ")"; |
| EmitMeasureParams(&file_, request, "_count", "_size"); |
| file_ << ";\n"; |
| file_ << kIndent << "FIDL_ALIGNDECL char _wr_bytes[_wr_num_bytes];\n"; |
| file_ << kIndent << method_info.request->c_name << "* _request = (" << method_info.request->c_name << "*)_wr_bytes;\n"; |
| file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n"; |
| file_ << kIndent << "_request->hdr.ordinal = " << method_info.ordinal << ";\n"; |
| EmitLinearizeMessage(&file_, "_request", "_wr_bytes", request); |
| file_ << kIndent << "zx_handle_t _handles[ZX_CHANNEL_MAX_MSG_HANDLES];\n"; |
| file_ << kIndent << "uint32_t _wr_num_handles = 0u;\n"; |
| file_ << kIndent << "zx_status_t _status = fidl_encode(&" << method_info.request->coded_name |
| << ", _wr_bytes, _wr_num_bytes, _handles, ZX_CHANNEL_MAX_MSG_HANDLES" |
| << ", &_wr_num_handles, NULL);\n"; |
| file_ << kIndent << "if (_status != ZX_OK)\n"; |
| file_ << kIndent << kIndent << "return _status;\n"; |
| if (!method_info.response) { |
| file_ << kIndent << "return zx_channel_write(_channel, 0u, _wr_bytes, _wr_num_bytes, _handles, _wr_num_handles);\n"; |
| } else { |
| file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")"; |
| EmitMeasureParams(&file_, response, "_capacity", "_capacity"); |
| file_ << ";\n"; |
| file_ << kIndent << "FIDL_ALIGNDECL char _rd_bytes[_rd_num_bytes];\n"; |
| if (!response.empty()) |
| file_ << kIndent << method_info.response->c_name << "* _response = (" << method_info.response->c_name << "*)_rd_bytes;\n"; |
| file_ << kIndent << "zx_channel_call_args_t _args = {\n"; |
| file_ << kIndent << kIndent << ".wr_bytes = _wr_bytes,\n"; |
| file_ << kIndent << kIndent << ".wr_handles = _handles,\n"; |
| file_ << kIndent << kIndent << ".rd_bytes = _rd_bytes,\n"; |
| file_ << kIndent << kIndent << ".rd_handles = _handles,\n"; |
| file_ << kIndent << kIndent << ".wr_num_bytes = _wr_num_bytes,\n"; |
| file_ << kIndent << kIndent << ".wr_num_handles = _wr_num_handles,\n"; |
| file_ << kIndent << kIndent << ".rd_num_bytes = _rd_num_bytes,\n"; |
| file_ << kIndent << kIndent << ".rd_num_handles = ZX_CHANNEL_MAX_MSG_HANDLES,\n"; |
| file_ << kIndent << "};\n"; |
| |
| file_ << kIndent << "uint32_t _actual_num_bytes = 0u;\n"; |
| file_ << kIndent << "uint32_t _actual_num_handles = 0u;\n"; |
| file_ << kIndent << "_status = zx_channel_call(_channel, 0u, ZX_TIME_INFINITE, &_args, &_actual_num_bytes, &_actual_num_handles);\n"; |
| file_ << kIndent << "if (_status != ZX_OK)\n"; |
| file_ << kIndent << kIndent << "return _status;\n"; |
| |
| // We check that we have enough capacity to copy out the parameters |
| // before decoding the message so that we can close the handles |
| // using |_handles| rather than trying to find them in the decoded |
| // message. |
| size_t count = CountSecondaryObjects(response); |
| if (count > 0u) { |
| file_ << kIndent << "if "; |
| if (count > 1u) |
| file_ << "("; |
| size_t i = 0; |
| for (const auto& member : response) { |
| if (member.kind == flat::Type::Kind::kVector) { |
| if (i++ > 0u) |
| file_ << " || "; |
| file_ << "(_response->" << member.name << ".count > " << member.name << "_capacity)"; |
| } else if (member.kind == flat::Type::Kind::kString) { |
| if (i++ > 0u) |
| file_ << " || "; |
| file_ << "(_response->" << member.name << ".size > " << member.name << "_capacity)"; |
| } |
| } |
| if (count > 1u) |
| file_ << ")"; |
| file_ << " {\n"; |
| file_ << kIndent << kIndent << "zx_handle_close_many(_handles, _actual_num_handles);\n"; |
| file_ << kIndent << kIndent << "return ZX_ERR_BUFFER_TOO_SMALL;\n"; |
| file_ << kIndent << "}\n"; |
| } |
| |
| // TODO(FIDL-162): Validate the response ordinal. C++ bindings also need to do that. |
| file_ << kIndent << "_status = fidl_decode(&" << method_info.response->coded_name |
| << ", _rd_bytes, _actual_num_bytes, _handles, _actual_num_handles, NULL);\n"; |
| file_ << kIndent << "if (_status != ZX_OK)\n"; |
| file_ << kIndent << kIndent << "return _status;\n"; |
| |
| for (const auto& member : response) { |
| const auto& name = member.name; |
| switch (member.kind) { |
| case flat::Type::Kind::kArray: |
| file_ << kIndent << "memcpy(out_" << name << ", _response->" << name << ", sizeof(out_" << name << "));\n"; |
| break; |
| case flat::Type::Kind::kVector: |
| file_ << kIndent << "memcpy(" << name << "_data, _response->" << name << ".data, sizeof(*" << name << "_data) * _response->" << name << ".count);\n"; |
| file_ << kIndent << "*out_" << name << "_count = _response->" << name << ".count;\n"; |
| break; |
| case flat::Type::Kind::kString: |
| file_ << kIndent << "memcpy(" << name << "_data, _response->" << name << ".data, _response->" << name << ".size);\n"; |
| file_ << kIndent << "*out_" << name << "_size = _response->" << name << ".size;\n"; |
| break; |
| case flat::Type::Kind::kHandle: |
| case flat::Type::Kind::kRequestHandle: |
| case flat::Type::Kind::kPrimitive: |
| case flat::Type::Kind::kIdentifier: |
| file_ << kIndent << "*" << name << " = _response->" << name << ";\n"; |
| break; |
| } |
| } |
| |
| file_ << kIndent << "return ZX_OK;\n"; |
| } |
| file_ << "}\n\n"; |
| } |
| } // namespace fidl |
| |
| void CGenerator::ProduceInterfaceServerDeclaration(const NamedInterface& named_interface) { |
| file_ << "typedef struct " << named_interface.c_name << "_ops {\n"; |
| for (const auto& method_info : named_interface.methods) { |
| if (!method_info.request) |
| continue; |
| std::vector<Member> request; |
| GetMethodParameters(library_, method_info, &request, nullptr); |
| bool has_response = method_info.response != nullptr; |
| file_ << kIndent; |
| EmitServerMethodDecl(&file_, method_info.identifier, request, has_response); |
| file_ << ";\n"; |
| } |
| file_ << "} " << named_interface.c_name << "_ops_t;\n\n"; |
| |
| EmitServerDispatchDecl(&file_, named_interface.c_name); |
| file_ << ";\n\n"; |
| |
| for (const auto& method_info : named_interface.methods) { |
| if (!method_info.request || !method_info.response) |
| continue; |
| std::vector<Member> response; |
| GetMethodParameters(library_, method_info, nullptr, &response); |
| EmitServerReplyDecl(&file_, method_info.c_name, response); |
| file_ << ";\n"; |
| } |
| |
| EmitBlank(&file_); |
| } |
| |
| void CGenerator::ProduceInterfaceServerImplementation(const NamedInterface& named_interface) { |
| EmitServerDispatchDecl(&file_, named_interface.c_name); |
| file_ << " {\n"; |
| file_ << kIndent << "if (msg->num_bytes < sizeof(fidl_message_header_t))\n"; |
| file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n"; |
| file_ << kIndent << "fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;\n"; |
| file_ << kIndent << "zx_status_t status = ZX_OK;\n"; |
| file_ << kIndent << "switch (hdr->ordinal) {\n"; |
| |
| for (const auto& method_info : named_interface.methods) { |
| if (!method_info.request) |
| continue; |
| file_ << kIndent << "case " << method_info.ordinal_name << ": {\n"; |
| file_ << kIndent << kIndent << "status = fidl_decode_msg(&" << method_info.request->coded_name << ", msg, NULL);\n"; |
| file_ << kIndent << kIndent << "if (status != ZX_OK)\n"; |
| file_ << kIndent << kIndent << kIndent << "break;\n"; |
| std::vector<Member> request; |
| GetMethodParameters(library_, method_info, &request, nullptr); |
| if (!request.empty()) |
| file_ << kIndent << kIndent << method_info.request->c_name << "* request = (" << method_info.request->c_name << "*)msg->bytes;\n"; |
| file_ << kIndent << kIndent << "status = (*ops->" << method_info.identifier << ")(ctx"; |
| for (const auto& member : request) { |
| switch (member.kind) { |
| case flat::Type::Kind::kArray: |
| case flat::Type::Kind::kHandle: |
| case flat::Type::Kind::kRequestHandle: |
| case flat::Type::Kind::kPrimitive: |
| file_ << ", request->" << member.name; |
| break; |
| case flat::Type::Kind::kVector: |
| file_ << ", (" << member.element_type << "*)request->" << member.name << ".data" |
| << ", request->" << member.name << ".count"; |
| break; |
| case flat::Type::Kind::kString: |
| file_ << ", request->" << member.name << ".data" |
| << ", request->" << member.name << ".size"; |
| break; |
| case flat::Type::Kind::kIdentifier: |
| file_ << ", &(request->" << member.name << ")"; |
| break; |
| } |
| } |
| if (method_info.response != nullptr) |
| file_ << ", txn"; |
| file_ << ");\n"; |
| file_ << kIndent << kIndent << "break;\n"; |
| file_ << kIndent << "}\n"; |
| } |
| file_ << kIndent << "default: {\n"; |
| file_ << kIndent << kIndent << "zx_handle_close_many(msg->handles, msg->num_handles);\n"; |
| file_ << kIndent << kIndent << "status = ZX_ERR_NOT_SUPPORTED;\n"; |
| file_ << kIndent << kIndent << "break;\n"; |
| file_ << kIndent << "}\n"; |
| file_ << kIndent << "}\n"; |
| file_ << kIndent << "return status;\n"; |
| file_ << "}\n\n"; |
| |
| for (const auto& method_info : named_interface.methods) { |
| if (!method_info.request || !method_info.response) |
| continue; |
| std::vector<Member> response; |
| GetMethodParameters(library_, method_info, nullptr, &response); |
| EmitServerReplyDecl(&file_, method_info.c_name, response); |
| file_ << " {\n"; |
| file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.response->c_name << ")"; |
| EmitMeasureParams(&file_, response, "_count", "_size"); |
| file_ << ";\n"; |
| file_ << kIndent << "char _wr_bytes[_wr_num_bytes];\n"; |
| file_ << kIndent << method_info.response->c_name << "* _response = (" << method_info.response->c_name << "*)_wr_bytes;\n"; |
| file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n"; |
| file_ << kIndent << "_response->hdr.ordinal = " << method_info.ordinal << ";\n"; |
| EmitLinearizeMessage(&file_, "_response", "_wr_bytes", response); |
| file_ << kIndent << "zx_handle_t _handles[ZX_CHANNEL_MAX_MSG_HANDLES];\n"; |
| file_ << kIndent << "fidl_msg_t _msg = {\n"; |
| file_ << kIndent << kIndent << ".bytes = _wr_bytes,\n"; |
| file_ << kIndent << kIndent << ".handles = _handles,\n"; |
| file_ << kIndent << kIndent << ".num_bytes = _wr_num_bytes,\n"; |
| file_ << kIndent << kIndent << ".num_handles = ZX_CHANNEL_MAX_MSG_HANDLES,\n"; |
| file_ << kIndent << "};\n"; |
| file_ << kIndent << "zx_status_t _status = fidl_encode_msg(&" |
| << method_info.response->coded_name << ", &_msg, &_msg.num_handles, NULL);\n"; |
| file_ << kIndent << "if (_status != ZX_OK)\n"; |
| file_ << kIndent << kIndent << "return _status;\n"; |
| file_ << kIndent << "return _txn->reply(_txn, &_msg);\n"; |
| file_ << "}\n\n"; |
| } |
| } |
| |
| std::ostringstream CGenerator::ProduceHeader() { |
| GeneratePrologues(); |
| |
| std::map<const flat::Decl*, NamedConst> named_consts = |
| NameConsts(library_->const_declarations_); |
| std::map<const flat::Decl*, NamedEnum> named_enums = NameEnums(library_->enum_declarations_); |
| std::map<const flat::Decl*, NamedInterface> named_interfaces = |
| NameInterfaces(library_->interface_declarations_); |
| std::map<const flat::Decl*, NamedStruct> named_structs = |
| NameStructs(library_->struct_declarations_); |
| std::map<const flat::Decl*, NamedUnion> named_unions = |
| NameUnions(library_->union_declarations_); |
| |
| file_ << "\n// Forward declarations\n\n"; |
| |
| for (const auto* decl : library_->declaration_order_) { |
| switch (decl->kind) { |
| case flat::Decl::Kind::kConst: { |
| auto iter = named_consts.find(decl); |
| if (iter != named_consts.end()) { |
| ProduceConstForwardDeclaration(iter->second); |
| } |
| break; |
| } |
| case flat::Decl::Kind::kEnum: { |
| auto iter = named_enums.find(decl); |
| if (iter != named_enums.end()) { |
| ProduceEnumForwardDeclaration(iter->second); |
| } |
| break; |
| } |
| case flat::Decl::Kind::kInterface: { |
| auto iter = named_interfaces.find(decl); |
| if (iter != named_interfaces.end()) { |
| ProduceInterfaceForwardDeclaration(iter->second); |
| } |
| break; |
| } |
| case flat::Decl::Kind::kStruct: { |
| auto iter = named_structs.find(decl); |
| if (iter != named_structs.end()) { |
| ProduceStructForwardDeclaration(iter->second); |
| } |
| break; |
| } |
| case flat::Decl::Kind::kUnion: { |
| auto iter = named_unions.find(decl); |
| if (iter != named_unions.end()) { |
| ProduceUnionForwardDeclaration(iter->second); |
| } |
| break; |
| } |
| default: |
| abort(); |
| } |
| } |
| |
| file_ << "\n// Extern declarations\n\n"; |
| |
| for (const auto* decl : library_->declaration_order_) { |
| switch (decl->kind) { |
| case flat::Decl::Kind::kConst: |
| case flat::Decl::Kind::kEnum: |
| case flat::Decl::Kind::kStruct: |
| case flat::Decl::Kind::kUnion: |
| // Only messages have extern fidl_type_t declarations. |
| break; |
| case flat::Decl::Kind::kInterface: { |
| auto iter = named_interfaces.find(decl); |
| if (iter != named_interfaces.end()) { |
| ProduceInterfaceExternDeclaration(iter->second); |
| } |
| break; |
| } |
| default: |
| abort(); |
| } |
| } |
| |
| file_ << "\n// Declarations\n\n"; |
| |
| for (const auto* decl : library_->declaration_order_) { |
| switch (decl->kind) { |
| case flat::Decl::Kind::kConst: { |
| auto iter = named_consts.find(decl); |
| if (iter != named_consts.end()) { |
| ProduceConstDeclaration(iter->second); |
| } |
| break; |
| } |
| case flat::Decl::Kind::kEnum: |
| // Enums can be entirely forward declared, as they have no |
| // dependencies other than standard headers. |
| break; |
| case flat::Decl::Kind::kInterface: { |
| auto iter = named_interfaces.find(decl); |
| if (iter != named_interfaces.end()) { |
| ProduceInterfaceDeclaration(iter->second); |
| } |
| break; |
| } |
| case flat::Decl::Kind::kStruct: { |
| auto iter = named_structs.find(decl); |
| if (iter != named_structs.end()) { |
| ProduceStructDeclaration(iter->second); |
| } |
| break; |
| } |
| case flat::Decl::Kind::kUnion: { |
| auto iter = named_unions.find(decl); |
| if (iter != named_unions.end()) { |
| ProduceUnionDeclaration(iter->second); |
| } |
| break; |
| } |
| default: |
| abort(); |
| } |
| } |
| |
| file_ << "\n// Simple bindings \n\n"; |
| |
| for (const auto* decl : library_->declaration_order_) { |
| switch (decl->kind) { |
| case flat::Decl::Kind::kConst: |
| case flat::Decl::Kind::kEnum: |
| case flat::Decl::Kind::kStruct: |
| case flat::Decl::Kind::kUnion: |
| // Only interfaces have client declarations. |
| break; |
| case flat::Decl::Kind::kInterface: { |
| if (!HasSimpleLayout(decl)) |
| break; |
| auto iter = named_interfaces.find(decl); |
| if (iter != named_interfaces.end()) { |
| ProduceInterfaceClientDeclaration(iter->second); |
| ProduceInterfaceServerDeclaration(iter->second); |
| } |
| break; |
| } |
| default: |
| abort(); |
| } |
| } |
| |
| GenerateEpilogues(); |
| |
| return std::move(file_); |
| } |
| |
| std::ostringstream CGenerator::ProduceClient() { |
| EmitFileComment(&file_); |
| EmitIncludeHeader(&file_, "<lib/fidl/coding.h>"); |
| EmitIncludeHeader(&file_, "<string.h>"); |
| EmitIncludeHeader(&file_, "<zircon/syscalls.h>"); |
| EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">"); |
| EmitBlank(&file_); |
| |
| std::map<const flat::Decl*, NamedInterface> named_interfaces = |
| NameInterfaces(library_->interface_declarations_); |
| |
| for (const auto* decl : library_->declaration_order_) { |
| switch (decl->kind) { |
| case flat::Decl::Kind::kConst: |
| case flat::Decl::Kind::kEnum: |
| case flat::Decl::Kind::kStruct: |
| case flat::Decl::Kind::kUnion: |
| // Only interfaces have client implementations. |
| break; |
| case flat::Decl::Kind::kInterface: { |
| if (!HasSimpleLayout(decl)) |
| break; |
| auto iter = named_interfaces.find(decl); |
| if (iter != named_interfaces.end()) { |
| ProduceInterfaceClientImplementation(iter->second); |
| } |
| break; |
| } |
| default: |
| abort(); |
| } |
| } |
| |
| return std::move(file_); |
| } |
| |
| std::ostringstream CGenerator::ProduceServer() { |
| EmitFileComment(&file_); |
| EmitIncludeHeader(&file_, "<lib/fidl/coding.h>"); |
| EmitIncludeHeader(&file_, "<string.h>"); |
| EmitIncludeHeader(&file_, "<zircon/syscalls.h>"); |
| EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">"); |
| EmitBlank(&file_); |
| |
| std::map<const flat::Decl*, NamedInterface> named_interfaces = |
| NameInterfaces(library_->interface_declarations_); |
| |
| for (const auto* decl : library_->declaration_order_) { |
| switch (decl->kind) { |
| case flat::Decl::Kind::kConst: |
| case flat::Decl::Kind::kEnum: |
| case flat::Decl::Kind::kStruct: |
| case flat::Decl::Kind::kUnion: |
| // Only interfaces have client implementations. |
| break; |
| case flat::Decl::Kind::kInterface: { |
| if (!HasSimpleLayout(decl)) |
| break; |
| auto iter = named_interfaces.find(decl); |
| if (iter != named_interfaces.end()) { |
| ProduceInterfaceServerImplementation(iter->second); |
| } |
| break; |
| } |
| default: |
| abort(); |
| } |
| } |
| |
| return std::move(file_); |
| } |
| |
| } // namespace fidl |