blob: 126a78a02954611e1b7eb5b2903627708b56e0d3 [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 "tools/fidl/fidlc/src/json_generator.h"
#include <zircon/assert.h>
#include "tools/fidl/fidlc/src/flat_ast.h"
#include "tools/fidl/fidlc/src/name.h"
#include "tools/fidl/fidlc/src/names.h"
#include "tools/fidl/fidlc/src/properties.h"
#include "tools/fidl/fidlc/src/types.h"
namespace fidlc {
void JSONGenerator::Generate(SourceSpan value) { EmitString(value.data()); }
void JSONGenerator::Generate(NameSpan value) {
GenerateObject([&]() {
GenerateObjectMember("filename", value.filename, Position::kFirst);
GenerateObjectMember("line", static_cast<uint32_t>(value.position.line));
GenerateObjectMember("column", static_cast<uint32_t>(value.position.column));
GenerateObjectMember("length", static_cast<uint32_t>(value.length));
});
}
void JSONGenerator::Generate(const ConstantValue& value) {
switch (value.kind) {
case ConstantValue::Kind::kUint8:
case ConstantValue::Kind::kZxUchar: {
auto& numeric_constant = static_cast<const NumericConstantValue<uint8_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kUint16: {
auto& numeric_constant = static_cast<const NumericConstantValue<uint16_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kUint32: {
auto& numeric_constant = static_cast<const NumericConstantValue<uint32_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kUint64:
case ConstantValue::Kind::kZxUsize64:
case ConstantValue::Kind::kZxUintptr64: {
auto& numeric_constant = static_cast<const NumericConstantValue<uint64_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kInt8: {
auto& numeric_constant = static_cast<const NumericConstantValue<int8_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kInt16: {
auto& numeric_constant = static_cast<const NumericConstantValue<int16_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kInt32: {
auto& numeric_constant = static_cast<const NumericConstantValue<int32_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kInt64: {
auto& numeric_constant = static_cast<const NumericConstantValue<int64_t>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kFloat32: {
auto& numeric_constant = static_cast<const NumericConstantValue<float>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kFloat64: {
auto& numeric_constant = static_cast<const NumericConstantValue<double>&>(value);
EmitNumeric(numeric_constant.value, kAsString);
break;
}
case ConstantValue::Kind::kBool: {
auto& bool_constant = static_cast<const BoolConstantValue&>(value);
EmitBoolean(static_cast<bool>(bool_constant), kAsString);
break;
}
case ConstantValue::Kind::kDocComment: {
auto& doc_comment_constant = static_cast<const DocCommentConstantValue&>(value);
EmitString(doc_comment_constant.MakeContents());
break;
}
case ConstantValue::Kind::kString: {
auto& string_constant = static_cast<const StringConstantValue&>(value);
EmitLiteral(string_constant.value);
break;
}
} // switch
}
void JSONGenerator::Generate(HandleSubtype value) { EmitString(NameHandleSubtype(value)); }
void JSONGenerator::Generate(Nullability value) {
switch (value) {
case Nullability::kNullable:
EmitBoolean(true);
break;
case Nullability::kNonnullable:
EmitBoolean(false);
break;
}
}
void JSONGenerator::Generate(Strictness value) { EmitBoolean(value == Strictness::kStrict); }
void JSONGenerator::Generate(Openness value) {
switch (value) {
case Openness::kOpen:
EmitString("open");
break;
case Openness::kAjar:
EmitString("ajar");
break;
case Openness::kClosed:
EmitString("closed");
break;
}
}
void JSONGenerator::Generate(const RawIdentifier& value) { EmitString(value.span().data()); }
void JSONGenerator::Generate(const 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 Constant& value) {
GenerateObject([&]() {
GenerateObjectMember("kind", NameFlatConstantKind(value.kind), Position::kFirst);
GenerateObjectMember("value", value.Value());
GenerateObjectMember("expression", value.span);
switch (value.kind) {
case Constant::Kind::kIdentifier: {
auto identifier = static_cast<const IdentifierConstant*>(&value);
GenerateObjectMember("identifier", identifier->reference.resolved().name());
break;
}
case Constant::Kind::kLiteral: {
auto literal = static_cast<const LiteralConstant*>(&value);
GenerateObjectMember("literal", *literal);
break;
}
case Constant::Kind::kBinaryOperator: {
// Avoid emitting a structure for binary operators in favor of "expression".
break;
}
}
});
}
void JSONGenerator::Generate(const Type* value) {
GenerateObject([&]() {
GenerateObjectMember("kind", NameFlatTypeKind(value), Position::kFirst);
switch (value->kind) {
case Type::Kind::kBox: {
const auto* type = static_cast<const BoxType*>(value)->boxed_type;
ZX_ASSERT(type->kind == Type::Kind::kIdentifier);
const auto* id_type = static_cast<const IdentifierType*>(type);
GenerateObjectMember("identifier", id_type->name);
GenerateObjectMember("nullable", id_type->nullability);
break;
}
case Type::Kind::kVector: {
// This code path should only be exercised if the type is "bytes." All
// other handling of kVector is handled in GenerateParameterizedType.
const auto* type = static_cast<const VectorType*>(value);
GenerateObjectMember("element_type", type->element_type);
if (type->ElementCount() < SizeValue::Max().value)
GenerateObjectMember("maybe_element_count", type->ElementCount());
GenerateObjectMember("nullable", type->nullability);
break;
}
case Type::Kind::kString: {
const auto* type = static_cast<const StringType*>(value);
if (type->MaxSize() < SizeValue::Max().value)
GenerateObjectMember("maybe_element_count", type->MaxSize());
GenerateObjectMember("nullable", type->nullability);
break;
}
case Type::Kind::kHandle: {
const auto* type = static_cast<const HandleType*>(value);
GenerateObjectMember("obj_type", static_cast<uint32_t>(type->subtype));
GenerateObjectMember("subtype", type->subtype);
GenerateObjectMember(
"rights", static_cast<const NumericConstantValue<uint32_t>*>(type->rights)->value);
GenerateObjectMember("nullable", type->nullability);
GenerateObjectMember("resource_identifier", NameFlatName(type->resource_decl->name));
break;
}
case Type::Kind::kPrimitive: {
const auto* type = static_cast<const PrimitiveType*>(value);
GenerateObjectMember("subtype", type->name);
break;
}
case Type::Kind::kInternal: {
const auto* type = static_cast<const InternalType*>(value);
switch (type->subtype) {
case InternalSubtype::kFrameworkErr:
GenerateObjectMember("subtype", std::string_view("framework_error"));
break;
}
break;
}
case Type::Kind::kIdentifier: {
const auto* type = static_cast<const IdentifierType*>(value);
GenerateObjectMember("identifier", type->name);
GenerateObjectMember("nullable", type->nullability);
break;
}
// We treat client_end the same as an IdentifierType of a protocol to avoid changing
// the JSON IR.
// TODO(https://fxbug.dev/42149402): clean up client/server end representation in the IR
case Type::Kind::kTransportSide: {
const auto* type = static_cast<const TransportSideType*>(value);
// This code path should only apply to client ends. The server end code
// path is colocated with the parameterized types.
ZX_ASSERT(type->end == TransportSide::kClient);
GenerateObjectMember("identifier", type->protocol_decl->name);
GenerateObjectMember("nullable", type->nullability);
GenerateObjectMember("protocol_transport", type->protocol_transport);
break;
}
case Type::Kind::kZxExperimentalPointer: {
const auto* type = static_cast<const ZxExperimentalPointerType*>(value);
GenerateObjectMember("pointee_type", type->pointee_type);
break;
}
case Type::Kind::kArray:
case Type::Kind::kUntypedNumeric:
ZX_PANIC("unexpected kind");
}
GenerateObjectMember("type_shape_v2", value->type_shape.value());
});
}
void JSONGenerator::Generate(const AttributeArg& value) {
GenerateObject([&]() {
ZX_ASSERT_MSG(
value.name.has_value(),
"anonymous attribute argument names should always be inferred during compilation");
GenerateObjectMember("name", value.name.value(), Position::kFirst);
GenerateObjectMember("type", value.value->type->name);
GenerateObjectMember("value", value.value);
ZX_ASSERT(value.span.valid());
GenerateObjectMember("location", NameSpan(value.span));
});
}
void JSONGenerator::Generate(const Attribute& value) {
GenerateObject([&]() {
const auto& name = to_lower_snake_case(std::string(value.name.data()));
GenerateObjectMember("name", name, Position::kFirst);
GenerateObjectMember("arguments", value.args);
ZX_ASSERT(value.span.valid());
GenerateObjectMember("location", NameSpan(value.span));
});
}
void JSONGenerator::Generate(const AttributeList& value) { Generate(value.attributes); }
void JSONGenerator::Generate(const RawOrdinal64& value) { EmitNumeric(value.value); }
void JSONGenerator::GenerateDeclName(const Name& name) {
GenerateObjectMember("name", name, Position::kFirst);
if (auto n = name.as_anonymous()) {
GenerateObjectMember("naming_context", n->context->Context());
} else {
std::vector<std::string> ctx = {std::string(name.decl_name())};
GenerateObjectMember("naming_context", ctx);
}
}
void JSONGenerator::Generate(const Name& name) {
// TODO(https://fxbug.dev/42174095): NameFlatName omits the library name for builtins,
// since we want error messages to say "uint32" not "fidl/uint32". However,
// builtins MAX and HEAD can end up in the JSON IR as identifier constants,
// and to satisfy the schema we must produce a proper compound identifier
// (with a library name). We should solve this in a cleaner way.
if (name.is_intrinsic() && name.decl_name() == "MAX") {
EmitString(std::string_view("fidl/MAX"));
} else if (name.is_intrinsic() && name.decl_name() == "HEAD") {
EmitString(std::string_view("fidl/HEAD"));
} else {
EmitString(NameFlatName(name));
}
}
void JSONGenerator::Generate(const Bits& value) {
GenerateObject([&]() {
GenerateDeclName(value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateTypeAndFromAlias(value.subtype_ctor.get());
// TODO(https://fxbug.dev/42156522): 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);
});
}
void JSONGenerator::Generate(const Bits::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
GenerateObjectMember("value", value.value);
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const Const& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateTypeAndFromAlias(value.type_ctor.get());
GenerateObjectMember("value", value.value);
});
}
void JSONGenerator::Generate(const Enum& value) {
GenerateObject([&]() {
GenerateDeclName(value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
// TODO(https://fxbug.dev/42156522): Due to legacy reasons, the 'type' of enums is actually
// the primitive subtype, and therefore cannot use
// GenerateTypeAndFromAlias here.
GenerateObjectMember("type", value.type->name);
GenerateExperimentalMaybeFromAlias(value.subtype_ctor->resolved_params);
GenerateObjectMember("members", value.members);
GenerateObjectMember("strict", value.strictness);
if (value.strictness == 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 Enum::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
GenerateObjectMember("value", value.value);
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const Protocol& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("openness", value.openness);
GenerateObjectMember("composed_protocols", value.composed_protocols);
GenerateObjectMember("methods", value.all_methods);
});
}
void JSONGenerator::Generate(const Protocol::ComposedProtocol& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.reference.resolved().name(), Position::kFirst);
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("location", NameSpan(value.reference.span()));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
});
}
void JSONGenerator::Generate(const Protocol::MethodWithInfo& method_with_info) {
ZX_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("strict", value.strictness);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
GenerateObjectMember("has_request", value.has_request);
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
if (value.maybe_request) {
GenerateTypeAndFromAlias(TypeKind::kRequestPayload, value.maybe_request.get(),
Position::kSubsequent);
}
GenerateObjectMember("has_response", value.has_response);
if (value.maybe_response) {
GenerateTypeAndFromAlias(TypeKind::kResponsePayload, value.maybe_response.get(),
Position::kSubsequent);
}
GenerateObjectMember("is_composed", method_with_info.is_composed);
GenerateObjectMember("has_error", value.has_error);
if (auto type_ctor = value.result_success_type_ctor)
GenerateObjectMember("maybe_response_success_type", type_ctor->type);
if (auto type_ctor = value.result_domain_error_type_ctor)
GenerateObjectMember("maybe_response_err_type", type_ctor->type);
});
}
void JSONGenerator::GenerateTypeAndFromAlias(const TypeConstructor* value, Position position) {
GenerateTypeAndFromAlias(TypeKind::kConcrete, value, position);
}
bool ShouldExposeAliasOfParametrizedType(const Type& type) {
bool is_server_end = false;
if (type.kind == Type::Kind::kTransportSide) {
const auto* transport_side = static_cast<const TransportSideType*>(&type);
is_server_end = transport_side->end == TransportSide::kServer;
}
return type.kind == Type::Kind::kArray || type.kind == Type::Kind::kVector || is_server_end;
}
void JSONGenerator::GenerateTypeAndFromAlias(TypeKind parent_type_kind,
const TypeConstructor* value, Position position) {
const auto* type = value->type;
const auto& invocation = value->resolved_params;
if (ShouldExposeAliasOfParametrizedType(*type)) {
if (invocation.from_alias) {
GenerateParameterizedType(parent_type_kind, type,
invocation.from_alias->partial_type_ctor.get(), position);
} else {
GenerateParameterizedType(parent_type_kind, type, value, position);
}
GenerateExperimentalMaybeFromAlias(invocation);
return;
}
std::string key;
switch (parent_type_kind) {
case kConcrete: {
key = "type";
break;
}
case kParameterized: {
key = "element_type";
break;
}
case kRequestPayload: {
key = "maybe_request_payload";
break;
}
case kResponsePayload: {
key = "maybe_response_payload";
break;
}
}
GenerateObjectMember(key, type, position);
GenerateExperimentalMaybeFromAlias(invocation);
}
void JSONGenerator::GenerateExperimentalMaybeFromAlias(const LayoutInvocation& invocation) {
if (invocation.from_alias)
GenerateObjectMember("experimental_maybe_from_alias", invocation);
}
void JSONGenerator::GenerateParameterizedType(TypeKind parent_type_kind, const Type* type,
const TypeConstructor* type_ctor, Position position) {
const auto& invocation = type_ctor->resolved_params;
std::string key = parent_type_kind == TypeKind::kConcrete ? "type" : "element_type";
GenerateObjectPunctuation(position);
EmitObjectKey(key);
GenerateObject([&]() {
GenerateObjectMember("kind", NameFlatTypeKind(type), Position::kFirst);
switch (type->kind) {
case Type::Kind::kArray: {
const auto* array_type = static_cast<const ArrayType*>(type);
if (!array_type->IsStringArray()) {
GenerateTypeAndFromAlias(TypeKind::kParameterized, invocation.element_type_raw);
}
GenerateObjectMember("element_count", array_type->element_count->value);
break;
}
case Type::Kind::kVector: {
const auto* vector_type = static_cast<const VectorType*>(type);
GenerateTypeAndFromAlias(TypeKind::kParameterized, invocation.element_type_raw);
if (vector_type->ElementCount() < SizeValue::Max().value)
GenerateObjectMember("maybe_element_count", vector_type->ElementCount());
GenerateObjectMember("nullable", vector_type->nullability);
break;
}
case Type::Kind::kTransportSide: {
const auto* server_end = static_cast<const TransportSideType*>(type);
// This code path should only apply to server ends. The client end code
// path is colocated with the identifier type code for protocols.
ZX_ASSERT(server_end->end == TransportSide::kServer);
GenerateObjectMember("subtype", server_end->protocol_decl->name);
// We don't need to call GenerateExperimentalMaybeFromAlias here like we
// do above because we're guaranteed that the protocol constraint didn't come
// from an alias: in the new syntax, protocols aren't types, and therefore
// `alias Foo = MyProtocol;` is not allowed.
GenerateObjectMember("nullable", server_end->nullability);
GenerateObjectMember("protocol_transport", server_end->protocol_transport);
break;
}
case Type::Kind::kZxExperimentalPointer: {
GenerateTypeAndFromAlias(TypeKind::kParameterized, invocation.element_type_raw);
break;
}
case Type::Kind::kIdentifier:
case Type::Kind::kString:
case Type::Kind::kPrimitive:
case Type::Kind::kBox:
case Type::Kind::kHandle:
case Type::Kind::kUntypedNumeric:
ZX_PANIC("unexpected kind");
case Type::Kind::kInternal: {
switch (static_cast<const InternalType*>(type)->subtype) {
case InternalSubtype::kFrameworkErr:
ZX_PANIC("unexpected kind");
}
}
}
GenerateObjectMember("type_shape_v2", type->type_shape.value());
});
}
void JSONGenerator::Generate(const Resource::Property& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
GenerateTypeAndFromAlias(value.type_ctor.get());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const Resource& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateTypeAndFromAlias(value.subtype_ctor.get());
GenerateObjectMember("properties", value.properties);
});
}
void JSONGenerator::Generate(const Service& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
});
}
void JSONGenerator::Generate(const Service::Member& value) {
GenerateObject([&]() {
GenerateTypeAndFromAlias(value.type_ctor.get(), Position::kFirst);
GenerateObjectMember("name", value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
});
}
void JSONGenerator::Generate(const Struct& value) {
GenerateObject([&]() {
GenerateDeclName(value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
GenerateObjectMember("resource", value.resourceness == Resourceness::kResource);
auto anon = value.name.as_anonymous();
bool is_empty_success_struct =
anon && anon->provenance == Name::Provenance::kGeneratedEmptySuccessStruct;
GenerateObjectMember("is_empty_success_struct", is_empty_success_struct);
GenerateObjectMember("type_shape_v2", value.type_shape.value());
});
}
void JSONGenerator::Generate(const Struct::Member& value) {
GenerateObject([&]() {
GenerateTypeAndFromAlias(value.type_ctor.get(), Position::kFirst);
GenerateObjectMember("name", value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
if (value.maybe_default_value)
GenerateObjectMember("maybe_default_value", value.maybe_default_value);
GenerateObjectMember("field_shape_v2", value.field_shape);
});
}
void JSONGenerator::Generate(const Table& value) {
GenerateObject([&]() {
GenerateDeclName(value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
GenerateObjectMember("strict", value.strictness);
GenerateObjectMember("resource", value.resourceness == Resourceness::kResource);
GenerateObjectMember("type_shape_v2", value.type_shape.value());
});
}
void JSONGenerator::Generate(const Table::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("ordinal", *value.ordinal, Position::kFirst);
GenerateTypeAndFromAlias(value.type_ctor.get());
GenerateObjectMember("name", value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty()) {
GenerateObjectMember("maybe_attributes", value.attributes);
}
});
}
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 Union& value) {
GenerateObject([&]() {
GenerateDeclName(value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
GenerateObjectMember("strict", value.strictness);
GenerateObjectMember("resource", value.resourceness == Resourceness::kResource);
auto anon = value.name.as_anonymous();
bool is_result = anon && anon->provenance == Name::Provenance::kGeneratedResultUnion;
GenerateObjectMember("is_result", is_result);
GenerateObjectMember("type_shape_v2", value.type_shape.value());
});
}
void JSONGenerator::Generate(const Union::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("ordinal", value.ordinal, Position::kFirst);
GenerateObjectMember("name", value.name);
GenerateTypeAndFromAlias(value.type_ctor.get());
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty()) {
GenerateObjectMember("maybe_attributes", value.attributes);
}
});
}
void JSONGenerator::Generate(const Overlay& value) {
GenerateObject([&]() {
GenerateDeclName(value.name);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateObjectMember("members", value.members);
ZX_ASSERT(value.strictness == Strictness::kStrict);
GenerateObjectMember("strict", value.strictness);
ZX_ASSERT(value.resourceness == Resourceness::kValue);
GenerateObjectMember("resource", value.resourceness == Resourceness::kResource);
GenerateObjectMember("type_shape_v2", value.type_shape.value());
});
}
void JSONGenerator::Generate(const Overlay::Member& value) {
GenerateObject([&]() {
GenerateObjectMember("ordinal", value.ordinal, Position::kFirst);
GenerateObjectMember("name", value.name);
GenerateTypeAndFromAlias(value.type_ctor.get());
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty()) {
GenerateObjectMember("maybe_attributes", value.attributes);
}
});
}
void JSONGenerator::Generate(const LayoutInvocation& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.from_alias->name, Position::kFirst);
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("args");
// In preparation of template support, it is better to expose a
// heterogeneous argument list to backends, rather than the currently
// limited internal view.
EmitArrayBegin();
if (value.element_type_resolved) {
Indent();
EmitNewlineWithIndent();
Generate(value.element_type_raw->layout.resolved().name());
Outdent();
EmitNewlineWithIndent();
}
EmitArrayEnd();
GenerateObjectMember("nullable", value.nullability);
if (value.size_raw)
GenerateObjectMember("maybe_size", *value.size_raw);
});
}
void JSONGenerator::Generate(const TypeConstructor& value) {
GenerateObject([&]() {
const auto* type = value.type;
bool is_box = false;
// TODO(https://fxbug.dev/42149402): We need to coerce client/server
// ends into the same representation as P, request<P>; and box<S> into S?
// For box, we just need to access the inner IdentifierType and the rest
// mostly works (except for the correct value for nullability)
if (type && type->kind == Type::Kind::kBox) {
type = static_cast<const BoxType*>(type)->boxed_type;
is_box = true;
}
const TransportSideType* server_end = nullptr;
if (type && type->kind == Type::Kind::kTransportSide) {
const auto* end_type = static_cast<const TransportSideType*>(type);
if (end_type->end == TransportSide::kClient) {
// for client ends, the partial_type_ctor name should be the protocol name
// (since client_end:P is P in the old syntax)
GenerateObjectMember("name", end_type->protocol_decl->name, Position::kFirst);
} else {
// for server ends, the partial_type_ctor name is just "request" (since
// server_end:P is request<P> in the old syntax), and we also need to
// emit the protocol "arg" below
GenerateObjectMember("name", Name::CreateIntrinsic(nullptr, "request"), Position::kFirst);
server_end = end_type;
}
} else {
GenerateObjectMember("name", value.type ? value.type->name : value.layout.resolved().name(),
Position::kFirst);
}
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("args");
const auto& invocation = value.resolved_params;
// In preparation of template support, it is better to expose a
// heterogeneous argument list to backends, rather than the currently
// limited internal view.
EmitArrayBegin();
if (server_end || is_box || invocation.element_type_resolved) {
Indent();
EmitNewlineWithIndent();
if (server_end) {
// TODO(https://fxbug.dev/42149402): Because the JSON IR still uses request<P>
// instead of server_end:P, we have to hardcode the P argument here.
GenerateObject([&]() {
GenerateObjectMember("name", server_end->protocol_decl->name, Position::kFirst);
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("args");
EmitArrayBegin();
EmitArrayEnd();
GenerateObjectMember("nullable", Nullability::kNonnullable);
});
} else if (is_box) {
Generate(*invocation.boxed_type_raw);
} else {
Generate(*invocation.element_type_raw);
}
Outdent();
EmitNewlineWithIndent();
}
EmitArrayEnd();
if (value.type && value.type->kind == Type::Kind::kBox) {
// invocation.nullability will always be non nullable, because users can't
// specify optional on box. however, we need to output nullable in this case
// in order to match the behavior for Struct?
GenerateObjectMember("nullable", Nullability::kNullable);
} else {
GenerateObjectMember("nullable", invocation.nullability);
}
if (invocation.size_raw)
GenerateObjectMember("maybe_size", *invocation.size_raw);
if (invocation.rights_raw)
GenerateObjectMember("handle_rights", *invocation.rights_raw);
});
}
void JSONGenerator::Generate(const Alias& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
// TODO(https://fxbug.dev/42158155): Remove "partial_type_ctor".
GenerateObjectMember("partial_type_ctor", value.partial_type_ctor);
GenerateTypeAndFromAlias(value.partial_type_ctor.get());
});
}
void JSONGenerator::Generate(const NewType& value) {
GenerateObject([&]() {
GenerateObjectMember("name", value.name, Position::kFirst);
GenerateObjectMember("location", NameSpan(value.name));
GenerateObjectMember("deprecated", value.availability.is_deprecated());
if (!value.attributes->Empty())
GenerateObjectMember("maybe_attributes", value.attributes);
GenerateTypeAndFromAlias(value.type_ctor.get());
});
}
void JSONGenerator::Generate(const Compilation::Dependency& dependency) {
GenerateObject([&]() {
auto library_name = LibraryName(dependency.library->name, ".");
GenerateObjectMember("name", library_name, Position::kFirst);
GenerateExternalDeclarationsMember(dependency.declarations);
});
}
void JSONGenerator::GenerateDeclarationsEntry(int count, const Name& name,
std::string_view decl_kind) {
if (count == 0) {
Indent();
EmitNewlineWithIndent();
} else {
EmitObjectSeparator();
}
EmitObjectKey(NameFlatName(name));
EmitString(decl_kind);
}
void JSONGenerator::GenerateDeclarationsMember(const Compilation::Declarations& declarations,
Position position) {
GenerateObjectPunctuation(position);
EmitObjectKey("declarations");
GenerateObject([&]() {
int count = 0;
for (const auto& decl : declarations.bits)
GenerateDeclarationsEntry(count++, decl->name, "bits");
for (const auto& decl : declarations.consts)
GenerateDeclarationsEntry(count++, decl->name, "const");
for (const auto& decl : declarations.enums)
GenerateDeclarationsEntry(count++, decl->name, "enum");
for (const auto& decl : declarations.resources)
GenerateDeclarationsEntry(count++, decl->name, "experimental_resource");
for (const auto& decl : declarations.protocols)
GenerateDeclarationsEntry(count++, decl->name, "protocol");
for (const auto& decl : declarations.services)
GenerateDeclarationsEntry(count++, decl->name, "service");
for (const auto& decl : declarations.structs)
GenerateDeclarationsEntry(count++, decl->name, "struct");
for (const auto& decl : declarations.tables)
GenerateDeclarationsEntry(count++, decl->name, "table");
for (const auto& decl : declarations.unions)
GenerateDeclarationsEntry(count++, decl->name, "union");
for (const auto& decl : declarations.overlays)
GenerateDeclarationsEntry(count++, decl->name, "overlay");
for (const auto& decl : declarations.aliases)
GenerateDeclarationsEntry(count++, decl->name, "alias");
for (const auto& decl : declarations.new_types)
GenerateDeclarationsEntry(count++, decl->name, "new_type");
});
}
void JSONGenerator::GenerateExternalDeclarationsEntry(
int count, const Name& name, std::string_view decl_kind,
std::optional<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 == Resourceness::kResource);
}
});
}
void JSONGenerator::GenerateExternalDeclarationsMember(
const Compilation::Declarations& declarations, Position position) {
GenerateObjectPunctuation(position);
EmitObjectKey("declarations");
GenerateObject([&]() {
int count = 0;
for (const auto& decl : declarations.bits)
GenerateExternalDeclarationsEntry(count++, decl->name, "bits", std::nullopt);
for (const auto& decl : declarations.consts)
GenerateExternalDeclarationsEntry(count++, decl->name, "const", std::nullopt);
for (const auto& decl : declarations.enums)
GenerateExternalDeclarationsEntry(count++, decl->name, "enum", std::nullopt);
for (const auto& decl : declarations.resources)
GenerateExternalDeclarationsEntry(count++, decl->name, "experimental_resource", std::nullopt);
for (const auto& decl : declarations.protocols)
GenerateExternalDeclarationsEntry(count++, decl->name, "protocol", std::nullopt);
for (const auto& decl : declarations.services)
GenerateExternalDeclarationsEntry(count++, decl->name, "service", std::nullopt);
for (const auto& decl : declarations.structs)
GenerateExternalDeclarationsEntry(count++, decl->name, "struct", decl->resourceness);
for (const auto& decl : declarations.tables)
GenerateExternalDeclarationsEntry(count++, decl->name, "table", decl->resourceness);
for (const auto& decl : declarations.unions)
GenerateExternalDeclarationsEntry(count++, decl->name, "union", decl->resourceness);
for (const auto& decl : declarations.overlays)
GenerateExternalDeclarationsEntry(count++, decl->name, "overlays", decl->resourceness);
for (const auto& decl : declarations.aliases)
GenerateExternalDeclarationsEntry(count++, decl->name, "alias", std::nullopt);
for (const auto& decl : declarations.new_types)
GenerateExternalDeclarationsEntry(count++, decl->name, "new_type", std::nullopt);
});
}
std::ostringstream JSONGenerator::Produce() {
ResetIndentLevel();
GenerateObject([&]() {
GenerateObjectMember("name", LibraryName(compilation_->library_name, "."), Position::kFirst);
GenerateObjectMember("platform", compilation_->platform->name());
GenerateObjectPunctuation(Position::kSubsequent);
EmitObjectKey("available");
GenerateObject([&]() {
auto position = Position::kFirst;
for (auto& [platform, version] : *compilation_->version_selection_) {
GenerateObjectMember(platform.name(), version.ToString(), position);
position = Position::kSubsequent;
};
});
if (!compilation_->library_attributes->Empty()) {
GenerateObjectMember("maybe_attributes", compilation_->library_attributes);
}
std::vector<std::string_view> active_experiments;
for (auto& [name, flag] : kAllExperimentalFlags) {
if (experimental_flags_.IsEnabled(flag))
active_experiments.push_back(name);
}
GenerateObjectMember("experiments", active_experiments);
GenerateObjectMember("library_dependencies", compilation_->direct_and_composed_dependencies);
GenerateObjectMember("bits_declarations", compilation_->declarations.bits);
GenerateObjectMember("const_declarations", compilation_->declarations.consts);
GenerateObjectMember("enum_declarations", compilation_->declarations.enums);
GenerateObjectMember("experimental_resource_declarations",
compilation_->declarations.resources);
GenerateObjectMember("protocol_declarations", compilation_->declarations.protocols);
GenerateObjectMember("service_declarations", compilation_->declarations.services);
GenerateObjectMember("struct_declarations", compilation_->declarations.structs);
GenerateObjectMember("external_struct_declarations", compilation_->external_structs);
GenerateObjectMember("table_declarations", compilation_->declarations.tables);
GenerateObjectMember("union_declarations", compilation_->declarations.unions);
if (experimental_flags_.IsEnabled(ExperimentalFlag::kZxCTypes)) {
GenerateObjectMember("overlay_declarations", compilation_->declarations.overlays);
}
GenerateObjectMember("alias_declarations", compilation_->declarations.aliases);
GenerateObjectMember("new_type_declarations", compilation_->declarations.new_types);
std::vector<std::string> declaration_order;
declaration_order.reserve(compilation_->declaration_order.size());
for (const auto decl : compilation_->declaration_order) {
declaration_order.push_back(NameFlatName(decl->name));
}
GenerateObjectMember("declaration_order", declaration_order);
GenerateDeclarationsMember(compilation_->declarations);
});
GenerateEOF();
return std::move(json_file_);
}
} // namespace fidlc