// 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 { // RAII helper class to reset the iostream to its original flags. class IOFlagsGuard { public: explicit IOFlagsGuard(std::ostream* stream) : stream_(stream), flags_(stream_->flags()) { } ~IOFlagsGuard() { stream_->setf(flags_); } private: std::ostream* stream_; std::ios::fmtflags flags_; }; // 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, flat::Decl::Kind::kStruct, "fidl_message_header_t", "hdr", {}, {}, types::Nullability::kNonnullable, {}, }; } CGenerator::Member EmptyStructMember() { return { .kind = flat::Type::Kind::kPrimitive, .type = NamePrimitiveCType(types::PrimitiveSubtype::kUint8), // Prepend the reserved uint8_t field with a single underscore, which is // for reserved identifiers (see ISO C standard, section 7.1.3 // ). .name = "_reserved", }; } CGenerator::Transport ParseTransport(StringView view) { if (view == "SocketControl") { return CGenerator::Transport::SocketControl; } return CGenerator::Transport::Channel; } // 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: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: *file << member.type << " " << member.name; break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kTable: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: *file << "const " << member.type << " " << member.name; break; case types::Nullability::kNonnullable: *file << "const " << member.type << "* " << member.name; break; } break; } 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 << "]"; } break; case flat::Type::Kind::kVector: *file << member.element_type << "* " << member.name << "_buffer, " << "size_t " << member.name << "_capacity, " << "size_t* out_" << member.name << "_count"; break; case flat::Type::Kind::kString: *file << "char* " << member.name << "_buffer, " << "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: *file << member.type << "* out_" << member.name; break; case flat::Type::Kind::kIdentifier: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: *file << member.type << "* out_" << member.name; break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kTable: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: *file << member.type << " out_" << member.name; break; case types::Nullability::kNonnullable: *file << member.type << "* out_" << member.name; break; } break; } break; } } void EmitClientMethodDecl(std::ostream* file, StringView method_name, const std::vector& request, const std::vector& 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& 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 EmitServerTryDispatchDecl(std::ostream* file, StringView interface_name) { *file << "zx_status_t " << interface_name << "_try_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& response) { *file << "zx_status_t " << method_name << "_reply(fidl_txn_t* _txn"; for (const auto& member : response) { *file << ", "; EmitMethodInParamDecl(file, member); } *file << ")"; } bool IsStoredOutOfLine(const CGenerator::Member& member) { if (member.kind == flat::Type::Kind::kVector || member.kind == flat::Type::Kind::kString) return true; if (member.kind == flat::Type::Kind::kIdentifier) { return member.nullability == types::Nullability::kNullable && (member.decl_kind == flat::Decl::Kind::kStruct || member.decl_kind == flat::Decl::Kind::kUnion); } return false; } void EmitMeasureInParams(std::ostream* file, const std::vector& params) { for (const auto& member : params) { if (member.kind == flat::Type::Kind::kVector) *file << " + FIDL_ALIGN(sizeof(*" << member.name << "_data) * " << member.name << "_count)"; else if (member.kind == flat::Type::Kind::kString) *file << " + FIDL_ALIGN(" << member.name << "_size)"; else if (IsStoredOutOfLine(member)) *file << " + (" << member.name << " ? FIDL_ALIGN(sizeof(*" << member.name << ")) : 0u)"; } } void EmitParameterSizeValidation(std::ostream* file, const std::vector& params) { for (const auto& member : params) { if (member.max_num_elements == std::numeric_limits::max()) continue; std::string param_name; if (member.kind == flat::Type::Kind::kVector) { param_name = member.name + "_count"; } else if (member.kind == flat::Type::Kind::kString) { param_name = member.name + "_size"; } else { assert(false && "only vector/string has size limit"); } *file << kIndent << "if (" << param_name << " > " << member.max_num_elements << ") {\n"; *file << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n"; *file << kIndent << "}\n"; } } void EmitMeasureOutParams(std::ostream* file, const std::vector& params) { for (const auto& member : params) { if (member.kind == flat::Type::Kind::kVector) *file << " + FIDL_ALIGN(sizeof(*" << member.name << "_buffer) * " << member.name << "_capacity)"; else if (member.kind == flat::Type::Kind::kString) *file << " + FIDL_ALIGN(" << member.name << "_capacity)"; else if (IsStoredOutOfLine(member)) *file << " + (out_" << member.name << " ? FIDL_ALIGN(sizeof(*out_" << member.name << ")) : 0u)"; } } void EmitArraySizeOf(std::ostream* file, const CGenerator::Member& member) { for (const auto c : member.array_counts) { *file << c; *file << " * "; } *file << "sizeof(" << member.element_type << ")"; } // 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& params) { size_t count = 0u; for (const auto& member : params) { if (IsStoredOutOfLine(member)) ++count; } return count; } void EmitLinearizeMessage(std::ostream* file, StringView receiver, StringView bytes, const std::vector& 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 << ", "; EmitArraySizeOf(file, member); *file << ");\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 << "_next += FIDL_ALIGN(" << name << "_size);\n"; *file << kIndent << "if (" << name << "_data) {\n"; *file << kIndent << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name << "_data, " << name << "_size);\n"; *file << kIndent << "} else {\n"; *file << kIndent << kIndent << "if (" << name << "_size != 0) {\n"; *file << kIndent << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n"; *file << kIndent << kIndent << "}\n"; if (member.nullability == types::Nullability::kNullable) { *file << kIndent << kIndent << receiver << "->" << name << ".data = NULL;\n"; } *file << kIndent << "}\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: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: *file << kIndent << receiver << "->" << name << " = " << name << ";\n"; break; case flat::Decl::Kind::kTable: assert(false && "c-codegen for tables not yet implemented"); break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: *file << kIndent << "if (" << name << ") {\n"; *file << kIndent << kIndent << receiver << "->" << name << " = (void*)&" << bytes << "[_next];\n"; *file << kIndent << kIndent << "memcpy(" << receiver << "->" << name << ", " << name << ", sizeof(*" << name << "));\n"; *file << kIndent << kIndent << "_next += sizeof(*" << name << ");\n"; *file << kIndent << "} else {\n"; *file << kIndent << kIndent << receiver << "->" << name << " = NULL;\n"; *file << kIndent << "}\n"; break; case types::Nullability::kNonnullable: *file << kIndent << receiver << "->" << name << " = *" << name << ";\n"; break; } break; } } } } // Various computational helper routines. void EnumValue(const flat::Constant* constant, std::string* out_value) { std::ostringstream member_value; const flat::ConstantValue& const_val = constant->Value(); switch (const_val.kind) { case flat::ConstantValue::Kind::kInt8: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kInt16: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kInt32: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kInt64: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kUint8: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kUint16: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kUint32: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kUint64: { auto value = static_cast&>(const_val); member_value << value; break; } case flat::ConstantValue::Kind::kBool: case flat::ConstantValue::Kind::kFloat32: case flat::ConstantValue::Kind::kFloat64: case flat::ConstantValue::Kind::kString: assert(false && "bad primitive type for an enum"); break; } *out_value = member_value.str(); } flat::Decl::Kind GetDeclKind(const flat::Library* library, const flat::Type* type) { if (type->kind != flat::Type::Kind::kIdentifier) return flat::Decl::Kind::kConst; auto identifier_type = static_cast(type); auto named_decl = library->LookupDeclByName(identifier_type->name); assert(named_decl && "library must contain declaration"); return named_decl->kind; } void ArrayCountsAndElementTypeName(const flat::Library* library, const flat::Type* type, std::vector* out_array_counts, std::string* out_element_type_name) { std::vector array_counts; for (;;) { switch (type->kind) { default: { *out_element_type_name = NameFlatCType(type, GetDeclKind(library, type)); *out_array_counts = array_counts; return; } case flat::Type::Kind::kArray: { auto array_type = static_cast(type); auto element_count = static_cast(array_type->element_count->Value()); array_counts.push_back(element_count.value); type = array_type->element_type.get(); continue; } } } } template CGenerator::Member CreateMember(const flat::Library* library, const T& decl) { std::string name = NameIdentifier(decl.name); const flat::Type* type = decl.type.get(); auto decl_kind = GetDeclKind(library, type); auto type_name = NameFlatCType(type, decl_kind); std::string element_type_name; std::vector array_counts; types::Nullability nullability = types::Nullability::kNonnullable; uint32_t max_num_elements = std::numeric_limits::max(); switch (type->kind) { case flat::Type::Kind::kArray: { ArrayCountsAndElementTypeName(library, type, &array_counts, &element_type_name); break; } case flat::Type::Kind::kVector: { auto vector_type = static_cast(type); const flat::Type* element_type = vector_type->element_type.get(); element_type_name = NameFlatCType(element_type, GetDeclKind(library, element_type)); max_num_elements = static_cast(vector_type->element_count.get()->Value()).value; break; } case flat::Type::Kind::kIdentifier: { auto identifier_type = static_cast(type); nullability = identifier_type->nullability; break; } case flat::Type::Kind::kString: { auto string_type = static_cast(type); nullability = string_type->nullability; max_num_elements = static_cast(string_type->max_size.get()->Value()).value; break; } case flat::Type::Kind::kHandle: break; case flat::Type::Kind::kRequestHandle: break; case flat::Type::Kind::kPrimitive: break; } return CGenerator::Member{ type->kind, decl_kind, std::move(type_name), std::move(name), std::move(element_type_name), std::move(array_counts), nullability, max_num_elements, }; } std::vector GenerateMembers(const flat::Library* library, const std::vector& union_members) { std::vector 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* request, std::vector* 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_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); // Dependencies are in pointer order... change to a deterministic // ordering prior to output. std::set add_includes; for (const auto& dep_library : library_->dependencies()) { if (dep_library == library_) continue; if (dep_library->HasAttribute("Internal")) continue; add_includes.insert(NameLibraryCHeader(dep_library->name())); } for (const auto& include : add_includes) { EmitIncludeHeader(&file_, "<" + include + ">"); } 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::GeneratePrimitiveDefine(StringView name, types::PrimitiveSubtype subtype, StringView value) { switch (subtype) { case types::PrimitiveSubtype::kInt8: case types::PrimitiveSubtype::kInt16: case types::PrimitiveSubtype::kInt32: case types::PrimitiveSubtype::kInt64: case types::PrimitiveSubtype::kUint8: case types::PrimitiveSubtype::kUint16: case types::PrimitiveSubtype::kUint32: case types::PrimitiveSubtype::kUint64: { std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype); file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n"; break; } case types::PrimitiveSubtype::kBool: case types::PrimitiveSubtype::kFloat32: case types::PrimitiveSubtype::kFloat64: { file_ << "#define " << name << " " << "(" << value << ")\n"; break; } default: abort(); } } void CGenerator::GenerateStringDefine(StringView name, StringView value) { file_ << "#define " << name << " " << 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& members, StructKind kind) { file_ << "struct " << name << " {\n"; if (kind == StructKind::kMessage) { file_ << kIndent << "FIDL_ALIGNDECL\n"; } auto emit_member = [this](const Member& member) { file_ << kIndent; EmitMemberDecl(&file_, member); file_ << ";\n"; }; for (const auto& member : members) { emit_member(member); } if (members.empty()) { emit_member(EmptyStructMember()); } file_ << "};\n"; } void CGenerator::GenerateTaggedUnionDeclaration(StringView name, const std::vector& members) { file_ << "struct " << name << " {\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 CGenerator::NameConsts(const std::vector>& const_infos) { std::map named_consts; for (const auto& const_info : const_infos) { named_consts.emplace(const_info.get(), NamedConst{NameName(const_info->name, "_", "_"), *const_info}); } return named_consts; } std::map CGenerator::NameEnums(const std::vector>& enum_infos) { std::map 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 CGenerator::NameInterfaces(const std::vector>& interface_infos) { std::map 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); } // TODO: Transport::SocketControl should imply NoHandles. named_interface.transport = ParseTransport(interface_info->GetAttribute("Transport")); for (const auto& method_pointer : interface_info->all_methods) { assert(method_pointer != nullptr); const auto& method = *method_pointer; 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{ std::move(c_name), std::move(coded_name), method.maybe_request->members, method.maybe_request->typeshape}); } 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{std::move(c_name), std::move(coded_name), method.maybe_response->members, method.maybe_response->typeshape}); } 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{std::move(c_name), std::move(coded_name), method.maybe_response->members, method.maybe_response->typeshape}); } } named_interface.methods.push_back(std::move(named_method)); } named_interfaces.emplace(interface_info.get(), std::move(named_interface)); } return named_interfaces; } std::map CGenerator::NameStructs(const std::vector>& struct_infos) { std::map named_structs; for (const auto& struct_info : struct_infos) { if (struct_info->anonymous) continue; 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 CGenerator::NameTables(const std::vector>& table_infos) { std::map named_tables; for (const auto& table_info : table_infos) { std::string c_name = NameName(table_info->name, "_", "_"); std::string coded_name = c_name + "Coded"; named_tables.emplace(table_info.get(), NamedTable{std::move(c_name), std::move(coded_name), *table_info}); } return named_tables; } std::map CGenerator::NameUnions(const std::vector>& union_infos) { std::map 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->subtype; 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(member.value.get(), &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) { { IOFlagsGuard reset_flags(&file_); file_ << "#define " << method_info.ordinal_name << " ((uint32_t)0x" << std::uppercase << std::hex << method_info.ordinal << std::dec << ")\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::ProduceTableForwardDeclaration(const NamedTable& 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) { const flat::Const& ci = named_const.const_info; // Some constants are not literals. Odd. if (ci.value->kind != flat::Constant::Kind::kLiteral) { return; } switch (ci.type->kind) { case flat::Type::Kind::kPrimitive: GeneratePrimitiveDefine(named_const.name, static_cast(ci.type.get())->subtype, static_cast(ci.value.get())->literal->location().data()); break; case flat::Type::Kind::kString: GenerateStringDefine(named_const.name, static_cast(ci.value.get())->literal->location().data()); break; default: abort(); } EmitBlank(&file_); } void CGenerator::ProduceMessageDeclaration(const NamedMessage& named_message) { // When we generate a request or response struct (i.e. messages), we must // both include the message header, and ensure the message is FIDL aligned. std::vector 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, StructKind::kMessage); 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 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, StructKind::kNonmessage); EmitBlank(&file_); } void CGenerator::ProduceUnionDeclaration(const NamedUnion& named_union) { std::vector 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 request; std::vector 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 request; std::vector response; GetMethodParameters(library_, method_info, &request, &response); size_t count = CountSecondaryObjects(request); size_t hcount = method_info.request->typeshape.MaxHandles(); bool encode = ((count > 0) || (hcount > 0)); EmitClientMethodDecl(&file_, method_info.c_name, request, response); file_ << " {\n"; EmitParameterSizeValidation(&file_, request); file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.request->c_name << ")"; EmitMeasureInParams(&file_, request); 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); if (encode) { file_ << kIndent << "uint32_t _wr_num_handles = 0u;\n"; switch (named_interface.transport) { case Transport::Channel: file_ << kIndent << "zx_handle_t _handles[ZX_CHANNEL_MAX_MSG_HANDLES];\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"; break; case Transport::SocketControl: file_ << kIndent << "zx_status_t _status = fidl_encode(&" << method_info.request->coded_name << ", _wr_bytes, _wr_num_bytes, NULL, 0, &_wr_num_handles, NULL);\n"; break; } file_ << kIndent << "if (_status != ZX_OK)\n"; file_ << kIndent << kIndent << "return _status;\n"; } else { file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only request\n"; } if (!method_info.response) { switch (named_interface.transport) { case Transport::Channel: if (encode) { file_ << kIndent << "return zx_channel_write(_channel, 0u, _wr_bytes, _wr_num_bytes, _handles, _wr_num_handles);\n"; } else { file_ << kIndent << "return zx_channel_write(_channel, 0u, _wr_bytes, _wr_num_bytes, NULL, 0);\n"; } break; case Transport::SocketControl: file_ << kIndent << "return fidl_socket_write_control(_channel, _wr_bytes, _wr_num_bytes);\n"; break; } } else { file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")"; EmitMeasureOutParams(&file_, response); 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"; switch (named_interface.transport) { case Transport::Channel: if (!encode) { file_ << kIndent << "zx_handle_t _handles[ZX_CHANNEL_MAX_MSG_HANDLES];\n"; } file_ << kIndent << "zx_channel_call_args_t _args = {\n"; file_ << kIndent << kIndent << ".wr_bytes = _wr_bytes,\n"; if (encode) { file_ << kIndent << kIndent << ".wr_handles = _handles,\n"; } else { file_ << kIndent << kIndent << ".wr_handles = NULL,\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"; if (encode) { file_ << kIndent << kIndent << ".wr_num_handles = _wr_num_handles,\n"; } else { file_ << kIndent << kIndent << ".wr_num_handles = 0,\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"; if (encode) { file_ << kIndent; } else { file_ << kIndent << "zx_status_t "; } file_ << "_status = zx_channel_call(_channel, 0u, ZX_TIME_INFINITE, &_args, &_actual_num_bytes, &_actual_num_handles);\n"; break; case Transport::SocketControl: file_ << kIndent << "size_t _actual_num_bytes = 0u;\n"; if (encode) { file_ << kIndent; } else { file_ << kIndent << "zx_status_t "; } file_ << "_status = fidl_socket_call_control(_channel, _wr_bytes, _wr_num_bytes, _rd_bytes, _rd_num_bytes, &_actual_num_bytes);\n"; break; } 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. 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)"; } else if (IsStoredOutOfLine(member)) { if (i++ > 0u) file_ << " || "; file_ << "((uintptr_t)_response->" << member.name << " == FIDL_ALLOC_PRESENT && out_" << member.name << " == NULL)"; } } if (count > 1u) file_ << ")"; file_ << " {\n"; if (named_interface.transport == Transport::Channel) { 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"; } hcount = method_info.response->typeshape.MaxHandles(); if ((count == 0) && (hcount == 0)) { file_ << kIndent << "// OPTIMIZED AWAY fidl_decode() of POD-only response\n"; if (named_interface.transport == Transport::Channel) { file_ << kIndent << "if (_actual_num_handles > 0) {\n"; file_ << kIndent << kIndent << "zx_handle_close_many(_handles, _actual_num_handles);\n"; file_ << kIndent << kIndent << "return ZX_ERR_INTERNAL;\n"; // WHAT ERROR? file_ << kIndent << "}\n"; } } else { // TODO(FIDL-162): Validate the response ordinal. C++ bindings also need to do that. switch (named_interface.transport) { case Transport::Channel: file_ << kIndent << "_status = fidl_decode(&" << method_info.response->coded_name << ", _rd_bytes, _actual_num_bytes, _handles, _actual_num_handles, NULL);\n"; break; case Transport::SocketControl: file_ << kIndent << "_status = fidl_decode(&" << method_info.response->coded_name << ", _rd_bytes, _actual_num_bytes, NULL, 0, NULL);\n"; break; } 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 << ", "; EmitArraySizeOf(&file_, member); file_ << ");\n"; break; case flat::Type::Kind::kVector: file_ << kIndent << "memcpy(" << name << "_buffer, _response->" << name << ".data, sizeof(*" << name << "_buffer) * _response->" << name << ".count);\n"; file_ << kIndent << "*out_" << name << "_count = _response->" << name << ".count;\n"; break; case flat::Type::Kind::kString: file_ << kIndent << "memcpy(" << name << "_buffer, _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: file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n"; break; case flat::Type::Kind::kIdentifier: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n"; break; case flat::Decl::Kind::kTable: assert(false && "c-codegen for tables not yet implemented"); break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: file_ << kIndent << "if (_response->" << name << ") {\n"; file_ << kIndent << kIndent << "*out_" << name << " = *(_response->" << name << ");\n"; file_ << kIndent << "} else {\n"; // We don't have a great way of signaling that the optional response member // was not in the message. That means these bindings aren't particularly // useful when the client needs to extract that bit. The best we can do is // zero out the value to make sure the client has defined behavior. // // In many cases, the response contains other information (e.g., a status code) // that lets the client do something reasonable. file_ << kIndent << kIndent << "memset(out_" << name << ", 0, sizeof(*out_" << name << "));\n"; file_ << kIndent << "}\n"; break; case types::Nullability::kNonnullable: file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n"; break; } break; } 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 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"; EmitServerTryDispatchDecl(&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 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) { EmitServerTryDispatchDecl(&file_, named_interface.c_name); file_ << " {\n"; file_ << kIndent << "if (msg->num_bytes < sizeof(fidl_message_header_t)) {\n"; file_ << kIndent << kIndent << "zx_handle_close_many(msg->handles, msg->num_handles);\n"; file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n"; file_ << kIndent << "}\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 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: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: file_ << ", request->" << member.name; break; case flat::Decl::Kind::kTable: assert(false && "c-codegen for tables not yet implemented"); break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: file_ << ", request->" << member.name; break; case types::Nullability::kNonnullable: file_ << ", &(request->" << member.name << ")"; break; } 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 << "return ZX_ERR_NOT_SUPPORTED;\n"; file_ << kIndent << "}\n"; file_ << kIndent << "}\n"; file_ << kIndent << "if (" << "status != ZX_OK && " << "status != ZX_ERR_STOP && " << "status != ZX_ERR_NEXT && " << "status != ZX_ERR_ASYNC) {\n"; file_ << kIndent << kIndent << "return ZX_ERR_INTERNAL;\n"; file_ << kIndent << "} else {\n"; file_ << kIndent << kIndent << "return status;\n"; file_ << kIndent << "}\n"; file_ << "}\n\n"; EmitServerDispatchDecl(&file_, named_interface.c_name); file_ << " {\n"; file_ << kIndent << "zx_status_t status = " << named_interface.c_name << "_try_dispatch(ctx, txn, msg, ops);\n"; file_ << kIndent << "if (status == ZX_ERR_NOT_SUPPORTED)\n"; file_ << kIndent << kIndent << "zx_handle_close_many(msg->handles, msg->num_handles);\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 response; GetMethodParameters(library_, method_info, nullptr, &response); size_t hcount = method_info.response->typeshape.MaxHandles(); bool encode = ((CountSecondaryObjects(response) > 0) || (hcount > 0)); EmitServerReplyDecl(&file_, method_info.c_name, response); file_ << " {\n"; file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.response->c_name << ")"; EmitMeasureInParams(&file_, response); 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); if (encode) { 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"; if (encode) { file_ << kIndent << kIndent << ".handles = _handles,\n"; } else { file_ << kIndent << kIndent << ".handles = NULL,\n"; } file_ << kIndent << kIndent << ".num_bytes = _wr_num_bytes,\n"; if (encode) { file_ << kIndent << kIndent << ".num_handles = ZX_CHANNEL_MAX_MSG_HANDLES,\n"; } else { file_ << kIndent << kIndent << ".num_handles = 0,\n"; } file_ << kIndent << "};\n"; if (encode) { 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"; } else { file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only reply\n"; } file_ << kIndent << "return _txn->reply(_txn, &_msg);\n"; file_ << "}\n\n"; } } std::ostringstream CGenerator::ProduceHeader() { GeneratePrologues(); std::map named_consts = NameConsts(library_->const_declarations_); std::map named_enums = NameEnums(library_->enum_declarations_); std::map named_interfaces = NameInterfaces(library_->interface_declarations_); std::map named_structs = NameStructs(library_->struct_declarations_); std::map named_tables = NameTables(library_->table_declarations_); std::map 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::kTable: { auto iter = named_tables.find(decl); if (iter != named_tables.end()) { ProduceTableForwardDeclaration(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::kTable: 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::kTable: // Tables are entirely forward declared, and their body is defined only in // implementation files. 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::kTable: 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_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">"); EmitBlank(&file_); std::map 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::kTable: 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_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">"); EmitBlank(&file_); std::map 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::kTable: 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