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