blob: 34622ef4daa63e0b28d5fb79ff113eef7bd39a65 [file] [log] [blame]
// 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 <zircon/assert.h>
#include <unordered_set>
#include "fidl/flat_ast.h"
#include "fidl/names.h"
#include "fidl/type_shape.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 = " ";
// Mapping of library name to set of declaration names.
// These declarations are treated as though they have the
// [ForDeprecatedCBindings] attribute even though they violate the constraints
// enforced on them.
//
// For protocols this means that some of the methods can't be supported and
// will simply be left out (unless they're listed below in allowed_methods).
//
// For structs this means that a member can have an unsupported type such as a
// vector of strings or a union.
const std::map<std::string, std::set<std::string>> allowed_decls({
{"fuchsia.tracing.provider", {"Provider", "ProviderConfig", "StartOptions"}},
{"fuchsia.logger", {"Log", "LogSink", "LogMessage", "LogListenerSafe", "LogFilterOptions"}},
{"fuchsia.hardware.power.statecontrol", {"Admin"}},
{"fidl.test.llcpp.dirent", {"DirEntTestInterface"}},
});
bool DeclAlwaysAllowed(const flat::Name& name) {
auto library_name = flat::LibraryName(name.library()->name, ".");
auto iter = allowed_decls.find(library_name);
if (iter != allowed_decls.end()) {
const auto& decls = iter->second;
if (decls.find(std::string(name.decl_name())) != decls.end()) {
return true;
}
}
return false;
}
// Mapping of library name to mapping of protocol name to set of methods.
// Data structures should be generated for these methods even if they violate
// the constraints of the simple C bindings.
std::map<std::string, std::map<std::string, std::set<std::string>>> allowed_methods({
{"fuchsia.device.manager", {{"DeviceController", {"CompleteRemoval", "Unbind"}}}},
{"fuchsia.hardware.power.statecontrol",
{{"Admin", {"Poweroff", "Reboot", "RebootToBootloader", "RebootToRecovery", "SuspendToRam"}}}},
});
bool MethodAlwaysAllowed(const flat::Protocol::Method& method) {
auto library_name = flat::LibraryName(method.owning_protocol->name.library()->name, ".");
auto iter = allowed_methods.find(library_name);
if (iter != allowed_methods.end()) {
const auto& protocols = iter->second;
auto protocol = protocols.find(std::string(method.owning_protocol->name.decl_name()));
if (protocol != protocols.end()) {
const auto& methods = protocol->second;
if (methods.find(std::string(method.name.data())) != methods.end()) {
return true;
}
}
}
return false;
}
bool DeclAllowed(const flat::Decl* decl) {
if (HasSimpleLayout(decl) || DeclAlwaysAllowed(decl->name)) {
return true;
}
switch (decl->kind) {
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
// bits, const, enum are always allowed.
return true;
default:
return false;
}
}
bool TypeAllowed(const flat::Type* type) {
ZX_ASSERT(type != nullptr);
// treat box types like we do nullable structs
if (type->kind == flat::Type::Kind::kBox)
type = static_cast<const flat::BoxType*>(type)->boxed_type;
if (type->kind == flat::Type::Kind::kIdentifier) {
auto identifier_type = static_cast<const flat::IdentifierType*>(type);
if (!DeclAllowed(identifier_type->type_decl)) {
return false;
}
}
return true;
}
bool PayloadLayoutAllowed(const std::unique_ptr<flat::TypeConstructor>& payload) {
if (!payload) {
return true;
}
auto id = static_cast<const flat::IdentifierType*>(payload->type);
// Since no new uses of the C bindings are allowed, fail on payloads that are either unions or
// tables, as support for such payloads was added after C binding usage was frozen.
if (id->type_decl->kind != flat::Decl::Kind::kStruct) {
return false;
}
auto as_struct = static_cast<const flat::Struct*>(id->type_decl);
for (const auto& member : as_struct->members) {
if (!TypeAllowed(member.type_ctor->type)) {
return false;
}
}
return true;
}
bool MethodAllowed(const flat::Protocol::Method& method) {
return MethodAlwaysAllowed(method) || (PayloadLayoutAllowed(method.maybe_request) &&
PayloadLayoutAllowed(method.maybe_response));
}
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
// <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>).
.name = "_reserved",
};
}
// Can encode and decode functions be generated for these members?
bool CanGenerateCodecFunctions(const std::vector<CGenerator::Member>& members) {
for (const auto& m : members) {
if (m.kind == flat::Type::Kind::kIdentifier &&
m.decl_kind.value() == flat::Decl::Kind::kUnion) {
return false;
}
}
return true;
}
// Functions named "Emit..." are called to actually emit to an std::ostream
// is here. No other functions should directly emit to the streams.
void EmitFileComment(std::ostream* file) {
*file << "// WARNING: This file is machine generated by fidlc.\n\n";
}
void EmitHeaderGuard(std::ostream* file) {
// TODO(fxbug.dev/704) Generate an appropriate header guard name.
*file << "#pragma once\n";
}
void EmitAllowlistCheck(std::ostream* file) {
*file << "#if !defined(FIDL_ALLOW_DEPRECATED_C_BINDINGS)\n";
*file << "#error This target is not allowed to include the deprecated C bindings header. \\\n";
*file << " Please consider migrating to the C++ bindings.\n";
*file << "#endif\n";
}
void EmitIncludeHeader(std::ostream* file, std::string_view 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::kBox:
ZX_PANIC("no box types should appear at this point");
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::kTransportSide:
case flat::Type::Kind::kPrimitive:
*file << member.type << " " << member.name;
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind.value()) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
ZX_PANIC("bad decl kind for member");
case flat::Decl::Kind::kNewType:
ZX_PANIC("c-codegen for new-types not implemented");
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
*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;
case flat::Type::Kind::kUntypedNumeric:
ZX_PANIC("should not have untyped numeric here");
}
}
void EmitMethodOutParamDecl(std::ostream* file, const CGenerator::Member& member) {
switch (member.kind) {
case flat::Type::Kind::kBox:
ZX_PANIC("no box types should appear at this point");
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::kTransportSide:
case flat::Type::Kind::kPrimitive:
*file << member.type << "* out_" << member.name;
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind.value()) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
ZX_PANIC("bad decl kind for member");
case flat::Decl::Kind::kNewType:
ZX_PANIC("c-codegen for new-types not implemented");
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
*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;
case flat::Type::Kind::kUntypedNumeric:
ZX_PANIC("should not have untyped numeric here");
}
}
void EmitClientMethodDecl(std::ostream* file, std::string_view 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 (const auto& member : response) {
*file << ", ";
EmitMethodOutParamDecl(file, member);
}
*file << ")";
}
void EmitServerMethodDecl(std::ostream* file, std::string_view 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, std::string_view protocol_name) {
*file << "zx_status_t " << protocol_name
<< "_dispatch(void* ctx, fidl_txn_t* txn, fidl_incoming_msg_t* msg, const " << protocol_name
<< "_ops_t* ops)";
}
void EmitServerTryDispatchDecl(std::ostream* file, std::string_view protocol_name) {
*file << "zx_status_t " << protocol_name
<< "_try_dispatch(void* ctx, fidl_txn_t* txn, fidl_incoming_msg_t* msg, const "
<< protocol_name << "_ops_t* ops)";
}
void EmitServerReplyDecl(std::ostream* file, std::string_view 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 << ")";
}
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) {
if (member.decl_kind.value() == flat::Decl::Kind::kTable) {
return true;
}
if (member.nullability == types::Nullability::kNullable) {
return member.decl_kind.value() == flat::Decl::Kind::kStruct ||
member.decl_kind.value() == flat::Decl::Kind::kUnion;
}
}
return false;
}
void EmitMeasureInParams(std::ostream* file, const std::vector<CGenerator::Member>& 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<CGenerator::Member>& params) {
for (const auto& member : params) {
if (member.max_num_elements == std::numeric_limits<uint32_t>::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 {
ZX_PANIC("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<CGenerator::Member>& 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 << ")";
}
void EmitMagicNumberCheck(std::ostream* file) {
*file << kIndent << "status = fidl_validate_txn_header(hdr);\n";
*file << kIndent << "if (status != ZX_OK) {\n";
*file << kIndent << kIndent << "FidlHandleCloseMany(msg->handles, msg->num_handles);\n";
*file << kIndent << kIndent << "ZX_DEBUG_ASSERT(status == ZX_ERR_PROTOCOL_NOT_SUPPORTED);";
*file << kIndent << kIndent << "return status;\n";
*file << kIndent << "}\n";
}
// This function assumes the |params| are part of a [ForDeprecatedCBindings] protocol.
// In particular, simple protocols 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 (IsStoredOutOfLine(member))
++count;
}
return count;
}
void EmitTxnHeader(std::ostream* file, std::string_view msg_name, std::string_view ordinal_name) {
*file << kIndent << "fidl_init_txn_header(&" << msg_name << "->hdr, 0, " << ordinal_name
<< ", FIDL_MESSAGE_HEADER_DYNAMIC_FLAGS_STRICT_METHOD);\n";
}
void EmitLinearizeMessage(std::ostream* file, std::string_view receiver, std::string_view 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::kBox:
ZX_PANIC("no box types should appear at this point");
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::kTransportSide:
case flat::Type::Kind::kPrimitive:
*file << kIndent << receiver << "->" << name << " = " << name << ";\n";
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind.value()) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
ZX_PANIC("bad decl kind for member");
case flat::Decl::Kind::kNewType:
ZX_PANIC("c-codegen for new-types not implemented");
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
*file << kIndent << receiver << "->" << name << " = " << name << ";\n";
break;
case flat::Decl::Kind::kTable:
ZX_PANIC("c-codegen for tables not implemented");
case flat::Decl::Kind::kUnion:
ZX_PANIC("c-codegen for unions not implemented");
case flat::Decl::Kind::kStruct:
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;
}
break;
case flat::Type::Kind::kUntypedNumeric:
ZX_PANIC("should not have untyped numeric here");
}
}
}
// Various computational helper routines.
void BitsValue(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::kUint8: {
auto& value = static_cast<const flat::NumericConstantValue<uint8_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint16: {
auto& value = static_cast<const flat::NumericConstantValue<uint16_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint32: {
auto& value = static_cast<const flat::NumericConstantValue<uint32_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint64: {
auto& value = static_cast<const flat::NumericConstantValue<uint64_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt8:
case flat::ConstantValue::Kind::kInt16:
case flat::ConstantValue::Kind::kInt32:
case flat::ConstantValue::Kind::kInt64:
case flat::ConstantValue::Kind::kBool:
case flat::ConstantValue::Kind::kFloat32:
case flat::ConstantValue::Kind::kFloat64:
case flat::ConstantValue::Kind::kDocComment:
case flat::ConstantValue::Kind::kString:
ZX_PANIC("bad primitive type for a bits declaration");
}
*out_value = member_value.str();
}
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 flat::NumericConstantValue<int8_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt16: {
auto& value = static_cast<const flat::NumericConstantValue<int16_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt32: {
auto& value = static_cast<const flat::NumericConstantValue<int32_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt64: {
auto& value = static_cast<const flat::NumericConstantValue<int64_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint8: {
auto& value = static_cast<const flat::NumericConstantValue<uint8_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint16: {
auto& value = static_cast<const flat::NumericConstantValue<uint16_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint32: {
auto& value = static_cast<const flat::NumericConstantValue<uint32_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint64: {
auto& value = static_cast<const flat::NumericConstantValue<uint64_t>&>(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::kDocComment:
case flat::ConstantValue::Kind::kString:
ZX_PANIC("bad primitive type for an enum");
}
*out_value = member_value.str();
}
void ArrayCountsAndElementTypeName(const flat::Type* type, std::vector<uint32_t>* out_array_counts,
std::string* out_element_type_name) {
std::vector<uint32_t> array_counts;
for (;;) {
switch (type->kind) {
default: {
*out_element_type_name = NameFlatCType(type);
*out_array_counts = array_counts;
return;
}
case flat::Type::Kind::kArray: {
auto array_type = static_cast<const flat::ArrayType*>(type);
array_counts.push_back(array_type->element_count->value);
type = array_type->element_type;
continue;
}
}
}
}
template <typename T>
CGenerator::Member CreateMember(const T& decl, bool* out_allowed = nullptr) {
std::string name = NameIdentifier(decl.name);
const flat::Type* type = decl.type_ctor->type;
// treat box types like we do nullable structs
if (type->kind == flat::Type::Kind::kBox)
type = static_cast<const flat::BoxType*>(type)->boxed_type;
auto type_name = NameFlatCType(type);
std::string element_type_name;
std::vector<uint32_t> array_counts;
types::Nullability nullability = types::Nullability::kNonnullable;
uint32_t max_num_elements = std::numeric_limits<uint32_t>::max();
if (out_allowed) {
*out_allowed = true;
}
std::optional<flat::Decl::Kind> decl_kind;
switch (type->kind) {
case flat::Type::Kind::kBox:
ZX_PANIC("no box types should appear at this point");
case flat::Type::Kind::kArray: {
ArrayCountsAndElementTypeName(type, &array_counts, &element_type_name);
break;
}
case flat::Type::Kind::kVector: {
auto vector_type = static_cast<const flat::VectorType*>(type);
const auto element_type = vector_type->element_type;
element_type_name = NameFlatCType(element_type);
max_num_elements = vector_type->element_count->value;
break;
}
case flat::Type::Kind::kIdentifier: {
auto identifier_type = static_cast<const flat::IdentifierType*>(type);
nullability = identifier_type->nullability;
if (out_allowed) {
*out_allowed = DeclAllowed(identifier_type->type_decl);
}
decl_kind = identifier_type->type_decl->kind;
break;
}
case flat::Type::Kind::kString: {
auto string_type = static_cast<const flat::StringType*>(type);
nullability = string_type->nullability;
max_num_elements = string_type->max_size->value;
break;
}
case flat::Type::Kind::kHandle:
case flat::Type::Kind::kTransportSide:
case flat::Type::Kind::kPrimitive:
break;
case flat::Type::Kind::kUntypedNumeric:
ZX_PANIC("should not have untyped numeric here");
}
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,
};
}
bool GetMethodParameters(const CGenerator::NamedMethod& method_info,
std::vector<CGenerator::Member>* request,
std::vector<CGenerator::Member>* response) {
if (request && method_info.request->parameters) {
request->reserve(method_info.request->parameters->size());
for (const auto& parameter : *method_info.request->parameters) {
bool allowed = true;
request->push_back(CreateMember(parameter, &allowed));
if (!allowed) {
request->clear();
if (response) {
response->clear();
}
return false;
}
}
}
if (response && method_info.response && method_info.response->parameters) {
response->reserve(method_info.response->parameters->size());
for (const auto& parameter : *method_info.response->parameters) {
bool allowed = true;
response->push_back(CreateMember(parameter, &allowed));
if (!allowed) {
if (request) {
request->clear();
}
response->clear();
return false;
}
}
}
return true;
}
} // namespace
uint32_t CGenerator::GetMaxHandlesFor(Transport transport, const TypeShape& typeshape) {
switch (transport) {
case Transport::Channel:
return std::min(kChannelMaxMessageHandles, typeshape.max_handles);
}
}
void CGenerator::GeneratePrologues() {
EmitFileComment(&file_);
EmitHeaderGuard(&file_);
EmitBlank(&file_);
EmitAllowlistCheck(&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>");
// Dependencies are in pointer order... change to a deterministic
// ordering prior to output.
std::set<std::string> add_includes;
for (const auto& dep : compilation_->direct_and_composed_dependencies) {
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(std::string_view name, types::PrimitiveSubtype subtype,
std::string_view value) {
std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype);
file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n";
}
void CGenerator::GeneratePrimitiveDefine(std::string_view name, types::PrimitiveSubtype subtype,
std::string_view 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;
}
} // switch
}
void CGenerator::GenerateStringDefine(std::string_view name, std::string_view value) {
file_ << "#define " << name << " " << value << "\n";
}
void CGenerator::GenerateIntegerTypedef(types::PrimitiveSubtype subtype, std::string_view name) {
std::string underlying_type = NamePrimitiveCType(subtype);
file_ << "typedef " << underlying_type << " " << name << ";\n";
}
void CGenerator::GenerateStructTypedef(std::string_view name) {
file_ << "typedef struct " << name << " " << name << ";\n";
}
void CGenerator::GenerateStructDeclaration(std::string_view name,
const std::vector<Member>& 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::GenerateTableDeclaration(std::string_view name) {
file_ << "struct " << name << " {\n";
file_ << kIndent << "fidl_table_t table_header;\n";
file_ << "};\n";
}
void CGenerator::GenerateTaggedUnionDeclaration(std::string_view name,
const std::vector<Member>& members) {
#ifdef FIDLC_DEPRECATE_C_UNIONS
file_ << "struct __attribute__ ((deprecated)) " << name << " {\n";
#else
file_ << "struct " << name << " {\n";
#endif
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";
}
std::map<const flat::Decl*, CGenerator::NamedBits> CGenerator::NameBits(
const std::vector<const flat::Bits*>& bits_infos) {
std::map<const flat::Decl*, NamedBits> named_bits;
for (const auto& bits_info : bits_infos) {
std::string bits_name = NameCodedName(bits_info->name);
named_bits.emplace(bits_info, NamedBits{std::move(bits_name), *bits_info});
}
return named_bits;
}
// TODO(fxbug.dev/27764) 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<const flat::Const*>& const_infos) {
std::map<const flat::Decl*, NamedConst> named_consts;
for (const auto& const_info : const_infos) {
named_consts.emplace(const_info, NamedConst{NameCodedName(const_info->name), *const_info});
}
return named_consts;
}
std::map<const flat::Decl*, CGenerator::NamedEnum> CGenerator::NameEnums(
const std::vector<const flat::Enum*>& enum_infos) {
std::map<const flat::Decl*, NamedEnum> named_enums;
for (const auto& enum_info : enum_infos) {
std::string enum_name = NameCodedName(enum_info->name);
named_enums.emplace(enum_info, NamedEnum{std::move(enum_name), *enum_info});
}
return named_enums;
}
std::map<const flat::Decl*, CGenerator::NamedProtocol> CGenerator::NameProtocols(
const std::vector<const flat::Protocol*>& protocol_infos) {
std::map<const flat::Decl*, NamedProtocol> named_protocols;
for (const auto& protocol_info : protocol_infos) {
NamedProtocol named_protocol;
named_protocol.c_name = NameCodedName(protocol_info->name);
if (protocol_info->attributes->Get("discoverable") != nullptr) {
named_protocol.discoverable_name = NameDiscoverable(*protocol_info);
}
named_protocol.transport = CGenerator::Transport::Channel;
for (const auto& method_with_info : protocol_info->all_methods) {
ZX_ASSERT(method_with_info.method != nullptr);
const auto& method = *method_with_info.method;
if (!MethodAllowed(method)) {
continue;
}
NamedMethod named_method;
std::string method_name = NameMethod(named_protocol.c_name, method);
named_method.ordinal = static_cast<uint64_t>(method.generated_ordinal64->value);
named_method.ordinal_name = NameOrdinal(method_name);
named_method.identifier = NameIdentifier(method.name);
named_method.c_name = method_name;
if (method.has_request) {
std::string c_name = NameMessage(method_name, types::MessageKind::kRequest);
std::string coded_name = NameTable(c_name);
TypeShape typeshape = TypeShape::ForEmptyPayload();
const std::vector<flat::Struct::Member>* members = nullptr;
if (method.maybe_request) {
auto id = static_cast<const flat::IdentifierType*>(method.maybe_request->type);
// Since no new uses of the C bindings are allowed, assert that payloads that are either
// unions or tables, as support for such payloads was added after C binding usage was
// frozen. The previous call to `MethodAllowed` should have exited early, so this assert
// should never be hit.
ZX_ASSERT_MSG(id->type_decl->kind == flat::Decl::Kind::kStruct,
"table/union method payloads disallowed");
auto as_struct = static_cast<const flat::Struct*>(id->type_decl);
typeshape = as_struct->typeshape(WireFormat::kV1NoEe);
members = &as_struct->members;
}
ZX_ASSERT(members == nullptr || !members->empty());
named_method.request = std::make_unique<NamedMessage>(
NamedMessage{std::move(c_name), std::move(coded_name), members, typeshape});
}
if (method.has_response) {
auto message_kind =
method.has_request ? types::MessageKind::kResponse : types::MessageKind::kEvent;
std::string c_name = NameMessage(method_name, message_kind);
std::string coded_name = NameTable(c_name);
TypeShape typeshape = TypeShape::ForEmptyPayload();
const std::vector<flat::Struct::Member>* members = nullptr;
if (method.maybe_response) {
auto id = static_cast<const flat::IdentifierType*>(method.maybe_response->type);
// Since no new uses of the C bindings are allowed, assert that payloads that are either
// unions or tables, as support for such payloads was added after C binding usage was
// frozen. The previous call to `MethodAllowed` should have exited early, so this assert
// should never be hit.
ZX_ASSERT_MSG(id->type_decl->kind == flat::Decl::Kind::kStruct,
"table/union method payloads disallowed");
auto as_struct = static_cast<const flat::Struct*>(id->type_decl);
typeshape = as_struct->typeshape(WireFormat::kV1NoEe);
members = &as_struct->members;
}
ZX_ASSERT(members == nullptr || !members->empty());
named_method.response = std::make_unique<NamedMessage>(
NamedMessage{std::move(c_name), std::move(coded_name), members, typeshape});
}
named_protocol.methods.push_back(std::move(named_method));
}
if (!named_protocol.methods.empty()) {
named_protocols.emplace(protocol_info, std::move(named_protocol));
}
}
return named_protocols;
}
std::map<const flat::Decl*, CGenerator::NamedStruct> CGenerator::NameStructs(
const std::vector<const flat::Struct*>& struct_infos,
const std::vector<const flat::Protocol*>& protocol_infos) {
std::set<const flat::Name> message_body_type_names;
for (const auto& protocol_info : protocol_infos) {
for (const auto& method_info : protocol_info->all_methods) {
if (method_info.method->maybe_request != nullptr) {
message_body_type_names.insert(method_info.method->maybe_request->layout.resolved().name());
}
if (method_info.method->maybe_response != nullptr) {
message_body_type_names.insert(
method_info.method->maybe_response->layout.resolved().name());
}
}
}
std::map<const flat::Decl*, NamedStruct> named_structs;
for (const auto& struct_info : struct_infos) {
// If this struct is only ever used as an anonymous transactional message body definition, there
// is no need to name it.
if (struct_info->name.as_anonymous() != nullptr &&
message_body_type_names.find(struct_info->name) != message_body_type_names.end()) {
continue;
}
std::string c_name = NameCodedName(struct_info->name);
std::string coded_name = c_name + "Coded";
named_structs.emplace(struct_info,
NamedStruct{std::move(c_name), std::move(coded_name), *struct_info});
}
return named_structs;
}
void CGenerator::ProduceBitsForwardDeclaration(const NamedBits& named_bits) {
auto subtype =
static_cast<const flat::PrimitiveType*>(named_bits.bits_info.subtype_ctor->type)->subtype;
GenerateIntegerTypedef(subtype, named_bits.name);
for (const auto& member : named_bits.bits_info.members) {
std::string member_name = named_bits.name + "_" + NameIdentifier(member.name);
std::string member_value;
BitsValue(member.value.get(), &member_value);
GenerateIntegerDefine(member_name, subtype, std::move(member_value));
}
EmitBlank(&file_);
}
void CGenerator::ProduceConstForwardDeclaration(const NamedConst& named_const) {
// TODO(fxbug.dev/27764)
}
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));
}
if (named_enum.enum_info.strictness == types::Strictness::kFlexible) {
// We emit the unknown member with two underscores to avoid any possibility
// of name clashes should the enum contain a member named 'unknown'.
std::string member_name = named_enum.name + "__UNKNOWN";
std::string member_value;
if (named_enum.enum_info.unknown_value_signed) {
member_value = std::to_string(named_enum.enum_info.unknown_value_signed.value());
} else {
member_value = std::to_string(named_enum.enum_info.unknown_value_unsigned.value());
}
GenerateIntegerDefine(member_name, subtype, member_value);
}
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolForwardDeclaration(const NamedProtocol& named_protocol) {
if (!named_protocol.discoverable_name.empty()) {
file_ << "#define " << named_protocol.c_name << "_Name \"" << named_protocol.discoverable_name
<< "\"\n";
}
for (const auto& method_info : named_protocol.methods) {
{
IOFlagsGuard reset_flags(&file_);
file_ << "#define " << method_info.ordinal_name << " ((uint64_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::ProduceProtocolExternDeclaration(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.methods) {
if (method_info.request) {
file_ << "__LOCAL extern const fidl_type_t " << method_info.request->coded_name << ";\n";
}
if (method_info.response) {
file_ << "__LOCAL 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_ctor->type->kind) {
case flat::Type::Kind::kPrimitive:
GeneratePrimitiveDefine(
named_const.name, static_cast<const flat::PrimitiveType*>(ci.type_ctor->type)->subtype,
static_cast<flat::LiteralConstant*>(ci.value.get())->literal->span().data());
break;
case flat::Type::Kind::kString:
GenerateStringDefine(
named_const.name,
static_cast<flat::LiteralConstant*>(ci.value.get())->literal->span().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<CGenerator::Member> members;
if (named_message.parameters) {
members.reserve(1 + named_message.parameters->size());
members.push_back(MessageHeader());
for (const auto& parameter : *named_message.parameters) {
members.push_back(CreateMember(parameter));
}
} else {
members.reserve(1);
members.push_back(MessageHeader());
}
GenerateStructDeclaration(named_message.c_name, members, StructKind::kMessage);
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolDeclaration(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.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(struct_member));
}
GenerateStructDeclaration(named_struct.c_name, members, StructKind::kNonmessage);
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolClientDeclaration(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
std::vector<Member> response;
if (GetMethodParameters(method_info, &request, &response)) {
if (CanGenerateCodecFunctions(request) && CanGenerateCodecFunctions(response)) {
EmitClientMethodDecl(&file_, method_info.c_name, request, response);
file_ << ";\n";
}
}
}
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolClientImplementation(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
std::vector<Member> response;
if (!GetMethodParameters(method_info, &request, &response) ||
!CanGenerateCodecFunctions(request) || !CanGenerateCodecFunctions(response)) {
continue;
}
size_t count = CountSecondaryObjects(request);
size_t request_hcount =
GetMaxHandlesFor(named_protocol.transport, method_info.request->typeshape);
size_t response_hcount = 0;
if (method_info.response) {
response_hcount = GetMaxHandlesFor(named_protocol.transport, method_info.response->typeshape);
}
bool has_padding = method_info.request->typeshape.has_padding;
bool encode_request = !request.empty() && ((count > 0) || (request_hcount > 0) || has_padding);
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";
EmitTxnHeader(&file_, "_request", method_info.ordinal_name);
EmitLinearizeMessage(&file_, "_request", "_wr_bytes", request);
const char* handle_infos_value = "NULL";
const char* handle_dispositions_value = "NULL";
if (request_hcount > 0) {
file_ << kIndent << "zx_handle_disposition_t _handle_dispositions[" << request_hcount
<< "];\n";
handle_dispositions_value = "_handle_dispositions";
}
if (response_hcount > 0) {
file_ << kIndent << "zx_handle_info_t _handle_infos[" << response_hcount << "];\n";
handle_infos_value = "_handle_infos";
}
if (encode_request) {
file_ << kIndent << "uint32_t _wr_num_handles = 0u;\n";
file_ << kIndent << "if (unlikely(_wr_num_bytes < sizeof(fidl_message_header_t))) {\n";
file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
file_ << kIndent << "}\n";
file_ << kIndent
<< "uint32_t _trimmed_wr_num_bytes = _wr_num_bytes - "
"(uint32_t)(sizeof(fidl_message_header_t));\n";
file_ << kIndent << "if (unlikely(_wr_bytes == NULL)) {\n";
file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
file_ << kIndent << "}\n";
file_ << kIndent
<< "uint8_t* _trimmed_wr_bytes = (uint8_t*)_wr_bytes + "
"sizeof(fidl_message_header_t);\n";
file_ << kIndent << "zx_status_t _encode_status = fidl_encode_etc(&"
<< method_info.request->coded_name << ", _trimmed_wr_bytes, _trimmed_wr_num_bytes, "
<< handle_dispositions_value << ", " << request_hcount
<< ", &_wr_num_handles, NULL);\n";
file_ << kIndent << "if (_encode_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _encode_status;\n";
} else {
file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only request\n";
}
if (!method_info.response) {
switch (named_protocol.transport) {
case Transport::Channel:
if (encode_request) {
file_ << kIndent
<< "return zx_channel_write_etc(_channel, 0u, _wr_bytes, _wr_num_bytes, "
<< handle_dispositions_value << ", _wr_num_handles);\n";
} else {
file_ << kIndent
<< "return zx_channel_write_etc(_channel, 0u, _wr_bytes, _wr_num_bytes, NULL, "
"0);\n";
}
break;
}
} else {
file_ << kIndent << "zx_status_t _status;\n";
file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")";
EmitMeasureOutParams(&file_, response);
file_ << ";\n";
file_ << kIndent << "uint32_t _rd_num_bytes_max = _rd_num_bytes;\n";
file_ << kIndent << "FIDL_ALIGNDECL uint8_t _rd_bytes_storage[_rd_num_bytes_max];\n";
file_ << kIndent << "uint8_t* _rd_bytes = _rd_bytes_storage;\n";
if (!response.empty()) {
file_ << kIndent << method_info.response->c_name << "* _response = ("
<< method_info.response->c_name << "*)_rd_bytes;\n";
}
switch (named_protocol.transport) {
case Transport::Channel:
file_ << kIndent << "zx_channel_call_etc_args_t _args = {\n";
file_ << kIndent << kIndent << ".wr_bytes = _wr_bytes,\n";
file_ << kIndent << kIndent << ".wr_handles = " << handle_dispositions_value << ",\n";
file_ << kIndent << kIndent << ".rd_bytes = _rd_bytes,\n";
file_ << kIndent << kIndent << ".rd_handles = " << handle_infos_value << ",\n";
file_ << kIndent << kIndent << ".wr_num_bytes = _wr_num_bytes,\n";
if (encode_request) {
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_max,\n";
file_ << kIndent << kIndent << ".rd_num_handles = " << response_hcount << ",\n";
file_ << kIndent << "};\n";
file_ << kIndent << "uint32_t _actual_num_bytes = 0u;\n";
file_ << kIndent << "uint32_t _actual_num_handles = 0u;\n";
file_ << "_status = zx_channel_call_etc(_channel, 0u, ZX_TIME_INFINITE, &_args, "
"&_actual_num_bytes, &_actual_num_handles);\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);
has_padding = method_info.response->typeshape.has_padding;
bool decode_response =
!response.empty() && ((count > 0) || (response_hcount > 0) || has_padding);
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 (response_hcount > 0) {
file_ << kIndent << kIndent
<< "FidlHandleInfoCloseMany(_handle_infos, _actual_num_handles);\n";
}
file_ << kIndent << kIndent << "return ZX_ERR_BUFFER_TOO_SMALL;\n";
file_ << kIndent << "}\n";
}
if (decode_response) {
// TODO(fxbug.dev/7499): Validate the response ordinal. C++ bindings also need to do that.
switch (named_protocol.transport) {
case Transport::Channel:
file_ << kIndent
<< "if (unlikely(_actual_num_bytes < sizeof(fidl_message_header_t))) {\n";
file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
file_ << kIndent << "}\n";
file_ << kIndent
<< "uint32_t _trimmed_rd_num_bytes = _actual_num_bytes - "
"(uint32_t)(sizeof(fidl_message_header_t));\n";
file_ << kIndent << "if (unlikely(_rd_bytes == NULL)) {\n";
file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
file_ << kIndent << "}\n";
file_ << kIndent
<< "uint8_t* _trimmed_rd_bytes = (uint8_t*)_rd_bytes + "
"sizeof(fidl_message_header_t);\n";
file_ << kIndent << "zx_status_t _decode_status = fidl_decode_etc(&"
<< method_info.response->coded_name
<< ", _trimmed_rd_bytes, _trimmed_rd_num_bytes, " << handle_infos_value
<< ", _actual_num_handles, NULL);\n";
break;
}
file_ << kIndent << "if (_decode_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _decode_status;\n";
} else {
file_ << kIndent << "// OPTIMIZED AWAY fidl_decode() of POD-only response\n";
}
for (const auto& member : response) {
const auto& name = member.name;
switch (member.kind) {
case flat::Type::Kind::kBox:
ZX_PANIC("no box types should appear at this point");
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::kTransportSide:
case flat::Type::Kind::kPrimitive:
file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind.value()) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
ZX_PANIC("bad decl kind for member");
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
break;
case flat::Decl::Kind::kTable:
ZX_PANIC("c-codegen for tables not implemented");
case flat::Decl::Kind::kUnion:
ZX_PANIC("c-codegen for unions not implemented");
case flat::Decl::Kind::kNewType:
ZX_PANIC("c-codegen for new-types not implemented");
case flat::Decl::Kind::kStruct:
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;
case flat::Type::Kind::kUntypedNumeric:
ZX_PANIC("should not have untyped numeric here");
}
}
file_ << kIndent << "return ZX_OK;\n";
}
file_ << "}\n\n";
}
}
void CGenerator::ProduceProtocolServerDeclaration(const NamedProtocol& named_protocol) {
file_ << "typedef struct " << named_protocol.c_name << "_ops {\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
if (GetMethodParameters(method_info, &request, nullptr) && CanGenerateCodecFunctions(request)) {
bool has_response = method_info.response != nullptr;
file_ << kIndent;
EmitServerMethodDecl(&file_, method_info.identifier, request, has_response);
file_ << ";\n";
}
}
file_ << "} " << named_protocol.c_name << "_ops_t;\n\n";
EmitServerDispatchDecl(&file_, named_protocol.c_name);
file_ << ";\n";
EmitServerTryDispatchDecl(&file_, named_protocol.c_name);
file_ << ";\n\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request || !method_info.response)
continue;
std::vector<Member> response;
if (GetMethodParameters(method_info, nullptr, &response) &&
CanGenerateCodecFunctions(response)) {
EmitServerReplyDecl(&file_, method_info.c_name, response);
file_ << ";\n";
}
}
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolServerImplementation(const NamedProtocol& named_protocol) {
EmitServerTryDispatchDecl(&file_, named_protocol.c_name);
file_ << " {\n";
file_ << kIndent << "if (msg->num_bytes < sizeof(fidl_message_header_t)) {\n";
file_ << kIndent << kIndent << "FidlHandleCloseMany(msg->handles, msg->num_handles);\n";
file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
file_ << kIndent << "}\n";
file_ << kIndent << "zx_status_t status = ZX_OK;\n";
file_ << kIndent << "fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;\n";
EmitMagicNumberCheck(&file_);
file_ << kIndent << "switch (hdr->ordinal) {\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
if (!GetMethodParameters(method_info, &request, nullptr)) {
continue;
}
file_ << kIndent << "case " << method_info.ordinal_name << ": {\n";
if (!request.empty()) {
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";
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::kBox:
ZX_PANIC("no box types should appear at this point");
case flat::Type::Kind::kArray:
case flat::Type::Kind::kHandle:
case flat::Type::Kind::kTransportSide:
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.value()) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
ZX_PANIC("bad decl kind for member");
case flat::Decl::Kind::kNewType:
ZX_PANIC("c-codegen for new-types not implemented");
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
file_ << ", request->" << member.name;
break;
case flat::Decl::Kind::kTable:
ZX_PANIC("c-codegen for tables not yet implemented");
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;
}
break;
case flat::Type::Kind::kUntypedNumeric:
ZX_PANIC("should not have untyped numeric here");
}
}
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_protocol.c_name);
file_ << " {\n";
file_ << kIndent << "zx_status_t status = " << named_protocol.c_name
<< "_try_dispatch(ctx, txn, msg, ops);\n";
file_ << kIndent << "if (status == ZX_ERR_NOT_SUPPORTED)\n";
file_ << kIndent << kIndent << "FidlHandleCloseMany(msg->handles, msg->num_handles);\n";
file_ << kIndent << "return status;\n";
file_ << "}\n\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request || !method_info.response)
continue;
std::vector<Member> response;
if (!GetMethodParameters(method_info, nullptr, &response) ||
!CanGenerateCodecFunctions(response)) {
continue;
}
size_t hcount = GetMaxHandlesFor(named_protocol.transport, method_info.response->typeshape);
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";
EmitTxnHeader(&file_, "_response", method_info.ordinal_name);
EmitLinearizeMessage(&file_, "_response", "_wr_bytes", response);
const char* handle_value = "NULL";
const char* handle_metadata_value = "NULL";
if (hcount > 0) {
file_ << kIndent << "zx_handle_t _handles[" << hcount << "];\n";
file_ << kIndent << "fidl_channel_handle_metadata_t _handle_metadata[" << hcount << "];\n";
handle_value = "_handles";
handle_metadata_value = "_handle_metadata";
}
file_ << kIndent << "fidl_outgoing_msg_t _msg = {\n";
file_ << kIndent << kIndent << ".type = FIDL_OUTGOING_MSG_TYPE_BYTE,\n";
file_ << kIndent << kIndent << ".byte = {\n";
file_ << kIndent << kIndent << kIndent << ".bytes = _wr_bytes,\n";
file_ << kIndent << kIndent << kIndent << ".handles = " << handle_value << ",\n";
file_ << kIndent << kIndent << kIndent << ".handle_metadata = (fidl_handle_metadata_t*)("
<< handle_metadata_value << "),\n";
file_ << kIndent << kIndent << kIndent << ".num_bytes = _wr_num_bytes,\n";
file_ << kIndent << kIndent << kIndent << ".num_handles = " << hcount << ",\n";
file_ << kIndent << kIndent << "},\n";
file_ << kIndent << "};\n";
bool has_padding = method_info.response->typeshape.has_padding;
bool encode_response = (hcount > 0) || CountSecondaryObjects(response) > 0 || has_padding;
if (encode_response) {
file_ << kIndent << "zx_status_t _status = fidl_encode_msg(&"
<< method_info.response->coded_name << ", &_msg.byte, &_msg.byte.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<const flat::Decl*, NamedBits> named_bits = NameBits(compilation_->declarations.bits);
std::map<const flat::Decl*, NamedConst> named_consts =
NameConsts(compilation_->declarations.consts);
std::map<const flat::Decl*, NamedEnum> named_enums = NameEnums(compilation_->declarations.enums);
std::map<const flat::Decl*, NamedProtocol> named_protocols =
NameProtocols(compilation_->declarations.protocols);
std::map<const flat::Decl*, NamedStruct> named_structs =
NameStructs(compilation_->declarations.structs, compilation_->declarations.protocols);
file_ << "\n// Forward declarations\n\n";
for (const auto* decl : compilation_->declaration_order) {
if (!DeclAllowed(decl)) {
continue;
}
switch (decl->kind) {
case flat::Decl::Kind::kBuiltin:
ZX_PANIC("unexpected builtin");
case flat::Decl::Kind::kBits: {
auto iter = named_bits.find(decl);
if (iter != named_bits.end()) {
ProduceBitsForwardDeclaration(iter->second);
}
break;
}
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::kNewType:
// TODO(fxbug.dev/7807): Do more than nothing.
break;
case flat::Decl::Kind::kProtocol: {
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kResource:
// Do nothing.
break;
case flat::Decl::Kind::kService:
// Do nothing.
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:
// Do nothing.
break;
case flat::Decl::Kind::kTypeAlias:
// TODO(fxbug.dev/7807): Do more than nothing.
break;
case flat::Decl::Kind::kUnion:
// Do nothing.
break;
} // switch
}
file_ << "\n// Extern declarations\n\n";
for (const auto* decl : compilation_->declaration_order) {
if (!DeclAllowed(decl)) {
continue;
}
switch (decl->kind) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kNewType:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only messages have extern fidl_type_t declarations.
break;
case flat::Decl::Kind::kProtocol: {
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolExternDeclaration(iter->second);
}
break;
}
} // switch
}
file_ << "\n// Declarations\n\n";
for (const auto* decl : compilation_->declaration_order) {
if (!DeclAllowed(decl)) {
continue;
}
switch (decl->kind) {
case flat::Decl::Kind::kBuiltin:
ZX_PANIC("unexpected builtin");
case flat::Decl::Kind::kBits:
// Bits can be entirely forward declared, as they have no
// dependencies other than standard headers.
break;
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::kNewType:
// TODO(fxbug.dev/7807): Do more than nothing.
break;
case flat::Decl::Kind::kProtocol: {
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kResource:
// Do nothing.
break;
case flat::Decl::Kind::kService:
// Do nothing.
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:
// Do nothing.
break;
case flat::Decl::Kind::kTypeAlias:
// TODO(fxbug.dev/7807): Do more than nothing.
break;
case flat::Decl::Kind::kUnion:
// Do nothing.
break;
} // switch
}
file_ << "\n// Simple bindings \n\n";
for (const auto* decl : compilation_->declaration_order) {
switch (decl->kind) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kNewType:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only protocols have client declarations.
break;
case flat::Decl::Kind::kProtocol: {
if (!HasSimpleLayout(decl))
break;
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolClientDeclaration(iter->second);
ProduceProtocolServerDeclaration(iter->second);
}
break;
}
} // switch
}
GenerateEpilogues();
return std::move(file_);
}
std::ostringstream CGenerator::ProduceClient() {
EmitFileComment(&file_);
EmitIncludeHeader(&file_, "<lib/fidl/coding.h>");
EmitIncludeHeader(&file_, "<lib/fidl/internal.h>");
EmitIncludeHeader(&file_, "<lib/fidl/txn_header.h>");
EmitIncludeHeader(&file_, "<alloca.h>");
EmitIncludeHeader(&file_, "<string.h>");
EmitIncludeHeader(&file_, "<zircon/assert.h>");
EmitIncludeHeader(&file_, "<zircon/syscalls.h>");
EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(compilation_->library_name) + ">");
EmitBlank(&file_);
std::map<const flat::Decl*, NamedProtocol> named_protocols =
NameProtocols(compilation_->declarations.protocols);
for (const auto* decl : compilation_->declaration_order) {
switch (decl->kind) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kNewType:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only protocols have client implementations.
break;
case flat::Decl::Kind::kProtocol: {
if (!HasSimpleLayout(decl))
break;
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolClientImplementation(iter->second);
}
break;
}
} // switch
}
return std::move(file_);
}
std::ostringstream CGenerator::ProduceServer() {
EmitFileComment(&file_);
EmitIncludeHeader(&file_, "<lib/fidl/coding.h>");
EmitIncludeHeader(&file_, "<lib/fidl/internal.h>");
EmitIncludeHeader(&file_, "<lib/fidl/txn_header.h>");
EmitIncludeHeader(&file_, "<alloca.h>");
EmitIncludeHeader(&file_, "<string.h>");
EmitIncludeHeader(&file_, "<zircon/assert.h>");
EmitIncludeHeader(&file_, "<zircon/syscalls.h>");
EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(compilation_->library_name) + ">");
EmitBlank(&file_);
std::map<const flat::Decl*, NamedProtocol> named_protocols =
NameProtocols(compilation_->declarations.protocols);
for (const auto* decl : compilation_->declaration_order) {
switch (decl->kind) {
case flat::Decl::Kind::kBuiltin:
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kNewType:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only protocols have client implementations.
break;
case flat::Decl::Kind::kProtocol: {
if (!HasSimpleLayout(decl))
break;
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolServerImplementation(iter->second);
}
break;
}
} // switch
}
return std::move(file_);
}
} // namespace fidl