blob: 84a4b4e134f2c3711735a6a30d89b5df7c284ec2 [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/json_generator.h"
#include "fidl/names.h"
#include "fidl/types.h"
namespace fidl {
void JSONGenerator::Generate(const flat::Decl* decl) { Generate(decl->name); }
void JSONGenerator::Generate(SourceSpan value) { EmitString(value.data()); }
void JSONGenerator::Generate(NameSpan value) {
if (omit_locations_)
return;
GenerateObject([&]() {
GenerateObjectMember("filename", value.filename, Position::kFirst);
GenerateObjectMember("line", (uint32_t)value.position.line);
GenerateObjectMember("column", (uint32_t)value.position.column);
GenerateObjectMember("length", (uint32_t)value.length);
});
}
void JSONGenerator::Generate(const flat::ConstantValue& value) {
switch (value.kind) {
case flat::ConstantValue::Kind::kUint8: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<uint8_t>&>(value);
EmitNumeric(static_cast<uint64_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kUint16: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<uint16_t>&>(value);
EmitNumeric(static_cast<uint16_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kUint32: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<uint32_t>&>(value);
EmitNumeric(static_cast<uint32_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kUint64: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<uint64_t>&>(value);
EmitNumeric(static_cast<uint64_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kInt8: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<int8_t>&>(value);
EmitNumeric(static_cast<int64_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kInt16: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<int16_t>&>(value);
EmitNumeric(static_cast<int16_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kInt32: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<int32_t>&>(value);
EmitNumeric(static_cast<int32_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kInt64: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<int64_t>&>(value);
EmitNumeric(static_cast<int64_t>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kFloat32: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<float>&>(value);
EmitNumeric(static_cast<float>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kFloat64: {
auto numeric_constant = reinterpret_cast<const flat::NumericConstantValue<double>&>(value);
EmitNumeric(static_cast<double>(numeric_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kBool: {
auto bool_constant = reinterpret_cast<const flat::BoolConstantValue&>(value);
EmitBoolean(static_cast<bool>(bool_constant), kAsString);
break;
}
case flat::ConstantValue::Kind::kString: {
auto string_constant = reinterpret_cast<const flat::StringConstantValue&>(value);
EmitLiteral(string_constant.value);
break;
}
} // switch
}
void JSONGenerator::Generate(types::HandleSubtype value) { EmitString(NameHandleSubtype(value)); }
void JSONGenerator::Generate(types::Nullability value) {
switch (value) {
case types::Nullability::kNullable:
EmitBoolean(true);
break;
case types::Nullability::kNonnullable:
EmitBoolean(false);
break;
}
}
void JSONGenerator::Generate(const raw::Identifier& value) { EmitString(value.span().data()); }
void JSONGenerator::Generate(const flat::LiteralConstant& value) {
GenerateObject([&]() {
GenerateObjectMember("kind", NameRawLiteralKind(value.literal->kind), Position::kFirst);
GenerateObjectMember("value", value.Value());
GenerateObjectMember("expression", value.literal->span().data());
});
}
void JSONGenerator::Generate(const flat::Constant& value) {
GenerateObject([&]() {
// TODO(pascallouis): We should explore exposing these in the JSON IR, such that the
// implicit bounds are made explicit by fidlc, rather than sprinkled throughout all
// backends.
//
// For now, do not emit synthesized constants
if (value.kind == flat::Constant::Kind::kSynthesized)
return;
GenerateObjectMember("kind", NameFlatConstantKind(value.kind), Position::kFirst);
GenerateObjectMember("value", value.Value());
GenerateObjectMember("expression", value.span);
switch (value.kind) {
case flat::Constant::Kind::kIdentifier: {
auto type = static_cast<const flat::IdentifierConstant*>(&value);
GenerateObjectMember("identifier", type->name);
break;
}
case flat::Constant::Kind::kLiteral: {
auto& type = static_cast<const flat::LiteralConstant&>(value);
GenerateObjectMember("literal", type);
break;
}
case flat::Constant::Kind::kBinaryOperator: {
// Avoid emitting a structure for binary operators in favor of "expression".
break;
}
case flat::Constant::Kind::kSynthesized:
break;
}
});
}
void JSONGenerator::Generate(const flat::Type* value) {
GenerateObject([&]() {
GenerateObjectMember("kind", NameFlatTypeKind(value->kind), Position::kFirst);
switch (value->kind) {
case flat::Type::Kind::kArray: {
auto type = static_cast<const flat::ArrayType*>(value);
GenerateObjectMember("element_type", type->element_type);
GenerateObjectMember("element_count", type->element_count->value);
break;
}
case flat::Type::Kind::kVector: {
auto type = static_cast<const flat::VectorType*>(value);
GenerateObjectMember("element_type", type->element_type);
if (*type->element_count < flat::Size::Max())
GenerateObjectMember("maybe_element_count", type->element_count->value);
GenerateObjectMember("nullable", type->nullability);
break;
}
case flat::Type::Kind::kString: {
auto type = static_cast<const flat::StringType*>(value);
if (*type->max_size < flat::Size::Max())
GenerateObjectMember("maybe_element_count", type->max_size->value);
GenerateObjectMember("nullable", type->nullability);
break;
}
case flat::Type::Kind::kHandle: {
auto type = static_cast<const flat::HandleType*>(value);
GenerateObjectMember("obj_type", type->obj_type);
GenerateObjectMember("subtype", type->subtype);
GenerateObjectMember(
"rights",
static_cast<const flat::NumericConstantValue<uint32_t>&>(type->rights->Value()).value);
GenerateObjectMember("nullable", type->nullability);
break;
}
case flat::Type::Kind::kRequestHandle: {
auto type = static_cast<const flat::RequestHandleType*>(value);
GenerateObjectMember("subtype", type->protocol_type->name);
// TODO(fxbug.dev/43803): Add required and optional rights.
GenerateObjectMember("nullable", type->nullability);
break;
}
case flat::Type::Kind::kPrimitive: {
auto type = static_cast<const flat::PrimitiveType*>(value);
GenerateObjectMember("subtype", type->name);
break;
}
case flat::Type::Kind::kIdentifier: {
auto type = static_cast<const flat::IdentifierType*>(value);
GenerateObjectMember("identifier", type->name);
GenerateObjectMember("nullable", type->nullability);
break;
}
}
});
}
void JSONGenerator::Generate(const raw::Attribute& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
if (value.value != "")
GenerateObjectMember("value", value.value);
else
GenerateObjectMember("value", std::string_view());
});
}
void JSONGenerator::Generate(const raw::AttributeList& value) { Generate(value.attributes); }
void JSONGenerator::Generate(const raw::Ordinal64& value) { EmitNumeric(value.value); }
void JSONGenerator::Generate(const flat::Name& value) {
// These look like (when there is a library)
// { "LIB.LIB.LIB", "ID" }
// or (when there is not)
// { "ID" }
Generate(NameFlatName(value));
}
void JSONGenerator::Generate(const flat::Bits& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateTypeAndFromTypeAlias(*value.subtype_ctor);
// TODO(fxbug.dev/7660): When all numbers are wrapped as string, we can simply
// call GenerateObjectMember directly.
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("mask");
EmitNumeric(value.mask, kAsString);
GenerateObjectMember("members", value.members);
GenerateObjectMember("strict", value.strictness == types::Strictness::kStrict);
});
}
void JSONGenerator::Generate(const flat::Bits::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("value", value.value);
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const flat::Const& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateTypeAndFromTypeAlias(*value.type_ctor);
GenerateObjectMember("value", value.value);
});
}
void JSONGenerator::Generate(const flat::Enum& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
// TODO(fxbug.dev/7660): Due to legacy reasons, the 'type' of enums is actually
// the primitive subtype, and therefore cannot use
// GenerateTypeAndFromTypeAlias here.
GenerateObjectMember("type", value.type->name);
if (value.subtype_ctor->from_type_alias)
GenerateObjectMember("experimental_maybe_from_type_alias",
value.subtype_ctor->from_type_alias.value());
GenerateObjectMember("members", value.members);
GenerateObjectMember("strict", value.strictness == types::Strictness::kStrict);
if (value.strictness == types::Strictness::kFlexible) {
if (value.unknown_value_signed) {
GenerateObjectMember("maybe_unknown_value", value.unknown_value_signed.value());
} else {
GenerateObjectMember("maybe_unknown_value", value.unknown_value_unsigned.value());
}
}
});
}
void JSONGenerator::Generate(const flat::Enum::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("value", value.value);
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const flat::Protocol& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("methods", value.all_methods);
});
}
void JSONGenerator::Generate(const flat::Protocol::MethodWithInfo& method_with_info) {
assert(method_with_info.method != nullptr);
const auto& value = *method_with_info.method;
GenerateObject([&]() {
GenerateObjectMember("ordinal", value.generated_ordinal64, Position::kFirst);
GenerateObjectMember("name", value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("has_request", value.maybe_request != nullptr);
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
if (value.maybe_request != nullptr) {
GenerateRequest("maybe_request", *value.maybe_request);
}
GenerateObjectMember("has_response", value.maybe_response != nullptr);
if (value.maybe_response != nullptr) {
GenerateRequest("maybe_response", *value.maybe_response);
}
GenerateObjectMember("is_composed", method_with_info.is_composed);
});
}
void JSONGenerator::GenerateTypeAndFromTypeAlias(const flat::TypeConstructor& value,
Position position) {
GenerateObjectMember("type", value.type, position);
if (value.from_type_alias)
GenerateObjectMember("experimental_maybe_from_type_alias", value.from_type_alias.value());
}
void JSONGenerator::GenerateRequest(const std::string& prefix, const flat::Struct& value) {
// Temporarily hardcode the generation of request/response struct members to use the old
// wire format, in order to maintain compatibility during the transition for fxbug.dev/7704.
// This block of code is copied from JsonWriter::GenerateArray (with the difference
// noted below), and will be removed once backends are updated to use anonymous structs.
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey(prefix);
EmitArrayBegin();
if (value.members.begin() != value.members.end()) {
Indent();
EmitNewlineWithIndent();
}
for (auto it = value.members.begin(); it != value.members.end(); ++it) {
if (it != value.members.begin())
EmitArraySeparator();
// call Generate with is_request_response = true on each struct member
Generate(*it, true);
}
if (value.members.begin() != value.members.end()) {
Outdent();
EmitNewlineWithIndent();
}
EmitArrayEnd();
if (!value.members.empty()) {
GenerateObjectMember(prefix + "_payload", value.name);
}
GenerateTypeShapes(prefix, value, true);
}
void JSONGenerator::Generate(const flat::Resource::Property& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateTypeAndFromTypeAlias(*value.type_ctor);
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const flat::Resource& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateTypeAndFromTypeAlias(*value.subtype_ctor);
GenerateObjectMember("properties", value.properties);
});
}
void JSONGenerator::Generate(const flat::Service& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
});
}
void JSONGenerator::Generate(const flat::Service::Member& value) {
GenerateObject([&]() {
GenerateTypeAndFromTypeAlias(*value.type_ctor, Position::kFirst);
GenerateObjectMember("name", value.name);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const flat::Struct& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("anonymous", value.is_request_or_response);
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
GenerateObjectMember("resource", value.resourceness == types::Resourceness::kResource);
GenerateTypeShapes(value);
});
}
void JSONGenerator::Generate(const flat::Struct* value) { Generate(*value); }
void JSONGenerator::Generate(const flat::Struct::Member& value, bool is_request_or_response) {
GenerateObject([&]() {
GenerateTypeAndFromTypeAlias(*value.type_ctor, Position::kFirst);
GenerateObjectMember("name", value.name);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
if (value.maybe_default_value)
GenerateObjectMember("maybe_default_value", value.maybe_default_value);
GenerateFieldShapes(value, is_request_or_response);
});
}
void JSONGenerator::Generate(const flat::Table& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
GenerateObjectMember("strict", value.strictness == types::Strictness::kStrict);
GenerateObjectMember("resource", value.resourceness == types::Resourceness::kResource);
GenerateTypeShapes(value);
});
}
void JSONGenerator::Generate(const flat::Table::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("ordinal", *value.ordinal, Position::kFirst);
if (value.maybe_used) {
assert(!value.span);
GenerateObjectMember("reserved", false);
GenerateTypeAndFromTypeAlias(*value.maybe_used->type_ctor);
GenerateObjectMember("name", value.maybe_used->name);
GenerateObjectMember("location", NameSpan(value.maybe_used->name));
if (value.maybe_used->attributes)
GenerateObjectMember("maybe_attributes", value.maybe_used->attributes);
// TODO(fxbug.dev/7932): Support defaults on tables.
} else {
assert(value.span);
GenerateObjectMember("reserved", true);
GenerateObjectMember("location", NameSpan(value.span.value()));
}
});
}
void JSONGenerator::Generate(const TypeShape& type_shape) {
GenerateObject([&]() {
GenerateObjectMember("inline_size", type_shape.inline_size, Position::kFirst);
GenerateObjectMember("alignment", type_shape.alignment);
GenerateObjectMember("depth", type_shape.depth);
GenerateObjectMember("max_handles", type_shape.max_handles);
GenerateObjectMember("max_out_of_line", type_shape.max_out_of_line);
GenerateObjectMember("has_padding", type_shape.has_padding);
GenerateObjectMember("has_flexible_envelope", type_shape.has_flexible_envelope);
});
}
void JSONGenerator::Generate(const FieldShape& field_shape) {
GenerateObject([&]() {
GenerateObjectMember("offset", field_shape.offset, Position::kFirst);
GenerateObjectMember("padding", field_shape.padding);
});
}
void JSONGenerator::Generate(const flat::Union& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
GenerateObjectMember("strict", value.strictness == types::Strictness::kStrict);
GenerateObjectMember("resource", value.resourceness == types::Resourceness::kResource);
GenerateTypeShapes(value);
});
}
void JSONGenerator::Generate(const flat::Union::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("ordinal", value.ordinal, Position::kFirst);
if (value.maybe_used) {
assert(!value.span);
GenerateObjectMember("reserved", false);
GenerateObjectMember("name", value.maybe_used->name);
GenerateTypeAndFromTypeAlias(*value.maybe_used->type_ctor);
GenerateObjectMember("location", NameSpan(value.maybe_used->name));
if (value.maybe_used->attributes)
GenerateObjectMember("maybe_attributes", value.maybe_used->attributes);
} else {
GenerateObjectMember("reserved", true);
GenerateObjectMember("location", NameSpan(value.span.value()));
}
});
}
void JSONGenerator::Generate(const flat::TypeConstructor::FromTypeAlias& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.decl->name, Position::kFirst);
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("args");
// In preparation of template support, it is better to expose a
// heterogenous argument list to backends, rather than the currently
// limited internal view.
EmitArrayBegin();
if (value.maybe_arg_type) {
Indent();
EmitNewlineWithIndent();
Generate(value.maybe_arg_type->name);
Outdent();
EmitNewlineWithIndent();
}
EmitArrayEnd();
GenerateObjectMember("nullable", value.nullability);
if (value.maybe_size)
GenerateObjectMember("maybe_size", *value.maybe_size);
});
}
void JSONGenerator::Generate(const flat::TypeConstructor& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.type ? value.type->name : value.name, Position::kFirst);
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("args");
// In preparation of template support, it is better to expose a
// heterogenous argument list to backends, rather than the currently
// limited internal view.
EmitArrayBegin();
if (value.maybe_arg_type_ctor) {
Indent();
EmitNewlineWithIndent();
Generate(*value.maybe_arg_type_ctor);
Outdent();
EmitNewlineWithIndent();
}
EmitArrayEnd();
GenerateObjectMember("nullable", value.nullability);
if (value.maybe_size)
GenerateObjectMember("maybe_size", value.maybe_size);
if (value.handle_rights)
GenerateObjectMember("handle_rights", value.handle_rights);
});
}
void JSONGenerator::Generate(const flat::TypeAlias& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
if (value.attributes)
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("partial_type_ctor", *value.partial_type_ctor);
});
}
void JSONGenerator::Generate(const flat::Library* library) {
GenerateObject([&]() {
auto library_name = flat::LibraryName(library, ".");
GenerateObjectMember("name", library_name, Position::kFirst);
GenerateExternalDeclarationsMember(library);
});
}
void JSONGenerator::GenerateTypeShapes(const flat::Object& object) {
GenerateTypeShapes("", object);
}
void JSONGenerator::GenerateTypeShapes(std::string prefix, const flat::Object& object,
bool is_request_or_response) {
if (prefix.size() > 0) {
prefix.push_back('_');
}
// NOTE: while the transition for fxbug.dev/7024 is ongoing, we need to treat request/responses
// specially as before, but this will be removed once the transition is complete
const auto& v1 = is_request_or_response ? WireFormat::kV1NoEe : WireFormat::kV1Header;
GenerateObjectMember(prefix + "type_shape_v1", TypeShape(object, v1));
}
void JSONGenerator::GenerateFieldShapes(const flat::Struct::Member& struct_member,
bool is_request_or_response) {
// NOTE: while the transition for fxbug.dev/7024 is ongoing, we need to treat request/responses
// specially as before, but this will be removed once the transition is complete
const auto& v1 = is_request_or_response ? WireFormat::kV1NoEe : WireFormat::kV1Header;
GenerateObjectMember("field_shape_v1", FieldShape(struct_member, v1));
}
void JSONGenerator::GenerateDeclarationsEntry(int count, const flat::Name& name,
std::string_view decl_kind) {
if (count == 0) {
Indent();
EmitNewlineWithIndent();
} else {
EmitObjectSeparator();
}
EmitObjectKey(NameFlatName(name));
EmitString(decl_kind);
}
void JSONGenerator::GenerateDeclarationsMember(const flat::Library* library, Position position) {
GenerateObjectPunctuation(position);
EmitObjectKey("declarations");
GenerateObject([&]() {
int count = 0;
for (const auto& decl : library->bits_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "bits");
for (const auto& decl : library->const_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "const");
for (const auto& decl : library->enum_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "enum");
for (const auto& decl : library->resource_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "experimental_resource");
for (const auto& decl : library->protocol_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "interface");
for (const auto& decl : library->service_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "service");
for (const auto& decl : library->struct_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "struct");
for (const auto& decl : library->table_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "table");
for (const auto& decl : library->union_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "union");
for (const auto& decl : library->type_alias_declarations_)
GenerateDeclarationsEntry(count++, decl->name, "type_alias");
});
}
void JSONGenerator::GenerateExternalDeclarationsEntry(
int count, const flat::Name& name, std::string_view decl_kind,
std::optional<types::Resourceness> maybe_resourceness) {
if (count == 0) {
Indent();
EmitNewlineWithIndent();
} else {
EmitObjectSeparator();
}
EmitObjectKey(NameFlatName(name));
GenerateObject([&]() {
GenerateObjectMember("kind", decl_kind, Position::kFirst);
if (maybe_resourceness) {
GenerateObjectMember("resource", *maybe_resourceness == types::Resourceness::kResource);
}
});
}
void JSONGenerator::GenerateExternalDeclarationsMember(const flat::Library* library,
Position position) {
GenerateObjectPunctuation(position);
EmitObjectKey("declarations");
GenerateObject([&]() {
int count = 0;
for (const auto& decl : library->bits_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "bits", std::nullopt);
for (const auto& decl : library->const_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "const", std::nullopt);
for (const auto& decl : library->enum_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "enum", std::nullopt);
for (const auto& decl : library->resource_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "experimental_resource", std::nullopt);
for (const auto& decl : library->protocol_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "interface", std::nullopt);
for (const auto& decl : library->service_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "service", std::nullopt);
for (const auto& decl : library->struct_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "struct", decl->resourceness);
for (const auto& decl : library->table_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "table", decl->resourceness);
for (const auto& decl : library->union_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "union", decl->resourceness);
for (const auto& decl : library->type_alias_declarations_)
GenerateExternalDeclarationsEntry(count++, decl->name, "type_alias", std::nullopt);
});
}
namespace {
struct LibraryComparator {
bool operator()(const flat::Library* lhs, const flat::Library* rhs) const {
assert(!lhs->name().empty());
assert(!rhs->name().empty());
return lhs->name() < rhs->name();
}
};
std::set<const flat::Library*, LibraryComparator> TransitiveDependencies(
const flat::Library* library) {
std::set<const flat::Library*, LibraryComparator> dependencies;
auto add_dependency = [&](const flat::Library* dep_library) {
if (!dep_library->HasAttribute("Internal")) {
dependencies.insert(dep_library);
}
};
for (const auto& dep_library : library->dependencies()) {
add_dependency(dep_library);
}
// Discover additional dependencies that are required to support
// cross-library protocol composition.
for (const auto& protocol : library->protocol_declarations_) {
for (const auto method_with_info : protocol->all_methods) {
if (auto request = method_with_info.method->maybe_request) {
for (const auto& member : request->members) {
if (auto dep_library = member.type_ctor->name.library()) {
add_dependency(dep_library);
}
}
}
if (auto response = method_with_info.method->maybe_response) {
for (const auto& member : response->members) {
if (auto dep_library = member.type_ctor->name.library()) {
add_dependency(dep_library);
}
}
}
add_dependency(method_with_info.method->owning_protocol->name.library());
}
}
dependencies.erase(library);
return dependencies;
}
// Return all structs that should be emitted in the JSON IR, which consists of
// two parts: all structs from this library (which includes all struct definitions
// and request/response payloads defined in this library), plus any request/response
// payloads defined in other libraries that are composed into a protocol in this
// library.
std::vector<const flat::Struct*> AllStructs(const flat::Library* library) {
std::vector<const flat::Struct*> all_structs;
for (const auto& struct_decl : library->struct_declarations_) {
if (struct_decl->is_request_or_response && struct_decl->members.empty())
continue;
all_structs.push_back(struct_decl.get());
}
for (const auto& protocol : library->protocol_declarations_) {
for (const auto method_with_info : protocol->all_methods) {
// these are already included in the library's struct declarations
if (!method_with_info.is_composed)
continue;
const auto& method = method_with_info.method;
if (method->maybe_request && !method->maybe_request->members.empty()) {
all_structs.push_back(method->maybe_request);
}
if (method->maybe_response && !method->maybe_response->members.empty()) {
all_structs.push_back(method->maybe_response);
}
}
}
return all_structs;
}
} // namespace
std::ostringstream JSONGenerator::Produce() {
ResetIndentLevel();
GenerateObject([&]() {
GenerateObjectMember("version", std::string_view("0.0.1"), Position::kFirst);
GenerateObjectMember("name", LibraryName(library_, "."));
if (auto attributes = library_->attributes(); attributes) {
GenerateObjectMember("maybe_attributes", *attributes);
}
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("library_dependencies");
GenerateArray(TransitiveDependencies(library_));
GenerateObjectMember("bits_declarations", library_->bits_declarations_);
GenerateObjectMember("const_declarations", library_->const_declarations_);
GenerateObjectMember("enum_declarations", library_->enum_declarations_);
GenerateObjectMember("experimental_resource_declarations", library_->resource_declarations_);
GenerateObjectMember("interface_declarations", library_->protocol_declarations_);
GenerateObjectMember("service_declarations", library_->service_declarations_);
GenerateObjectMember("struct_declarations", AllStructs(library_));
GenerateObjectMember("table_declarations", library_->table_declarations_);
GenerateObjectMember("union_declarations", library_->union_declarations_);
GenerateObjectMember("type_alias_declarations", library_->type_alias_declarations_);
// The library's declaration_order_ contains all the declarations for all
// transitive dependencies. The backend only needs the declaration order
// for this specific library.
std::vector<std::string> declaration_order;
for (flat::Decl* decl : library_->declaration_order_) {
if (decl->kind == flat::Decl::Kind::kStruct) {
auto struct_decl = static_cast<flat::Struct*>(decl);
if (struct_decl->is_request_or_response)
continue;
}
if (decl->name.library() == library_)
declaration_order.push_back(NameFlatName(decl->name));
}
GenerateObjectMember("declaration_order", declaration_order);
GenerateDeclarationsMember(library_);
});
GenerateEOF();
return std::move(json_file_);
}
} // namespace fidl