blob: 9ce526a9b4c2b95e4c5100d8e9104ff454872c25 [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/flat_ast.h"
#include <assert.h>
#include <stdio.h>
#include <algorithm>
#include <regex>
#include <sstream>
#include "fidl/attributes.h"
#include "fidl/lexer.h"
#include "fidl/names.h"
#include "fidl/ordinals.h"
#include "fidl/parser.h"
#include "fidl/raw_ast.h"
#include "fidl/utils.h"
namespace fidl {
namespace flat {
namespace {
class ScopeInsertResult {
public:
explicit ScopeInsertResult(std::unique_ptr<SourceLocation> previous_occurrence)
: previous_occurrence_(std::move(previous_occurrence)) {}
static ScopeInsertResult Ok() { return ScopeInsertResult(nullptr); }
static ScopeInsertResult FailureAt(SourceLocation previous) {
return ScopeInsertResult(std::make_unique<SourceLocation>(previous));
}
bool ok() const {
return previous_occurrence_ == nullptr;
}
const SourceLocation& previous_occurrence() const {
assert(!ok());
return *previous_occurrence_;
}
private:
std::unique_ptr<SourceLocation> previous_occurrence_;
};
template <typename T>
class Scope {
public:
ScopeInsertResult Insert(const T& t, SourceLocation location) {
auto iter = scope_.find(t);
if (iter != scope_.end()) {
return ScopeInsertResult::FailureAt(iter->second);
} else {
scope_.emplace(t, location);
return ScopeInsertResult::Ok();
}
}
typename std::map<T, SourceLocation>::const_iterator begin() const {
return scope_.begin();
}
typename std::map<T, SourceLocation>::const_iterator end() const {
return scope_.end();
}
private:
std::map<T, SourceLocation> scope_;
};
struct MethodScope {
Scope<uint32_t> ordinals;
Scope<StringView> names;
Scope<const Interface*> interfaces;
};
// A helper class to track when a Decl is compiling and compiled.
class Compiling {
public:
explicit Compiling(Decl* decl)
: decl_(decl) {
decl_->compiling = true;
}
~Compiling() {
decl_->compiling = false;
decl_->compiled = true;
}
private:
Decl* decl_;
};
} // namespace
constexpr uint32_t kMessageAlign = 8u;
uint32_t AlignTo(uint64_t size, uint64_t alignment) {
return static_cast<uint32_t>(
std::min((size + alignment - 1) & -alignment,
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())));
}
uint32_t ClampedMultiply(uint32_t a, uint32_t b) {
return static_cast<uint32_t>(
std::min(static_cast<uint64_t>(a) * static_cast<uint64_t>(b),
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())));
}
uint32_t ClampedAdd(uint32_t a, uint32_t b) {
return static_cast<uint32_t>(
std::min(static_cast<uint64_t>(a) + static_cast<uint64_t>(b),
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max())));
}
TypeShape AlignTypeshape(TypeShape shape, uint32_t alignment) {
uint32_t new_alignment = std::max(shape.Alignment(), alignment);
uint32_t new_size = AlignTo(shape.Size(), new_alignment);
return TypeShape(new_size, new_alignment, shape.Depth(), shape.MaxHandles(), shape.MaxOutOfLine());
}
TypeShape Struct::Shape(std::vector<FieldShape*>* fields, uint32_t extra_handles) {
uint32_t size = 0u;
uint32_t alignment = 1u;
uint32_t depth = 0u;
uint32_t max_handles = 0u;
uint32_t max_out_of_line = 0u;
for (FieldShape* field : *fields) {
TypeShape typeshape = field->Typeshape();
alignment = std::max(alignment, typeshape.Alignment());
size = AlignTo(size, typeshape.Alignment());
field->SetOffset(size);
size += typeshape.Size();
depth = std::max(depth, typeshape.Depth());
max_handles = ClampedAdd(max_handles, typeshape.MaxHandles());
max_out_of_line = ClampedAdd(max_out_of_line, typeshape.MaxOutOfLine());
}
max_handles = ClampedAdd(max_handles, extra_handles);
size = AlignTo(size, alignment);
if (fields->empty()) {
assert(size == 0);
assert(alignment == 1);
// Empty structs are defined to have a size of 1 (a single byte).
size = 1;
}
return TypeShape(size, alignment, depth, max_handles, max_out_of_line);
}
TypeShape Union::Shape(const std::vector<flat::Union::Member>& members) {
uint32_t size = 0u;
uint32_t alignment = 1u;
uint32_t depth = 0u;
uint32_t max_handles = 0u;
uint32_t max_out_of_line = 0u;
for (const auto& member : members) {
const auto& fieldshape = member.fieldshape;
size = std::max(size, fieldshape.Size());
alignment = std::max(alignment, fieldshape.Alignment());
depth = std::max(depth, fieldshape.Depth());
max_handles = std::max(max_handles, fieldshape.Typeshape().MaxHandles());
max_out_of_line = std::max(max_out_of_line, fieldshape.Typeshape().MaxOutOfLine());
}
size = AlignTo(size, alignment);
return TypeShape(size, alignment, depth, max_handles, max_out_of_line);
}
TypeShape FidlMessageTypeShape(std::vector<FieldShape*>* fields) {
auto struct_shape = Struct::Shape(fields);
return AlignTypeshape(struct_shape, kMessageAlign);
}
TypeShape PointerTypeShape(const TypeShape& element, uint32_t max_element_count = 1u) {
// Because FIDL supports recursive data structures, we might not have
// computed the TypeShape for the element we're pointing to. In that case,
// the size will be zero and we'll use |numeric_limits<uint32_t>::max()| as
// the depth. We'll never see a zero size for a real TypeShape because empty
// structs are banned.
//
// We're careful to check for saturation before incrementing the depth
// because recursive data structures have a depth pegged at the numeric
// limit.
uint32_t depth = std::numeric_limits<uint32_t>::max();
if (element.Size() > 0 && element.Depth() < std::numeric_limits<uint32_t>::max())
depth = ClampedAdd(element.Depth(), 1);
// The element(s) will be stored out-of-line.
uint32_t elements_size = ClampedMultiply(element.Size(), max_element_count);
// Out-of-line data is aligned to 8 bytes.
elements_size = AlignTo(elements_size, 8);
// The elements may each carry their own out-of-line data.
uint32_t elements_out_of_line = ClampedMultiply(element.MaxOutOfLine(), max_element_count);
uint32_t max_handles = ClampedMultiply(element.MaxHandles(), max_element_count);
uint32_t max_out_of_line = ClampedAdd(elements_size, elements_out_of_line);
return TypeShape(8u, 8u, depth, max_handles, max_out_of_line);
}
TypeShape CEnvelopeTypeShape(const TypeShape& contained_type) {
auto packed_sizes_field = FieldShape(PrimitiveType::Shape(types::PrimitiveSubtype::kUint64));
auto pointer_type = FieldShape(PointerTypeShape(contained_type));
std::vector<FieldShape*> header{&packed_sizes_field, &pointer_type};
return Struct::Shape(&header);
}
TypeShape Table::Shape(std::vector<TypeShape*>* fields, uint32_t extra_handles) {
uint32_t element_depth = 0u;
uint32_t max_handles = 0u;
uint32_t max_out_of_line = 0u;
uint32_t array_size = 0u;
for (auto field : *fields) {
if (field == nullptr) {
continue;
}
auto envelope = CEnvelopeTypeShape(*field);
element_depth = std::max(element_depth, envelope.Depth());
array_size = ClampedAdd(array_size, envelope.Size());
max_handles = ClampedAdd(max_handles, envelope.MaxHandles());
max_out_of_line = ClampedAdd(max_out_of_line, envelope.MaxOutOfLine());
assert(envelope.Alignment() == 8u);
}
auto pointer_element = TypeShape(array_size, 8u, 1 + element_depth,
max_handles, max_out_of_line);
auto num_fields = FieldShape(PrimitiveType::Shape(types::PrimitiveSubtype::kUint32));
auto data_field = FieldShape(PointerTypeShape(pointer_element));
std::vector<FieldShape*> header{&num_fields, &data_field};
return Struct::Shape(&header, extra_handles);
}
TypeShape XUnion::Shape(const std::vector<flat::XUnion::Member>& members, uint32_t extra_handles) {
uint32_t depth = 0u;
uint32_t max_handles = 0u;
uint32_t max_out_of_line = 0u;
for (const auto& member : members) {
const auto& envelope = CEnvelopeTypeShape(member.fieldshape.Typeshape());
depth = ClampedAdd(depth, envelope.Depth());
max_handles = ClampedAdd(max_handles, envelope.MaxHandles());
max_out_of_line = std::max(max_out_of_line, envelope.MaxOutOfLine());
}
return TypeShape(24u, 8u, depth, max_handles, max_out_of_line);
}
TypeShape ArrayType::Shape(TypeShape element, uint32_t count) {
// TODO(FIDL-345): once TypeShape builders are done and methods can fail, do a
// __builtin_mul_overflow and fail on overflow instead of ClampedMultiply(element.Size(), count)
return TypeShape(ClampedMultiply(element.Size(), count),
element.Alignment(),
element.Depth(),
ClampedMultiply(element.MaxHandles(), count),
ClampedMultiply(element.MaxOutOfLine(), count));
}
TypeShape VectorType::Shape(TypeShape element, uint32_t max_element_count) {
auto size = FieldShape(PrimitiveType::Shape(types::PrimitiveSubtype::kUint64));
auto data = FieldShape(PointerTypeShape(element, max_element_count));
std::vector<FieldShape*> header{&size, &data};
return Struct::Shape(&header);
}
TypeShape StringType::Shape(uint32_t max_length) {
auto size = FieldShape(PrimitiveType::Shape(types::PrimitiveSubtype::kInt64));
auto data = FieldShape(PointerTypeShape(PrimitiveType::Shape(types::PrimitiveSubtype::kUint8), max_length));
std::vector<FieldShape*> header{&size, &data};
return Struct::Shape(&header, 0);
}
TypeShape HandleType::Shape() {
return TypeShape(4u, 4u, 0u, 1u);
}
uint32_t PrimitiveType::SubtypeSize(types::PrimitiveSubtype subtype) {
switch (subtype) {
case types::PrimitiveSubtype::kBool:
case types::PrimitiveSubtype::kInt8:
case types::PrimitiveSubtype::kUint8:
return 1u;
case types::PrimitiveSubtype::kInt16:
case types::PrimitiveSubtype::kUint16:
return 2u;
case types::PrimitiveSubtype::kFloat32:
case types::PrimitiveSubtype::kInt32:
case types::PrimitiveSubtype::kUint32:
return 4u;
case types::PrimitiveSubtype::kFloat64:
case types::PrimitiveSubtype::kInt64:
case types::PrimitiveSubtype::kUint64:
return 8u;
}
}
TypeShape PrimitiveType::Shape(types::PrimitiveSubtype subtype) {
return TypeShape(SubtypeSize(subtype), SubtypeSize(subtype));
}
bool Decl::HasAttribute(fidl::StringView name) const {
if (!attributes)
return false;
return attributes->HasAttribute(name);
}
fidl::StringView Decl::GetAttribute(fidl::StringView name) const {
if (!attributes)
return fidl::StringView();
for (const auto& attribute : attributes->attributes) {
if (StringView(attribute.name) == name) {
if (attribute.value != "") {
const auto& value = attribute.value;
return fidl::StringView(value.data(), value.size());
}
// Don't search for another attribute with the same name.
break;
}
}
return fidl::StringView();
}
std::string Decl::GetName() const {
return name.name_part();
}
bool IsSimple(const Type* type, const FieldShape& fieldshape) {
switch (type->kind) {
case Type::Kind::kVector: {
auto vector_type = static_cast<const VectorType*>(type);
if (*vector_type->element_count == Size::Max())
return false;
switch (vector_type->element_type->kind) {
case Type::Kind::kHandle:
case Type::Kind::kRequestHandle:
case Type::Kind::kPrimitive:
return true;
case Type::Kind::kArray:
case Type::Kind::kVector:
case Type::Kind::kString:
case Type::Kind::kIdentifier:
return false;
}
}
case Type::Kind::kString: {
auto string_type = static_cast<const StringType*>(type);
return *string_type->max_size < Size::Max();
}
case Type::Kind::kArray:
case Type::Kind::kHandle:
case Type::Kind::kRequestHandle:
case Type::Kind::kPrimitive:
return fieldshape.Depth() == 0u;
case Type::Kind::kIdentifier: {
auto identifier_type = static_cast<const IdentifierType*>(type);
switch (identifier_type->nullability) {
case types::Nullability::kNullable:
// If the identifier is nullable, then we can handle a depth of 1
// because the secondary object is directly accessible.
return fieldshape.Depth() <= 1u;
case types::Nullability::kNonnullable:
return fieldshape.Depth() == 0u;
}
}
}
}
bool Typespace::Create(const flat::Name& name,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
const Type** out_type) {
std::unique_ptr<Type> type;
if (!CreateNotOwned(name, arg_type, handle_subtype, size, nullability, &type))
return false;
types_.push_back(std::move(type));
*out_type = types_.back().get();
return true;
}
bool Typespace::CreateNotOwned(const flat::Name& name,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) {
// TODO(pascallouis): lookup whether we've already created the type, and
// return it rather than create a new one. Lookup must be by name,
// arg_type, size, and nullability.
auto maybe_location = name.maybe_location();
auto type_template = LookupTemplate(name);
if (type_template == nullptr) {
std::string message("unknown type ");
message.append(name.name_part());
error_reporter_->ReportError(maybe_location, message);
return false;
}
return type_template->Create(maybe_location, arg_type, handle_subtype, size,
nullability, out_type);
}
void Typespace::AddTemplate(std::unique_ptr<TypeTemplate> type_template) {
templates_.emplace(type_template->name(), std::move(type_template));
}
const TypeTemplate* Typespace::LookupTemplate(const flat::Name& name) const {
Name global_name(nullptr, name.name_part());
auto iter1 = templates_.find(&global_name);
if (iter1 != templates_.end())
return iter1->second.get();
auto iter2 = templates_.find(&name);
if (iter2 != templates_.end())
return iter2->second.get();
return nullptr;
}
bool TypeTemplate::Fail(const SourceLocation* maybe_location, const std::string& content) const {
std::string message(NameName(name_, ".", "/"));
message.append(" ");
message.append(content);
error_reporter_->ReportError(maybe_location, message);
return false;
}
class PrimitiveTypeTemplate : public TypeTemplate {
public:
PrimitiveTypeTemplate(Typespace* typespace, ErrorReporter* error_reporter,
const std::string& name, types::PrimitiveSubtype subtype)
: TypeTemplate(Name(nullptr, name), typespace, error_reporter),
subtype_(subtype) {}
bool Create(const SourceLocation* maybe_location,
const Type* maybe_arg_type,
const types::HandleSubtype* handle_subtype,
const Size* maybe_size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
if (maybe_arg_type != nullptr)
return CannotBeParameterized(maybe_location);
if (maybe_size != nullptr)
return CannotHaveSize(maybe_location);
if (nullability == types::Nullability::kNullable)
return CannotBeNullable(maybe_location);
*out_type = std::make_unique<PrimitiveType>(subtype_);
return true;
}
private:
const types::PrimitiveSubtype subtype_;
};
class BytesTypeTemplate : public TypeTemplate {
public:
BytesTypeTemplate(Typespace* typespace, ErrorReporter* error_reporter)
: TypeTemplate(Name(nullptr, "bytes"), typespace, error_reporter),
uint8_type_(types::PrimitiveSubtype::kUint8) {}
bool Create(const SourceLocation* maybe_location,
const Type* maybe_arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
if (maybe_arg_type != nullptr)
return CannotBeParameterized(maybe_location);
if (size == nullptr)
size = &max_size;
*out_type = std::make_unique<VectorType>(&uint8_type_, size, nullability);
return true;
}
private:
const PrimitiveType uint8_type_;
Size max_size = Size::Max();
};
class ArrayTypeTemplate : public TypeTemplate {
public:
ArrayTypeTemplate(Typespace* typespace, ErrorReporter* error_reporter)
: TypeTemplate(Name(nullptr, "array"), typespace, error_reporter) {}
bool Create(const SourceLocation* maybe_location,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
if (arg_type == nullptr)
return MustBeParameterized(maybe_location);
if (size == nullptr)
return MustHaveSize(maybe_location);
if (nullability == types::Nullability::kNullable)
return CannotBeNullable(maybe_location);
*out_type = std::make_unique<ArrayType>(arg_type, size);
return true;
}
};
class VectorTypeTemplate : public TypeTemplate {
public:
VectorTypeTemplate(Typespace* typespace, ErrorReporter* error_reporter)
: TypeTemplate(Name(nullptr, "vector"), typespace, error_reporter) {}
bool Create(const SourceLocation* maybe_location,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
if (arg_type == nullptr)
return MustBeParameterized(maybe_location);
if (size == nullptr)
size = &max_size;
*out_type = std::make_unique<VectorType>(arg_type, size, nullability);
return true;
}
private:
Size max_size = Size::Max();
};
class StringTypeTemplate : public TypeTemplate {
public:
StringTypeTemplate(Typespace* typespace, ErrorReporter* error_reporter)
: TypeTemplate(Name(nullptr, "string"), typespace, error_reporter) {}
bool Create(const SourceLocation* maybe_location,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
if (arg_type != nullptr)
return CannotBeParameterized(maybe_location);
if (size == nullptr)
size = &max_size;
*out_type = std::make_unique<StringType>(size, nullability);
return true;
}
private:
Size max_size = Size::Max();
};
class HandleTypeTemplate : public TypeTemplate {
public:
HandleTypeTemplate(Typespace* typespace, ErrorReporter* error_reporter)
: TypeTemplate(Name(nullptr, "handle"), typespace, error_reporter) {}
bool Create(const SourceLocation* maybe_location,
const Type* maybe_arg_type,
const types::HandleSubtype* maybe_handle_subtype,
const Size* maybe_size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
assert(maybe_arg_type == nullptr);
if (maybe_size != nullptr)
return CannotHaveSize(maybe_location);
auto handle_subtype = types::HandleSubtype::kHandle;
if (maybe_handle_subtype != nullptr)
handle_subtype = *maybe_handle_subtype;
*out_type = std::make_unique<HandleType>(handle_subtype, nullability);
return true;
}
};
class RequestTypeTemplate : public TypeTemplate {
public:
RequestTypeTemplate(Typespace* typespace, ErrorReporter* error_reporter)
: TypeTemplate(Name(nullptr, "request"), typespace, error_reporter) {}
bool Create(const SourceLocation* maybe_location,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* maybe_size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
if (arg_type == nullptr)
return MustBeParameterized(maybe_location);
if (arg_type->kind != Type::Kind::kIdentifier)
return Fail(maybe_location, "must be an interface");
auto interface_type = static_cast<const IdentifierType*>(arg_type);
if (interface_type->type_decl->kind != Decl::Kind::kInterface)
return Fail(maybe_location, "must be an interface");
if (maybe_size != nullptr)
return CannotHaveSize(maybe_location);
*out_type = std::make_unique<RequestHandleType>(interface_type, nullability);
return true;
}
private:
// TODO(pascallouis): Make Min/Max an actual value on NumericConstantValue
// class, to simply write &Size::Max() above.
Size max_size = Size::Max();
};
class TypeDeclTypeTemplate : public TypeTemplate {
public:
TypeDeclTypeTemplate(Name name, Typespace* typespace, ErrorReporter* error_reporter,
Library* library, TypeDecl* type_decl)
: TypeTemplate(std::move(name), typespace, error_reporter),
library_(library), type_decl_(type_decl) {}
bool Create(const SourceLocation* maybe_location,
const Type* arg_type,
const types::HandleSubtype* handle_subtype,
const Size* size,
types::Nullability nullability,
std::unique_ptr<Type>* out_type) const {
if (!type_decl_->compiled && type_decl_->kind != Decl::Kind::kInterface) {
if (type_decl_->compiling) {
type_decl_->recursive = true;
} else {
if (!library_->CompileDecl(type_decl_)) {
return false;
}
}
}
auto typeshape = type_decl_->typeshape;
switch (type_decl_->kind) {
case Decl::Kind::kInterface:
typeshape = HandleType::Shape();
break;
case Decl::Kind::kXUnion:
// Do nothing here: nullable XUnions have the same encoding
// representation as non-optional XUnions (i.e. nullable XUnions are
// inlined).
break;
case Decl::Kind::kTable:
if (nullability == types::Nullability::kNullable)
return CannotBeNullable(maybe_location);
break;
default:
if (nullability == types::Nullability::kNullable)
typeshape = PointerTypeShape(typeshape);
break;
}
*out_type = std::make_unique<IdentifierType>(
// TODO(FIDL-447): We have to create a copy because IdentifierType
// has an owned name. Fix this.
Name(name()->library(), std::string(name()->name_part())),
nullability, type_decl_, typeshape);
return true;
}
private:
Library* library_;
TypeDecl* type_decl_;
};
class TypeAliasTypeTemplate : public TypeTemplate {
public:
TypeAliasTypeTemplate(Name name, Typespace* typespace, ErrorReporter* error_reporter,
Library* library,
std::unique_ptr<TypeConstructor> partial_type_ctor)
: TypeTemplate(std::move(name), typespace, error_reporter),
library_(library), partial_type_ctor_(std::move(partial_type_ctor)) {}
bool Create(const SourceLocation* maybe_location,
const Type* maybe_arg_type,
const types::HandleSubtype* no_handle_subtype,
const Size* maybe_size,
types::Nullability maybe_nullability,
std::unique_ptr<Type>* out_type) const {
assert(!no_handle_subtype);
const Type* arg_type = nullptr;
if (partial_type_ctor_->maybe_arg_type_ctor) {
if (maybe_arg_type) {
return Fail(maybe_location, "cannot parametrize twice");
}
if (!partial_type_ctor_->maybe_arg_type_ctor->type) {
if (!library_->CompileTypeConstructor(
partial_type_ctor_->maybe_arg_type_ctor.get(),
nullptr /* out_typeshape */))
return false;
}
arg_type = partial_type_ctor_->maybe_arg_type_ctor->type;
} else {
arg_type = maybe_arg_type;
}
const Size* size = nullptr;
if (partial_type_ctor_->maybe_size) {
if (maybe_size) {
return Fail(maybe_location, "cannot bound twice");
}
if (!library_->ResolveConstant(partial_type_ctor_->maybe_size.get(), &library_->kSizeType))
return Fail(maybe_location, "unable to parse size bound");
size = static_cast<const Size*>(&partial_type_ctor_->maybe_size->Value());
} else {
size = maybe_size;
}
types::Nullability nullability;
if (partial_type_ctor_->nullability == types::Nullability::kNullable) {
if (maybe_nullability == types::Nullability::kNullable) {
return Fail(maybe_location, "cannot indicate nullability twice");
}
nullability = types::Nullability::kNullable;
} else {
nullability = maybe_nullability;
}
return typespace_->CreateNotOwned(partial_type_ctor_->name, arg_type,
nullptr /* handle_subtype */,
size, nullability, out_type);
}
private:
Library* library_;
std::unique_ptr<TypeConstructor> partial_type_ctor_;
};
Typespace Typespace::RootTypes(ErrorReporter* error_reporter) {
Typespace root_typespace(error_reporter);
auto add_template = [&](std::unique_ptr<TypeTemplate> type_template) {
auto name = type_template->name();
root_typespace.templates_.emplace(name, std::move(type_template));
};
auto add_primitive = [&](const std::string& name, types::PrimitiveSubtype subtype) {
add_template(std::make_unique<PrimitiveTypeTemplate>(
&root_typespace, error_reporter, name, subtype));
};
add_primitive("bool", types::PrimitiveSubtype::kBool);
add_primitive("int8", types::PrimitiveSubtype::kInt8);
add_primitive("int16", types::PrimitiveSubtype::kInt16);
add_primitive("int32", types::PrimitiveSubtype::kInt32);
add_primitive("int64", types::PrimitiveSubtype::kInt64);
add_primitive("uint8", types::PrimitiveSubtype::kUint8);
add_primitive("uint16", types::PrimitiveSubtype::kUint16);
add_primitive("uint32", types::PrimitiveSubtype::kUint32);
add_primitive("uint64", types::PrimitiveSubtype::kUint64);
add_primitive("float32", types::PrimitiveSubtype::kFloat32);
add_primitive("float64", types::PrimitiveSubtype::kFloat64);
// TODO(FIDL-483): Remove when there is generalized support.
add_primitive("byte", types::PrimitiveSubtype::kUint8);
add_template(std::make_unique<BytesTypeTemplate>(
&root_typespace, error_reporter));
add_template(std::make_unique<ArrayTypeTemplate>(
&root_typespace, error_reporter));
add_template(std::make_unique<VectorTypeTemplate>(
&root_typespace, error_reporter));
add_template(std::make_unique<StringTypeTemplate>(
&root_typespace, error_reporter));
add_template(std::make_unique<HandleTypeTemplate>(
&root_typespace, error_reporter));
add_template(std::make_unique<RequestTypeTemplate>(
&root_typespace, error_reporter));
return root_typespace;
}
AttributeSchema::AttributeSchema(const std::set<Placement>& allowed_placements,
const std::set<std::string> allowed_values,
Constraint constraint)
: allowed_placements_(allowed_placements),
allowed_values_(allowed_values),
constraint_(std::move(constraint)) {}
void AttributeSchema::ValidatePlacement(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
Placement placement) const {
if (allowed_placements_.size() == 0)
return;
auto iter = allowed_placements_.find(placement);
if (iter != allowed_placements_.end())
return;
std::string message("placement of attribute '");
message.append(attribute.name);
message.append("' disallowed here");
error_reporter->ReportError(attribute.location(), message);
}
void AttributeSchema::ValidateValue(ErrorReporter* error_reporter,
const raw::Attribute& attribute) const {
if (allowed_values_.size() == 0)
return;
auto iter = allowed_values_.find(attribute.value);
if (iter != allowed_values_.end())
return;
std::string message("attribute '");
message.append(attribute.name);
message.append("' has invalid value '");
message.append(attribute.value);
message.append("', should be one of '");
bool first = true;
for (const auto& hint : allowed_values_) {
if (!first)
message.append(", ");
message.append(hint);
message.append("'");
first = false;
}
error_reporter->ReportError(attribute.location(), message);
}
void AttributeSchema::ValidateConstraint(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
const Decl* decl) const {
auto check = error_reporter->Checkpoint();
auto passed = constraint_(error_reporter, attribute, decl);
if (passed) {
assert(check.NoNewErrors() && "cannot add errors and pass");
} else if (check.NoNewErrors()) {
std::string message("declaration did not satisfy constraint of attribute '");
message.append(attribute.name);
message.append("' with value '");
message.append(attribute.value);
message.append("'");
// TODO(pascallouis): It would be nicer to use the location of
// the declaration, however we do not keep it around today.
error_reporter->ReportError(attribute.location(), message);
}
}
bool SimpleLayoutConstraint(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
const Decl* decl) {
assert(decl->kind == Decl::Kind::kStruct);
auto struct_decl = static_cast<const Struct*>(decl);
bool ok = true;
for (const auto& member : struct_decl->members) {
if (!IsSimple(member.type_ctor.get()->type, member.fieldshape)) {
std::string message("member '");
message.append(member.name.data());
message.append("' is not simple");
error_reporter->ReportError(member.name, message);
ok = false;
}
}
return ok;
}
bool ParseBound(ErrorReporter* error_reporter, const SourceLocation& location,
const std::string& input, uint32_t* out_value) {
auto result = utils::ParseNumeric(input, out_value, 10);
switch (result) {
case utils::ParseNumericResult::kOutOfBounds:
error_reporter->ReportError(location, "bound is too big");
return false;
case utils::ParseNumericResult::kMalformed: {
std::string message("unable to parse bound '");
message.append(input);
message.append("'");
error_reporter->ReportError(location, message);
return false;
}
case utils::ParseNumericResult::kSuccess:
return true;
}
}
bool MaxBytesConstraint(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
const Decl* decl) {
uint32_t bound;
if (!ParseBound(error_reporter, attribute.location(), attribute.value, &bound))
return false;
uint32_t max_bytes = std::numeric_limits<uint32_t>::max();
switch (decl->kind) {
case Decl::Kind::kStruct: {
auto struct_decl = static_cast<const Struct*>(decl);
max_bytes = struct_decl->typeshape.Size() + struct_decl->typeshape.MaxOutOfLine();
break;
}
case Decl::Kind::kTable: {
auto table_decl = static_cast<const Table*>(decl);
max_bytes = table_decl->typeshape.Size() + table_decl->typeshape.MaxOutOfLine();
break;
}
case Decl::Kind::kUnion: {
auto union_decl = static_cast<const Union*>(decl);
max_bytes = union_decl->typeshape.Size() + union_decl->typeshape.MaxOutOfLine();
break;
}
case Decl::Kind::kXUnion: {
auto xunion_decl = static_cast<const XUnion*>(decl);
max_bytes = xunion_decl->typeshape.Size() + xunion_decl->typeshape.MaxOutOfLine();
break;
}
default:
assert(false && "unexpected kind");
return false;
}
if (max_bytes > bound) {
std::ostringstream message;
message << "too large: only ";
message << bound;
message << " bytes allowed, but ";
message << max_bytes;
message << " bytes found";
error_reporter->ReportError(attribute.location(), message.str());
return false;
}
return true;
}
bool MaxHandlesConstraint(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
const Decl* decl) {
uint32_t bound;
if (!ParseBound(error_reporter, attribute.location(), attribute.value.c_str(), &bound))
return false;
uint32_t max_handles = std::numeric_limits<uint32_t>::max();
switch (decl->kind) {
case Decl::Kind::kStruct: {
auto struct_decl = static_cast<const Struct*>(decl);
max_handles = struct_decl->typeshape.MaxHandles();
break;
}
case Decl::Kind::kTable: {
auto table_decl = static_cast<const Table*>(decl);
max_handles = table_decl->typeshape.MaxHandles();
break;
}
case Decl::Kind::kUnion: {
auto union_decl = static_cast<const Union*>(decl);
max_handles = union_decl->typeshape.MaxHandles();
break;
}
case Decl::Kind::kXUnion: {
auto xunion_decl = static_cast<const XUnion*>(decl);
max_handles = xunion_decl->typeshape.MaxHandles();
break;
}
default:
assert(false && "unexpected kind");
return false;
}
if (max_handles > bound) {
std::ostringstream message;
message << "too many handles: only ";
message << bound;
message << " allowed, but ";
message << max_handles;
message << " found";
error_reporter->ReportError(attribute.location(), message.str());
return false;
}
return true;
}
bool ResultShapeConstraint(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
const Decl* decl) {
assert(decl->kind == Decl::Kind::kUnion);
auto union_decl = static_cast<const Union*>(decl);
assert(union_decl->members.size() == 2);
auto error_type = union_decl->members.at(1).type_ctor->type;
const PrimitiveType* error_primitive = nullptr;
if (error_type->kind == Type::Kind::kPrimitive) {
error_primitive = static_cast<const PrimitiveType*>(error_type);
} else if (error_type->kind == Type::Kind::kIdentifier) {
auto identifier_type = static_cast<const IdentifierType*>(error_type);
if (identifier_type->type_decl->kind == Decl::Kind::kEnum) {
auto error_enum = static_cast<const Enum*>(identifier_type->type_decl);
assert(error_enum->subtype_ctor->type->kind == Type::Kind::kPrimitive);
error_primitive = static_cast<const PrimitiveType*>(error_enum->subtype_ctor->type);
}
}
if (!error_primitive ||
(error_primitive->subtype != types::PrimitiveSubtype::kInt32 &&
error_primitive->subtype != types::PrimitiveSubtype::kUint32)) {
error_reporter->ReportError(
decl->name.maybe_location(),
"invalid error type: must be int32, uint32 or an enum therof");
return false;
}
return true;
}
static std::string Trim(std::string s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
return !std::isspace(ch);
}));
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
return !std::isspace(ch);
})
.base(),
s.end());
return s;
}
bool TransportConstraint(ErrorReporter* error_reporter,
const raw::Attribute& attribute,
const Decl* decl) {
// Parse comma separated transports
const std::string& value = attribute.value;
std::string::size_type prev_pos = 0;
std::string::size_type pos;
std::vector<std::string> transports;
while ((pos = value.find(',', prev_pos)) != std::string::npos) {
transports.emplace_back(Trim(value.substr(prev_pos, pos - prev_pos)));
prev_pos = pos + 1;
}
transports.emplace_back(Trim(value.substr(prev_pos)));
// Validate that they're ok
static const std::set<std::string> kValidTransports = {
"Channel",
"SocketControl",
"OvernetEmbedded",
"OvernetInternal",
};
for (auto transport : transports) {
if (kValidTransports.count(transport) == 0) {
std::ostringstream out;
out << "invalid transport type: got " << transport << " expected one of ";
bool first = true;
for (const auto& t : kValidTransports) {
if (!first) {
out << ", ";
}
first = false;
out << t;
}
error_reporter->ReportError(decl->name.maybe_location(), out.str());
return false;
}
}
return true;
}
Libraries::Libraries() {
// clang-format off
AddAttributeSchema("Discoverable", AttributeSchema({
AttributeSchema::Placement::kInterfaceDecl,
}, {
"",
}));
AddAttributeSchema("Doc", AttributeSchema({
/* any placement */
}, {
/* any value */
}));
AddAttributeSchema("FragileBase", AttributeSchema({
AttributeSchema::Placement::kInterfaceDecl,
}, {
"",
}));
AddAttributeSchema("Layout", AttributeSchema({
AttributeSchema::Placement::kInterfaceDecl,
}, {
"Simple",
},
SimpleLayoutConstraint));
AddAttributeSchema("MaxBytes", AttributeSchema({
AttributeSchema::Placement::kInterfaceDecl,
AttributeSchema::Placement::kMethod,
AttributeSchema::Placement::kStructDecl,
AttributeSchema::Placement::kTableDecl,
AttributeSchema::Placement::kUnionDecl,
AttributeSchema::Placement::kXUnionDecl,
}, {
/* any value */
},
MaxBytesConstraint));
AddAttributeSchema("MaxHandles", AttributeSchema({
AttributeSchema::Placement::kInterfaceDecl,
AttributeSchema::Placement::kMethod,
AttributeSchema::Placement::kStructDecl,
AttributeSchema::Placement::kTableDecl,
AttributeSchema::Placement::kUnionDecl,
AttributeSchema::Placement::kXUnionDecl,
}, {
/* any value */
},
MaxHandlesConstraint));
AddAttributeSchema("Result", AttributeSchema({
AttributeSchema::Placement::kUnionDecl,
}, {
"",
},
ResultShapeConstraint));
AddAttributeSchema("Selector", AttributeSchema({
AttributeSchema::Placement::kMethod,
AttributeSchema::Placement::kXUnionMember,
}, {
/* any value */
}));
AddAttributeSchema("Transport", AttributeSchema({
AttributeSchema::Placement::kInterfaceDecl,
}, {
/* any value */
}, TransportConstraint));
// clang-format on
}
bool Libraries::Insert(std::unique_ptr<Library> library) {
std::vector<fidl::StringView> library_name = library->name();
auto iter = all_libraries_.emplace(library_name, std::move(library));
return iter.second;
}
bool Libraries::Lookup(const std::vector<StringView>& library_name,
Library** out_library) const {
auto iter = all_libraries_.find(library_name);
if (iter == all_libraries_.end()) {
return false;
}
*out_library = iter->second.get();
return true;
}
size_t EditDistance(const std::string& sequence1, const std::string& sequence2) {
size_t s1_length = sequence1.length();
size_t s2_length = sequence2.length();
size_t row1[s1_length + 1];
size_t row2[s1_length + 1];
size_t* last_row = row1;
size_t* this_row = row2;
for (size_t i = 0; i <= s1_length; i++)
last_row[i] = i;
for (size_t j = 0; j < s2_length; j++) {
this_row[0] = j + 1;
auto s2c = sequence2[j];
for (size_t i = 1; i <= s1_length; i++) {
auto s1c = sequence1[i - 1];
this_row[i] = std::min(std::min(
last_row[i] + 1, this_row[i - 1] + 1),
last_row[i - 1] + (s1c == s2c ? 0 : 1));
}
std::swap(last_row, this_row);
}
return last_row[s1_length];
}
const AttributeSchema* Libraries::RetrieveAttributeSchema(ErrorReporter* error_reporter,
const raw::Attribute& attribute) const {
const auto& attribute_name = attribute.name;
auto iter = attribute_schemas_.find(attribute_name);
if (iter != attribute_schemas_.end()) {
const auto& schema = iter->second;
return &schema;
}
// Skip typo check?
if (error_reporter == nullptr)
return nullptr;
// Match against all known attributes.
for (const auto& name_and_schema : attribute_schemas_) {
auto edit_distance = EditDistance(name_and_schema.first, attribute_name);
if (0 < edit_distance && edit_distance < 2) {
std::string message("suspect attribute with name '");
message.append(attribute_name);
message.append("'; did you mean '");
message.append(name_and_schema.first);
message.append("'?");
error_reporter->ReportWarning(attribute.location(), message);
return nullptr;
}
}
return nullptr;
}
bool Dependencies::Register(StringView filename, Library* dep_library,
const std::unique_ptr<raw::Identifier>& maybe_alias) {
auto library_name = dep_library->name();
if (!InsertByName(filename, library_name, dep_library)) {
return false;
}
if (maybe_alias) {
std::vector<StringView> alias_name = {maybe_alias->location().data()};
if (!InsertByName(filename, alias_name, dep_library)) {
return false;
}
}
dependencies_aggregate_.insert(dep_library);
return true;
}
bool Dependencies::InsertByName(StringView filename, const std::vector<StringView>& name,
Library* library) {
auto iter = dependencies_.find(filename);
if (iter == dependencies_.end()) {
dependencies_.emplace(filename, std::make_unique<ByName>());
}
iter = dependencies_.find(filename);
assert(iter != dependencies_.end());
auto insert = iter->second->emplace(name, library);
return insert.second;
}
bool Dependencies::Lookup(StringView filename, const std::vector<StringView>& name,
Library** out_library) {
auto iter1 = dependencies_.find(filename);
if (iter1 == dependencies_.end()) {
return false;
}
auto iter2 = iter1->second->find(name);
if (iter2 == iter1->second->end()) {
return false;
}
*out_library = iter2->second;
return true;
}
// Consuming the AST is primarily concerned with walking the tree and
// flattening the representation. The AST's declaration nodes are
// converted into the Library's foo_declaration structures. This means pulling
// a struct declaration inside an interface out to the top level and
// so on.
std::string LibraryName(const Library* library, StringView separator) {
if (library != nullptr) {
return StringJoin(library->name(), separator);
}
return std::string();
}
bool Library::Fail(StringView message) {
error_reporter_->ReportError(message);
return false;
}
bool Library::Fail(const SourceLocation* maybe_location, StringView message) {
error_reporter_->ReportError(maybe_location, message);
return false;
}
void Library::ValidateAttributesPlacement(AttributeSchema::Placement placement,
const raw::AttributeList* attributes) {
if (attributes == nullptr)
return;
for (const auto& attribute : attributes->attributes) {
auto schema = all_libraries_->RetrieveAttributeSchema(error_reporter_, attribute);
if (schema != nullptr) {
schema->ValidatePlacement(error_reporter_, attribute, placement);
schema->ValidateValue(error_reporter_, attribute);
}
}
}
void Library::ValidateAttributesConstraints(const Decl* decl,
const raw::AttributeList* attributes) {
if (attributes == nullptr)
return;
for (const auto& attribute : attributes->attributes) {
auto schema = all_libraries_->RetrieveAttributeSchema(nullptr, attribute);
if (schema != nullptr)
schema->ValidateConstraint(error_reporter_, attribute, decl);
}
}
SourceLocation Library::GeneratedSimpleName(const std::string& name) {
return generated_source_file_.AddLine(name);
}
Name Library::NextAnonymousName() {
// TODO(FIDL-596): Improve anonymous name generation. We want to be
// specific about how these names are generated once they appear in the
// JSON IR, and are exposed to the backends.
std::ostringstream data;
data << "SomeLongAnonymousPrefix";
data << anon_counter_++;
return Name(this, GeneratedSimpleName(data.str()));
}
Name Library::DerivedName(const std::vector<StringView>& components) {
return Name(this, GeneratedSimpleName(StringJoin(components, "_")));
}
bool Library::CompileCompoundIdentifier(const raw::CompoundIdentifier* compound_identifier,
SourceLocation location, Name* name_out) {
const auto& components = compound_identifier->components;
assert(components.size() >= 1);
SourceLocation decl_name = components.back()->location();
if (components.size() == 1) {
*name_out = Name(this, decl_name);
return true;
}
std::vector<StringView> library_name;
for (auto iter = components.begin();
iter != components.end() - 1;
++iter) {
library_name.push_back((*iter)->location().data());
}
auto filename = location.source_file().filename();
Library* dep_library = nullptr;
if (!dependencies_.Lookup(filename, library_name, &dep_library)) {
std::string message("Unknown dependent library ");
message += NameLibrary(library_name);
message += ". Did you require it with `using`?";
const auto& location = components[0]->location();
return Fail(location, message);
}
// Resolve the name.
*name_out = Name(dep_library, decl_name);
return true;
}
void Library::RegisterConst(Const* decl) {
const Name* name = &decl->name;
constants_.emplace(name, decl);
}
bool Library::RegisterDecl(Decl* decl) {
const Name* name = &decl->name;
auto iter = declarations_.emplace(name, decl);
if (!iter.second) {
std::string message = "Name collision: ";
message.append(name->name_part());
return Fail(*name, message);
}
switch (decl->kind) {
case Decl::Kind::kBits:
case Decl::Kind::kEnum:
case Decl::Kind::kStruct:
case Decl::Kind::kTable:
case Decl::Kind::kUnion:
case Decl::Kind::kXUnion:
case Decl::Kind::kInterface: {
auto type_decl = static_cast<TypeDecl*>(decl);
auto type_template = std::make_unique<TypeDeclTypeTemplate>(
Name(name->library(), std::string(name->name_part())),
typespace_, error_reporter_, this, type_decl);
typespace_->AddTemplate(std::move(type_template));
break;
}
default:
assert(decl->kind == Decl::Kind::kConst);
}
return true;
}
bool Library::ConsumeConstant(std::unique_ptr<raw::Constant> raw_constant, SourceLocation location,
std::unique_ptr<Constant>* out_constant) {
switch (raw_constant->kind) {
case raw::Constant::Kind::kIdentifier: {
auto identifier = static_cast<raw::IdentifierConstant*>(raw_constant.get());
Name name;
if (!CompileCompoundIdentifier(identifier->identifier.get(), location, &name)) {
return false;
}
*out_constant = std::make_unique<IdentifierConstant>(std::move(name));
break;
}
case raw::Constant::Kind::kLiteral: {
auto literal = static_cast<raw::LiteralConstant*>(raw_constant.get());
*out_constant = std::make_unique<LiteralConstant>(std::move(literal->literal));
break;
}
}
return true;
}
bool Library::ConsumeTypeConstructor(std::unique_ptr<raw::TypeConstructor> raw_type_ctor,
SourceLocation location,
std::unique_ptr<TypeConstructor>* out_type_ctor) {
Name name;
if (!CompileCompoundIdentifier(raw_type_ctor->identifier.get(), location, &name))
return false;
std::unique_ptr<TypeConstructor> maybe_arg_type_ctor;
if (raw_type_ctor->maybe_arg_type_ctor != nullptr) {
if (!ConsumeTypeConstructor(std::move(raw_type_ctor->maybe_arg_type_ctor), location, &maybe_arg_type_ctor))
return false;
}
std::unique_ptr<Constant> maybe_size;
if (raw_type_ctor->maybe_size != nullptr) {
if (!ConsumeConstant(std::move(raw_type_ctor->maybe_size), location, &maybe_size))
return false;
}
*out_type_ctor = std::make_unique<TypeConstructor>(
std::move(name),
std::move(maybe_arg_type_ctor),
std::move(raw_type_ctor->maybe_handle_subtype),
std::move(maybe_size),
raw_type_ctor->nullability);
return true;
}
bool Library::ConsumeUsing(std::unique_ptr<raw::Using> using_directive) {
if (using_directive->maybe_type_ctor)
return ConsumeTypeAlias(std::move(using_directive));
std::vector<StringView> library_name;
for (const auto& component : using_directive->using_path->components) {
library_name.push_back(component->location().data());
}
Library* dep_library = nullptr;
if (!all_libraries_->Lookup(library_name, &dep_library)) {
std::string message("Could not find library named ");
message += NameLibrary(library_name);
message += ". Did you include its sources with --files?";
const auto& location = using_directive->using_path->components[0]->location();
return Fail(location, message);
}
auto filename = using_directive->location().source_file().filename();
if (!dependencies_.Register(filename, dep_library, using_directive->maybe_alias)) {
std::string message("Library ");
message += NameLibrary(library_name);
message += " already imported. Did you require it twice?";
return Fail(message);
}
// Import declarations, and type aliases of dependent library.
const auto& declarations = dep_library->declarations_;
declarations_.insert(declarations.begin(), declarations.end());
return true;
}
bool Library::ConsumeTypeAlias(std::unique_ptr<raw::Using> using_directive) {
assert(using_directive->maybe_type_ctor);
auto location = using_directive->using_path->components[0]->location();
auto alias_name = Name(this, location);
std::unique_ptr<TypeConstructor> partial_type_ctor_;
if (!ConsumeTypeConstructor(std::move(using_directive->maybe_type_ctor), location,
&partial_type_ctor_))
return false;
typespace_->AddTemplate(std::make_unique<TypeAliasTypeTemplate>(
std::move(alias_name), typespace_, error_reporter_, this, std::move(partial_type_ctor_)));
return true;
}
bool Library::ConsumeBitsDeclaration(std::unique_ptr<raw::BitsDeclaration> bits_declaration) {
std::vector<Bits::Member> members;
for (auto& member : bits_declaration->members) {
auto location = member->identifier->location();
std::unique_ptr<Constant> value;
if (!ConsumeConstant(std::move(member->value), location, &value))
return false;
members.emplace_back(location, std::move(value), std::move(member->attributes));
// TODO(pascallouis): right now, members are not registered. Look into
// registering them, potentially under the bits name qualifier such as
// <name_of_bits>.<name_of_member>.
}
std::unique_ptr<TypeConstructor> type_ctor;
if (bits_declaration->maybe_type_ctor) {
if (!ConsumeTypeConstructor(std::move(bits_declaration->maybe_type_ctor),
bits_declaration->location(), &type_ctor))
return false;
} else {
type_ctor = std::make_unique<TypeConstructor>(
Name(nullptr, "uint32"),
nullptr /* maybe_arg_type */,
nullptr /* maybe_handle_subtype */,
nullptr /* maybe_size */,
types::Nullability::kNonnullable);
}
bits_declarations_.push_back(std::make_unique<Bits>(
std::move(bits_declaration->attributes),
Name(this, bits_declaration->identifier->location()),
std::move(type_ctor),
std::move(members)));
if (!RegisterDecl(bits_declarations_.back().get()))
return false;
return true;
}
bool Library::ConsumeConstDeclaration(std::unique_ptr<raw::ConstDeclaration> const_declaration) {
auto attributes = std::move(const_declaration->attributes);
auto location = const_declaration->identifier->location();
auto name = Name(this, location);
std::unique_ptr<TypeConstructor> type_ctor;
if (!ConsumeTypeConstructor(std::move(const_declaration->type_ctor), location, &type_ctor))
return false;
std::unique_ptr<Constant> constant;
if (!ConsumeConstant(std::move(const_declaration->constant), location, &constant))
return false;
const_declarations_.push_back(std::make_unique<Const>(std::move(attributes), std::move(name),
std::move(type_ctor), std::move(constant)));
auto decl = const_declarations_.back().get();
RegisterConst(decl);
return RegisterDecl(decl);
}
bool Library::ConsumeEnumDeclaration(std::unique_ptr<raw::EnumDeclaration> enum_declaration) {
std::vector<Enum::Member> members;
for (auto& member : enum_declaration->members) {
auto location = member->identifier->location();
std::unique_ptr<Constant> value;
if (!ConsumeConstant(std::move(member->value), location, &value))
return false;
members.emplace_back(location, std::move(value), std::move(member->attributes));
// TODO(pascallouis): right now, members are not registered. Look into
// registering them, potentially under the enum name qualifier such as
// <name_of_enum>.<name_of_member>.
}
std::unique_ptr<TypeConstructor> type_ctor;
if (enum_declaration->maybe_type_ctor) {
if (!ConsumeTypeConstructor(std::move(enum_declaration->maybe_type_ctor),
enum_declaration->location(), &type_ctor))
return false;
} else {
type_ctor = std::make_unique<TypeConstructor>(
Name(nullptr, "uint32"),
nullptr /* maybe_arg_type */,
nullptr /* maybe_handle_subtype */,
nullptr /* maybe_size */,
types::Nullability::kNonnullable);
}
enum_declarations_.push_back(std::make_unique<Enum>(
std::move(enum_declaration->attributes),
Name(this, enum_declaration->identifier->location()),
std::move(type_ctor),
std::move(members)));
if (!RegisterDecl(enum_declarations_.back().get()))
return false;
return true;
}
bool Library::CreateMethodResult(const Name& interface_name,
raw::InterfaceMethod* method,
Struct* in_response,
Struct** out_response) {
// Compile the error type.
auto error_location = method->maybe_error_ctor->location();
std::unique_ptr<TypeConstructor> error_type_ctor;
if (!ConsumeTypeConstructor(std::move(method->maybe_error_ctor), error_location, &error_type_ctor))
return false;
// Make the Result union containing the response struct and the
// error type.
Union::Member response_member{
IdentifierTypeForDecl(in_response, types::Nullability::kNonnullable),
GeneratedSimpleName("response"),
nullptr};
Union::Member error_member{
std::move(error_type_ctor),
GeneratedSimpleName("err"),
nullptr};
SourceLocation method_name = method->identifier->location();
Name result_name = DerivedName({interface_name.name_part(), method_name.data(), "Result"});
std::vector<Union::Member> result_members;
result_members.push_back(std::move(response_member));
result_members.push_back(std::move(error_member));
std::vector<raw::Attribute> result_attributes;
result_attributes.emplace_back(*method, "Result", "");
auto result_attributelist = std::make_unique<raw::AttributeList>(*method,
std::move(result_attributes));
union_declarations_.push_back(std::make_unique<Union>(std::move(result_attributelist),
std::move(result_name),
std::move(result_members)));
auto result_decl = union_declarations_.back().get();
if (!RegisterDecl(result_decl))
return false;
// Make a new response struct for the method containing just the
// result union.
std::vector<Struct::Member> response_members;
response_members.push_back(Struct::Member(IdentifierTypeForDecl(result_decl, types::Nullability::kNonnullable),
GeneratedSimpleName("result"), nullptr, nullptr));
struct_declarations_.push_back(std::make_unique<Struct>(nullptr, NextAnonymousName(), std::move(response_members), true));
*out_response = struct_declarations_.back().get();
if (!RegisterDecl(*out_response))
return false;
return true;
}
bool Library::ConsumeInterfaceDeclaration(
std::unique_ptr<raw::InterfaceDeclaration> interface_declaration) {
auto attributes = std::move(interface_declaration->attributes);
auto name = Name(this, interface_declaration->identifier->location());
std::set<Name> superinterfaces;
for (auto& superinterface : interface_declaration->superinterfaces) {
auto& protocol_name = superinterface->protocol_name;
auto location = protocol_name->components[0]->location();
Name superinterface_name;
if (!CompileCompoundIdentifier(protocol_name.get(), location, &superinterface_name)) {
return false;
}
if (!superinterfaces.insert(std::move(superinterface_name)).second)
return Fail(superinterface_name, "protocol composed multiple times");
}
std::vector<Interface::Method> methods;
for (auto& method : interface_declaration->methods) {
std::unique_ptr<raw::Ordinal> ordinal_literal =
std::make_unique<raw::Ordinal>(fidl::ordinals::GetOrdinal(library_name_, name.name_part(), *method));
std::unique_ptr<raw::Ordinal> generated_ordinal =
std::make_unique<raw::Ordinal>(fidl::ordinals::GetGeneratedOrdinal(library_name_, name.name_part(), *method));
auto attributes = std::move(method->attributes);
SourceLocation method_name = method->identifier->location();
Struct* maybe_request = nullptr;
if (method->maybe_request != nullptr) {
Name request_name = NextAnonymousName();
if (!ConsumeParameterList(std::move(request_name), std::move(method->maybe_request), true, &maybe_request))
return false;
}
bool has_error = (method->maybe_error_ctor != nullptr);
Struct* maybe_response = nullptr;
if (method->maybe_response != nullptr) {
Name response_name = has_error ? DerivedName({name.name_part(), method_name.data(), "Response"}) : NextAnonymousName();
if (!ConsumeParameterList(std::move(response_name), std::move(method->maybe_response), !has_error, &maybe_response))
return false;
}
if (has_error) {
if (!CreateMethodResult(name, method.get(), maybe_response, &maybe_response))
return false;
}
assert(maybe_request != nullptr || maybe_response != nullptr);
methods.emplace_back(std::move(attributes),
std::move(generated_ordinal),
std::move(ordinal_literal),
std::move(method_name), std::move(maybe_request),
std::move(maybe_response));
}
interface_declarations_.push_back(
std::make_unique<Interface>(std::move(attributes), std::move(name),
std::move(superinterfaces), std::move(methods)));
return RegisterDecl(interface_declarations_.back().get());
}
std::unique_ptr<TypeConstructor> Library::IdentifierTypeForDecl(const Decl* decl, types::Nullability nullability) {
return std::make_unique<TypeConstructor>(
Name(decl->name.library(), decl->name.name_part()),
nullptr /* maybe_arg_type */,
nullptr /* maybe_handle_subtype */,
nullptr /* maybe_size */,
nullability);
}
bool Library::ConsumeParameterList(Name name, std::unique_ptr<raw::ParameterList> parameter_list,
bool anonymous, Struct** out_struct_decl) {
std::vector<Struct::Member> members;
for (auto& parameter : parameter_list->parameter_list) {
const SourceLocation name = parameter->identifier->location();
std::unique_ptr<TypeConstructor> type_ctor;
if (!ConsumeTypeConstructor(std::move(parameter->type_ctor), name, &type_ctor))
return false;
members.emplace_back(
std::move(type_ctor), name,
nullptr /* maybe_default_value */,
nullptr /* attributes */);
}
struct_declarations_.push_back(
std::make_unique<Struct>(nullptr /* attributes */, std::move(name), std::move(members),
anonymous));
auto struct_decl = struct_declarations_.back().get();
if (!RegisterDecl(struct_decl))
return false;
*out_struct_decl = struct_decl;
return true;
}
bool Library::ConsumeStructDeclaration(std::unique_ptr<raw::StructDeclaration> struct_declaration) {
auto attributes = std::move(struct_declaration->attributes);
auto name = Name(this, struct_declaration->identifier->location());
std::vector<Struct::Member> members;
for (auto& member : struct_declaration->members) {
std::unique_ptr<TypeConstructor> type_ctor;
auto location = member->identifier->location();
if (!ConsumeTypeConstructor(std::move(member->type_ctor), location, &type_ctor))
return false;
std::unique_ptr<Constant> maybe_default_value;
if (member->maybe_default_value != nullptr) {
if (!ConsumeConstant(std::move(member->maybe_default_value), location,
&maybe_default_value))
return false;
}
auto attributes = std::move(member->attributes);
members.emplace_back(std::move(type_ctor), member->identifier->location(),
std::move(maybe_default_value), std::move(attributes));
}
struct_declarations_.push_back(
std::make_unique<Struct>(std::move(attributes), std::move(name), std::move(members)));
return RegisterDecl(struct_declarations_.back().get());
}
bool Library::ConsumeTableDeclaration(std::unique_ptr<raw::TableDeclaration> table_declaration) {
auto attributes = std::move(table_declaration->attributes);
auto name = Name(this, table_declaration->identifier->location());
std::vector<Table::Member> members;
for (auto& member : table_declaration->members) {
auto ordinal_literal = std::move(member->ordinal);
if (member->maybe_used) {
std::unique_ptr<TypeConstructor> type_ctor;
if (!ConsumeTypeConstructor(std::move(member->maybe_used->type_ctor), member->location(), &type_ctor))
return false;
std::unique_ptr<Constant> maybe_default_value;
if (member->maybe_used->maybe_default_value != nullptr) {
if (!ConsumeConstant(std::move(member->maybe_used->maybe_default_value),
member->location(), &maybe_default_value))
return false;
}
if (type_ctor->nullability != types::Nullability::kNonnullable) {
return Fail(member->location(), "Table members cannot be nullable");
}
auto attributes = std::move(member->maybe_used->attributes);
members.emplace_back(std::move(ordinal_literal), std::move(type_ctor),
member->maybe_used->identifier->location(),
std::move(maybe_default_value), std::move(attributes));
} else {
members.emplace_back(std::move(ordinal_literal), member->location());
}
}
table_declarations_.push_back(
std::make_unique<Table>(std::move(attributes), std::move(name), std::move(members)));
return RegisterDecl(table_declarations_.back().get());
}
bool Library::ConsumeUnionDeclaration(std::unique_ptr<raw::UnionDeclaration> union_declaration) {
std::vector<Union::Member> members;
for (auto& member : union_declaration->members) {
auto location = member->identifier->location();
std::unique_ptr<TypeConstructor> type_ctor;
if (!ConsumeTypeConstructor(std::move(member->type_ctor), location, &type_ctor))
return false;
auto attributes = std::move(member->attributes);
members.emplace_back(std::move(type_ctor), location, std::move(attributes));
}
auto attributes = std::move(union_declaration->attributes);
auto name = Name(this, union_declaration->identifier->location());
union_declarations_.push_back(
std::make_unique<Union>(std::move(attributes), std::move(name), std::move(members)));
return RegisterDecl(union_declarations_.back().get());
}
bool Library::ConsumeXUnionDeclaration(std::unique_ptr<raw::XUnionDeclaration> xunion_declaration) {
auto name = Name(this, xunion_declaration->identifier->location());
std::vector<XUnion::Member> members;
for (auto& member : xunion_declaration->members) {
std::unique_ptr<raw::Ordinal> ordinal =
std::make_unique<raw::Ordinal>(fidl::ordinals::GetOrdinal(library_name_, name.name_part(), *member));
auto location = member->identifier->location();
std::unique_ptr<TypeConstructor> type_ctor;
if (!ConsumeTypeConstructor(std::move(member->type_ctor), location, &type_ctor))
return false;
if (type_ctor->nullability != types::Nullability::kNonnullable) {
return Fail(member->location(), "Extensible union members cannot be nullable");
}
members.emplace_back(std::move(ordinal), std::move(type_ctor), location, std::move(member->attributes));
}
xunion_declarations_.push_back(
std::make_unique<XUnion>(std::move(xunion_declaration->attributes), std::move(name), std::move(members)));
return RegisterDecl(xunion_declarations_.back().get());
}
bool Library::ConsumeFile(std::unique_ptr<raw::File> file) {
if (file->attributes) {
ValidateAttributesPlacement(AttributeSchema::Placement::kLibrary, file->attributes.get());
if (!attributes_) {
attributes_ = std::move(file->attributes);
} else {
AttributesBuilder attributes_builder(error_reporter_, std::move(attributes_->attributes));
for (auto& attribute : file->attributes->attributes) {
if (!attributes_builder.Insert(std::move(attribute)))
return false;
}
attributes_ = std::make_unique<raw::AttributeList>(raw::SourceElement(file->attributes->start_, file->attributes->end_),
attributes_builder.Done());
}
}
// All fidl files in a library should agree on the library name.
std::vector<StringView> new_name;
for (const auto& part : file->library_name->components) {
new_name.push_back(part->location().data());
}
if (!library_name_.empty()) {
if (new_name != library_name_) {
return Fail(file->library_name->components[0]->location(),
"Two files in the library disagree about the name of the library");
}
} else {
library_name_ = new_name;
}
auto using_list = std::move(file->using_list);
for (auto& using_directive : using_list) {
if (!ConsumeUsing(std::move(using_directive))) {
return false;
}
}
auto bits_declaration_list = std::move(file->bits_declaration_list);
for (auto& bits_declaration : bits_declaration_list) {
if (!ConsumeBitsDeclaration(std::move(bits_declaration))) {
return false;
}
}
auto const_declaration_list = std::move(file->const_declaration_list);
for (auto& const_declaration : const_declaration_list) {
if (!ConsumeConstDeclaration(std::move(const_declaration))) {
return false;
}
}
auto enum_declaration_list = std::move(file->enum_declaration_list);
for (auto& enum_declaration : enum_declaration_list) {
if (!ConsumeEnumDeclaration(std::move(enum_declaration))) {
return false;
}
}
auto interface_declaration_list = std::move(file->interface_declaration_list);
for (auto& interface_declaration : interface_declaration_list) {
if (!ConsumeInterfaceDeclaration(std::move(interface_declaration))) {
return false;
}
}
auto struct_declaration_list = std::move(file->struct_declaration_list);
for (auto& struct_declaration : struct_declaration_list) {
if (!ConsumeStructDeclaration(std::move(struct_declaration))) {
return false;
}
}
auto table_declaration_list = std::move(file->table_declaration_list);
for (auto& table_declaration : table_declaration_list) {
if (!ConsumeTableDeclaration(std::move(table_declaration))) {
return false;
}
}
auto union_declaration_list = std::move(file->union_declaration_list);
for (auto& union_declaration : union_declaration_list) {
if (!ConsumeUnionDeclaration(std::move(union_declaration))) {
return false;
}
}
auto xunion_declaration_list = std::move(file->xunion_declaration_list);
for (auto& xunion_declaration : xunion_declaration_list) {
if (!ConsumeXUnionDeclaration(std::move(xunion_declaration))) {
return false;
}
}
return true;
}
bool Library::ResolveConstant(Constant* constant, const Type* type) {
assert(constant != nullptr);
if (constant->IsResolved())
return true;
switch (constant->kind) {
case Constant::Kind::kIdentifier: {
auto identifier_constant = static_cast<IdentifierConstant*>(constant);
return ResolveIdentifierConstant(identifier_constant, type);
}
case Constant::Kind::kLiteral: {
auto literal_constant = static_cast<LiteralConstant*>(constant);
return ResolveLiteralConstant(literal_constant, type);
}
case Constant::Kind::kSynthesized: {
assert(false && "Compiler bug: synthesized constant does not have a resolved value!");
}
}
__UNREACHABLE;
}
bool Library::ResolveIdentifierConstant(IdentifierConstant* identifier_constant, const Type* type) {
assert(TypeCanBeConst(type) &&
"Compiler bug: resolving identifier constant to non-const-able type!");
auto decl = LookupDeclByName(identifier_constant->name);
if (!decl || decl->kind != Decl::Kind::kConst)
return false;
// Recursively resolve constants
auto const_decl = static_cast<Const*>(decl);
if (!CompileConst(const_decl))
return false;
assert(const_decl->value->IsResolved());
const ConstantValue& const_val = const_decl->value->Value();
std::unique_ptr<ConstantValue> resolved_val;
switch (type->kind) {
case Type::Kind::kString: {
if (!TypeIsConvertibleTo(const_decl->type_ctor->type, type))
goto fail_cannot_convert;
if (!const_val.Convert(ConstantValue::Kind::kString, &resolved_val))
goto fail_cannot_convert;
break;
}
case Type::Kind::kPrimitive: {
auto primitive_type = static_cast<const PrimitiveType*>(type);
switch (primitive_type->subtype) {
case types::PrimitiveSubtype::kBool:
if (!const_val.Convert(ConstantValue::Kind::kBool, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kInt8:
if (!const_val.Convert(ConstantValue::Kind::kInt8, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kInt16:
if (!const_val.Convert(ConstantValue::Kind::kInt16, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kInt32:
if (!const_val.Convert(ConstantValue::Kind::kInt32, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kInt64:
if (!const_val.Convert(ConstantValue::Kind::kInt64, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kUint8:
if (!const_val.Convert(ConstantValue::Kind::kUint8, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kUint16:
if (!const_val.Convert(ConstantValue::Kind::kUint16, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kUint32:
if (!const_val.Convert(ConstantValue::Kind::kUint32, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kUint64:
if (!const_val.Convert(ConstantValue::Kind::kUint64, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kFloat32:
if (!const_val.Convert(ConstantValue::Kind::kFloat32, &resolved_val))
goto fail_cannot_convert;
break;
case types::PrimitiveSubtype::kFloat64:
if (!const_val.Convert(ConstantValue::Kind::kFloat64, &resolved_val))
goto fail_cannot_convert;
break;
}
break;
}
default: {
assert(false &&
"Compiler bug: const-able type not handled during identifier constant resolution!");
}
}
identifier_constant->ResolveTo(std::move(resolved_val));
return true;
fail_cannot_convert:
std::ostringstream msg_stream;
msg_stream << NameFlatConstant(identifier_constant) << ", of type ";
msg_stream << NameFlatTypeConstructor(const_decl->type_ctor.get());
msg_stream << ", cannot be converted to type " << NameFlatType(type);
return Fail(msg_stream.str());
}
bool Library::ResolveLiteralConstant(LiteralConstant* literal_constant, const Type* type) {
switch (literal_constant->literal->kind) {
case raw::Literal::Kind::kString: {
if (type->kind != Type::Kind::kString)
goto return_fail;
auto string_type = static_cast<const StringType*>(type);
auto string_literal = static_cast<raw::StringLiteral*>(literal_constant->literal.get());
auto string_data = string_literal->location().data();
// TODO(pascallouis): because data() contains the raw content,
// with the two " to identify strings, we need to take this
// into account. We should expose the actual size of string
// literals properly, and take into account escaping.
uint64_t string_size = string_data.size() - 2;
if (string_type->max_size->value < string_size) {
std::ostringstream msg_stream;
msg_stream << NameFlatConstant(literal_constant) << " (string:" << string_size;
msg_stream << ") exceeds the size bound of type " << NameFlatType(type);
return Fail(literal_constant->literal->location(), msg_stream.str());
}
literal_constant->ResolveTo(
std::make_unique<StringConstantValue>(string_literal->location().data()));
return true;
}
case raw::Literal::Kind::kTrue: {
if (type->kind != Type::Kind::kPrimitive)
goto return_fail;
if (static_cast<const PrimitiveType*>(type)->subtype != types::PrimitiveSubtype::kBool)
goto return_fail;
literal_constant->ResolveTo(std::make_unique<BoolConstantValue>(true));
return true;
}
case raw::Literal::Kind::kFalse: {
if (type->kind != Type::Kind::kPrimitive)
goto return_fail;
if (static_cast<const PrimitiveType*>(type)->subtype != types::PrimitiveSubtype::kBool)
goto return_fail;
literal_constant->ResolveTo(std::make_unique<BoolConstantValue>(false));
return true;
}
case raw::Literal::Kind::kNumeric: {
if (type->kind != Type::Kind::kPrimitive)
goto return_fail;
// These must be initialized out of line to allow for goto statement
const raw::NumericLiteral* numeric_literal;
const PrimitiveType* primitive_type;
numeric_literal =
static_cast<const raw::NumericLiteral*>(literal_constant->literal.get());
primitive_type = static_cast<const PrimitiveType*>(type);
switch (primitive_type->subtype) {
case types::PrimitiveSubtype::kInt8: {
int8_t value;
if (!ParseNumericLiteral<int8_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<int8_t>>(value));
return true;
}
case types::PrimitiveSubtype::kInt16: {
int16_t value;
if (!ParseNumericLiteral<int16_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<int16_t>>(value));
return true;
}
case types::PrimitiveSubtype::kInt32: {
int32_t value;
if (!ParseNumericLiteral<int32_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<int32_t>>(value));
return true;
}
case types::PrimitiveSubtype::kInt64: {
int64_t value;
if (!ParseNumericLiteral<int64_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<int64_t>>(value));
return true;
}
case types::PrimitiveSubtype::kUint8: {
uint8_t value;
if (!ParseNumericLiteral<uint8_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<uint8_t>>(value));
return true;
}
case types::PrimitiveSubtype::kUint16: {
uint16_t value;
if (!ParseNumericLiteral<uint16_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<uint16_t>>(value));
return true;
}
case types::PrimitiveSubtype::kUint32: {
uint32_t value;
if (!ParseNumericLiteral<uint32_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<uint32_t>>(value));
return true;
}
case types::PrimitiveSubtype::kUint64: {
uint64_t value;
if (!ParseNumericLiteral<uint64_t>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<uint64_t>>(value));
return true;
}
case types::PrimitiveSubtype::kFloat32: {
float value;
if (!ParseNumericLiteral<float>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<float>>(value));
return true;
}
case types::PrimitiveSubtype::kFloat64: {
double value;
if (!ParseNumericLiteral<double>(numeric_literal, &value))
goto return_fail;
literal_constant->ResolveTo(std::make_unique<NumericConstantValue<double>>(value));
return true;
}
default:
goto return_fail;
}
return_fail:
std::ostringstream msg_stream;
msg_stream << NameFlatConstant(literal_constant) << " cannot be interpreted as type ";
msg_stream << NameFlatType(type);
return Fail(literal_constant->literal->location(), msg_stream.str());
}
}
}
bool Library::TypeCanBeConst(const Type* type) {
switch (type->kind) {
case flat::Type::Kind::kString:
return type->nullability != types::Nullability::kNullable;
case flat::Type::Kind::kPrimitive:
return true;
default:
return false;
} // switch
}
bool Library::TypeIsConvertibleTo(const Type* from_type, const Type* to_type) {
switch (to_type->kind) {
case flat::Type::Kind::kString: {
if (from_type->kind != flat::Type::Kind::kString)
return false;
auto from_string_type = static_cast<const flat::StringType*>(from_type);
auto to_string_type = static_cast<const flat::StringType*>(to_type);
if (to_string_type->nullability == types::Nullability::kNonnullable &&
from_string_type->nullability != types::Nullability::kNonnullable)
return false;
if (to_string_type->max_size->value < from_string_type->max_size->value)
return false;
return true;
}
case flat::Type::Kind::kPrimitive: {
if (from_type->kind != flat::Type::Kind::kPrimitive) {
return false;
}
auto from_primitive_type = static_cast<const flat::PrimitiveType*>(from_type);
auto to_primitive_type = static_cast<const flat::PrimitiveType*>(to_type);
switch (to_primitive_type->subtype) {
case types::PrimitiveSubtype::kBool:
return from_primitive_type->subtype == types::PrimitiveSubtype::kBool;
default:
// TODO(pascallouis): be more precise about convertibility, e.g. it
// should not be allowed to convert a float to an int.
return from_primitive_type->subtype != types::PrimitiveSubtype::kBool;
}
}
default:
return false;
} // switch
}
Decl* Library::LookupConstant(const TypeConstructor* type_ctor, const Name& name) {
auto decl = LookupDeclByName(type_ctor->name);
if (decl == nullptr) {
// This wasn't a named type. Thus we are looking up a
// top-level constant, of string or primitive type.
auto iter = constants_.find(&name);
if (iter == constants_.end()) {
return nullptr;
}
return iter->second;
}
// We must otherwise be looking for an enum member.
if (decl->kind != Decl::Kind::kEnum) {
return nullptr;
}
auto enum_decl = static_cast<Enum*>(decl);
for (auto& member : enum_decl->members) {
if (member.name.data() == name.name_part()) {
return enum_decl;
}
}
// The enum didn't have a member of that name!
return nullptr;
}
// Library resolution is concerned with resolving identifiers to their
// declarations, and with computing type sizes and alignments.
Decl* Library::LookupDeclByName(const Name& name) const {
auto iter = declarations_.find(&name);
if (iter == declarations_.end()) {
return nullptr;
}
return iter->second;
}
template <typename NumericType>
bool Library::ParseNumericLiteral(const raw::NumericLiteral* literal,
NumericType* out_value) const {
assert(literal != nullptr);
assert(out_value != nullptr);
auto data = literal->location().data();
std::string string_data(data.data(), data.data() + data.size());
auto result = utils::ParseNumeric(string_data, out_value);
return result == utils::ParseNumericResult::kSuccess;
}
// Calculating declaration dependencies is largely serving the C/C++ family of languages bindings.
// For instance, the declaration of a struct member type must be defined before the containing
// struct if that member is stored inline.
// Given the FIDL declarations:
//
// struct D2 { D1 d; }
// struct D1 { int32 x; }
//
// We must first declare D1, followed by D2 when emitting C code.
//
// Below, an edge from D1 to D2 means that we must see the declaration of of D1 before
// the declaration of D2, i.e. the calculated set of |out_edges| represents all the declarations
// that |decl| depends on.
//
// Notes:
// - Nullable structs do not require dependency edges since they are boxed via a
// pointer indirection, and their content placed out-of-line.
// - However, xunions always require dependency edges since nullability does not affect
// their layout.
bool Library::DeclDependencies(Decl* decl, std::set<Decl*>* out_edges) {
std::set<Decl*> edges;
auto maybe_add_decl = [this, &edges](const TypeConstructor* type_ctor) {
for (;;) {
const auto& name = type_ctor->name;
if (name.name_part() == "request") {
return;
} else if (type_ctor->maybe_arg_type_ctor) {
type_ctor = type_ctor->maybe_arg_type_ctor.get();
} else if (type_ctor->nullability == types::Nullability::kNullable) {
if (auto decl = LookupDeclByName(name); decl && decl->kind == Decl::Kind::kXUnion) {
edges.insert(decl);
}
return;
} else {
if (auto decl = LookupDeclByName(name); decl && decl->kind != Decl::Kind::kInterface) {
edges.insert(decl);
}
return;
}
}
};
auto maybe_add_constant = [this, &edges](const TypeConstructor* type_ctor,
const Constant* constant) -> bool {
switch (constant->kind) {
case Constant::Kind::kIdentifier: {
auto identifier = static_cast<const flat::IdentifierConstant*>(constant);
auto decl = LookupConstant(type_ctor, identifier->name);
if (decl == nullptr) {
std::string message("Unable to find the constant named: ");
message += identifier->name.name_part();
return Fail(identifier->name, message.data());
}
edges.insert(decl);
break;
}
case Constant::Kind::kLiteral:
case Constant::Kind::kSynthesized: {
// Literal and synthesized constants have no dependencies on other declarations.
break;
}
}
return true;
};
switch (decl->kind) {
case Decl::Kind::kBits: {
auto bits_decl = static_cast<const Bits*>(decl);
for (const auto& member : bits_decl->members) {
maybe_add_constant(bits_decl->subtype_ctor.get(), member.value.get());
}
break;
}
case Decl::Kind::kConst: {
auto const_decl = static_cast<const Const*>(decl);
if (!maybe_add_constant(const_decl->type_ctor.get(), const_decl->value.get()))
return false;
break;
}
case Decl::Kind::kEnum: {
auto enum_decl = static_cast<const Enum*>(decl);
for (const auto& member : enum_decl->members) {
maybe_add_constant(enum_decl->subtype_ctor.get(), member.value.get());
}
break;
}
case Decl::Kind::kInterface: {
auto interface_decl = static_cast<const Interface*>(decl);
for (const auto& superinterface : interface_decl->superinterfaces) {
if (auto type_decl = LookupDeclByName(superinterface); type_decl)
edges.insert(type_decl);
}
for (const auto& method : interface_decl->methods) {
if (method.maybe_request != nullptr) {
edges.insert(method.maybe_request);
}
if (method.maybe_response != nullptr) {
edges.insert(method.maybe_response);
}
}
break;
}
case Decl::Kind::kStruct: {
auto struct_decl = static_cast<const Struct*>(decl);
for (const auto& member : struct_decl->members) {
maybe_add_decl(member.type_ctor.get());
if (member.maybe_default_value) {
if (!maybe_add_constant(member.type_ctor.get(), member.maybe_default_value.get()))
return false;
}
}
break;
}
case Decl::Kind::kTable: {
auto table_decl = static_cast<const Table*>(decl);
for (const auto& member : table_decl->members) {
if (!member.maybe_used)
continue;
maybe_add_decl(member.maybe_used->type_ctor.get());
if (member.maybe_used->maybe_default_value) {
if (!maybe_add_constant(member.maybe_used->type_ctor.get(),
member.maybe_used->maybe_default_value.get()))
return false;
}
}
break;
}
case Decl::Kind::kUnion: {
auto union_decl = static_cast<const Union*>(decl);
for (const auto& member : union_decl->members) {
maybe_add_decl(member.type_ctor.get());
}
break;
}
case Decl::Kind::kXUnion: {
auto xunion_decl = static_cast<const XUnion*>(decl);
for (const auto& member : xunion_decl->members) {
maybe_add_decl(member.type_ctor.get());
}
break;
}
}
*out_edges = std::move(edges);
return true;
}
namespace {
// To compare two Decl's in the same library, it suffices to compare the unqualified names of the Decl's.
struct CmpDeclInLibrary {
bool operator()(const Decl* a, const Decl* b) const {
assert(a->name != b->name || a == b);
return a->name < b->name;
}
};
} // namespace
bool Library::SortDeclarations() {
// |degree| is the number of undeclared dependencies for each decl.
std::map<Decl*, uint32_t, CmpDeclInLibrary> degrees;
// |inverse_dependencies| records the decls that depend on each decl.
std::map<Decl*, std::vector<Decl*>, CmpDeclInLibrary> inverse_dependencies;
for (auto& name_and_decl : declarations_) {
Decl* decl = name_and_decl.second;
degrees[decl] = 0u;
}
for (auto& name_and_decl : declarations_) {
Decl* decl = name_and_decl.second;
std::set<Decl*> deps;
if (!DeclDependencies(decl, &deps))
return false;
degrees[decl] += deps.size();
for (Decl* dep : deps) {
inverse_dependencies[dep].push_back(decl);
}
}
// Start with all decls that have no incoming edges.
std::vector<Decl*> decls_without_deps;
for (const auto& decl_and_degree : degrees) {
if (decl_and_degree.second == 0u) {
decls_without_deps.push_back(decl_and_degree.first);
}
}
while (!decls_without_deps.empty()) {
// Pull one out of the queue.
auto decl = decls_without_deps.back();
decls_without_deps.pop_back();
assert(degrees[decl] == 0u);
declaration_order_.push_back(decl);
// Decrement the incoming degree of all the other decls it
// points to.
auto& inverse_deps = inverse_dependencies[decl];
for (Decl* inverse_dep : inverse_deps) {
uint32_t& degree = degrees[inverse_dep];
assert(degree != 0u);
degree -= 1;
if (degree == 0u)
decls_without_deps.push_back(inverse_dep);
}
}
if (declaration_order_.size() != degrees.size()) {
// We didn't visit all the edges! There was a cycle.
return Fail("There is an includes-cycle in declarations");
}
return true;
}
bool Library::CompileDecl(Decl* decl) {
Compiling guard(decl);
switch (decl->kind) {
case Decl::Kind::kBits: {
auto bits_decl = static_cast<Bits*>(decl);
if (!CompileBits(bits_decl))
return false;
break;
}
case Decl::Kind::kConst: {
auto const_decl = static_cast<Const*>(decl);
if (!CompileConst(const_decl))
return false;
break;
}
case Decl::Kind::kEnum: {
auto enum_decl = static_cast<Enum*>(decl);
if (!CompileEnum(enum_decl))
return false;
break;
}
case Decl::Kind::kInterface: {
auto interface_decl = static_cast<Interface*>(decl);
if (!CompileInterface(interface_decl))
return false;
break;
}
case Decl::Kind::kStruct: {
auto struct_decl = static_cast<Struct*>(decl);
if (!CompileStruct(struct_decl))
return false;
break;
}
case Decl::Kind::kTable: {
auto table_decl = static_cast<Table*>(decl);
if (!CompileTable(table_decl))
return false;
break;
}
case Decl::Kind::kUnion: {
auto union_decl = static_cast<Union*>(decl);
if (!CompileUnion(union_decl))
return false;
break;
}
case Decl::Kind::kXUnion: {
auto xunion_decl = static_cast<XUnion*>(decl);
if (!CompileXUnion(xunion_decl))
return false;
break;
}
} // switch
return true;
}
bool Library::VerifyDeclAttributes(Decl* decl) {
assert(decl->compiled && "verification must happen after compilation of decls");
auto placement_ok = error_reporter_->Checkpoint();
switch (decl->kind) {
case Decl::Kind::kBits: {
auto bits_declaration = static_cast<Bits*>(decl);
// Attributes: check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kBitsDecl,
bits_declaration->attributes.get());
for (const auto& member : bits_declaration->members) {
ValidateAttributesPlacement(
AttributeSchema::Placement::kBitsMember,
member.attributes.get());
}
if (placement_ok.NoNewErrors()) {
// Attributes: check constraints.
ValidateAttributesConstraints(
bits_declaration,
bits_declaration->attributes.get());
}
break;
}
case Decl::Kind::kConst: {
auto const_decl = static_cast<Const*>(decl);
// Attributes: for const declarations, we only check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kConstDecl, const_decl->attributes.get());
break;
}
case Decl::Kind::kEnum: {
auto enum_declaration = static_cast<Enum*>(decl);
// Attributes: check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kEnumDecl,
enum_declaration->attributes.get());
for (const auto& member : enum_declaration->members) {
ValidateAttributesPlacement(
AttributeSchema::Placement::kEnumMember,
member.attributes.get());
}
if (placement_ok.NoNewErrors()) {
// Attributes: check constraints.
ValidateAttributesConstraints(
enum_declaration,
enum_declaration->attributes.get());
}
break;
}
case Decl::Kind::kInterface: {
auto interface_declaration = static_cast<Interface*>(decl);
// Attributes: check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kInterfaceDecl,
interface_declaration->attributes.get());
for (const auto method : interface_declaration->all_methods) {
ValidateAttributesPlacement(
AttributeSchema::Placement::kMethod,
method->attributes.get());
}
if (placement_ok.NoNewErrors()) {
// Attributes: check constraints.
for (const auto method : interface_declaration->all_methods) {
if (method->maybe_request) {
ValidateAttributesConstraints(
method->maybe_request,
interface_declaration->attributes.get());
ValidateAttributesConstraints(
method->maybe_request,
method->attributes.get());
}
if (method->maybe_response) {
ValidateAttributesConstraints(
method->maybe_response,
interface_declaration->attributes.get());
ValidateAttributesConstraints(
method->maybe_response,
method->attributes.get());
}
}
}
break;
}
case Decl::Kind::kStruct: {
auto struct_declaration = static_cast<Struct*>(decl);
// Attributes: check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kStructDecl,
struct_declaration->attributes.get());
for (const auto& member : struct_declaration->members) {
ValidateAttributesPlacement(
AttributeSchema::Placement::kStructMember,
member.attributes.get());
}
if (placement_ok.NoNewErrors()) {
// Attributes: check constraint.
ValidateAttributesConstraints(
struct_declaration,
struct_declaration->attributes.get());
}
break;
}
case Decl::Kind::kTable: {
auto table_declaration = static_cast<Table*>(decl);
// Attributes: check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kTableDecl,
table_declaration->attributes.get());
for (const auto& member : table_declaration->members) {
if (member.maybe_used) {
ValidateAttributesPlacement(
AttributeSchema::Placement::kTableMember,
member.maybe_used->attributes.get());
}
}
if (placement_ok.NoNewErrors()) {
// Attributes: check constraint.
ValidateAttributesConstraints(
table_declaration,
table_declaration->attributes.get());
}
break;
}
case Decl::Kind::kUnion: {
auto union_declaration = static_cast<Union*>(decl);
// Attributes: check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kUnionDecl,
union_declaration->attributes.get());
for (const auto& member : union_declaration->members) {
ValidateAttributesPlacement(
AttributeSchema::Placement::kUnionMember,
member.attributes.get());
}
if (placement_ok.NoNewErrors()) {
// Attributes: check constraint.
ValidateAttributesConstraints(
union_declaration,
union_declaration->attributes.get());
}
break;
}
case Decl::Kind::kXUnion: {
auto xunion_declaration = static_cast<XUnion*>(decl);
// Attributes: check placement.
ValidateAttributesPlacement(
AttributeSchema::Placement::kXUnionDecl,
xunion_declaration->attributes.get());
for (const auto& member : xunion_declaration->members) {
ValidateAttributesPlacement(
AttributeSchema::Placement::kXUnionMember,
member.attributes.get());
}
if (placement_ok.NoNewErrors()) {
// Attributes: check constraint.
ValidateAttributesConstraints(
xunion_declaration,
xunion_declaration->attributes.get());
}
}
} // switch
return true;
}
bool Library::CompileBits(Bits* bits_declaration) {
if (!CompileTypeConstructor(bits_declaration->subtype_ctor.get(), &bits_declaration->typeshape))
return false;
if (bits_declaration->subtype_ctor->type->kind != Type::Kind::kPrimitive) {
std::string message("bits may only be of unsigned integral primitive type, found ");
message.append(NameFlatType(bits_declaration->subtype_ctor->type));
return Fail(*bits_declaration, message);
}
// Validate constants.
auto primitive_type = static_cast<const PrimitiveType*>(bits_declaration->subtype_ctor->type);
switch (primitive_type->subtype) {
case types::PrimitiveSubtype::kUint8:
if (!ValidateBitsMembers<uint8_t>(bits_declaration))
return false;
break;
case types::PrimitiveSubtype::kUint16:
if (!ValidateBitsMembers<uint16_t>(bits_declaration))
return false;
break;
case types::PrimitiveSubtype::kUint32:
if (!ValidateBitsMembers<uint32_t>(bits_declaration))
return false;
break;
case types::PrimitiveSubtype::kUint64:
if (!ValidateBitsMembers<uint64_t>(bits_declaration))
return false;
break;
case types::PrimitiveSubtype::kBool:
case types::PrimitiveSubtype::kInt8:
case types::PrimitiveSubtype::kInt16:
case types::PrimitiveSubtype::kInt32:
case types::PrimitiveSubtype::kInt64:
case types::PrimitiveSubtype::kFloat32:
case types::PrimitiveSubtype::kFloat64:
std::string message("bits may only be of unsigned integral primitive type, found ");
message.append(NameFlatType(bits_declaration->subtype_ctor->type));
return Fail(*bits_declaration, message);
}
return true;
}
bool Library::CompileConst(Const* const_declaration) {
TypeShape typeshape;
if (!CompileTypeConstructor(const_declaration->type_ctor.get(), &typeshape))
return false;
const auto* const_type = const_declaration->type_ctor.get()->type;
if (!TypeCanBeConst(const_type)) {
std::ostringstream msg_stream;
msg_stream << "invalid constant type " << NameFlatType(const_type);
return Fail(*const_declaration, msg_stream.str());
}
if (!ResolveConstant(const_declaration->value.get(), const_type))
return Fail(*const_declaration, "unable to resolve constant value");
return true;
}
bool Library::CompileEnum(Enum* enum_declaration) {
if (!CompileTypeConstructor(enum_declaration->subtype_ctor.get(), &enum_declaration->typeshape))
return false;
if (enum_declaration->subtype_ctor->type->kind != Type::Kind::kPrimitive) {
std::string message("enums may only be of integral primitive type, found ");
message.append(NameFlatType(enum_declaration->subtype_ctor->type));
return Fail(*enum_declaration, message);
}
// Validate constants.
auto primitive_type = static_cast<const PrimitiveType*>(enum_declaration->subtype_ctor->type);
enum_declaration->type = primitive_type;
switch (primitive_type->subtype) {
case types::PrimitiveSubtype::kInt8:
if (!ValidateEnumMembers<int8_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kInt16:
if (!ValidateEnumMembers<int16_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kInt32:
if (!ValidateEnumMembers<int32_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kInt64:
if (!ValidateEnumMembers<int64_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kUint8:
if (!ValidateEnumMembers<uint8_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kUint16:
if (!ValidateEnumMembers<uint16_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kUint32:
if (!ValidateEnumMembers<uint32_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kUint64:
if (!ValidateEnumMembers<uint64_t>(enum_declaration))
return false;
break;
case types::PrimitiveSubtype::kBool:
case types::PrimitiveSubtype::kFloat32:
case types::PrimitiveSubtype::kFloat64:
std::string message("enums may only be of integral primitive type, found ");
message.append(NameFlatType(enum_declaration->subtype_ctor->type));
return Fail(*enum_declaration, message);
}
return true;
}
bool HasSimpleLayout(const Decl* decl) {
return decl->GetAttribute("Layout") == "Simple";
}
bool Library::CompileInterface(Interface* interface_declaration) {
MethodScope method_scope;
auto CheckScopes = [this, &interface_declaration, &method_scope](const Interface* interface, auto Visitor) -> bool {
for (const auto& name : interface->superinterfaces) {
auto decl = LookupDeclByName(name);
// TODO(FIDL-603): Special handling here should not be required, we
// should first rely on creating the types representing composed
// protocols.
if (!decl) {
std::string message("unknown type ");
message.append(name.name_part());
return Fail(name, message);
}
if (decl->kind != Decl::Kind::kInterface)
return Fail(name, "This superinterface declaration is not an interface");
if (!decl->HasAttribute("FragileBase")) {
std::string message = "interface ";
message += NameName(name, ".", "/");
message += " is not marked by [FragileBase] attribute, disallowing interface ";
message += NameName(interface_declaration->name, ".", "/");
message += " from inheriting from it";
return Fail(name, message);
}
auto superinterface = static_cast<const Interface*>(decl);
auto maybe_location = superinterface->name.maybe_location();
assert(maybe_location);
if (method_scope.interfaces.Insert(superinterface, *maybe_location).ok()) {
if (!Visitor(superinterface, Visitor))
return false;
} else {
// Otherwise we have already seen this interface in
// the inheritance graph.
}
}
for (const auto& method : interface->methods) {
auto name_result = method_scope.names.Insert(method.name.data(), method.name);
if (!name_result.ok())
return Fail(method.name,
"Multiple methods with the same name in an interface; last occurrence was at " +
name_result.previous_occurrence().position());
auto ordinal_result = method_scope.ordinals.Insert(method.ordinal->value, method.name);
if (method.ordinal->value == 0)
return Fail(method.ordinal->location(), "Ordinal value 0 disallowed.");
if (!ordinal_result.ok()) {
std::string replacement_method(
fidl::ordinals::GetSelector(method.attributes.get(), method.name));
replacement_method.push_back('_');
return Fail(method.ordinal->location(),
"Multiple methods with the same ordinal in an interface; previous was at " +
ordinal_result.previous_occurrence().position() + ". If these " +
"were automatically generated, consider using attribute " +
"[Selector=\"" + replacement_method + "\"] to change the " +
"name used to calculate the ordinal.");
}
// Add a pointer to this method to the interface_declarations list.
interface_declaration->all_methods.push_back(&method);
}
return true;
};
if (!CheckScopes(interface_declaration, CheckScopes))
return false;
interface_declaration->typeshape = HandleType::Shape();
for (auto& method : interface_declaration->methods) {
auto CreateMessage = [&](Struct* message) -> bool {
Scope<StringView> scope;
for (auto& param : message->members) {
if (!scope.Insert(param.name.data(), param.name).ok())
return Fail(param.name, "Multiple parameters with the same name in a method");
if (!CompileTypeConstructor(param.type_ctor.get(), &param.fieldshape.Typeshape()))
return false;
}
return true;
};
if (method.maybe_request) {
if (!CreateMessage(method.maybe_request))
return false;
}
if (method.maybe_response) {
if (!CreateMessage(method.maybe_response))
return false;
}
}
return true;
}
bool Library::CompileStruct(Struct* struct_declaration) {
Scope<StringView> scope;
std::vector<FieldShape*> fidl_struct;
uint32_t max_member_handles = 0;
for (auto& member : struct_declaration->members) {
auto name_result = scope.Insert(member.name.data(), member.name);
if (!name_result.ok())
return Fail(member.name,
"Multiple struct fields with the same name; previous was at " +
name_result.previous_occurrence().position());
if (!CompileTypeConstructor(member.type_ctor.get(), &member.fieldshape.Typeshape()))
return false;
fidl_struct.push_back(&member.fieldshape);
}
if (struct_declaration->recursive) {
max_member_handles = std::numeric_limits<uint32_t>::max();
} else {
// Member handles will be counted by CStructTypeShape.
max_member_handles = 0;
}
struct_declaration->typeshape = Struct::Shape(&fidl_struct, max_member_handles);
return true;
}
bool Library::CompileTable(Table* table_declaration) {
Scope<StringView> name_scope;
Scope<uint32_t> ordinal_scope;
uint32_t max_member_handles = 0;
for (auto& member : table_declaration->members) {
auto ordinal_result = ordinal_scope.Insert(member.ordinal->value, member.ordinal->location());
if (!ordinal_result.ok())
return Fail(member.ordinal->location(),
"Multiple table fields with the same ordinal; previous was at " +
ordinal_result.previous_occurrence().position());
if (member.maybe_used) {
auto name_result = name_scope.Insert(member.maybe_used->name.data(), member.maybe_used->name);
if (!name_result.ok())
return Fail(member.maybe_used->name,
"Multiple table fields with the same name; previous was at " +
name_result.previous_occurrence().position());
if (!CompileTypeConstructor(member.maybe_used->type_ctor.get(), &member.maybe_used->typeshape))
return false;
}
}
uint32_t last_ordinal_seen = 0;
for (const auto& ordinal_and_loc : ordinal_scope) {
if (ordinal_and_loc.first != last_ordinal_seen + 1) {
return Fail(ordinal_and_loc.second,
"Missing ordinal (table ordinals do not form a dense space)");
}
last_ordinal_seen = ordinal_and_loc.first;
}
if (table_declaration->recursive) {
max_member_handles = std::numeric_limits<uint32_t>::max();
} else {
// Member handles will be counted by CTableTypeShape.
max_member_handles = 0;
}
std::vector<TypeShape*> fields(table_declaration->members.size());
for (auto& member : table_declaration->members) {
if (member.maybe_used) {
fields[member.ordinal->value - 1] = &member.maybe_used->typeshape;
}
}
table_declaration->typeshape = Table::Shape(&fields, max_member_handles);
return true;
}
bool Library::CompileUnion(Union* union_declaration) {
Scope<StringView> scope;
for (auto& member : union_declaration->members) {
auto name_result = scope.Insert(member.name.data(), member.name);
if (!name_result.ok())
return Fail(member.name,
"Multiple union members with the same name; previous was at " +
name_result.previous_occurrence().position());
if (!CompileTypeConstructor(member.type_ctor.get(), &member.fieldshape.Typeshape()))
return false;
}
auto tag = FieldShape(PrimitiveType::Shape(types::PrimitiveSubtype::kUint32));
union_declaration->membershape = FieldShape(Union::Shape(union_declaration->members));
uint32_t extra_handles = 0;
if (union_declaration->recursive && union_declaration->membershape.MaxHandles()) {
extra_handles = std::numeric_limits<uint32_t>::max();
}
std::vector<FieldShape*> fidl_union = {&tag, &union_declaration->membershape};
union_declaration->typeshape = Struct::Shape(&fidl_union, extra_handles);
// This is either 4 or 8, depending on whether any union members
// have alignment 8.
auto offset = union_declaration->membershape.Offset();
for (auto& member : union_declaration->members) {
member.fieldshape.SetOffset(offset);
}
return true;
}
bool Library::CompileXUnion(XUnion* xunion_declaration) {
Scope<StringView> scope;
Scope<uint32_t> ordinal_scope;
for (auto& member : xunion_declaration->members) {
auto ordinal_result = ordinal_scope.Insert(member.ordinal->value, member.ordinal->location());
if (!ordinal_result.ok())
return Fail(member.ordinal->location(),
"Multiple xunion fields with the same ordinal; previous was at " +
ordinal_result.previous_occurrence().position());
auto name_result = scope.Insert(member.name.data(), member.name);
if (!name_result.ok())
return Fail(member.name,
"Multiple xunion members with the same name; previous was at " +
name_result.previous_occurrence().position());
if (!CompileTypeConstructor(member.type_ctor.get(), &member.fieldshape.Typeshape()))
return false;
}
uint32_t max_member_handles;
if (xunion_declaration->recursive) {
max_member_handles = std::numeric_limits<uint32_t>::max();
} else {
// Member handles will be counted by CXUnionTypeShape.
max_member_handles = 0u;
}
xunion_declaration->typeshape = XUnion::Shape(xunion_declaration->members, max_member_handles);
return true;
}
bool Library::CompileLibraryName() {
const std::regex pattern("^[a-z][a-z0-9]*$");
for (const auto& part_view : library_name_) {
std::string part = part_view;
if (!std::regex_match(part, pattern)) {
return Fail("Invalid library name part " + part);
}
}
return true;
}
bool Library::Compile() {
for (const auto& dep_library : dependencies_.dependencies()) {
constants_.insert(dep_library->constants_.begin(), dep_library->constants_.end());
}
// Verify that the library's name is valid.
if (!CompileLibraryName()) {
return false;
}
if (!SortDeclarations()) {
return false;
}
// We process declarations in topologically sorted order. For
// example, we process a struct member's type before the entire
// struct.
for (Decl* decl : declaration_order_) {
if (!CompileDecl(decl))
return false;
}
// Beware, hacky solution: method request and response are structs, whose
// typeshape is computed separately. However, in the JSON IR, we add 16 bytes
// to request and response to account for the header size (and ensure the
// alignment is at least 4 bytes). Now that we represent request and responses
// as structs, we should represent this differently in the JSON IR, and let the
// backends figure this out, or better yet, describe the header directly.
//
// For now though, we fixup the representation after the fact.
for (auto& interface_decl : interface_declarations_) {
for (auto& method : interface_decl->all_methods) {
auto FixupMessage = [&](Struct* message) {
auto header_field_shape = FieldShape(TypeShape(16u, 4u));
std::vector<FieldShape*> message_struct;
message_struct.push_back(&header_field_shape);
for (auto& param : message->members)
message_struct.push_back(&param.fieldshape);
message->typeshape = FidlMessageTypeShape(&message_struct);
};
if (method->maybe_request)
FixupMessage(method->maybe_request);
if (method->maybe_response)
FixupMessage(method->maybe_response);
}
}
for (Decl* decl : declaration_order_) {
if (!VerifyDeclAttributes(decl))
return false;
}
return error_reporter_->errors().size() == 0;
}
bool Library::CompileTypeConstructor(TypeConstructor* type_ctor, TypeShape* out_typeshape) {
const Type* maybe_arg_type = nullptr;
if (type_ctor->maybe_arg_type_ctor != nullptr) {
if (!CompileTypeConstructor(type_ctor->maybe_arg_type_ctor.get(), nullptr))
return false;
maybe_arg_type = type_ctor->maybe_arg_type_ctor->type;
}
const Size* size = nullptr;
if (type_ctor->maybe_size != nullptr) {
if (!ResolveConstant(type_ctor->maybe_size.get(), &kSizeType))
return Fail(type_ctor->name.maybe_location(), "unable to parse size bound");
size = static_cast<const Size*>(&type_ctor->maybe_size->Value());
}
if (!typespace_->Create(type_ctor->name, maybe_arg_type, type_ctor->maybe_handle_subtype.get(),
size, type_ctor->nullability,
&type_ctor->type))
return false;
if (out_typeshape)
*out_typeshape = type_ctor->type->shape;
return true;
}
template <typename DeclType, typename MemberType>
bool Library::ValidateMembers(DeclType* decl, MemberValidator<MemberType> validator) {
assert(decl != nullptr);
constexpr const char* decl_type = std::is_same_v<DeclType, Enum> ? "enum" : "bits";
Scope<std::string> name_scope;
Scope<MemberType> value_scope;
bool success = true;
for (auto& member : decl->members) {
assert(member.value != nullptr && "Compiler bug: member value is null!");
if (!ResolveConstant(member.value.get(), decl->subtype_ctor->type)) {
std::string failure_message = "unable to resolve ";
failure_message += decl_type;
failure_message += " member";
return Fail(member.name, failure_message);
}
// Check that the member identifier hasn't been used yet
std::string name = NameIdentifier(member.name);
auto name_result = name_scope.Insert(name, member.name);
if (!name_result.ok()) {
std::ostringstream msg_stream;
msg_stream << "name of member " << name;
msg_stream << " conflicts with previously declared member in the ";
msg_stream << decl_type << " " << decl->GetName();
// We can log the error and then continue validating for other issues in the decl
success = Fail(member.name, msg_stream.str());
}
MemberType value = static_cast<const NumericConstantValue<MemberType>&>(
member.value->Value())
.value;
auto value_result = value_scope.Insert(value, member.name);
if (!value_result.ok()) {
std::ostringstream msg_stream;
msg_stream << "value of member " << name;
msg_stream << " conflicts with previously declared member ";
msg_stream << NameIdentifier(value_result.previous_occurrence()) << " in the ";
msg_stream << decl_type << " " << decl->GetName();
// We can log the error and then continue validating other members for other bugs
success = Fail(member.name, msg_stream.str());
}
std::string validation_failure;
if (!validator(value, &validation_failure)) {
success = Fail(member.name, validation_failure);
}
}
return success;
}
template <typename T>
static bool IsPowerOfTwo(T t) {
if (t == 0) {
return false;
}
if ((t & (t - 1)) != 0) {
return false;
}
return true;
}
template <typename MemberType>
bool Library::ValidateBitsMembers(Bits* bits_decl) {
static_assert(std::is_unsigned<MemberType>::value && !std::is_same<MemberType, bool>::value,
"Bits members must be an unsigned integral type!");
// Each bits member must be a power of two.
auto validator = [](MemberType member, std::string* out_error) {
if (!IsPowerOfTwo(member)) {
*out_error = "bits members must be powers of two";
return false;
}
return true;
};
return ValidateMembers<Bits, MemberType>(bits_decl, validator);
}
template <typename MemberType>
bool Library::ValidateEnumMembers(Enum* enum_decl) {
static_assert(std::is_integral<MemberType>::value && !std::is_same<MemberType, bool>::value,
"Enum members must be an integral type!");
// No additional validation is required for enums.
auto validator = [](MemberType member, std::string* out_error) { return true; };
return ValidateMembers<Enum, MemberType>(enum_decl, validator);
}
bool Library::HasAttribute(fidl::StringView name) const {
if (!attributes_)
return false;
return attributes_->HasAttribute(name);
}
const std::set<Library*>& Library::dependencies() const {
return dependencies_.dependencies();
}
} // namespace flat
} // namespace fidl