blob: 31a90493149ee706ad6e98842eb29465641c7dba [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/flat_ast.h"
#include "fidl/names.h"
#include "fidl/type_shape.h"
namespace fidl {
namespace {
// RAII helper class to reset the iostream to its original flags.
class IOFlagsGuard {
public:
explicit IOFlagsGuard(std::ostream* stream) : stream_(stream), flags_(stream_->flags()) {}
~IOFlagsGuard() { stream_->setf(flags_); }
private:
std::ostream* stream_;
std::ios::fmtflags flags_;
};
// Various string values are looked up or computed in these
// functions. Nothing else should be dealing in string literals, or
// computing strings from these or AST values.
constexpr const char* kIndent = " ";
// Mapping of library name to set of declaration names.
// These declarations are treated as though they have the
// [ForDeprecatedCBindings] attribute even though they violate the constraints
// enforced on them.
//
// For protocols this means that some of the methods can't be supported and
// will simply be left out (unless they're listed below in allowed_methods).
//
// For structs this means that a member can have an unsupported type such as a
// vector of strings or a union.
const std::map<std::string, std::set<std::string>> allowed_decls({
{"fuchsia.tracing.provider", {"Provider", "ProviderConfig", "StartOptions"}},
{"fuchsia.logger", {"Log", "LogSink", "LogMessage", "LogListenerSafe", "LogFilterOptions"}},
{"fuchsia.device.manager",
{
"BindInstruction",
"CompositeDeviceDescriptor",
"Coordinator",
"DevhostController",
"DeviceController",
"DeviceFragment",
"DeviceFragmentPart",
"DeviceMetadata",
"DeviceProperty",
}},
{"fuchsia.hardware.power.statecontrol", {"Admin"}},
{"fidl.test.llcpp.dirent", {"DirEntTestInterface"}},
});
bool DeclAlwaysAllowed(const flat::Name& name) {
auto library_name = flat::LibraryName(name.library(), ".");
auto iter = allowed_decls.find(library_name);
if (iter != allowed_decls.end()) {
const auto& decls = iter->second;
if (decls.find(std::string(name.decl_name())) != decls.end()) {
return true;
}
}
return false;
}
// Mapping of library name to mapping of protocol name to set of methods.
// Data structures should be generated for these methods even if they violate
// the constraints of the simple C bindings.
std::map<std::string, std::map<std::string, std::set<std::string>>> allowed_methods({
{"fuchsia.device.manager", {{"DeviceController", {"CompleteRemoval", "Unbind"}}}},
{"fuchsia.hardware.power.statecontrol",
{{"Admin", {"Poweroff", "Reboot", "RebootToBootloader", "RebootToRecovery", "SuspendToRam"}}}},
{"fuchsia.io", {{"Node", {"Describe", "OnOpen", "Open"}}}},
});
bool MethodAlwaysAllowed(const flat::Protocol::Method& method) {
auto library_name = flat::LibraryName(method.owning_protocol->name.library(), ".");
auto iter = allowed_methods.find(library_name);
if (iter != allowed_methods.end()) {
const auto& protocols = iter->second;
auto protocol = protocols.find(std::string(method.owning_protocol->name.decl_name()));
if (protocol != protocols.end()) {
const auto& methods = protocol->second;
if (methods.find(std::string(method.name.data())) != methods.end()) {
return true;
}
}
}
return false;
}
bool TypeAllowed(const flat::Library* library, const flat::Type* type);
bool DeclAllowed(const flat::Decl* decl) {
if (HasSimpleLayout(decl) || DeclAlwaysAllowed(decl->name)) {
return true;
}
switch (decl->kind) {
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
// bits, const, enum are always allowed.
return true;
default:
return false;
}
}
bool IdentifierAllowed(const flat::Library* library, const flat::Name& name) {
if (DeclAlwaysAllowed(name)) {
return true;
}
const flat::Decl* decl = library->LookupDeclByName(name);
assert(decl != nullptr);
return DeclAllowed(decl);
}
bool TypeAllowed(const flat::Library* library, const flat::Type* type) {
assert(type != nullptr);
if (type->kind == flat::Type::Kind::kIdentifier) {
auto identifier_type = static_cast<const flat::IdentifierType*>(type);
if (!IdentifierAllowed(library, identifier_type->name)) {
return false;
}
}
return true;
}
bool MessageStructAllowed(const flat::Library* library, const flat::Struct* args) {
if (!args) {
return true;
}
for (const auto& member : args->members) {
if (!TypeAllowed(library, member.type_ctor->type)) {
return false;
}
}
return true;
}
bool MethodAllowed(const flat::Library* library, const flat::Protocol::Method& method) {
return MethodAlwaysAllowed(method) || (MessageStructAllowed(library, method.maybe_request) &&
MessageStructAllowed(library, method.maybe_response));
}
CGenerator::Member MessageHeader() {
return {
flat::Type::Kind::kIdentifier,
flat::Decl::Kind::kStruct,
"fidl_message_header_t",
"hdr",
{},
{},
types::Nullability::kNonnullable,
{},
};
}
CGenerator::Member EmptyStructMember() {
return {
.kind = flat::Type::Kind::kPrimitive,
.type = NamePrimitiveCType(types::PrimitiveSubtype::kUint8),
// Prepend the reserved uint8_t field with a single underscore, which is
// for reserved identifiers (see ISO C standard, section 7.1.3
// <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>).
.name = "_reserved",
};
}
CGenerator::Transport ParseTransport(std::string_view view) {
return CGenerator::Transport::Channel;
}
// Can encode and decode functions be generated for these members?
bool CanGenerateCodecFunctions(const std::vector<CGenerator::Member>& members) {
for (const auto& m : members) {
switch (m.decl_kind) {
case flat::Decl::Kind::kUnion:
return false;
default:
break;
}
}
return true;
}
// Functions named "Emit..." are called to actually emit to an std::ostream
// is here. No other functions should directly emit to the streams.
void EmitFileComment(std::ostream* file) {
*file << "// WARNING: This file is machine generated by fidlc.\n\n";
}
void EmitHeaderGuard(std::ostream* file) {
// TODO(fxbug.dev/704) Generate an appropriate header guard name.
*file << "#pragma once\n";
}
void EmitIncludeHeader(std::ostream* file, std::string_view header) {
*file << "#include " << header << "\n";
}
void EmitBeginExternC(std::ostream* file) {
*file << "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n";
}
void EmitEndExternC(std::ostream* file) { *file << "#if defined(__cplusplus)\n}\n#endif\n"; }
void EmitBlank(std::ostream* file) { *file << "\n"; }
void EmitMemberDecl(std::ostream* file, const CGenerator::Member& member) {
*file << member.type << " " << member.name;
for (uint32_t array_count : member.array_counts) {
*file << "[" << array_count << "]";
}
}
void EmitMethodInParamDecl(std::ostream* file, const CGenerator::Member& member) {
switch (member.kind) {
case flat::Type::Kind::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:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
*file << member.type << " " << member.name;
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
*file << "const " << member.type << " " << member.name;
break;
case types::Nullability::kNonnullable:
*file << "const " << member.type << "* " << member.name;
break;
}
break;
}
break;
}
}
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:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
*file << member.type << "* out_" << member.name;
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
*file << member.type << " out_" << member.name;
break;
case types::Nullability::kNonnullable:
*file << member.type << "* out_" << member.name;
break;
}
break;
}
break;
}
}
void EmitClientMethodDecl(std::ostream* file, std::string_view method_name,
const std::vector<CGenerator::Member>& request,
const std::vector<CGenerator::Member>& response) {
*file << "zx_status_t " << method_name << "(zx_handle_t _channel";
for (const auto& member : request) {
*file << ", ";
EmitMethodInParamDecl(file, member);
}
for (auto member : response) {
*file << ", ";
EmitMethodOutParamDecl(file, member);
}
*file << ")";
}
void EmitServerMethodDecl(std::ostream* file, std::string_view method_name,
const std::vector<CGenerator::Member>& request, bool has_response) {
*file << "zx_status_t "
<< " (*" << method_name << ")(void* ctx";
for (const auto& member : request) {
*file << ", ";
EmitMethodInParamDecl(file, member);
}
if (has_response) {
*file << ", fidl_txn_t* txn";
}
*file << ")";
}
void EmitServerDispatchDecl(std::ostream* file, std::string_view protocol_name) {
*file << "zx_status_t " << protocol_name
<< "_dispatch(void* ctx, fidl_txn_t* txn, fidl_incoming_msg_t* msg, const " << protocol_name
<< "_ops_t* ops)";
}
void EmitServerTryDispatchDecl(std::ostream* file, std::string_view protocol_name) {
*file << "zx_status_t " << protocol_name
<< "_try_dispatch(void* ctx, fidl_txn_t* txn, fidl_incoming_msg_t* msg, const "
<< protocol_name << "_ops_t* ops)";
}
void EmitServerReplyDecl(std::ostream* file, std::string_view method_name,
const std::vector<CGenerator::Member>& response) {
*file << "zx_status_t " << method_name << "_reply(fidl_txn_t* _txn";
for (const auto& member : response) {
*file << ", ";
EmitMethodInParamDecl(file, member);
}
*file << ")";
}
bool IsStoredOutOfLine(const CGenerator::Member& member) {
if (member.kind == flat::Type::Kind::kVector || member.kind == flat::Type::Kind::kString)
return true;
if (member.kind == flat::Type::Kind::kIdentifier) {
if (member.decl_kind == flat::Decl::Kind::kTable)
return true;
if (member.nullability == types::Nullability::kNullable)
return member.decl_kind == flat::Decl::Kind::kStruct ||
member.decl_kind == flat::Decl::Kind::kUnion;
}
return false;
}
void EmitMeasureInParams(std::ostream* file, const std::vector<CGenerator::Member>& params) {
for (const auto& member : params) {
if (member.kind == flat::Type::Kind::kVector)
*file << " + FIDL_ALIGN(sizeof(*" << member.name << "_data) * " << member.name << "_count)";
else if (member.kind == flat::Type::Kind::kString)
*file << " + FIDL_ALIGN(" << member.name << "_size)";
else if (IsStoredOutOfLine(member))
*file << " + (" << member.name << " ? FIDL_ALIGN(sizeof(*" << member.name << ")) : 0u)";
}
}
void EmitParameterSizeValidation(std::ostream* file,
const std::vector<CGenerator::Member>& params) {
for (const auto& member : params) {
if (member.max_num_elements == std::numeric_limits<uint32_t>::max())
continue;
std::string param_name;
if (member.kind == flat::Type::Kind::kVector) {
param_name = member.name + "_count";
} else if (member.kind == flat::Type::Kind::kString) {
param_name = member.name + "_size";
} else {
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 << ")";
}
void EmitMagicNumberCheck(std::ostream* file) {
*file << kIndent << "status = fidl_validate_txn_header(hdr);\n";
*file << kIndent << "if (status != ZX_OK) {\n";
*file << kIndent << kIndent << "FidlHandleInfoCloseMany(msg->handles, msg->num_handles);\n";
*file << kIndent << kIndent << "ZX_DEBUG_ASSERT(status == ZX_ERR_PROTOCOL_NOT_SUPPORTED);";
*file << kIndent << kIndent << "return status;\n";
*file << kIndent << "}\n";
}
// This function assumes the |params| are part of a [ForDeprecatedCBindings] protocol.
// In particular, simple protocols don't have nullable structs or nested
// vectors. The only secondary objects they contain are top-level vectors and
// strings.
size_t CountSecondaryObjects(const std::vector<CGenerator::Member>& params) {
size_t count = 0u;
for (const auto& member : params) {
if (IsStoredOutOfLine(member))
++count;
}
return count;
}
void EmitTxnHeader(std::ostream* file, const std::string& msg_name,
const std::string& ordinal_name) {
*file << kIndent << "fidl_init_txn_header(&" << msg_name << "->hdr, 0, " << ordinal_name
<< ");\n";
}
void EmitLinearizeMessage(std::ostream* file, std::string_view receiver, std::string_view bytes,
const std::vector<CGenerator::Member>& request) {
if (CountSecondaryObjects(request) > 0)
*file << kIndent << "uint32_t _next = sizeof(*" << receiver << ");\n";
for (const auto& member : request) {
const auto& name = member.name;
switch (member.kind) {
case flat::Type::Kind::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:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
*file << kIndent << receiver << "->" << name << " = " << name << ";\n";
break;
case flat::Decl::Kind::kTable:
assert(false && "c-codegen for tables not implemented");
break;
case flat::Decl::Kind::kUnion:
assert(false && "c-codegen for unions not implemented");
break;
case flat::Decl::Kind::kStruct:
switch (member.nullability) {
case types::Nullability::kNullable:
*file << kIndent << "if (" << name << ") {\n";
*file << kIndent << kIndent << receiver << "->" << name << " = (void*)&" << bytes
<< "[_next];\n";
*file << kIndent << kIndent << "memcpy(" << receiver << "->" << name << ", " << name
<< ", sizeof(*" << name << "));\n";
*file << kIndent << kIndent << "_next += sizeof(*" << name << ");\n";
*file << kIndent << "} else {\n";
*file << kIndent << kIndent << receiver << "->" << name << " = NULL;\n";
*file << kIndent << "}\n";
break;
case types::Nullability::kNonnullable:
*file << kIndent << receiver << "->" << name << " = *" << name << ";\n";
break;
}
break;
}
}
}
}
// Various computational helper routines.
void BitsValue(const flat::Constant* constant, std::string* out_value) {
std::ostringstream member_value;
const flat::ConstantValue& const_val = constant->Value();
switch (const_val.kind) {
case flat::ConstantValue::Kind::kUint8: {
auto value = static_cast<const flat::NumericConstantValue<uint8_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint16: {
auto value = static_cast<const flat::NumericConstantValue<uint16_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint32: {
auto value = static_cast<const flat::NumericConstantValue<uint32_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint64: {
auto value = static_cast<const flat::NumericConstantValue<uint64_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt8:
case flat::ConstantValue::Kind::kInt16:
case flat::ConstantValue::Kind::kInt32:
case flat::ConstantValue::Kind::kInt64:
case flat::ConstantValue::Kind::kBool:
case flat::ConstantValue::Kind::kFloat32:
case flat::ConstantValue::Kind::kFloat64:
case flat::ConstantValue::Kind::kString:
assert(false && "bad primitive type for a bits declaration");
break;
}
*out_value = member_value.str();
}
void EnumValue(const flat::Constant* constant, std::string* out_value) {
std::ostringstream member_value;
const flat::ConstantValue& const_val = constant->Value();
switch (const_val.kind) {
case flat::ConstantValue::Kind::kInt8: {
auto value = static_cast<const flat::NumericConstantValue<int8_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt16: {
auto value = static_cast<const flat::NumericConstantValue<int16_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt32: {
auto value = static_cast<const flat::NumericConstantValue<int32_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kInt64: {
auto value = static_cast<const flat::NumericConstantValue<int64_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint8: {
auto value = static_cast<const flat::NumericConstantValue<uint8_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint16: {
auto value = static_cast<const flat::NumericConstantValue<uint16_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint32: {
auto value = static_cast<const flat::NumericConstantValue<uint32_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kUint64: {
auto value = static_cast<const flat::NumericConstantValue<uint64_t>&>(const_val);
member_value << value;
break;
}
case flat::ConstantValue::Kind::kBool:
case flat::ConstantValue::Kind::kFloat32:
case flat::ConstantValue::Kind::kFloat64:
case flat::ConstantValue::Kind::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);
array_counts.push_back(array_type->element_count->value);
type = array_type->element_type;
continue;
}
}
}
}
template <typename T>
CGenerator::Member CreateMember(const flat::Library* library, const T& decl,
bool* out_allowed = nullptr) {
std::string name = NameIdentifier(decl.name);
const flat::Type* type = decl.type_ctor->type;
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();
if (out_allowed) {
*out_allowed = true;
}
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 auto element_type = vector_type->element_type;
element_type_name = NameFlatCType(element_type, GetDeclKind(library, element_type));
max_num_elements = vector_type->element_count->value;
break;
}
case flat::Type::Kind::kIdentifier: {
auto identifier_type = static_cast<const flat::IdentifierType*>(type);
nullability = identifier_type->nullability;
if (out_allowed) {
*out_allowed = IdentifierAllowed(library, identifier_type->name);
}
break;
}
case flat::Type::Kind::kString: {
auto string_type = static_cast<const flat::StringType*>(type);
nullability = string_type->nullability;
max_num_elements = string_type->max_size->value;
break;
}
case flat::Type::Kind::kHandle:
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,
};
}
bool 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) {
bool allowed = true;
request->push_back(CreateMember(library, parameter, &allowed));
if (!allowed) {
request->clear();
if (response) {
response->clear();
}
return false;
}
}
}
if (response && method_info.response) {
response->reserve(method_info.response->parameters.size());
for (const auto& parameter : method_info.response->parameters) {
bool allowed = true;
response->push_back(CreateMember(library, parameter, &allowed));
if (!allowed) {
if (request) {
request->clear();
}
response->clear();
return false;
}
}
}
return true;
}
} // namespace
uint32_t CGenerator::GetMaxHandlesFor(Transport transport, const TypeShape& typeshape) {
switch (transport) {
case Transport::Channel:
return std::min(kChannelMaxMessageHandles, typeshape.MaxHandles());
}
assert(false && "what transport?");
return 0u;
}
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(std::string_view name, types::PrimitiveSubtype subtype,
std::string_view value) {
std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype);
file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n";
}
void CGenerator::GeneratePrimitiveDefine(std::string_view name, types::PrimitiveSubtype subtype,
std::string_view value) {
switch (subtype) {
case types::PrimitiveSubtype::kInt8:
case types::PrimitiveSubtype::kInt16:
case types::PrimitiveSubtype::kInt32:
case types::PrimitiveSubtype::kInt64:
case types::PrimitiveSubtype::kUint8:
case types::PrimitiveSubtype::kUint16:
case types::PrimitiveSubtype::kUint32:
case types::PrimitiveSubtype::kUint64: {
std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype);
file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n";
break;
}
case types::PrimitiveSubtype::kBool:
case types::PrimitiveSubtype::kFloat32:
case types::PrimitiveSubtype::kFloat64: {
file_ << "#define " << name << " "
<< "(" << value << ")\n";
break;
}
} // switch
}
void CGenerator::GenerateStringDefine(std::string_view name, std::string_view value) {
file_ << "#define " << name << " " << value << "\n";
}
void CGenerator::GenerateIntegerTypedef(types::PrimitiveSubtype subtype, std::string_view name) {
std::string underlying_type = NamePrimitiveCType(subtype);
file_ << "typedef " << underlying_type << " " << name << ";\n";
}
void CGenerator::GenerateStructTypedef(std::string_view name) {
file_ << "typedef struct " << name << " " << name << ";\n";
}
void CGenerator::GenerateStructDeclaration(std::string_view name,
const std::vector<Member>& members, StructKind kind) {
file_ << "struct " << name << " {\n";
if (kind == StructKind::kMessage) {
file_ << kIndent << "FIDL_ALIGNDECL\n";
}
auto emit_member = [this](const Member& member) {
file_ << kIndent;
EmitMemberDecl(&file_, member);
file_ << ";\n";
};
for (const auto& member : members) {
emit_member(member);
}
if (members.empty()) {
emit_member(EmptyStructMember());
}
file_ << "};\n";
}
void CGenerator::GenerateTableDeclaration(std::string_view name) {
file_ << "struct " << name << " {\n";
file_ << kIndent << "fidl_table_t table_header;\n";
file_ << "};\n";
}
void CGenerator::GenerateTaggedUnionDeclaration(std::string_view name,
const std::vector<Member>& members) {
#ifdef FIDLC_DEPRECATE_C_UNIONS
file_ << "struct __attribute__ ((deprecated)) " << name << " {\n";
#else
file_ << "struct " << name << " {\n";
#endif
file_ << kIndent << "fidl_union_tag_t tag;\n";
file_ << kIndent << "union {\n";
for (const auto& member : members) {
file_ << kIndent << kIndent;
EmitMemberDecl(&file_, member);
file_ << ";\n";
}
file_ << kIndent << "};\n";
file_ << "};\n";
}
std::map<const flat::Decl*, CGenerator::NamedBits> CGenerator::NameBits(
const std::vector<std::unique_ptr<flat::Bits>>& bits_infos) {
std::map<const flat::Decl*, NamedBits> named_bits;
for (const auto& bits_info : bits_infos) {
std::string bits_name = NameCodedName(bits_info->name);
named_bits.emplace(bits_info.get(), NamedBits{std::move(bits_name), *bits_info});
}
return named_bits;
}
// TODO(fxbug.dev/27764) These should maybe check for global name
// collisions? Otherwise, is there some other way they should fail?
std::map<const flat::Decl*, CGenerator::NamedConst> CGenerator::NameConsts(
const std::vector<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{NameCodedName(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 = NameCodedName(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::NamedProtocol> CGenerator::NameProtocols(
const std::vector<std::unique_ptr<flat::Protocol>>& protocol_infos) {
std::map<const flat::Decl*, NamedProtocol> named_protocols;
for (const auto& protocol_info : protocol_infos) {
NamedProtocol named_protocol;
named_protocol.c_name = NameCodedName(protocol_info->name);
if (protocol_info->HasAttribute("Discoverable")) {
named_protocol.discoverable_name = NameDiscoverable(*protocol_info);
}
named_protocol.transport = ParseTransport(protocol_info->GetAttribute("Transport"));
for (const auto& method_with_info : protocol_info->all_methods) {
assert(method_with_info.method != nullptr);
const auto& method = *method_with_info.method;
if (!MethodAllowed(library_, method)) {
continue;
}
NamedMethod named_method;
std::string method_name = NameMethod(named_protocol.c_name, method);
named_method.ordinal = static_cast<uint64_t>(method.generated_ordinal64->value);
named_method.ordinal_name = NameOrdinal(method_name);
named_method.identifier = NameIdentifier(method.name);
named_method.c_name = method_name;
if (method.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(WireFormat::kV1NoEe)});
}
if (method.maybe_response != nullptr) {
auto message_kind =
method.maybe_request ? types::MessageKind::kResponse : types::MessageKind::kEvent;
std::string c_name = NameMessage(method_name, message_kind);
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(WireFormat::kV1NoEe)});
}
named_protocol.methods.push_back(std::move(named_method));
}
if (named_protocol.methods.size() > 0) {
named_protocols.emplace(protocol_info.get(), std::move(named_protocol));
}
}
return named_protocols;
}
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->is_request_or_response)
continue;
std::string c_name = NameCodedName(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;
}
void CGenerator::ProduceBitsForwardDeclaration(const NamedBits& named_bits) {
auto subtype =
static_cast<const flat::PrimitiveType*>(named_bits.bits_info.subtype_ctor->type)->subtype;
GenerateIntegerTypedef(subtype, named_bits.name);
for (const auto& member : named_bits.bits_info.members) {
std::string member_name = named_bits.name + "_" + NameIdentifier(member.name);
std::string member_value;
BitsValue(member.value.get(), &member_value);
GenerateIntegerDefine(member_name, subtype, std::move(member_value));
}
EmitBlank(&file_);
}
void CGenerator::ProduceConstForwardDeclaration(const NamedConst& named_const) {
// TODO(fxbug.dev/27764)
}
void CGenerator::ProduceEnumForwardDeclaration(const NamedEnum& named_enum) {
types::PrimitiveSubtype subtype = named_enum.enum_info.type->subtype;
GenerateIntegerTypedef(subtype, named_enum.name);
for (const auto& member : named_enum.enum_info.members) {
std::string member_name = named_enum.name + "_" + NameIdentifier(member.name);
std::string member_value;
EnumValue(member.value.get(), &member_value);
GenerateIntegerDefine(member_name, subtype, std::move(member_value));
}
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolForwardDeclaration(const NamedProtocol& named_protocol) {
if (!named_protocol.discoverable_name.empty()) {
file_ << "#define " << named_protocol.c_name << "_Name \"" << named_protocol.discoverable_name
<< "\"\n";
}
for (const auto& method_info : named_protocol.methods) {
{
IOFlagsGuard reset_flags(&file_);
file_ << "#define " << method_info.ordinal_name << " ((uint64_t)0x" << std::uppercase
<< std::hex << method_info.ordinal << std::dec << ")\n";
}
if (method_info.request)
GenerateStructTypedef(method_info.request->c_name);
if (method_info.response)
GenerateStructTypedef(method_info.response->c_name);
}
}
void CGenerator::ProduceStructForwardDeclaration(const NamedStruct& named_struct) {
GenerateStructTypedef(named_struct.c_name);
}
void CGenerator::ProduceProtocolExternDeclaration(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.methods) {
if (method_info.request) {
file_ << "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_ctor->type->kind) {
case flat::Type::Kind::kPrimitive:
GeneratePrimitiveDefine(
named_const.name, static_cast<const flat::PrimitiveType*>(ci.type_ctor->type)->subtype,
static_cast<flat::LiteralConstant*>(ci.value.get())->literal->span().data());
break;
case flat::Type::Kind::kString:
GenerateStringDefine(
named_const.name,
static_cast<flat::LiteralConstant*>(ci.value.get())->literal->span().data());
break;
default:
abort();
}
EmitBlank(&file_);
}
void CGenerator::ProduceMessageDeclaration(const NamedMessage& named_message) {
// When we generate a request or response struct (i.e. messages), we must
// both include the message header, and ensure the message is FIDL aligned.
std::vector<CGenerator::Member> members;
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::ProduceProtocolDeclaration(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.methods) {
if (method_info.request)
ProduceMessageDeclaration(*method_info.request);
if (method_info.response)
ProduceMessageDeclaration(*method_info.response);
}
}
void CGenerator::ProduceStructDeclaration(const NamedStruct& named_struct) {
std::vector<CGenerator::Member> members;
members.reserve(named_struct.struct_info.members.size());
for (const auto& struct_member : named_struct.struct_info.members) {
members.push_back(CreateMember(library_, struct_member));
}
GenerateStructDeclaration(named_struct.c_name, members, StructKind::kNonmessage);
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolClientDeclaration(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
std::vector<Member> response;
if (GetMethodParameters(library_, method_info, &request, &response)) {
if (CanGenerateCodecFunctions(request) && CanGenerateCodecFunctions(response)) {
EmitClientMethodDecl(&file_, method_info.c_name, request, response);
file_ << ";\n";
}
}
}
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolClientImplementation(const NamedProtocol& named_protocol) {
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
std::vector<Member> response;
if (!GetMethodParameters(library_, method_info, &request, &response) ||
!CanGenerateCodecFunctions(request) || !CanGenerateCodecFunctions(response)) {
continue;
}
size_t count = CountSecondaryObjects(request);
size_t request_hcount =
GetMaxHandlesFor(named_protocol.transport, method_info.request->typeshape);
size_t response_hcount = 0;
if (method_info.response) {
response_hcount = GetMaxHandlesFor(named_protocol.transport, method_info.response->typeshape);
}
bool has_padding = method_info.request->typeshape.HasPadding();
bool encode_request = (count > 0) || (request_hcount > 0) || has_padding;
EmitClientMethodDecl(&file_, method_info.c_name, request, response);
file_ << " {\n";
EmitParameterSizeValidation(&file_, request);
file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.request->c_name << ")";
EmitMeasureInParams(&file_, request);
file_ << ";\n";
file_ << kIndent << "FIDL_ALIGNDECL char _wr_bytes[_wr_num_bytes];\n";
file_ << kIndent << method_info.request->c_name << "* _request = ("
<< method_info.request->c_name << "*)_wr_bytes;\n";
file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n";
EmitTxnHeader(&file_, "_request", method_info.ordinal_name);
EmitLinearizeMessage(&file_, "_request", "_wr_bytes", request);
const char* handle_infos_value = "NULL";
const char* handle_dispositions_value = "NULL";
if (request_hcount > 0) {
file_ << kIndent << "zx_handle_disposition_t _handle_dispositions[" << request_hcount
<< "];\n";
handle_dispositions_value = "_handle_dispositions";
}
if (response_hcount > 0) {
file_ << kIndent << "zx_handle_info_t _handle_infos[" << response_hcount << "];\n";
handle_infos_value = "_handle_infos";
}
if (encode_request) {
file_ << kIndent << "uint32_t _wr_num_handles = 0u;\n";
file_ << kIndent << "zx_status_t _status = fidl_encode_etc(&"
<< method_info.request->coded_name << ", _wr_bytes, _wr_num_bytes, "
<< handle_dispositions_value << ", " << request_hcount
<< ", &_wr_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 request\n";
}
if (!method_info.response) {
switch (named_protocol.transport) {
case Transport::Channel:
if (encode_request) {
file_ << kIndent
<< "return zx_channel_write_etc(_channel, 0u, _wr_bytes, _wr_num_bytes, "
<< handle_dispositions_value << ", _wr_num_handles);\n";
} else {
file_ << kIndent
<< "return zx_channel_write_etc(_channel, 0u, _wr_bytes, _wr_num_bytes, NULL, "
"0);\n";
}
break;
}
} else {
file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")";
EmitMeasureOutParams(&file_, response);
file_ << ";\n";
file_ << kIndent << "uint32_t _rd_num_bytes_max = _rd_num_bytes;\n";
file_ << kIndent << "FIDL_ALIGNDECL uint8_t _rd_bytes_storage[_rd_num_bytes_max];\n";
file_ << kIndent << "uint8_t* _rd_bytes = _rd_bytes_storage;\n";
if (!response.empty())
file_ << kIndent << method_info.response->c_name << "* _response = ("
<< method_info.response->c_name << "*)_rd_bytes;\n";
switch (named_protocol.transport) {
case Transport::Channel:
file_ << kIndent << "zx_channel_call_etc_args_t _args = {\n";
file_ << kIndent << kIndent << ".wr_bytes = _wr_bytes,\n";
file_ << kIndent << kIndent << ".wr_handles = " << handle_dispositions_value << ",\n";
file_ << kIndent << kIndent << ".rd_bytes = _rd_bytes,\n";
file_ << kIndent << kIndent << ".rd_handles = " << handle_infos_value << ",\n";
file_ << kIndent << kIndent << ".wr_num_bytes = _wr_num_bytes,\n";
if (encode_request) {
file_ << kIndent << kIndent << ".wr_num_handles = _wr_num_handles,\n";
} else {
file_ << kIndent << kIndent << ".wr_num_handles = 0,\n";
}
file_ << kIndent << kIndent << ".rd_num_bytes = _rd_num_bytes_max,\n";
file_ << kIndent << kIndent << ".rd_num_handles = " << response_hcount << ",\n";
file_ << kIndent << "};\n";
file_ << kIndent << "uint32_t _actual_num_bytes = 0u;\n";
file_ << kIndent << "uint32_t _actual_num_handles = 0u;\n";
if (encode_request) {
file_ << kIndent;
} else {
file_ << kIndent << "zx_status_t ";
}
file_ << "_status = zx_channel_call_etc(_channel, 0u, ZX_TIME_INFINITE, &_args, "
"&_actual_num_bytes, &_actual_num_handles);\n";
break;
}
file_ << kIndent << "if (_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _status;\n";
// We check that we have enough capacity to copy out the parameters
// before decoding the message so that we can close the handles
// using |_handles| rather than trying to find them in the decoded
// message.
count = CountSecondaryObjects(response);
has_padding = method_info.response->typeshape.HasPadding();
bool decode_response = (count > 0) || (response_hcount > 0) || has_padding;
if (count > 0u) {
file_ << kIndent << "if ";
if (count > 1u)
file_ << "(";
size_t i = 0;
for (const auto& member : response) {
if (member.kind == flat::Type::Kind::kVector) {
if (i++ > 0u)
file_ << " || ";
file_ << "(_response->" << member.name << ".count > " << member.name << "_capacity)";
} else if (member.kind == flat::Type::Kind::kString) {
if (i++ > 0u)
file_ << " || ";
file_ << "(_response->" << member.name << ".size > " << member.name << "_capacity)";
} else if (IsStoredOutOfLine(member)) {
if (i++ > 0u)
file_ << " || ";
file_ << "((uintptr_t)_response->" << member.name << " == FIDL_ALLOC_PRESENT && out_"
<< member.name << " == NULL)";
}
}
if (count > 1u)
file_ << ")";
file_ << " {\n";
if (response_hcount > 0) {
file_ << kIndent << kIndent
<< "FidlHandleInfoCloseMany(_handle_infos, _actual_num_handles);\n";
}
file_ << kIndent << kIndent << "return ZX_ERR_BUFFER_TOO_SMALL;\n";
file_ << kIndent << "}\n";
}
if (decode_response) {
// TODO(fxbug.dev/7499): Validate the response ordinal. C++ bindings also need to do that.
switch (named_protocol.transport) {
case Transport::Channel:
file_ << kIndent << "_status = fidl_decode_etc(&" << method_info.response->coded_name
<< ", _rd_bytes, _actual_num_bytes, " << handle_infos_value
<< ", _actual_num_handles, NULL);\n";
break;
}
file_ << kIndent << "if (_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _status;\n";
} else {
file_ << kIndent << "// OPTIMIZED AWAY fidl_decode() of POD-only response\n";
}
for (const auto& member : response) {
const auto& name = member.name;
switch (member.kind) {
case flat::Type::Kind::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:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
break;
case flat::Decl::Kind::kTable:
assert(false && "c-codegen for tables not implemented");
break;
case flat::Decl::Kind::kUnion:
assert(false && "c-codegen for unions not implemented");
break;
case flat::Decl::Kind::kStruct:
switch (member.nullability) {
case types::Nullability::kNullable:
file_ << kIndent << "if (_response->" << name << ") {\n";
file_ << kIndent << kIndent << "*out_" << name << " = *(_response->" << name
<< ");\n";
file_ << kIndent << "} else {\n";
// We don't have a great way of signaling that the optional response member
// was not in the message. That means these bindings aren't particularly
// useful when the client needs to extract that bit. The best we can do is
// zero out the value to make sure the client has defined behavior.
//
// In many cases, the response contains other information (e.g., a status code)
// that lets the client do something reasonable.
file_ << kIndent << kIndent << "memset(out_" << name << ", 0, sizeof(*out_"
<< name << "));\n";
file_ << kIndent << "}\n";
break;
case types::Nullability::kNonnullable:
file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
break;
}
break;
}
break;
}
}
file_ << kIndent << "return ZX_OK;\n";
}
file_ << "}\n\n";
}
}
void CGenerator::ProduceProtocolServerDeclaration(const NamedProtocol& named_protocol) {
file_ << "typedef struct " << named_protocol.c_name << "_ops {\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
if (GetMethodParameters(library_, method_info, &request, nullptr) &&
CanGenerateCodecFunctions(request)) {
bool has_response = method_info.response != nullptr;
file_ << kIndent;
EmitServerMethodDecl(&file_, method_info.identifier, request, has_response);
file_ << ";\n";
}
}
file_ << "} " << named_protocol.c_name << "_ops_t;\n\n";
EmitServerDispatchDecl(&file_, named_protocol.c_name);
file_ << ";\n";
EmitServerTryDispatchDecl(&file_, named_protocol.c_name);
file_ << ";\n\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request || !method_info.response)
continue;
std::vector<Member> response;
if (GetMethodParameters(library_, method_info, nullptr, &response) &&
CanGenerateCodecFunctions(response)) {
EmitServerReplyDecl(&file_, method_info.c_name, response);
file_ << ";\n";
}
}
EmitBlank(&file_);
}
void CGenerator::ProduceProtocolServerImplementation(const NamedProtocol& named_protocol) {
EmitServerTryDispatchDecl(&file_, named_protocol.c_name);
file_ << " {\n";
file_ << kIndent << "if (msg->num_bytes < sizeof(fidl_message_header_t)) {\n";
file_ << kIndent << kIndent << "FidlHandleInfoCloseMany(msg->handles, msg->num_handles);\n";
file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
file_ << kIndent << "}\n";
file_ << kIndent << "zx_status_t status = ZX_OK;\n";
file_ << kIndent << "fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;\n";
EmitMagicNumberCheck(&file_);
file_ << kIndent << "switch (hdr->ordinal) {\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request)
continue;
std::vector<Member> request;
if (!GetMethodParameters(library_, method_info, &request, nullptr)) {
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";
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:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
assert(false && "bad decl kind for member");
break;
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kProtocol:
file_ << ", request->" << member.name;
break;
case flat::Decl::Kind::kTable:
assert(false && "c-codegen for tables not yet implemented");
break;
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kUnion:
switch (member.nullability) {
case types::Nullability::kNullable:
file_ << ", request->" << member.name;
break;
case types::Nullability::kNonnullable:
file_ << ", &(request->" << member.name << ")";
break;
}
break;
}
}
}
if (method_info.response != nullptr)
file_ << ", txn";
file_ << ");\n";
file_ << kIndent << kIndent << "break;\n";
file_ << kIndent << "}\n";
}
file_ << kIndent << "default: {\n";
file_ << kIndent << kIndent << "return ZX_ERR_NOT_SUPPORTED;\n";
file_ << kIndent << "}\n";
file_ << kIndent << "}\n";
file_ << kIndent << "if ("
<< "status != ZX_OK && "
<< "status != ZX_ERR_STOP && "
<< "status != ZX_ERR_NEXT && "
<< "status != ZX_ERR_ASYNC) {\n";
file_ << kIndent << kIndent << "return ZX_ERR_INTERNAL;\n";
file_ << kIndent << "} else {\n";
file_ << kIndent << kIndent << "return status;\n";
file_ << kIndent << "}\n";
file_ << "}\n\n";
EmitServerDispatchDecl(&file_, named_protocol.c_name);
file_ << " {\n";
file_ << kIndent << "zx_status_t status = " << named_protocol.c_name
<< "_try_dispatch(ctx, txn, msg, ops);\n";
file_ << kIndent << "if (status == ZX_ERR_NOT_SUPPORTED)\n";
file_ << kIndent << kIndent << "FidlHandleInfoCloseMany(msg->handles, msg->num_handles);\n";
file_ << kIndent << "return status;\n";
file_ << "}\n\n";
for (const auto& method_info : named_protocol.methods) {
if (!method_info.request || !method_info.response)
continue;
std::vector<Member> response;
if (!GetMethodParameters(library_, method_info, nullptr, &response) ||
!CanGenerateCodecFunctions(response)) {
continue;
}
size_t hcount = GetMaxHandlesFor(named_protocol.transport, method_info.response->typeshape);
EmitServerReplyDecl(&file_, method_info.c_name, response);
file_ << " {\n";
file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.response->c_name << ")";
EmitMeasureInParams(&file_, response);
file_ << ";\n";
file_ << kIndent << "char _wr_bytes[_wr_num_bytes];\n";
file_ << kIndent << method_info.response->c_name << "* _response = ("
<< method_info.response->c_name << "*)_wr_bytes;\n";
file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n";
EmitTxnHeader(&file_, "_response", method_info.ordinal_name);
EmitLinearizeMessage(&file_, "_response", "_wr_bytes", response);
const char* handle_value = "NULL";
if (hcount > 0) {
file_ << kIndent << "zx_handle_disposition_t _handles[" << hcount << "];\n";
handle_value = "_handles";
}
file_ << kIndent << "fidl_outgoing_msg_t _msg = {\n";
file_ << kIndent << kIndent << ".type = FIDL_OUTGOING_MSG_TYPE_BYTE,\n";
file_ << kIndent << kIndent << ".byte = {\n";
file_ << kIndent << kIndent << kIndent << ".bytes = _wr_bytes,\n";
file_ << kIndent << kIndent << kIndent << ".handles = " << handle_value << ",\n";
file_ << kIndent << kIndent << kIndent << ".num_bytes = _wr_num_bytes,\n";
file_ << kIndent << kIndent << kIndent << ".num_handles = " << hcount << ",\n";
file_ << kIndent << kIndent << "},\n";
file_ << kIndent << "};\n";
bool has_padding = method_info.response->typeshape.HasPadding();
bool encode_response = (hcount > 0) || CountSecondaryObjects(response) > 0 || has_padding;
if (encode_response) {
file_ << kIndent << "zx_status_t _status = fidl_encode_msg(&"
<< method_info.response->coded_name << ", &_msg.byte, &_msg.byte.num_handles, NULL);\n";
file_ << kIndent << "if (_status != ZX_OK)\n";
file_ << kIndent << kIndent << "return _status;\n";
} else {
file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only reply\n";
}
file_ << kIndent << "return _txn->reply(_txn, &_msg);\n";
file_ << "}\n\n";
}
}
std::ostringstream CGenerator::ProduceHeader() {
GeneratePrologues();
std::map<const flat::Decl*, NamedBits> named_bits = NameBits(library_->bits_declarations_);
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*, NamedProtocol> named_protocols =
NameProtocols(library_->protocol_declarations_);
std::map<const flat::Decl*, NamedStruct> named_structs =
NameStructs(library_->struct_declarations_);
file_ << "\n// Forward declarations\n\n";
for (const auto* decl : library_->declaration_order_) {
if (!DeclAllowed(decl)) {
continue;
}
switch (decl->kind) {
case flat::Decl::Kind::kBits: {
auto iter = named_bits.find(decl);
if (iter != named_bits.end()) {
ProduceBitsForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kConst: {
auto iter = named_consts.find(decl);
if (iter != named_consts.end()) {
ProduceConstForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kEnum: {
auto iter = named_enums.find(decl);
if (iter != named_enums.end()) {
ProduceEnumForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kProtocol: {
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kResource:
// Do nothing.
break;
case flat::Decl::Kind::kService:
// Do nothing.
break;
case flat::Decl::Kind::kStruct: {
auto iter = named_structs.find(decl);
if (iter != named_structs.end()) {
ProduceStructForwardDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kTable:
// Do nothing.
break;
case flat::Decl::Kind::kTypeAlias:
// TODO(fxbug.dev/7807): Do more than nothing.
break;
case flat::Decl::Kind::kUnion:
// Do nothing.
break;
} // switch
}
file_ << "\n// Extern declarations\n\n";
for (const auto* decl : library_->declaration_order_) {
if (!DeclAllowed(decl)) {
continue;
}
switch (decl->kind) {
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only messages have extern fidl_type_t declarations.
break;
case flat::Decl::Kind::kProtocol: {
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolExternDeclaration(iter->second);
}
break;
}
} // switch
}
file_ << "\n// Declarations\n\n";
for (const auto* decl : library_->declaration_order_) {
if (!DeclAllowed(decl)) {
continue;
}
switch (decl->kind) {
case flat::Decl::Kind::kBits:
// Bits can be entirely forward declared, as they have no
// dependencies other than standard headers.
break;
case flat::Decl::Kind::kConst: {
auto iter = named_consts.find(decl);
if (iter != named_consts.end()) {
ProduceConstDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kEnum:
// Enums can be entirely forward declared, as they have no
// dependencies other than standard headers.
break;
case flat::Decl::Kind::kProtocol: {
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kResource:
// Do nothing.
break;
case flat::Decl::Kind::kService:
// Do nothing.
break;
case flat::Decl::Kind::kStruct: {
auto iter = named_structs.find(decl);
if (iter != named_structs.end()) {
ProduceStructDeclaration(iter->second);
}
break;
}
case flat::Decl::Kind::kTable:
// Do nothing.
break;
case flat::Decl::Kind::kTypeAlias:
// TODO(fxbug.dev/7807): Do more than nothing.
break;
case flat::Decl::Kind::kUnion:
// Do nothing.
break;
} // switch
}
file_ << "\n// Simple bindings \n\n";
for (const auto* decl : library_->declaration_order_) {
switch (decl->kind) {
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only protocols have client declarations.
break;
case flat::Decl::Kind::kProtocol: {
if (!HasSimpleLayout(decl))
break;
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolClientDeclaration(iter->second);
ProduceProtocolServerDeclaration(iter->second);
}
break;
}
} // switch
}
GenerateEpilogues();
return std::move(file_);
}
std::ostringstream CGenerator::ProduceClient() {
EmitFileComment(&file_);
EmitIncludeHeader(&file_, "<lib/fidl/coding.h>");
EmitIncludeHeader(&file_, "<lib/fidl/internal.h>");
EmitIncludeHeader(&file_, "<lib/fidl/txn_header.h>");
EmitIncludeHeader(&file_, "<alloca.h>");
EmitIncludeHeader(&file_, "<string.h>");
EmitIncludeHeader(&file_, "<zircon/assert.h>");
EmitIncludeHeader(&file_, "<zircon/syscalls.h>");
EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">");
EmitBlank(&file_);
std::map<const flat::Decl*, NamedProtocol> named_protocols =
NameProtocols(library_->protocol_declarations_);
for (const auto* decl : library_->declaration_order_) {
switch (decl->kind) {
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only protocols have client implementations.
break;
case flat::Decl::Kind::kProtocol: {
if (!HasSimpleLayout(decl))
break;
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolClientImplementation(iter->second);
}
break;
}
} // switch
}
return std::move(file_);
}
std::ostringstream CGenerator::ProduceServer() {
EmitFileComment(&file_);
EmitIncludeHeader(&file_, "<lib/fidl/coding.h>");
EmitIncludeHeader(&file_, "<lib/fidl/internal.h>");
EmitIncludeHeader(&file_, "<lib/fidl/txn_header.h>");
EmitIncludeHeader(&file_, "<alloca.h>");
EmitIncludeHeader(&file_, "<string.h>");
EmitIncludeHeader(&file_, "<zircon/assert.h>");
EmitIncludeHeader(&file_, "<zircon/syscalls.h>");
EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">");
EmitBlank(&file_);
std::map<const flat::Decl*, NamedProtocol> named_protocols =
NameProtocols(library_->protocol_declarations_);
for (const auto* decl : library_->declaration_order_) {
switch (decl->kind) {
case flat::Decl::Kind::kBits:
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kEnum:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kStruct:
case flat::Decl::Kind::kTable:
case flat::Decl::Kind::kTypeAlias:
case flat::Decl::Kind::kUnion:
// Only protocols have client implementations.
break;
case flat::Decl::Kind::kProtocol: {
if (!HasSimpleLayout(decl))
break;
auto iter = named_protocols.find(decl);
if (iter != named_protocols.end()) {
ProduceProtocolServerImplementation(iter->second);
}
break;
}
} // switch
}
return std::move(file_);
}
} // namespace fidl