blob: d61acd5882e194dc1b06d85036c39492f67e26d5 [file] [log] [blame]
// Copyright 2019 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/coded_types_generator.h"
#include "fidl/coded_ast.h"
#include "fidl/names.h"
#include "fidl/types.h"
namespace fidl {
CodedTypesGenerator::FlattenedStructMember::FlattenedStructMember(const flat::StructMember& member,
const WireFormat wire_format)
: type(member.type_ctor->type),
name(member.name),
inline_size(member.typeshape(wire_format).InlineSize()),
offset(member.fieldshape(wire_format).Offset()),
padding(member.fieldshape(wire_format).Padding()) {}
std::vector<CodedTypesGenerator::FlattenedStructMember> CodedTypesGenerator::FlattenedStructMembers(
const flat::Struct& input, const WireFormat wire_format) {
auto get_struct_decl = [](const flat::StructMember& member) -> const flat::Struct* {
if (member.type_ctor->nullability == types::Nullability::kNullable) {
return nullptr;
}
const flat::Type* type = member.type_ctor->type;
if (type->kind != flat::Type::Kind::kIdentifier) {
return nullptr;
}
auto identifier_type = static_cast<const flat::IdentifierType*>(type);
if (identifier_type->type_decl->kind != flat::Decl::Kind::kStruct) {
return nullptr;
}
return static_cast<const flat::Struct*>(identifier_type->type_decl);
};
std::vector<FlattenedStructMember> result;
for (const auto& member : input.members) {
auto flattened_member = FlattenedStructMember(member, wire_format);
auto struct_decl = get_struct_decl(member);
if (!struct_decl) {
result.push_back(flattened_member);
continue;
}
if (struct_decl->members.size() == 0) {
result.push_back(flattened_member);
continue;
}
auto flattened_members = FlattenedStructMembers(*struct_decl, wire_format);
for (size_t i = 0; i < flattened_members.size(); i++) {
auto inner_member = flattened_members[i];
if (i == flattened_members.size() - 1) {
inner_member.padding += flattened_member.padding;
}
inner_member.offset += flattened_member.offset;
result.push_back(inner_member);
}
}
return result;
}
std::vector<const coded::Type*> CodedTypesGenerator::AllCodedTypes() const {
std::vector<const coded::Type*> coded_types;
coded_types.reserve(coded_types_.size() + named_coded_types_.size());
for (const auto& coded_type : coded_types_) {
assert(coded_type.get());
if (!coded_type->is_coding_needed)
continue;
coded_types.push_back(coded_type.get());
}
for (const auto& [_, coded_type] : named_coded_types_) {
assert(coded_type.get());
coded_types.push_back(coded_type.get());
}
return coded_types;
}
const coded::Type* CodedTypesGenerator::CompileType(const flat::Type* type,
coded::CodingContext context,
const WireFormat wire_format) {
switch (type->kind) {
case flat::Type::Kind::kArray: {
auto array_type = static_cast<const flat::ArrayType*>(type);
auto coded_element_type = CompileType(array_type->element_type,
coded::CodingContext::kOutsideEnvelope, wire_format);
auto iter = array_type_map_.find(array_type);
if (iter != array_type_map_.end())
return iter->second;
uint32_t array_size = array_type->typeshape(wire_format).InlineSize();
uint32_t element_size = array_type->element_type->typeshape(wire_format).InlineSize();
auto name = NameCodedArray(coded_element_type->coded_name, array_size);
auto coded_array_type = std::make_unique<coded::ArrayType>(
std::move(name), coded_element_type, array_size, element_size, context);
array_type_map_[array_type] = coded_array_type.get();
coded_types_.push_back(std::move(coded_array_type));
return coded_types_.back().get();
}
case flat::Type::Kind::kVector: {
auto vector_type = static_cast<const flat::VectorType*>(type);
auto iter = vector_type_map_.find(vector_type);
if (iter != vector_type_map_.end())
return iter->second;
auto coded_element_type = CompileType(vector_type->element_type,
coded::CodingContext::kOutsideEnvelope, wire_format);
uint32_t max_count = vector_type->element_count->value;
uint32_t element_size = coded_element_type->size;
std::string_view element_name = coded_element_type->coded_name;
auto name = NameCodedVector(element_name, max_count, vector_type->nullability);
auto coded_vector_type = std::make_unique<coded::VectorType>(
std::move(name), coded_element_type, max_count, element_size, vector_type->nullability);
vector_type_map_[vector_type] = coded_vector_type.get();
coded_types_.push_back(std::move(coded_vector_type));
return coded_types_.back().get();
}
case flat::Type::Kind::kString: {
auto string_type = static_cast<const flat::StringType*>(type);
auto iter = string_type_map_.find(string_type);
if (iter != string_type_map_.end())
return iter->second;
uint32_t max_size = string_type->max_size->value;
auto name = NameCodedString(max_size, string_type->nullability);
auto coded_string_type =
std::make_unique<coded::StringType>(std::move(name), max_size, string_type->nullability);
string_type_map_[string_type] = coded_string_type.get();
coded_types_.push_back(std::move(coded_string_type));
return coded_types_.back().get();
}
case flat::Type::Kind::kHandle: {
auto handle_type = static_cast<const flat::HandleType*>(type);
auto iter = handle_type_map_.find(handle_type);
if (iter != handle_type_map_.end())
return iter->second;
types::Rights rights = static_cast<const flat::NumericConstantValue<types::Rights>&>(
handle_type->rights->Value());
auto name = NameCodedHandle(handle_type->subtype, rights, handle_type->nullability);
auto coded_handle_type = std::make_unique<coded::HandleType>(
std::move(name), handle_type->subtype, rights, handle_type->nullability);
handle_type_map_[handle_type] = coded_handle_type.get();
coded_types_.push_back(std::move(coded_handle_type));
return coded_types_.back().get();
}
case flat::Type::Kind::kRequestHandle: {
auto request_type = static_cast<const flat::RequestHandleType*>(type);
auto iter = request_type_map_.find(request_type);
if (iter != request_type_map_.end())
return iter->second;
auto name = NameCodedRequestHandle(NameCodedName(request_type->protocol_type->name),
request_type->nullability);
auto coded_request_type =
std::make_unique<coded::RequestHandleType>(std::move(name), request_type->nullability);
request_type_map_[request_type] = coded_request_type.get();
coded_types_.push_back(std::move(coded_request_type));
return coded_types_.back().get();
}
case flat::Type::Kind::kPrimitive: {
auto primitive_type = static_cast<const flat::PrimitiveType*>(type);
auto iter = primitive_type_map_.find(primitive_type);
if (iter != primitive_type_map_.end())
return iter->second;
auto name = NameFlatName(primitive_type->name);
auto coded_primitive_type = std::make_unique<coded::PrimitiveType>(
std::move(name), primitive_type->subtype,
primitive_type->typeshape(wire_format).InlineSize(), context);
primitive_type_map_[primitive_type] = coded_primitive_type.get();
coded_types_.push_back(std::move(coded_primitive_type));
return coded_types_.back().get();
}
case flat::Type::Kind::kIdentifier: {
auto identifier_type = static_cast<const flat::IdentifierType*>(type);
auto iter = named_coded_types_.find(identifier_type->name);
if (iter == named_coded_types_.end()) {
assert(false && "unknown type in named type map!");
}
// We may need to set the emit-pointer bit on structs, unions, and xunions now.
auto coded_type = iter->second.get();
switch (coded_type->kind) {
case coded::Type::Kind::kStruct: {
// Structs were compiled as part of decl compilation,
// but we may now need to generate the StructPointer.
if (identifier_type->nullability != types::Nullability::kNullable)
return coded_type;
auto iter = struct_type_map_.find(identifier_type);
if (iter != struct_type_map_.end()) {
return iter->second;
}
auto coded_struct_type = static_cast<coded::StructType*>(coded_type);
auto struct_pointer_type = std::make_unique<coded::StructPointerType>(
NamePointer(coded_struct_type->coded_name), coded_struct_type,
identifier_type->typeshape(wire_format).inline_size);
coded_struct_type->maybe_reference_type = struct_pointer_type.get();
struct_type_map_[identifier_type] = struct_pointer_type.get();
coded_types_.push_back(std::move(struct_pointer_type));
return coded_types_.back().get();
}
case coded::Type::Kind::kTable: {
// Tables cannot be nullable.
assert(identifier_type->nullability != types::Nullability::kNullable);
return coded_type;
}
case coded::Type::Kind::kXUnion: {
if (identifier_type->nullability != types::Nullability::kNullable) {
return coded_type;
}
auto coded_xunion_type = static_cast<coded::XUnionType*>(coded_type);
return coded_xunion_type->maybe_reference_type;
}
case coded::Type::Kind::kProtocol: {
auto iter = protocol_type_map_.find(identifier_type);
if (iter != protocol_type_map_.end())
return iter->second;
auto name = NameCodedProtocolHandle(NameCodedName(identifier_type->name),
identifier_type->nullability);
auto coded_protocol_type = std::make_unique<coded::ProtocolHandleType>(
std::move(name), identifier_type->nullability);
protocol_type_map_[identifier_type] = coded_protocol_type.get();
coded_types_.push_back(std::move(coded_protocol_type));
return coded_types_.back().get();
}
case coded::Type::Kind::kEnum:
case coded::Type::Kind::kBits:
return coded_type;
case coded::Type::Kind::kPrimitive:
case coded::Type::Kind::kProtocolHandle:
case coded::Type::Kind::kStructPointer:
case coded::Type::Kind::kMessage:
case coded::Type::Kind::kRequestHandle:
case coded::Type::Kind::kHandle:
case coded::Type::Kind::kArray:
case coded::Type::Kind::kVector:
case coded::Type::Kind::kString:
assert(false && "anonymous type in named type map!");
break;
}
__builtin_unreachable();
}
}
}
void CodedTypesGenerator::CompileFields(const flat::Decl* decl, const WireFormat wire_format) {
switch (decl->kind) {
case flat::Decl::Kind::kProtocol: {
auto protocol_decl = static_cast<const flat::Protocol*>(decl);
coded::ProtocolType* coded_protocol =
static_cast<coded::ProtocolType*>(named_coded_types_[decl->name].get());
size_t i = 0;
for (const auto& method_with_info : protocol_decl->all_methods) {
assert(method_with_info.method != nullptr);
const auto& method = *method_with_info.method;
auto CompileMessage = [&](const flat::Struct& message) -> void {
std::unique_ptr<coded::MessageType>& coded_message =
coded_protocol->messages_during_compile[i++];
std::vector<coded::StructElement>& request_elements = coded_message->elements;
uint32_t field_num = 0;
bool is_noop = true;
for (const auto parameter : FlattenedStructMembers(message, wire_format)) {
auto coded_parameter_type =
CompileType(parameter.type, coded::CodingContext::kOutsideEnvelope, wire_format);
if (!coded_parameter_type->is_noop) {
request_elements.push_back(coded::StructField(
parameter.type->Resourceness(), parameter.offset, coded_parameter_type));
is_noop = false;
}
if (parameter.padding != 0) {
request_elements.push_back(coded::StructPadding::FromLength(
parameter.inline_size + parameter.offset, parameter.padding));
is_noop = false;
}
field_num++;
}
coded_message->is_noop = is_noop;
// We move the coded_message to coded_types_ so that we'll generate tables for the
// message in the proper order.
coded_types_.push_back(std::move(coded_message));
// We also keep back pointers to reference to these messages via the
// coded_protocol.
coded_protocol->messages_after_compile.push_back(
static_cast<const coded::MessageType*>(coded_types_.back().get()));
};
if (method.maybe_request) {
CompileMessage(*method.maybe_request);
}
if (method.maybe_response) {
CompileMessage(*method.maybe_response);
}
}
break;
}
case flat::Decl::Kind::kStruct: {
auto struct_decl = static_cast<const flat::Struct*>(decl);
if (struct_decl->is_request_or_response)
break;
coded::StructType* coded_struct =
static_cast<coded::StructType*>(named_coded_types_[decl->name].get());
std::vector<coded::StructElement>& struct_elements = coded_struct->elements;
uint32_t field_num = 0;
bool is_noop = true;
for (const auto member : FlattenedStructMembers(*struct_decl, wire_format)) {
std::string member_name = coded_struct->coded_name + "_" + std::string(member.name.data());
auto coded_member_type =
CompileType(member.type, coded::CodingContext::kOutsideEnvelope, wire_format);
if (!coded_member_type->is_noop) {
struct_elements.push_back(
coded::StructField(member.type->Resourceness(), member.offset, coded_member_type));
is_noop = false;
}
if (member.padding != 0) {
struct_elements.push_back(
coded::StructPadding::FromLength(member.inline_size + member.offset, member.padding));
is_noop = false;
}
field_num++;
}
coded_struct->is_noop = is_noop;
break;
}
case flat::Decl::Kind::kUnion: {
auto union_decl = static_cast<const flat::Union*>(decl);
auto type = named_coded_types_[decl->name].get();
switch (wire_format) {
case WireFormat::kV1Header:
[[fallthrough]];
case WireFormat::kV1NoEe: {
coded::XUnionType* coded_xunion = static_cast<coded::XUnionType*>(type);
coded::XUnionType* nullable_coded_xunion = coded_xunion->maybe_reference_type;
assert(nullable_coded_xunion != nullptr &&
"Named coded xunion must have a reference type!");
assert(coded_xunion->fields.empty() &&
"The coded xunion fields are being compiled twice!");
std::set<uint32_t> members;
for (const auto& member_ref : union_decl->MembersSortedByXUnionOrdinal()) {
const auto& member = member_ref.get();
if (!members.emplace(member.ordinal->value).second) {
assert(false && "Duplicate ordinal found in table generation");
}
if (member.maybe_used) {
const auto* coded_member_type =
CompileType(member.maybe_used->type_ctor->type,
coded::CodingContext::kInsideEnvelope, wire_format);
coded_xunion->fields.emplace_back(coded_member_type);
nullable_coded_xunion->fields.emplace_back(coded_member_type);
} else {
coded_xunion->fields.emplace_back(nullptr);
nullable_coded_xunion->fields.emplace_back(nullptr);
}
}
break;
}
}
break;
}
case flat::Decl::Kind::kTable: {
auto table_decl = static_cast<const flat::Table*>(decl);
coded::TableType* coded_table =
static_cast<coded::TableType*>(named_coded_types_[decl->name].get());
std::vector<coded::TableField>& table_fields = coded_table->fields;
std::map<uint32_t, const flat::Table::Member*> members;
for (const auto& member : table_decl->members) {
if (!members.emplace(member.ordinal->value, &member).second) {
assert(false && "Duplicate ordinal found in table generation");
}
}
for (const auto& member_pair : members) {
const auto& member = *member_pair.second;
if (!member.maybe_used)
continue;
std::string member_name =
coded_table->coded_name + "_" + std::string(member.maybe_used->name.data());
auto coded_member_type = CompileType(member.maybe_used->type_ctor->type,
coded::CodingContext::kInsideEnvelope, wire_format);
table_fields.emplace_back(coded_member_type, member.ordinal->value);
}
break;
}
default: {
break;
}
}
}
void CodedTypesGenerator::CompileDecl(const flat::Decl* decl, const WireFormat wire_format) {
switch (decl->kind) {
case flat::Decl::Kind::kBits: {
auto bits_decl = static_cast<const flat::Bits*>(decl);
std::string bits_name = NameCodedName(bits_decl->name);
auto primitive_type = static_cast<const flat::PrimitiveType*>(bits_decl->subtype_ctor->type);
named_coded_types_.emplace(
bits_decl->name, std::make_unique<coded::BitsType>(
std::move(bits_name), primitive_type->subtype,
primitive_type->typeshape(wire_format).InlineSize(), bits_decl->mask,
NameFlatName(bits_decl->name), bits_decl->strictness));
break;
}
case flat::Decl::Kind::kEnum: {
auto enum_decl = static_cast<const flat::Enum*>(decl);
std::string enum_name = NameCodedName(enum_decl->name);
std::vector<uint64_t> members;
for (const auto& member : enum_decl->members) {
std::unique_ptr<flat::ConstantValue> value;
uint64_t uint64 = 0;
bool ok = member.value->Value().Convert(flat::ConstantValue::Kind::kUint64, &value);
if (ok) {
uint64 = static_cast<flat::NumericConstantValue<uint64_t>*>(value.get())->value;
} else {
ok = member.value->Value().Convert(flat::ConstantValue::Kind::kInt64, &value);
if (ok) {
// Note: casting int64_t to uint64_t is well-defined.
uint64 = static_cast<uint64_t>(
static_cast<flat::NumericConstantValue<int64_t>*>(value.get())->value);
} else {
assert(false && "Failed to convert enum member to uint64 or int64");
}
}
members.push_back(uint64);
}
named_coded_types_.emplace(
enum_decl->name,
std::make_unique<coded::EnumType>(std::move(enum_name), enum_decl->type->subtype,
enum_decl->type->typeshape(wire_format).InlineSize(),
std::move(members), NameFlatName(enum_decl->name),
enum_decl->strictness));
break;
}
case flat::Decl::Kind::kProtocol: {
auto protocol_decl = static_cast<const flat::Protocol*>(decl);
std::string protocol_name = NameCodedName(protocol_decl->name);
std::string protocol_qname = NameFlatName(protocol_decl->name);
std::vector<std::unique_ptr<coded::MessageType>> protocol_messages;
for (const auto& method_with_info : protocol_decl->all_methods) {
assert(method_with_info.method != nullptr);
const auto& method = *method_with_info.method;
std::string method_name = NameMethod(protocol_name, method);
std::string method_qname = NameMethod(protocol_qname, method);
auto CreateMessage = [&](const flat::Struct& message, types::MessageKind kind) -> void {
std::string message_name = NameMessage(method_name, kind);
std::string message_qname = NameMessage(method_qname, kind);
protocol_messages.push_back(std::make_unique<coded::MessageType>(
std::move(message_name), std::vector<coded::StructElement>(),
message.typeshape(wire_format).InlineSize(), std::move(message_qname)));
};
if (method.maybe_request) {
CreateMessage(*method.maybe_request, types::MessageKind::kRequest);
}
if (method.maybe_response) {
auto kind =
method.maybe_request ? types::MessageKind::kResponse : types::MessageKind::kEvent;
CreateMessage(*method.maybe_response, kind);
}
}
named_coded_types_.emplace(
decl->name, std::make_unique<coded::ProtocolType>(std::move(protocol_messages)));
break;
}
case flat::Decl::Kind::kTable: {
auto table_decl = static_cast<const flat::Table*>(decl);
std::string table_name = NameCodedName(table_decl->name);
named_coded_types_.emplace(decl->name,
std::make_unique<coded::TableType>(
std::move(table_name), std::vector<coded::TableField>(),
table_decl->typeshape(wire_format).InlineSize(),
NameFlatName(table_decl->name), table_decl->resourceness));
break;
}
case flat::Decl::Kind::kStruct: {
auto struct_decl = static_cast<const flat::Struct*>(decl);
if (struct_decl->is_request_or_response)
break;
std::string struct_name = NameCodedName(struct_decl->name);
named_coded_types_.emplace(
decl->name,
std::make_unique<coded::StructType>(
std::move(struct_name), std::vector<coded::StructElement>(),
struct_decl->typeshape(wire_format).InlineSize(), NameFlatName(struct_decl->name)));
break;
}
case flat::Decl::Kind::kUnion: {
auto union_decl = static_cast<const flat::Union*>(decl);
std::string union_name = NameCodedName(union_decl->name);
switch (wire_format) {
case WireFormat::kV1Header:
[[fallthrough]];
case WireFormat::kV1NoEe: {
std::string nullable_xunion_name = NameCodedNullableName(union_decl->name);
// Always create the reference type
auto nullable_xunion_type = std::make_unique<coded::XUnionType>(
std::move(nullable_xunion_name), std::vector<coded::XUnionField>(),
NameFlatName(union_decl->name), types::Nullability::kNullable, union_decl->strictness,
union_decl->resourceness.value());
coded::XUnionType* nullable_xunion_ptr = nullable_xunion_type.get();
coded_types_.push_back(std::move(nullable_xunion_type));
auto xunion_type = std::make_unique<coded::XUnionType>(
std::move(union_name), std::vector<coded::XUnionField>(),
NameFlatName(union_decl->name), types::Nullability::kNonnullable,
union_decl->strictness, union_decl->resourceness.value());
xunion_type->maybe_reference_type = nullable_xunion_ptr;
named_coded_types_.emplace(decl->name, std::move(xunion_type));
break;
}
}
break;
}
case flat::Decl::Kind::kConst:
case flat::Decl::Kind::kResource:
case flat::Decl::Kind::kService:
case flat::Decl::Kind::kTypeAlias:
// Nothing to do.
break;
}
}
void CodedTypesGenerator::CompileCodedTypes(const WireFormat wire_format) {
for (const auto& decl : library_->declaration_order_) {
CompileDecl(decl, wire_format);
}
for (const auto& decl : library_->declaration_order_) {
if (decl->name.library() != library_)
continue;
CompileFields(decl, wire_format);
}
}
} // namespace fidl