blob: e90fd6ca111e0f352c61fd9fa2b681136a64c068 [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 {
// RAII helper class to reset the iostream to its original flags.
class IOFlagsGuard {
public:
explicit IOFlagsGuard(std::ostream* stream)
: stream_(stream), flags_(stream_->flags()) {
}
~IOFlagsGuard() {
stream_->setf(flags_);
}
private:
std::ostream* stream_;
std::ios::fmtflags flags_;
};
// Various string values are looked up or computed in these
// functions. Nothing else should be dealing in string literals, or
// computing strings from these or AST values.
constexpr const char* kIndent = " ";
CGenerator::Member MessageHeader() {
return {
flat::Type::Kind::kIdentifier,
flat::Decl::Kind::kStruct,
"fidl_message_header_t",
"hdr",
{},
{},
types::Nullability::kNonnullable,
{},
};
}
CGenerator::Member EmptyStructMember() {
return {
.kind = flat::Type::Kind::kPrimitive,
.type = NamePrimitiveCType(types::PrimitiveSubtype::kUint8),
// Prepend the reserved uint8_t field with a single underscore, which is
// for reserved identifiers (see ISO C standard, section 7.1.3
// <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>).
.name = "_reserved",
};
}
CGenerator::Transport ParseTransport(StringView view) {
if (view == "SocketControl") {
return CGenerator::Transport::SocketControl;
}
return CGenerator::Transport::Channel;
}
// Functions named "Emit..." are called to actually emit to an std::ostream
// is here. No other functions should directly emit to the streams.
std::ostream& operator<<(std::ostream& stream, StringView view) {
stream.rdbuf()->sputn(view.data(), view.size());
return stream;
}
void EmitFileComment(std::ostream* file) {
*file << "// WARNING: This file is machine generated by fidlc.\n\n";
}
void EmitHeaderGuard(std::ostream* file) {
// TODO(704) Generate an appropriate header guard name.
*file << "#pragma once\n";
}
void EmitIncludeHeader(std::ostream* file, StringView header) {
*file << "#include " << header << "\n";
}
void EmitBeginExternC(std::ostream* file) {
*file << "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n";
}
void EmitEndExternC(std::ostream* file) {
*file << "#if defined(__cplusplus)\n}\n#endif\n";
}
void EmitBlank(std::ostream* file) {
*file << "\n";
}
void EmitMemberDecl(std::ostream* file, const CGenerator::Member& member) {
*file << member.type << " " << member.name;
for (uint32_t array_count : member.array_counts) {
*file << "[" << array_count << "]";
}
}
void EmitMethodInParamDecl(std::ostream* file, const CGenerator::Member& member) {
switch (member.kind) {
case flat::Type::Kind::kArray:
*file << "const " << member.type << " " << member.name;
for (uint32_t array_count : member.array_counts) {
*file << "[" << array_count << "]";
}
break;
case flat::Type::Kind::kVector:
*file << "const " << member.element_type << "* " << member.name << "_data, "
<< "size_t " << member.name << "_count";
break;
case flat::Type::Kind::kString:
*file << "const char* " << member.name << "_data, "
<< "size_t " << member.name << "_size";
break;
case flat::Type::Kind::kHandle:
case flat::Type::Kind::kRequestHandle:
case flat::Type::Kind::kPrimitive:
*file << member.type << " " << member.name;
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind) {
case flat::Decl::Kind::kConst:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kInterface:
*file << member.type << " " << member.name;
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kUnion:
case flat::Decl::Kind::kXUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
*file << "const " << member.type << " " << member.name;
break;
case types::Nullability::kNonnullable:
*file << "const " << member.type << "* " << member.name;
break;
}
break;
}
break;
}
}
void EmitMethodOutParamDecl(std::ostream* file, const CGenerator::Member& member) {
switch (member.kind) {
case flat::Type::Kind::kArray:
*file << member.type << " out_" << member.name;
for (uint32_t array_count : member.array_counts) {
*file << "[" << array_count << "]";
}
break;
case flat::Type::Kind::kVector:
*file << member.element_type << "* " << member.name << "_buffer, "
<< "size_t " << member.name << "_capacity, "
<< "size_t* out_" << member.name << "_count";
break;
case flat::Type::Kind::kString:
*file << "char* " << member.name << "_buffer, "
<< "size_t " << member.name << "_capacity, "
<< "size_t* out_" << member.name << "_size";
break;
case flat::Type::Kind::kHandle:
case flat::Type::Kind::kRequestHandle:
case flat::Type::Kind::kPrimitive:
*file << member.type << "* out_" << member.name;
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind) {
case flat::Decl::Kind::kConst:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kInterface:
*file << member.type << "* out_" << member.name;
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kUnion:
case flat::Decl::Kind::kXUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
*file << member.type << " out_" << member.name;
break;
case types::Nullability::kNonnullable:
*file << member.type << "* out_" << member.name;
break;
}
break;
}
break;
}
}
void EmitClientMethodDecl(std::ostream* file, StringView method_name,
const std::vector<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 EmitServerTryDispatchDecl(std::ostream* file, StringView interface_name) {
*file << "zx_status_t " << interface_name
<< "_try_dispatch(void* ctx, fidl_txn_t* txn, fidl_msg_t* msg, const "
<< interface_name << "_ops_t* ops)";
}
void EmitServerReplyDecl(std::ostream* file, StringView method_name,
const std::vector<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) {
return member.nullability == types::Nullability::kNullable &&
(member.decl_kind == flat::Decl::Kind::kStruct || member.decl_kind == flat::Decl::Kind::kUnion || member.decl_kind == flat::Decl::Kind::kXUnion);
}
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 {
assert(false && "only vector/string has size limit");
}
*file << kIndent
<< "if (" << param_name << " > " << member.max_num_elements << ") {\n";
*file << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
*file << kIndent << "}\n";
}
}
void EmitMeasureOutParams(std::ostream* file,
const std::vector<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 << ")";
}
// 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 (IsStoredOutOfLine(member))
++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 << ", ";
EmitArraySizeOf(file, member);
*file << ");\n";
break;
case flat::Type::Kind::kVector:
*file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
*file << kIndent << receiver << "->" << name << ".count = " << name << "_count;\n";
*file << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name << "_data, sizeof(*" << name << "_data) * " << name << "_count);\n";
*file << kIndent << "_next += FIDL_ALIGN(sizeof(*" << name << "_data) * " << name << "_count);\n";
break;
case flat::Type::Kind::kString:
*file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
*file << kIndent << receiver << "->" << name << ".size = " << name << "_size;\n";
*file << kIndent << "_next += FIDL_ALIGN(" << name << "_size);\n";
*file << kIndent << "if (" << name << "_data) {\n";
*file << kIndent << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name << "_data, " << name << "_size);\n";
*file << kIndent << "} else {\n";
*file << kIndent << kIndent << "if (" << name << "_size != 0) {\n";
*file << kIndent << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
*file << kIndent << kIndent << "}\n";
if (member.nullability == types::Nullability::kNullable) {
*file << kIndent << kIndent << receiver << "->" << name << ".data = NULL;\n";
}
*file << kIndent << "}\n";
break;
case flat::Type::Kind::kHandle:
case flat::Type::Kind::kRequestHandle:
case flat::Type::Kind::kPrimitive:
*file << kIndent << receiver << "->" << name << " = " << name << ";\n";
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind) {
case flat::Decl::Kind::kConst:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kInterface:
*file << kIndent << receiver << "->" << name << " = " << name << ";\n";
break;
case flat::Decl::Kind::kTable:
assert(false && "c-codegen for tables not yet implemented");
break;
case flat::Decl::Kind::kXUnion:
assert(false && "c-codegen for extensible unions not yet implemented");
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
*file << kIndent << "if (" << name << ") {\n";
*file << kIndent << kIndent << receiver << "->" << name << " = (void*)&" << bytes << "[_next];\n";
*file << kIndent << kIndent << "memcpy(" << receiver << "->" << name << ", " << name << ", sizeof(*" << name << "));\n";
*file << kIndent << kIndent << "_next += sizeof(*" << name << ");\n";
*file << kIndent << "} else {\n";
*file << kIndent << kIndent << receiver << "->" << name << " = NULL;\n";
*file << kIndent << "}\n";
break;
case types::Nullability::kNonnullable:
*file << kIndent << receiver << "->" << name << " = *" << name << ";\n";
break;
}
break;
}
}
}
}
// Various computational helper routines.
void EnumValue(const flat::Constant* constant, std::string* out_value) {
std::ostringstream member_value;
const flat::ConstantValue& const_val = constant->Value();
switch (const_val.kind) {
case flat::ConstantValue::Kind::kInt8: {
auto value = static_cast<const 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::kString:
assert(false && "bad primitive type for an enum");
break;
}
*out_value = member_value.str();
}
flat::Decl::Kind GetDeclKind(const flat::Library* library, const flat::Type* type) {
if (type->kind != flat::Type::Kind::kIdentifier)
return flat::Decl::Kind::kConst;
auto identifier_type = static_cast<const flat::IdentifierType*>(type);
auto named_decl = library->LookupDeclByName(identifier_type->name);
assert(named_decl && "library must contain declaration");
return named_decl->kind;
}
void ArrayCountsAndElementTypeName(const flat::Library* library, const flat::Type* type,
std::vector<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, GetDeclKind(library, type));
*out_array_counts = array_counts;
return;
}
case flat::Type::Kind::kArray: {
auto array_type = static_cast<const flat::ArrayType*>(type);
auto element_count = static_cast<const flat::Size&>(array_type->element_count->Value());
array_counts.push_back(element_count.value);
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 decl_kind = GetDeclKind(library, type);
auto type_name = NameFlatCType(type, decl_kind);
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();
switch (type->kind) {
case flat::Type::Kind::kArray: {
ArrayCountsAndElementTypeName(library,
type,
&array_counts,
&element_type_name);
break;
}
case flat::Type::Kind::kVector: {
auto vector_type = static_cast<const flat::VectorType*>(type);
const flat::Type* element_type = vector_type->element_type.get();
element_type_name =
NameFlatCType(element_type, GetDeclKind(library, element_type));
max_num_elements =
static_cast<const flat::Size&>(vector_type->element_count.get()->Value()).value;
break;
}
case flat::Type::Kind::kIdentifier: {
auto identifier_type =
static_cast<const flat::IdentifierType*>(type);
nullability = identifier_type->nullability;
break;
}
case flat::Type::Kind::kString: {
auto string_type = static_cast<const flat::StringType*>(type);
nullability = string_type->nullability;
max_num_elements =
static_cast<const flat::Size&>(string_type->max_size.get()->Value()).value;
break;
}
case flat::Type::Kind::kHandle:
break;
case flat::Type::Kind::kRequestHandle:
break;
case flat::Type::Kind::kPrimitive:
break;
}
return CGenerator::Member{
type->kind,
decl_kind,
std::move(type_name),
std::move(name),
std::move(element_type_name),
std::move(array_counts),
nullability,
max_num_elements,
};
}
std::vector<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;
}
std::vector<CGenerator::Member>
GenerateMembers(const flat::Library* library,
const std::vector<flat::XUnion::Member>& xunion_members) {
std::vector<CGenerator::Member> members;
members.reserve(xunion_members.size());
for (const auto& xunion_member : xunion_members) {
members.push_back(CreateMember(library, xunion_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>");
// Dependencies are in pointer order... change to a deterministic
// ordering prior to output.
std::set<std::string> add_includes;
for (const auto& dep_library : library_->dependencies()) {
if (dep_library == library_)
continue;
if (dep_library->HasAttribute("Internal"))
continue;
add_includes.insert(NameLibraryCHeader(dep_library->name()));
}
for (const auto& include : add_includes) {
EmitIncludeHeader(&file_, "<" + include + ">");
}
EmitBlank(&file_);
EmitBeginExternC(&file_);
EmitBlank(&file_);
}
void CGenerator::GenerateEpilogues() {
EmitEndExternC(&file_);
}
void CGenerator::GenerateIntegerDefine(StringView name, types::PrimitiveSubtype subtype,
StringView value) {
std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype);
file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n";
}
void CGenerator::GeneratePrimitiveDefine(StringView name, types::PrimitiveSubtype subtype,
StringView value) {
switch (subtype) {
case types::PrimitiveSubtype::kInt8:
case types::PrimitiveSubtype::kInt16:
case types::PrimitiveSubtype::kInt32:
case types::PrimitiveSubtype::kInt64:
case types::PrimitiveSubtype::kUint8:
case types::PrimitiveSubtype::kUint16:
case types::PrimitiveSubtype::kUint32:
case types::PrimitiveSubtype::kUint64: {
std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype);
file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n";
break;
}
case types::PrimitiveSubtype::kBool:
case types::PrimitiveSubtype::kFloat32:
case types::PrimitiveSubtype::kFloat64: {
file_ << "#define " << name << " "
<< "(" << value << ")\n";
break;
}
default:
abort();
}
}
void CGenerator::GenerateStringDefine(StringView name, StringView value) {
file_ << "#define " << name << " " << value << "\n";
}
void CGenerator::GenerateIntegerTypedef(types::PrimitiveSubtype subtype, StringView name) {
std::string underlying_type = NamePrimitiveCType(subtype);
file_ << "typedef " << underlying_type << " " << name << ";\n";
}
void CGenerator::GenerateStructTypedef(StringView name) {
file_ << "typedef struct " << name << " " << name << ";\n";
}
void CGenerator::GenerateStructDeclaration(StringView name, const std::vector<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::GenerateTaggedUnionDeclaration(StringView name,
const std::vector<Member>& members) {
file_ << "struct " << name << " {\n";
file_ << kIndent << "fidl_union_tag_t tag;\n";
file_ << kIndent << "union {\n";
for (const auto& member : members) {
file_ << kIndent << kIndent;
EmitMemberDecl(&file_, member);
file_ << ";\n";
}
file_ << kIndent << "};\n";
file_ << "};\n";
}
void CGenerator::GenerateTaggedXUnionDeclaration(StringView name,
const std::vector<Member>& members) {
// XUnions are (intentionally) unimplemented for C bindings.
}
// 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{NameName(const_info->name, "_", "_"), *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);
}
// TODO: Transport::SocketControl should imply NoHandles.
named_interface.transport = ParseTransport(interface_info->GetAttribute("Transport"));
for (const auto& method_pointer : interface_info->all_methods) {
assert(method_pointer != nullptr);
const auto& method = *method_pointer;
NamedMethod named_method;
std::string method_name = NameMethod(named_interface.c_name, method);
named_method.ordinal = method.ordinal->value;
named_method.ordinal_name = NameOrdinal(method_name);
named_method.identifier = NameIdentifier(method.name);
named_method.c_name = method_name;
if (method.maybe_request != nullptr) {
std::string c_name = NameMessage(method_name, types::MessageKind::kRequest);
std::string coded_name = NameTable(c_name);
named_method.request = std::make_unique<NamedMessage>(NamedMessage{
std::move(c_name), std::move(coded_name),
method.maybe_request->members, method.maybe_request->typeshape});
}
if (method.maybe_response != nullptr) {
if (method.maybe_request == nullptr) {
std::string c_name =
NameMessage(method_name, types::MessageKind::kEvent);
std::string coded_name = NameTable(c_name);
named_method.response = std::make_unique<NamedMessage>(
NamedMessage{std::move(c_name), std::move(coded_name),
method.maybe_response->members,
method.maybe_response->typeshape});
} else {
std::string c_name = NameMessage(method_name, types::MessageKind::kResponse);
std::string coded_name = NameTable(c_name);
named_method.response = std::make_unique<NamedMessage>(
NamedMessage{std::move(c_name), std::move(coded_name),
method.maybe_response->members,
method.maybe_response->typeshape});
}
}
named_interface.methods.push_back(std::move(named_method));
}
named_interfaces.emplace(interface_info.get(), std::move(named_interface));
}
return named_interfaces;
}
std::map<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) {
if (struct_info->anonymous)
continue;
std::string c_name = NameName(struct_info->name, "_", "_");
std::string coded_name = c_name + "Coded";
named_structs.emplace(struct_info.get(),
NamedStruct{std::move(c_name), std::move(coded_name), *struct_info});
}
return named_structs;
}
std::map<const flat::Decl*, CGenerator::NamedTable>
CGenerator::NameTables(const std::vector<std::unique_ptr<flat::Table>>& table_infos) {
std::map<const flat::Decl*, NamedTable> named_tables;
for (const auto& table_info : table_infos) {
std::string c_name = NameName(table_info->name, "_", "_");
std::string coded_name = c_name + "Coded";
named_tables.emplace(table_info.get(),
NamedTable{std::move(c_name), std::move(coded_name), *table_info});
}
return named_tables;
}
std::map<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;
}
std::map<const flat::Decl*, CGenerator::NamedXUnion>
CGenerator::NameXUnions(const std::vector<std::unique_ptr<flat::XUnion>>& xunion_infos) {
std::map<const flat::Decl*, NamedXUnion> named_xunions;
for (const auto& xunion_info : xunion_infos) {
std::string xunion_name = NameName(xunion_info->name, "_", "_");
named_xunions.emplace(xunion_info.get(), NamedXUnion{std::move(xunion_name), *xunion_info});
}
return named_xunions;
}
void CGenerator::ProduceConstForwardDeclaration(const NamedConst& named_const) {
// TODO(TO-702)
}
void CGenerator::ProduceEnumForwardDeclaration(const NamedEnum& named_enum) {
types::PrimitiveSubtype subtype = named_enum.enum_info.type->subtype;
GenerateIntegerTypedef(subtype, named_enum.name);
for (const auto& member : named_enum.enum_info.members) {
std::string member_name = named_enum.name + "_" + NameIdentifier(member.name);
std::string member_value;
EnumValue(member.value.get(), &member_value);
GenerateIntegerDefine(member_name, subtype, std::move(member_value));
}
EmitBlank(&file_);
}
void CGenerator::ProduceInterfaceForwardDeclaration(const NamedInterface& named_interface) {
if (!named_interface.discoverable_name.empty()) {
file_ << "#define " << named_interface.c_name << "_Name \"" << named_interface.discoverable_name << "\"\n";
}
for (const auto& method_info : named_interface.methods) {
{
IOFlagsGuard reset_flags(&file_);
file_ << "#define " << method_info.ordinal_name << " ((uint32_t)0x"
<< std::uppercase << std::hex << method_info.ordinal << std::dec << ")\n";
}
if (method_info.request)
GenerateStructTypedef(method_info.request->c_name);
if (method_info.response)
GenerateStructTypedef(method_info.response->c_name);
}
}
void CGenerator::ProduceStructForwardDeclaration(const NamedStruct& named_struct) {
GenerateStructTypedef(named_struct.c_name);
}
void CGenerator::ProduceTableForwardDeclaration(const NamedTable& named_struct) {
GenerateStructTypedef(named_struct.c_name);
}
void CGenerator::ProduceUnionForwardDeclaration(const NamedUnion& named_union) {
GenerateStructTypedef(named_union.name);
}
void CGenerator::ProduceXUnionForwardDeclaration(const NamedXUnion& named_xunion) {
GenerateStructTypedef(named_xunion.name);
}
void CGenerator::ProduceInterfaceExternDeclaration(const NamedInterface& named_interface) {
for (const auto& method_info : named_interface.methods) {
if (method_info.request)
file_ << "extern const fidl_type_t " << method_info.request->coded_name << ";\n";
if (method_info.response)
file_ << "extern const fidl_type_t " << method_info.response->coded_name
<< ";\n";
}
}
void CGenerator::ProduceConstDeclaration(const NamedConst& named_const) {
const flat::Const& ci = named_const.const_info;
// Some constants are not literals. Odd.
if (ci.value->kind != flat::Constant::Kind::kLiteral) {
return;
}
switch (ci.type->kind) {
case flat::Type::Kind::kPrimitive:
GeneratePrimitiveDefine(named_const.name,
static_cast<flat::PrimitiveType*>(ci.type.get())->subtype,
static_cast<flat::LiteralConstant*>(ci.value.get())->literal->location().data());
break;
case flat::Type::Kind::kString:
GenerateStringDefine(named_const.name,
static_cast<flat::LiteralConstant*>(ci.value.get())->literal->location().data());
break;
default:
abort();
}
EmitBlank(&file_);
}
void CGenerator::ProduceMessageDeclaration(const NamedMessage& named_message) {
// When we generate a request or response struct (i.e. messages), we must
// both include the message header, and ensure the message is FIDL aligned.
std::vector<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, StructKind::kMessage);
EmitBlank(&file_);
}
void CGenerator::ProduceInterfaceDeclaration(const NamedInterface& named_interface) {
for (const auto& method_info : named_interface.methods) {
if (method_info.request)
ProduceMessageDeclaration(*method_info.request);
if (method_info.response)
ProduceMessageDeclaration(*method_info.response);
}
}
void CGenerator::ProduceStructDeclaration(const NamedStruct& named_struct) {
std::vector<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, StructKind::kNonmessage);
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::ProduceXUnionDeclaration(const NamedXUnion& named_xunion) {
std::vector<CGenerator::Member> members =
GenerateMembers(library_, named_xunion.xunion_info.members);
GenerateTaggedXUnionDeclaration(named_xunion.name, members);
uint32_t tag = 0u;
for (const auto& member : named_xunion.xunion_info.members) {
std::string tag_name = NameXUnionTag(named_xunion.name, member);
auto xunion_tag_type = types::PrimitiveSubtype::kUint32;
std::ostringstream value;
value << tag;
GenerateIntegerDefine(std::move(tag_name), xunion_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);
size_t count = CountSecondaryObjects(request);
size_t hcount = method_info.request->typeshape.MaxHandles();
bool encode = ((count > 0) || (hcount > 0));
EmitClientMethodDecl(&file_, method_info.c_name, request, response);
file_ << " {\n";
EmitParameterSizeValidation(&file_, request);
file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.request->c_name << ")";
EmitMeasureInParams(&file_, request);
file_ << ";\n";
file_ << kIndent << "FIDL_ALIGNDECL char _wr_bytes[_wr_num_bytes];\n";
file_ << kIndent << method_info.request->c_name << "* _request = (" << method_info.request->c_name << "*)_wr_bytes;\n";
file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n";
file_ << kIndent << "_request->hdr.ordinal = " << method_info.ordinal << ";\n";
EmitLinearizeMessage(&file_, "_request", "_wr_bytes", request);
if (encode) {
file_ << kIndent << "uint32_t _wr_num_handles = 0u;\n";
switch (named_interface.transport) {
case Transport::Channel:
file_ << kIndent << "zx_handle_t _handles[ZX_CHANNEL_MAX_MSG_HANDLES];\n";
file_ << kIndent << "zx_status_t _status = fidl_encode(&" << method_info.request->coded_name
<< ", _wr_bytes, _wr_num_bytes, _handles, ZX_CHANNEL_MAX_MSG_HANDLES"
<< ", &_wr_num_handles, NULL);\n";
break;
case Transport::SocketControl:
file_ << kIndent << "zx_status_t _status = fidl_encode(&" << method_info.request->coded_name
<< ", _wr_bytes, _wr_num_bytes, NULL, 0, &_wr_num_handles, NULL);\n";
break;
}
file_ << kIndent << "if (_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _status;\n";
} else {
file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only request\n";
}
if (!method_info.response) {
switch (named_interface.transport) {
case Transport::Channel:
if (encode) {
file_ << kIndent << "return zx_channel_write(_channel, 0u, _wr_bytes, _wr_num_bytes, _handles, _wr_num_handles);\n";
} else {
file_ << kIndent << "return zx_channel_write(_channel, 0u, _wr_bytes, _wr_num_bytes, NULL, 0);\n";
}
break;
case Transport::SocketControl:
file_ << kIndent << "return fidl_socket_write_control(_channel, _wr_bytes, _wr_num_bytes);\n";
break;
}
} else {
file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")";
EmitMeasureOutParams(&file_, response);
file_ << ";\n";
file_ << kIndent << "FIDL_ALIGNDECL char _rd_bytes[_rd_num_bytes];\n";
if (!response.empty())
file_ << kIndent << method_info.response->c_name << "* _response = (" << method_info.response->c_name << "*)_rd_bytes;\n";
switch (named_interface.transport) {
case Transport::Channel:
if (!encode) {
file_ << kIndent << "zx_handle_t _handles[ZX_CHANNEL_MAX_MSG_HANDLES];\n";
}
file_ << kIndent << "zx_channel_call_args_t _args = {\n";
file_ << kIndent << kIndent << ".wr_bytes = _wr_bytes,\n";
if (encode) {
file_ << kIndent << kIndent << ".wr_handles = _handles,\n";
} else {
file_ << kIndent << kIndent << ".wr_handles = NULL,\n";
}
file_ << kIndent << kIndent << ".rd_bytes = _rd_bytes,\n";
file_ << kIndent << kIndent << ".rd_handles = _handles,\n";
file_ << kIndent << kIndent << ".wr_num_bytes = _wr_num_bytes,\n";
if (encode) {
file_ << kIndent << kIndent << ".wr_num_handles = _wr_num_handles,\n";
} else {
file_ << kIndent << kIndent << ".wr_num_handles = 0,\n";
}
file_ << kIndent << kIndent << ".rd_num_bytes = _rd_num_bytes,\n";
file_ << kIndent << kIndent << ".rd_num_handles = ZX_CHANNEL_MAX_MSG_HANDLES,\n";
file_ << kIndent << "};\n";
file_ << kIndent << "uint32_t _actual_num_bytes = 0u;\n";
file_ << kIndent << "uint32_t _actual_num_handles = 0u;\n";
if (encode) {
file_ << kIndent;
} else {
file_ << kIndent << "zx_status_t ";
}
file_ << "_status = zx_channel_call(_channel, 0u, ZX_TIME_INFINITE, &_args, &_actual_num_bytes, &_actual_num_handles);\n";
break;
case Transport::SocketControl:
file_ << kIndent << "size_t _actual_num_bytes = 0u;\n";
if (encode) {
file_ << kIndent;
} else {
file_ << kIndent << "zx_status_t ";
}
file_ << "_status = fidl_socket_call_control(_channel, _wr_bytes, _wr_num_bytes, _rd_bytes, _rd_num_bytes, &_actual_num_bytes);\n";
break;
}
file_ << kIndent << "if (_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _status;\n";
// We check that we have enough capacity to copy out the parameters
// before decoding the message so that we can close the handles
// using |_handles| rather than trying to find them in the decoded
// message.
count = CountSecondaryObjects(response);
if (count > 0u) {
file_ << kIndent << "if ";
if (count > 1u)
file_ << "(";
size_t i = 0;
for (const auto& member : response) {
if (member.kind == flat::Type::Kind::kVector) {
if (i++ > 0u)
file_ << " || ";
file_ << "(_response->" << member.name << ".count > " << member.name << "_capacity)";
} else if (member.kind == flat::Type::Kind::kString) {
if (i++ > 0u)
file_ << " || ";
file_ << "(_response->" << member.name << ".size > " << member.name << "_capacity)";
} else if (IsStoredOutOfLine(member)) {
if (i++ > 0u)
file_ << " || ";
file_ << "((uintptr_t)_response->" << member.name << " == FIDL_ALLOC_PRESENT && out_" << member.name << " == NULL)";
}
}
if (count > 1u)
file_ << ")";
file_ << " {\n";
if (named_interface.transport == Transport::Channel) {
file_ << kIndent << kIndent << "zx_handle_close_many(_handles, _actual_num_handles);\n";
}
file_ << kIndent << kIndent << "return ZX_ERR_BUFFER_TOO_SMALL;\n";
file_ << kIndent << "}\n";
}
hcount = method_info.response->typeshape.MaxHandles();
if ((count == 0) && (hcount == 0)) {
file_ << kIndent << "// OPTIMIZED AWAY fidl_decode() of POD-only response\n";
if (named_interface.transport == Transport::Channel) {
file_ << kIndent << "if (_actual_num_handles > 0) {\n";
file_ << kIndent << kIndent << "zx_handle_close_many(_handles, _actual_num_handles);\n";
file_ << kIndent << kIndent << "return ZX_ERR_INTERNAL;\n"; // WHAT ERROR?
file_ << kIndent << "}\n";
}
} else {
// TODO(FIDL-162): Validate the response ordinal. C++ bindings also need to do that.
switch (named_interface.transport) {
case Transport::Channel:
file_ << kIndent << "_status = fidl_decode(&" << method_info.response->coded_name
<< ", _rd_bytes, _actual_num_bytes, _handles, _actual_num_handles, NULL);\n";
break;
case Transport::SocketControl:
file_ << kIndent << "_status = fidl_decode(&" << method_info.response->coded_name
<< ", _rd_bytes, _actual_num_bytes, NULL, 0, NULL);\n";
break;
}
file_ << kIndent << "if (_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _status;\n";
}
for (const auto& member : response) {
const auto& name = member.name;
switch (member.kind) {
case flat::Type::Kind::kArray:
file_ << kIndent << "memcpy(out_" << name << ", _response->" << name << ", ";
EmitArraySizeOf(&file_, member);
file_ << ");\n";
break;
case flat::Type::Kind::kVector:
file_ << kIndent << "memcpy(" << name << "_buffer, _response->" << name << ".data, sizeof(*" << name << "_buffer) * _response->" << name << ".count);\n";
file_ << kIndent << "*out_" << name << "_count = _response->" << name << ".count;\n";
break;
case flat::Type::Kind::kString:
file_ << kIndent << "memcpy(" << name << "_buffer, _response->" << name << ".data, _response->" << name << ".size);\n";
file_ << kIndent << "*out_" << name << "_size = _response->" << name << ".size;\n";
break;
case flat::Type::Kind::kHandle:
case flat::Type::Kind::kRequestHandle:
case flat::Type::Kind::kPrimitive:
file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
break;
case flat::Type::Kind::kIdentifier:
switch (member.decl_kind) {
case flat::Decl::Kind::kConst:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kInterface:
file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
break;
case flat::Decl::Kind::kTable:
assert(false && "c-codegen for tables not yet implemented");
break;
case flat::Decl::Kind::kXUnion:
assert(false && "c-codegen for extensible unions not yet implemented");
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
file_ << kIndent << "if (_response->" << name << ") {\n";
file_ << kIndent << kIndent << "*out_" << name << " = *(_response->" << name << ");\n";
file_ << kIndent << "} else {\n";
// We don't have a great way of signaling that the optional response member
// was not in the message. That means these bindings aren't particularly
// useful when the client needs to extract that bit. The best we can do is
// zero out the value to make sure the client has defined behavior.
//
// In many cases, the response contains other information (e.g., a status code)
// that lets the client do something reasonable.
file_ << kIndent << kIndent << "memset(out_" << name << ", 0, sizeof(*out_" << name << "));\n";
file_ << kIndent << "}\n";
break;
case types::Nullability::kNonnullable:
file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
break;
}
break;
}
break;
}
}
file_ << kIndent << "return ZX_OK;\n";
}
file_ << "}\n\n";
}
} // namespace fidl
void CGenerator::ProduceInterfaceServerDeclaration(const NamedInterface& named_interface) {
file_ << "typedef struct " << named_interface.c_name << "_ops {\n";
for (const auto& method_info : named_interface.methods) {
if (!method_info.request)
continue;
std::vector<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";
EmitServerTryDispatchDecl(&file_, named_interface.c_name);
file_ << ";\n\n";
for (const auto& method_info : named_interface.methods) {
if (!method_info.request || !method_info.response)
continue;
std::vector<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) {
EmitServerTryDispatchDecl(&file_, named_interface.c_name);
file_ << " {\n";
file_ << kIndent << "if (msg->num_bytes < sizeof(fidl_message_header_t)) {\n";
file_ << kIndent << kIndent << "zx_handle_close_many(msg->handles, msg->num_handles);\n";
file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
file_ << kIndent << "}\n";
file_ << kIndent << "fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;\n";
file_ << kIndent << "zx_status_t status = ZX_OK;\n";
file_ << kIndent << "switch (hdr->ordinal) {\n";
for (const auto& method_info : named_interface.methods) {
if (!method_info.request)
continue;
file_ << kIndent << "case " << method_info.ordinal_name << ": {\n";
file_ << kIndent << kIndent << "status = fidl_decode_msg(&" << method_info.request->coded_name << ", msg, NULL);\n";
file_ << kIndent << kIndent << "if (status != ZX_OK)\n";
file_ << kIndent << kIndent << kIndent << "break;\n";
std::vector<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:
switch (member.decl_kind) {
case flat::Decl::Kind::kConst:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kInterface:
file_ << ", request->" << member.name;
break;
case flat::Decl::Kind::kTable:
assert(false && "c-codegen for tables not yet implemented");
break;
case flat::Decl::Kind::kXUnion:
assert(false && "c-codegen for extensible unions not yet implemented");
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
file_ << ", request->" << member.name;
break;
case types::Nullability::kNonnullable:
file_ << ", &(request->" << member.name << ")";
break;
}
break;
}
}
}
if (method_info.response != nullptr)
file_ << ", txn";
file_ << ");\n";
file_ << kIndent << kIndent << "break;\n";
file_ << kIndent << "}\n";
}
file_ << kIndent << "default: {\n";
file_ << kIndent << kIndent << "return ZX_ERR_NOT_SUPPORTED;\n";
file_ << kIndent << "}\n";
file_ << kIndent << "}\n";
file_ << kIndent << "if ("
<< "status != ZX_OK && "
<< "status != ZX_ERR_STOP && "
<< "status != ZX_ERR_NEXT && "
<< "status != ZX_ERR_ASYNC) {\n";
file_ << kIndent << kIndent << "return ZX_ERR_INTERNAL;\n";
file_ << kIndent << "} else {\n";
file_ << kIndent << kIndent << "return status;\n";
file_ << kIndent << "}\n";
file_ << "}\n\n";
EmitServerDispatchDecl(&file_, named_interface.c_name);
file_ << " {\n";
file_ << kIndent << "zx_status_t status = "
<< named_interface.c_name << "_try_dispatch(ctx, txn, msg, ops);\n";
file_ << kIndent << "if (status == ZX_ERR_NOT_SUPPORTED)\n";
file_ << kIndent << kIndent << "zx_handle_close_many(msg->handles, msg->num_handles);\n";
file_ << kIndent << "return status;\n";
file_ << "}\n\n";
for (const auto& method_info : named_interface.methods) {
if (!method_info.request || !method_info.response)
continue;
std::vector<Member> response;
GetMethodParameters(library_, method_info, nullptr, &response);
size_t hcount = method_info.response->typeshape.MaxHandles();
bool encode = ((CountSecondaryObjects(response) > 0) || (hcount > 0));
EmitServerReplyDecl(&file_, method_info.c_name, response);
file_ << " {\n";
file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.response->c_name << ")";
EmitMeasureInParams(&file_, response);
file_ << ";\n";
file_ << kIndent << "char _wr_bytes[_wr_num_bytes];\n";
file_ << kIndent << method_info.response->c_name << "* _response = (" << method_info.response->c_name << "*)_wr_bytes;\n";
file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n";
file_ << kIndent << "_response->hdr.ordinal = " << method_info.ordinal << ";\n";
EmitLinearizeMessage(&file_, "_response", "_wr_bytes", response);
if (encode) {
file_ << kIndent << "zx_handle_t _handles[ZX_CHANNEL_MAX_MSG_HANDLES];\n";
}
file_ << kIndent << "fidl_msg_t _msg = {\n";
file_ << kIndent << kIndent << ".bytes = _wr_bytes,\n";
if (encode) {
file_ << kIndent << kIndent << ".handles = _handles,\n";
} else {
file_ << kIndent << kIndent << ".handles = NULL,\n";
}
file_ << kIndent << kIndent << ".num_bytes = _wr_num_bytes,\n";
if (encode) {
file_ << kIndent << kIndent << ".num_handles = ZX_CHANNEL_MAX_MSG_HANDLES,\n";
} else {
file_ << kIndent << kIndent << ".num_handles = 0,\n";
}
file_ << kIndent << "};\n";
if (encode) {
file_ << kIndent << "zx_status_t _status = fidl_encode_msg(&"
<< method_info.response->coded_name << ", &_msg, &_msg.num_handles, NULL);\n";
file_ << kIndent << "if (_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _status;\n";
} else {
file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only reply\n";
}
file_ << kIndent << "return _txn->reply(_txn, &_msg);\n";
file_ << "}\n\n";
}
}
std::ostringstream CGenerator::ProduceHeader() {
GeneratePrologues();
std::map<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*, NamedTable> named_tables =
NameTables(library_->table_declarations_);
std::map<const flat::Decl*, NamedUnion> named_unions =
NameUnions(library_->union_declarations_);
std::map<const flat::Decl*, NamedXUnion> named_xunions =
NameXUnions(library_->xunion_declarations_);
file_ << "\n// Forward declarations\n\n";
for (const auto* decl : library_->declaration_order_) {
switch (decl->kind) {
case flat::Decl::Kind::kConst: {
auto iter = named_consts.find(decl);
if (iter != named_consts.end()) {
ProduceConstForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kEnum: {
auto iter = named_enums.find(decl);
if (iter != named_enums.end()) {
ProduceEnumForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kInterface: {
auto iter = named_interfaces.find(decl);
if (iter != named_interfaces.end()) {
ProduceInterfaceForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kStruct: {
auto iter = named_structs.find(decl);
if (iter != named_structs.end()) {
ProduceStructForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kTable: {
auto iter = named_tables.find(decl);
if (iter != named_tables.end()) {
ProduceTableForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kUnion: {
auto iter = named_unions.find(decl);
if (iter != named_unions.end()) {
ProduceUnionForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kXUnion: {
auto iter = named_xunions.find(decl);
if (iter != named_xunions.end()) {
ProduceXUnionForwardDeclaration(iter->second);
}
break;
}
default:
abort();
}
}
file_ << "\n// Extern declarations\n\n";
for (const auto* decl : library_->declaration_order_) {
switch (decl->kind) {
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kUnion:
case flat::Decl::Kind::kXUnion:
// Only messages have extern fidl_type_t declarations.
break;
case flat::Decl::Kind::kInterface: {
auto iter = named_interfaces.find(decl);
if (iter != named_interfaces.end()) {
ProduceInterfaceExternDeclaration(iter->second);
}
break;
}
default:
abort();
}
}
file_ << "\n// Declarations\n\n";
for (const auto* decl : library_->declaration_order_) {
switch (decl->kind) {
case flat::Decl::Kind::kConst: {
auto iter = named_consts.find(decl);
if (iter != named_consts.end()) {
ProduceConstDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kEnum:
// Enums can be entirely forward declared, as they have no
// dependencies other than standard headers.
break;
case flat::Decl::Kind::kInterface: {
auto iter = named_interfaces.find(decl);
if (iter != named_interfaces.end()) {
ProduceInterfaceDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kStruct: {
auto iter = named_structs.find(decl);
if (iter != named_structs.end()) {
ProduceStructDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kTable:
// Tables are entirely forward declared, and their body is defined only in
// implementation files.
break;
case flat::Decl::Kind::kUnion: {
auto iter = named_unions.find(decl);
if (iter != named_unions.end()) {
ProduceUnionDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kXUnion: {
auto iter = named_xunions.find(decl);
if (iter != named_xunions.end()) {
ProduceXUnionDeclaration(iter->second);
}
break;
}
default:
abort();
}
}
file_ << "\n// Simple bindings \n\n";
for (const auto* decl : library_->declaration_order_) {
switch (decl->kind) {
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kUnion:
case flat::Decl::Kind::kXUnion:
// 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_, "<lib/fidl/transport.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::kTable:
case flat::Decl::Kind::kUnion:
case flat::Decl::Kind::kXUnion:
// 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::kTable:
case flat::Decl::Kind::kUnion:
case flat::Decl::Kind::kXUnion:
// 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