blob: 4d00f70169ef910479dd3f682b1cfbf835b3ec22 [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.
#ifndef TOOLS_FIDL_FIDLC_INCLUDE_FIDL_CODED_AST_H_
#define TOOLS_FIDL_FIDLC_INCLUDE_FIDL_CODED_AST_H_
#include <stdint.h>
#include <cassert>
#include <limits>
#include <memory>
#include <string>
#include <variant>
#include <vector>
#include "check.h"
#include "types.h"
// The types in this file define structures that much more closely map
// the coding tables (i.e., fidl_type_t) for (de)serialization,
// defined at ulib/fidl/include/coding.h and so on.
// In particular, compared to the flat_ast version:
// - All files in the library are resolved together
// - Names have been unnested and fully qualified
// - All data structure sizes and layouts have been computed
// See
// https://fuchsia.dev/fuchsia-src/development/languages/fidl/reference/compiler#c_family_runtime
// for additional context
namespace fidl {
namespace coded {
enum struct CodingContext {
// The coding table of this type will be used to represent data within
// an envelope. This will affect the 'coding needed'.
kInsideEnvelope,
// The coding table of this type will be used to represent data outside
// of an envelope, and default 'coding needed' is appropriate here.
kOutsideEnvelope,
};
struct Type;
struct StructType;
struct StructField {
StructField(types::Resourceness resourceness, uint32_t offset, const Type* type)
: resourceness(resourceness), offset(offset), type(type) {}
types::Resourceness resourceness;
const uint32_t offset;
const Type* type;
};
struct StructPadding {
StructPadding(uint32_t offset, std::variant<uint16_t, uint32_t, uint64_t> mask)
: offset(offset), mask(mask) {}
// TODO(bprosnitz) This computes a mask for a single padding segment.
// It is inefficient if multiple padding segments can be covered by a single mask.
// (e.g. struct{uint8, uint16, uint8, uint16} has two padding segments but can
// be covered by a single uint64 mask)
static StructPadding FromLength(uint32_t offset, uint32_t length) {
assert(length != 0 && "padding shouldn't be created for zero-length offsets");
if (length <= 2) {
return StructPadding(offset & ~1, BuildMask<uint16_t>(offset & 1, length));
} else if (length <= 4) {
return StructPadding(offset & ~3, BuildMask<uint32_t>(offset & 3, length));
} else if (length < 8) {
return StructPadding(offset & ~7, BuildMask<uint64_t>(offset & 7, length));
} else {
assert(false && "length should be < 8");
}
__builtin_unreachable();
}
const uint32_t offset;
const std::variant<uint16_t, uint32_t, uint64_t> mask;
private:
template <typename MaskType>
static MaskType BuildMask(uint32_t offset, uint32_t length) {
MaskType mask = 0;
uint8_t* bytes = reinterpret_cast<uint8_t*>(&mask);
for (uint32_t i = offset; i < offset + length; i++) {
bytes[i] = 0xff;
}
return mask;
}
};
using StructElement = std::variant<const StructField, const StructPadding>;
struct TableField {
TableField(const Type* type, uint32_t ordinal) : type(type), ordinal(ordinal) {}
const Type* type;
const uint32_t ordinal;
};
struct XUnionField {
XUnionField(const Type* type) : type(type) {}
const Type* type;
};
struct Type {
virtual ~Type() = default;
enum struct Kind : uint8_t {
kPrimitive,
kEnum,
kBits,
kHandle,
kProtocolHandle,
kRequestHandle,
kStruct,
kTable,
kXUnion,
kStructPointer,
kMessage,
kProtocol,
kArray,
kString,
kVector,
};
Type(Kind kind, std::string coded_name, uint32_t size, bool is_coding_needed, bool is_noop)
: is_coding_needed(is_coding_needed),
is_noop(is_noop),
kind(kind),
size(size),
coded_name(std::move(coded_name)) {}
const bool is_coding_needed;
// is_noop indicates that the walker doesn't need to do any action on a coding table entry of
// this type.
// For instance, the walker can skip uint8 fields in a struct, so uint8 primitive types have
// is_noop = true. However, bools need to be validated so bool primitive types have
// is_noop = false.
bool is_noop;
const Kind kind;
uint32_t size;
const std::string coded_name;
};
struct PrimitiveType : public Type {
PrimitiveType(std::string name, types::PrimitiveSubtype subtype, uint32_t size,
CodingContext context)
: Type(Kind::kPrimitive, std::move(name), size, true,
subtype != types::PrimitiveSubtype::kBool),
subtype(subtype) {}
const types::PrimitiveSubtype subtype;
};
struct EnumType : public Type {
EnumType(std::string name, types::PrimitiveSubtype subtype, uint32_t size,
std::vector<uint64_t> members, std::string qname, types::Strictness strictness)
: Type(Kind::kEnum, std::move(name), size, true, false),
subtype(subtype),
members(std::move(members)),
qname(std::move(qname)),
strictness(strictness) {}
const types::PrimitiveSubtype subtype;
const std::vector<uint64_t> members;
const std::string qname;
types::Strictness strictness;
};
struct BitsType : public Type {
BitsType(std::string name, types::PrimitiveSubtype subtype, uint32_t size, uint64_t mask,
std::string qname, types::Strictness strictness)
: Type(Kind::kBits, std::move(name), size, true, false),
subtype(subtype),
mask(mask),
qname(std::move(qname)),
strictness(strictness) {}
const types::PrimitiveSubtype subtype;
const uint64_t mask;
const std::string qname;
types::Strictness strictness;
};
struct HandleType : public Type {
HandleType(std::string name, types::HandleSubtype subtype, types::Rights rights,
types::Nullability nullability)
: Type(Kind::kHandle, std::move(name), 4u, true, false),
subtype(subtype),
rights(rights),
nullability(nullability) {}
const types::HandleSubtype subtype;
const types::Rights rights;
const types::Nullability nullability;
};
struct ProtocolHandleType : public Type {
ProtocolHandleType(std::string name, types::Nullability nullability)
: Type(Kind::kProtocolHandle, std::move(name), 4u, true, false), nullability(nullability) {}
const types::Nullability nullability;
};
struct RequestHandleType : public Type {
RequestHandleType(std::string name, types::Nullability nullability)
: Type(Kind::kRequestHandle, std::move(name), 4u, true, false), nullability(nullability) {}
const types::Nullability nullability;
};
struct StructPointerType;
struct StructType : public Type {
StructType(std::string name, std::vector<StructElement> elements, uint32_t size,
std::string qname)
: Type(Kind::kStruct, std::move(name), size, true, false),
elements(std::move(elements)),
qname(std::move(qname)) {
FIDL_CHECK(elements.size() <= std::numeric_limits<uint16_t>::max(),
"coding table stores element_count in uint16_t");
}
std::vector<StructElement> elements;
std::string qname;
StructPointerType* maybe_reference_type = nullptr;
};
struct StructPointerType : public Type {
StructPointerType(std::string name, const Type* type, const uint32_t pointer_size)
: Type(Kind::kStructPointer, std::move(name), pointer_size, true, false),
element_type(static_cast<const StructType*>(type)) {
assert(type->kind == Type::Kind::kStruct);
}
const StructType* element_type;
};
struct TableType : public Type {
TableType(std::string name, std::vector<TableField> fields, uint32_t size, std::string qname,
types::Resourceness resourceness)
: Type(Kind::kTable, std::move(name), size, true, false),
fields(std::move(fields)),
qname(std::move(qname)),
resourceness(resourceness) {}
std::vector<TableField> fields;
std::string qname;
types::Resourceness resourceness;
};
struct XUnionType : public Type {
XUnionType(std::string name, std::vector<XUnionField> fields, std::string qname,
types::Nullability nullability, types::Strictness strictness,
types::Resourceness resourceness)
: Type(Kind::kXUnion, std::move(name), 24u, true, false),
fields(std::move(fields)),
qname(std::move(qname)),
nullability(nullability),
strictness(strictness),
resourceness(resourceness) {}
std::vector<XUnionField> fields;
const std::string qname;
types::Nullability nullability;
types::Strictness strictness;
XUnionType* maybe_reference_type = nullptr;
types::Resourceness resourceness;
};
struct MessageType : public Type {
MessageType(std::string name, std::vector<StructElement> elements, uint32_t size,
std::string qname)
: Type(Kind::kMessage, std::move(name), size, true, false),
elements(std::move(elements)),
qname(std::move(qname)) {}
std::vector<StructElement> elements;
std::string qname;
};
struct ProtocolType : public Type {
explicit ProtocolType(std::vector<std::unique_ptr<MessageType>> messages_during_compile)
// N.B. ProtocolTypes are never used in the eventual coding table generation.
: Type(Kind::kProtocol, "", 0, false, false),
messages_during_compile(std::move(messages_during_compile)) {}
// Note: the messages are moved from the protocol type into the
// CodedTypesGenerator coded_types_ vector during assembly.
std::vector<std::unique_ptr<MessageType>> messages_during_compile;
// Back pointers to fully compiled message types, owned by the
// CodedTypesGenerator coded_types_ vector.
std::vector<const MessageType*> messages_after_compile;
};
struct ArrayType : public Type {
ArrayType(std::string name, const Type* element_type, uint32_t array_size, uint32_t element_size,
CodingContext context)
: Type(Kind::kArray, std::move(name), array_size, true, element_type->is_noop),
element_type(element_type),
element_size(element_size) {
FIDL_CHECK(element_size <= std::numeric_limits<uint16_t>::max(),
"coding table stores element_size in uint16_t");
}
const Type* const element_type;
const uint32_t element_size;
};
struct StringType : public Type {
StringType(std::string name, uint32_t max_size, types::Nullability nullability)
: Type(Kind::kString, std::move(name), 16u, true, false),
max_size(max_size),
nullability(nullability) {}
const uint32_t max_size;
const types::Nullability nullability;
};
struct VectorType : public Type {
VectorType(std::string name, const Type* element_type, uint32_t max_count, uint32_t element_size,
types::Nullability nullability)
// Note: vectors have is_noop = false, but there is the potential to optimize this in the
// future.
: Type(Kind::kVector, std::move(name), 16u, true, false),
element_type(element_type),
max_count(max_count),
element_size(element_size),
nullability(nullability) {}
const Type* const element_type;
const uint32_t max_count;
const uint32_t element_size;
const types::Nullability nullability;
};
} // namespace coded
} // namespace fidl
#endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_CODED_AST_H_