Mercurial > hg > sv-dependency-builds
diff win64-msvc/include/capnp/schema.h @ 148:b4bfdf10c4b3
Update Win64 capnp builds to v0.6
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Mon, 22 May 2017 18:56:49 +0100 |
parents | 42a73082be24 |
children |
line wrap: on
line diff
--- a/win64-msvc/include/capnp/schema.h Mon May 22 10:01:37 2017 +0100 +++ b/win64-msvc/include/capnp/schema.h Mon May 22 18:56:49 2017 +0100 @@ -1,934 +1,934 @@ -// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors -// Licensed under the MIT License: -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#ifndef CAPNP_SCHEMA_H_ -#define CAPNP_SCHEMA_H_ - -#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) -#pragma GCC system_header -#endif - -#if CAPNP_LITE -#error "Reflection APIs, including this header, are not available in lite mode." -#endif - -#include <capnp/schema.capnp.h> - -namespace capnp { - -class Schema; -class StructSchema; -class EnumSchema; -class InterfaceSchema; -class ConstSchema; -class ListSchema; -class Type; - -template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; }; -template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Which Type; }; -template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Which Type; }; -template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; }; -template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; }; -template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; }; -template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; }; - -template <typename T> -using SchemaType = typename SchemaType_<T>::Type; -// SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct. - -namespace _ { // private -extern const RawSchema NULL_SCHEMA; -extern const RawSchema NULL_STRUCT_SCHEMA; -extern const RawSchema NULL_ENUM_SCHEMA; -extern const RawSchema NULL_INTERFACE_SCHEMA; -extern const RawSchema NULL_CONST_SCHEMA; -// The schema types default to these null (empty) schemas in case of error, especially when -// exceptions are disabled. -} // namespace _ (private) - -class Schema { - // Convenience wrapper around capnp::schema::Node. - -public: - inline Schema(): raw(&_::NULL_SCHEMA.defaultBrand) {} - - template <typename T> - static inline SchemaType<T> from() { return SchemaType<T>::template fromImpl<T>(); } - // Get the Schema for a particular compiled-in type. - - schema::Node::Reader getProto() const; - // Get the underlying Cap'n Proto representation of the schema node. (Note that this accessor - // has performance comparable to accessors of struct-typed fields on Reader classes.) - - kj::ArrayPtr<const word> asUncheckedMessage() const; - // Get the encoded schema node content as a single message segment. It is safe to read as an - // unchecked message. - - Schema getDependency(uint64_t id) const KJ_DEPRECATED("Does not handle generics correctly."); - // DEPRECATED: This method cannot correctly account for generic type parameter bindings that - // may apply to the dependency. Instead of using this method, use a method of the Schema API - // that corresponds to the exact kind of dependency. For example, to get a field type, use - // StructSchema::Field::getType(). - // - // Gets the Schema for one of this Schema's dependencies. For example, if this Schema is for a - // struct, you could look up the schema for one of its fields' types. Throws an exception if this - // schema doesn't actually depend on the given id. - // - // Note that not all type IDs found in the schema node are considered "dependencies" -- only the - // ones that are needed to implement the dynamic API are. That includes: - // - Field types. - // - Group types. - // - scopeId for group nodes, but NOT otherwise. - // - Method parameter and return types. - // - // The following are NOT considered dependencies: - // - Nested nodes. - // - scopeId for a non-group node. - // - Annotations. - // - // To obtain schemas for those, you would need a SchemaLoader. - - bool isBranded() const; - // Returns true if this schema represents a non-default parameterization of this type. - - Schema getGeneric() const; - // Get the version of this schema with any brands removed. - - class BrandArgumentList; - BrandArgumentList getBrandArgumentsAtScope(uint64_t scopeId) const; - // Gets the values bound to the brand parameters at the given scope. - - StructSchema asStruct() const; - EnumSchema asEnum() const; - InterfaceSchema asInterface() const; - ConstSchema asConst() const; - // Cast the Schema to a specific type. Throws an exception if the type doesn't match. Use - // getProto() to determine type, e.g. getProto().isStruct(). - - inline bool operator==(const Schema& other) const { return raw == other.raw; } - inline bool operator!=(const Schema& other) const { return raw != other.raw; } - // Determine whether two Schemas are wrapping the exact same underlying data, by identity. If - // you want to check if two Schemas represent the same type (but possibly different versions of - // it), compare their IDs instead. - - template <typename T> - void requireUsableAs() const; - // Throws an exception if a value with this Schema cannot safely be cast to a native value of - // the given type. This passes if either: - // - *this == from<T>() - // - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and - // loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader. - - kj::StringPtr getShortDisplayName() const; - // Get the short version of the node's display name. - -private: - const _::RawBrandedSchema* raw; - - inline explicit Schema(const _::RawBrandedSchema* raw): raw(raw) { - KJ_IREQUIRE(raw->lazyInitializer == nullptr, - "Must call ensureInitialized() on RawSchema before constructing Schema."); - } - - template <typename T> static inline Schema fromImpl() { - return Schema(&_::rawSchema<T>()); - } - - void requireUsableAs(const _::RawSchema* expected) const; - - uint32_t getSchemaOffset(const schema::Value::Reader& value) const; - - Type getBrandBinding(uint64_t scopeId, uint index) const; - // Look up the binding for a brand parameter used by this Schema. Returns `AnyPointer` if the - // parameter is not bound. - // - // TODO(someday): Public interface for iterating over all bindings? - - Schema getDependency(uint64_t id, uint location) const; - // Look up schema for a particular dependency of this schema. `location` is the dependency - // location number as defined in _::RawBrandedSchema. - - Type interpretType(schema::Type::Reader proto, uint location) const; - // Interpret a schema::Type in the given location within the schema, compiling it into a - // Type object. - - friend class StructSchema; - friend class EnumSchema; - friend class InterfaceSchema; - friend class ConstSchema; - friend class ListSchema; - friend class SchemaLoader; - friend class Type; - friend kj::StringTree _::structString( - _::StructReader reader, const _::RawBrandedSchema& schema); - friend kj::String _::enumString(uint16_t value, const _::RawBrandedSchema& schema); -}; - -kj::StringPtr KJ_STRINGIFY(const Schema& schema); - -class Schema::BrandArgumentList { - // A list of generic parameter bindings for parameters of some particular type. Note that since - // parameters on an outer type apply to all inner types as well, a deeply-nested type can have - // multiple BrandArgumentLists that apply to it. - // - // A BrandArgumentList only represents the arguments that the client of the type specified. Since - // new parameters can be added over time, this list may not cover all defined parameters for the - // type. Missing parameters should be treated as AnyPointer. This class's implementation of - // operator[] already does this for you; out-of-bounds access will safely return AnyPointer. - -public: - inline BrandArgumentList(): scopeId(0), size_(0), bindings(nullptr) {} - - inline uint size() const { return size_; } - Type operator[](uint index) const; - - typedef _::IndexingIterator<const BrandArgumentList, Type> Iterator; - inline Iterator begin() const { return Iterator(this, 0); } - inline Iterator end() const { return Iterator(this, size()); } - -private: - uint64_t scopeId; - uint size_; - bool isUnbound; - const _::RawBrandedSchema::Binding* bindings; - - inline BrandArgumentList(uint64_t scopeId, bool isUnbound) - : scopeId(scopeId), size_(0), isUnbound(isUnbound), bindings(nullptr) {} - inline BrandArgumentList(uint64_t scopeId, uint size, - const _::RawBrandedSchema::Binding* bindings) - : scopeId(scopeId), size_(size), isUnbound(false), bindings(bindings) {} - - friend class Schema; -}; - -// ------------------------------------------------------------------- - -class StructSchema: public Schema { -public: - inline StructSchema(): Schema(&_::NULL_STRUCT_SCHEMA.defaultBrand) {} - - class Field; - class FieldList; - class FieldSubset; - - FieldList getFields() const; - // List top-level fields of this struct. This list will contain top-level groups (including - // named unions) but not the members of those groups. The list does, however, contain the - // members of the unnamed union, if there is one. - - FieldSubset getUnionFields() const; - // If the field contains an unnamed union, get a list of fields in the union, ordered by - // ordinal. Since discriminant values are assigned sequentially by ordinal, you may index this - // list by discriminant value. - - FieldSubset getNonUnionFields() const; - // Get the fields of this struct which are not in an unnamed union, ordered by ordinal. - - kj::Maybe<Field> findFieldByName(kj::StringPtr name) const; - // Find the field with the given name, or return null if there is no such field. If the struct - // contains an unnamed union, then this will find fields of that union in addition to fields - // of the outer struct, since they exist in the same namespace. It will not, however, find - // members of groups (including named unions) -- you must first look up the group itself, - // then dig into its type. - - Field getFieldByName(kj::StringPtr name) const; - // Like findFieldByName() but throws an exception on failure. - - kj::Maybe<Field> getFieldByDiscriminant(uint16_t discriminant) const; - // Finds the field whose `discriminantValue` is equal to the given value, or returns null if - // there is no such field. (If the schema does not represent a union or a struct containing - // an unnamed union, then this always returns null.) - -private: - StructSchema(Schema base): Schema(base) {} - template <typename T> static inline StructSchema fromImpl() { - return StructSchema(Schema(&_::rawBrandedSchema<T>())); - } - friend class Schema; - friend class Type; -}; - -class StructSchema::Field { -public: - Field() = default; - - inline schema::Field::Reader getProto() const { return proto; } - inline StructSchema getContainingStruct() const { return parent; } - - inline uint getIndex() const { return index; } - // Get the index of this field within the containing struct or union. - - Type getType() const; - // Get the type of this field. Note that this is preferred over getProto().getType() as this - // method will apply generics. - - uint32_t getDefaultValueSchemaOffset() const; - // For struct, list, and object fields, returns the offset, in words, within the first segment of - // the struct's schema, where this field's default value pointer is located. The schema is - // always stored as a single-segment unchecked message, which in turn means that the default - // value pointer itself can be treated as the root of an unchecked message -- if you know where - // to find it, which is what this method helps you with. - // - // For blobs, returns the offset of the beginning of the blob's content within the first segment - // of the struct's schema. - // - // This is primarily useful for code generators. The C++ code generator, for example, embeds - // the entire schema as a raw word array within the generated code. Of course, to implement - // field accessors, it needs access to those fields' default values. Embedding separate copies - // of those default values would be redundant since they are already included in the schema, but - // seeking through the schema at runtime to find the default values would be ugly. Instead, - // the code generator can use getDefaultValueSchemaOffset() to find the offset of the default - // value within the schema, and can simply apply that offset at runtime. - // - // If the above does not make sense, you probably don't need this method. - - inline bool operator==(const Field& other) const; - inline bool operator!=(const Field& other) const { return !(*this == other); } - -private: - StructSchema parent; - uint index; - schema::Field::Reader proto; - - inline Field(StructSchema parent, uint index, schema::Field::Reader proto) - : parent(parent), index(index), proto(proto) {} - - friend class StructSchema; -}; - -kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field); - -class StructSchema::FieldList { -public: - FieldList() = default; // empty list - - inline uint size() const { return list.size(); } - inline Field operator[](uint index) const { return Field(parent, index, list[index]); } - - typedef _::IndexingIterator<const FieldList, Field> Iterator; - inline Iterator begin() const { return Iterator(this, 0); } - inline Iterator end() const { return Iterator(this, size()); } - -private: - StructSchema parent; - List<schema::Field>::Reader list; - - inline FieldList(StructSchema parent, List<schema::Field>::Reader list) - : parent(parent), list(list) {} - - friend class StructSchema; -}; - -class StructSchema::FieldSubset { -public: - FieldSubset() = default; // empty list - - inline uint size() const { return size_; } - inline Field operator[](uint index) const { - return Field(parent, indices[index], list[indices[index]]); - } - - typedef _::IndexingIterator<const FieldSubset, Field> Iterator; - inline Iterator begin() const { return Iterator(this, 0); } - inline Iterator end() const { return Iterator(this, size()); } - -private: - StructSchema parent; - List<schema::Field>::Reader list; - const uint16_t* indices; - uint size_; - - inline FieldSubset(StructSchema parent, List<schema::Field>::Reader list, - const uint16_t* indices, uint size) - : parent(parent), list(list), indices(indices), size_(size) {} - - friend class StructSchema; -}; - -// ------------------------------------------------------------------- - -class EnumSchema: public Schema { -public: - inline EnumSchema(): Schema(&_::NULL_ENUM_SCHEMA.defaultBrand) {} - - class Enumerant; - class EnumerantList; - - EnumerantList getEnumerants() const; - - kj::Maybe<Enumerant> findEnumerantByName(kj::StringPtr name) const; - - Enumerant getEnumerantByName(kj::StringPtr name) const; - // Like findEnumerantByName() but throws an exception on failure. - -private: - EnumSchema(Schema base): Schema(base) {} - template <typename T> static inline EnumSchema fromImpl() { - return EnumSchema(Schema(&_::rawBrandedSchema<T>())); - } - friend class Schema; - friend class Type; -}; - -class EnumSchema::Enumerant { -public: - Enumerant() = default; - - inline schema::Enumerant::Reader getProto() const { return proto; } - inline EnumSchema getContainingEnum() const { return parent; } - - inline uint16_t getOrdinal() const { return ordinal; } - inline uint getIndex() const { return ordinal; } - - inline bool operator==(const Enumerant& other) const; - inline bool operator!=(const Enumerant& other) const { return !(*this == other); } - -private: - EnumSchema parent; - uint16_t ordinal; - schema::Enumerant::Reader proto; - - inline Enumerant(EnumSchema parent, uint16_t ordinal, schema::Enumerant::Reader proto) - : parent(parent), ordinal(ordinal), proto(proto) {} - - friend class EnumSchema; -}; - -class EnumSchema::EnumerantList { -public: - EnumerantList() = default; // empty list - - inline uint size() const { return list.size(); } - inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); } - - typedef _::IndexingIterator<const EnumerantList, Enumerant> Iterator; - inline Iterator begin() const { return Iterator(this, 0); } - inline Iterator end() const { return Iterator(this, size()); } - -private: - EnumSchema parent; - List<schema::Enumerant>::Reader list; - - inline EnumerantList(EnumSchema parent, List<schema::Enumerant>::Reader list) - : parent(parent), list(list) {} - - friend class EnumSchema; -}; - -// ------------------------------------------------------------------- - -class InterfaceSchema: public Schema { -public: - inline InterfaceSchema(): Schema(&_::NULL_INTERFACE_SCHEMA.defaultBrand) {} - - class Method; - class MethodList; - - MethodList getMethods() const; - - kj::Maybe<Method> findMethodByName(kj::StringPtr name) const; - - Method getMethodByName(kj::StringPtr name) const; - // Like findMethodByName() but throws an exception on failure. - - class SuperclassList; - - SuperclassList getSuperclasses() const; - // Get the immediate superclasses of this type, after applying generics. - - bool extends(InterfaceSchema other) const; - // Returns true if `other` is a superclass of this interface (including if `other == *this`). - - kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const; - // Find the superclass of this interface with the given type ID. Returns null if the interface - // extends no such type. - -private: - InterfaceSchema(Schema base): Schema(base) {} - template <typename T> static inline InterfaceSchema fromImpl() { - return InterfaceSchema(Schema(&_::rawBrandedSchema<T>())); - } - friend class Schema; - friend class Type; - - kj::Maybe<Method> findMethodByName(kj::StringPtr name, uint& counter) const; - bool extends(InterfaceSchema other, uint& counter) const; - kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId, uint& counter) const; - // We protect against malicious schemas with large or cyclic hierarchies by cutting off the - // search when the counter reaches a threshold. -}; - -class InterfaceSchema::Method { -public: - Method() = default; - - inline schema::Method::Reader getProto() const { return proto; } - inline InterfaceSchema getContainingInterface() const { return parent; } - - inline uint16_t getOrdinal() const { return ordinal; } - inline uint getIndex() const { return ordinal; } - - StructSchema getParamType() const; - StructSchema getResultType() const; - // Get the parameter and result types, including substituting generic parameters. - - inline bool operator==(const Method& other) const; - inline bool operator!=(const Method& other) const { return !(*this == other); } - -private: - InterfaceSchema parent; - uint16_t ordinal; - schema::Method::Reader proto; - - inline Method(InterfaceSchema parent, uint16_t ordinal, - schema::Method::Reader proto) - : parent(parent), ordinal(ordinal), proto(proto) {} - - friend class InterfaceSchema; -}; - -class InterfaceSchema::MethodList { -public: - MethodList() = default; // empty list - - inline uint size() const { return list.size(); } - inline Method operator[](uint index) const { return Method(parent, index, list[index]); } - - typedef _::IndexingIterator<const MethodList, Method> Iterator; - inline Iterator begin() const { return Iterator(this, 0); } - inline Iterator end() const { return Iterator(this, size()); } - -private: - InterfaceSchema parent; - List<schema::Method>::Reader list; - - inline MethodList(InterfaceSchema parent, List<schema::Method>::Reader list) - : parent(parent), list(list) {} - - friend class InterfaceSchema; -}; - -class InterfaceSchema::SuperclassList { -public: - SuperclassList() = default; // empty list - - inline uint size() const { return list.size(); } - InterfaceSchema operator[](uint index) const; - - typedef _::IndexingIterator<const SuperclassList, InterfaceSchema> Iterator; - inline Iterator begin() const { return Iterator(this, 0); } - inline Iterator end() const { return Iterator(this, size()); } - -private: - InterfaceSchema parent; - List<schema::Superclass>::Reader list; - - inline SuperclassList(InterfaceSchema parent, List<schema::Superclass>::Reader list) - : parent(parent), list(list) {} - - friend class InterfaceSchema; -}; - -// ------------------------------------------------------------------- - -class ConstSchema: public Schema { - // Represents a constant declaration. - // - // `ConstSchema` can be implicitly cast to DynamicValue to read its value. - -public: - inline ConstSchema(): Schema(&_::NULL_CONST_SCHEMA.defaultBrand) {} - - template <typename T> - ReaderFor<T> as() const; - // Read the constant's value. This is a convenience method equivalent to casting the ConstSchema - // to a DynamicValue and then calling its `as<T>()` method. For dependency reasons, this method - // is defined in <capnp/dynamic.h>, which you must #include explicitly. - - uint32_t getValueSchemaOffset() const; - // Much like StructSchema::Field::getDefaultValueSchemaOffset(), if the constant has pointer - // type, this gets the offset from the beginning of the constant's schema node to a pointer - // representing the constant value. - - Type getType() const; - -private: - ConstSchema(Schema base): Schema(base) {} - friend class Schema; -}; - -// ------------------------------------------------------------------- - -class Type { -public: - struct BrandParameter { - uint64_t scopeId; - uint index; - }; - struct ImplicitParameter { - uint index; - }; - - inline Type(); - inline Type(schema::Type::Which primitive); - inline Type(StructSchema schema); - inline Type(EnumSchema schema); - inline Type(InterfaceSchema schema); - inline Type(ListSchema schema); - inline Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind); - inline Type(BrandParameter param); - inline Type(ImplicitParameter param); - - template <typename T> - inline static Type from(); - - inline schema::Type::Which which() const; - - StructSchema asStruct() const; - EnumSchema asEnum() const; - InterfaceSchema asInterface() const; - ListSchema asList() const; - // Each of these methods may only be called if which() returns the corresponding type. - - kj::Maybe<BrandParameter> getBrandParameter() const; - // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular - // AnyPointer and not a parameter. - - kj::Maybe<ImplicitParameter> getImplicitParameter() const; - // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular - // AnyPointer and not a parameter. "Implicit parameters" refer to type parameters on methods. - - inline schema::Type::AnyPointer::Unconstrained::Which whichAnyPointerKind() const; - // Only callable if which() returns ANY_POINTER. - - inline bool isVoid() const; - inline bool isBool() const; - inline bool isInt8() const; - inline bool isInt16() const; - inline bool isInt32() const; - inline bool isInt64() const; - inline bool isUInt8() const; - inline bool isUInt16() const; - inline bool isUInt32() const; - inline bool isUInt64() const; - inline bool isFloat32() const; - inline bool isFloat64() const; - inline bool isText() const; - inline bool isData() const; - inline bool isList() const; - inline bool isEnum() const; - inline bool isStruct() const; - inline bool isInterface() const; - inline bool isAnyPointer() const; - - bool operator==(const Type& other) const; - inline bool operator!=(const Type& other) const { return !(*this == other); } - - size_t hashCode() const; - - inline Type wrapInList(uint depth = 1) const; - // Return the Type formed by wrapping this type in List() `depth` times. - - inline Type(schema::Type::Which derived, const _::RawBrandedSchema* schema); - // For internal use. - -private: - schema::Type::Which baseType; // type not including applications of List() - uint8_t listDepth; // 0 for T, 1 for List(T), 2 for List(List(T)), ... - - bool isImplicitParam; - // If true, this refers to an implicit method parameter. baseType must be ANY_POINTER, scopeId - // must be zero, and paramIndex indicates the parameter index. - - union { - uint16_t paramIndex; - // If baseType is ANY_POINTER but this Type actually refers to a type parameter, this is the - // index of the parameter among the parameters at its scope, and `scopeId` below is the type ID - // of the scope where the parameter was defined. - - schema::Type::AnyPointer::Unconstrained::Which anyPointerKind; - // If scopeId is zero and isImplicitParam is false. - }; - - union { - const _::RawBrandedSchema* schema; // if type is struct, enum, interface... - uint64_t scopeId; // if type is AnyPointer but it's actually a type parameter... - }; - - Type(schema::Type::Which baseType, uint8_t listDepth, const _::RawBrandedSchema* schema) - : baseType(baseType), listDepth(listDepth), schema(schema) { - KJ_IREQUIRE(baseType != schema::Type::ANY_POINTER); - } - - void requireUsableAs(Type expected) const; - - friend class ListSchema; // only for requireUsableAs() -}; - -// ------------------------------------------------------------------- - -class ListSchema { - // ListSchema is a little different because list types are not described by schema nodes. So, - // ListSchema doesn't subclass Schema. - -public: - ListSchema() = default; - - static ListSchema of(schema::Type::Which primitiveType); - static ListSchema of(StructSchema elementType); - static ListSchema of(EnumSchema elementType); - static ListSchema of(InterfaceSchema elementType); - static ListSchema of(ListSchema elementType); - static ListSchema of(Type elementType); - // Construct the schema for a list of the given type. - - static ListSchema of(schema::Type::Reader elementType, Schema context) - KJ_DEPRECATED("Does not handle generics correctly."); - // DEPRECATED: This method cannot correctly account for generic type parameter bindings that - // may apply to the input type. Instead of using this method, use a method of the Schema API - // that corresponds to the exact kind of dependency. For example, to get a field type, use - // StructSchema::Field::getType(). - // - // Construct from an element type schema. Requires a context which can handle getDependency() - // requests for any type ID found in the schema. - - Type getElementType() const; - - inline schema::Type::Which whichElementType() const; - // Get the element type's "which()". ListSchema does not actually store a schema::Type::Reader - // describing the element type, but if it did, this would be equivalent to calling - // .getBody().which() on that type. - - StructSchema getStructElementType() const; - EnumSchema getEnumElementType() const; - InterfaceSchema getInterfaceElementType() const; - ListSchema getListElementType() const; - // Get the schema for complex element types. Each of these throws an exception if the element - // type is not of the requested kind. - - inline bool operator==(const ListSchema& other) const { return elementType == other.elementType; } - inline bool operator!=(const ListSchema& other) const { return elementType != other.elementType; } - - template <typename T> - void requireUsableAs() const; - -private: - Type elementType; - - inline explicit ListSchema(Type elementType): elementType(elementType) {} - - template <typename T> - struct FromImpl; - template <typename T> static inline ListSchema fromImpl() { - return FromImpl<T>::get(); - } - - void requireUsableAs(ListSchema expected) const; - - friend class Schema; -}; - -// ======================================================================================= -// inline implementation - -template <> inline schema::Type::Which Schema::from<Void>() { return schema::Type::VOID; } -template <> inline schema::Type::Which Schema::from<bool>() { return schema::Type::BOOL; } -template <> inline schema::Type::Which Schema::from<int8_t>() { return schema::Type::INT8; } -template <> inline schema::Type::Which Schema::from<int16_t>() { return schema::Type::INT16; } -template <> inline schema::Type::Which Schema::from<int32_t>() { return schema::Type::INT32; } -template <> inline schema::Type::Which Schema::from<int64_t>() { return schema::Type::INT64; } -template <> inline schema::Type::Which Schema::from<uint8_t>() { return schema::Type::UINT8; } -template <> inline schema::Type::Which Schema::from<uint16_t>() { return schema::Type::UINT16; } -template <> inline schema::Type::Which Schema::from<uint32_t>() { return schema::Type::UINT32; } -template <> inline schema::Type::Which Schema::from<uint64_t>() { return schema::Type::UINT64; } -template <> inline schema::Type::Which Schema::from<float>() { return schema::Type::FLOAT32; } -template <> inline schema::Type::Which Schema::from<double>() { return schema::Type::FLOAT64; } -template <> inline schema::Type::Which Schema::from<Text>() { return schema::Type::TEXT; } -template <> inline schema::Type::Which Schema::from<Data>() { return schema::Type::DATA; } - -inline Schema Schema::getDependency(uint64_t id) const { - return getDependency(id, 0); -} - -inline bool Schema::isBranded() const { - return raw != &raw->generic->defaultBrand; -} - -inline Schema Schema::getGeneric() const { - return Schema(&raw->generic->defaultBrand); -} - -template <typename T> -inline void Schema::requireUsableAs() const { - requireUsableAs(&_::rawSchema<T>()); -} - -inline bool StructSchema::Field::operator==(const Field& other) const { - return parent == other.parent && index == other.index; -} -inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const { - return parent == other.parent && ordinal == other.ordinal; -} -inline bool InterfaceSchema::Method::operator==(const Method& other) const { - return parent == other.parent && ordinal == other.ordinal; -} - -inline ListSchema ListSchema::of(StructSchema elementType) { - return ListSchema(Type(elementType)); -} -inline ListSchema ListSchema::of(EnumSchema elementType) { - return ListSchema(Type(elementType)); -} -inline ListSchema ListSchema::of(InterfaceSchema elementType) { - return ListSchema(Type(elementType)); -} -inline ListSchema ListSchema::of(ListSchema elementType) { - return ListSchema(Type(elementType)); -} -inline ListSchema ListSchema::of(Type elementType) { - return ListSchema(elementType); -} - -inline Type ListSchema::getElementType() const { - return elementType; -} - -inline schema::Type::Which ListSchema::whichElementType() const { - return elementType.which(); -} - -inline StructSchema ListSchema::getStructElementType() const { - return elementType.asStruct(); -} - -inline EnumSchema ListSchema::getEnumElementType() const { - return elementType.asEnum(); -} - -inline InterfaceSchema ListSchema::getInterfaceElementType() const { - return elementType.asInterface(); -} - -inline ListSchema ListSchema::getListElementType() const { - return elementType.asList(); -} - -template <typename T> -inline void ListSchema::requireUsableAs() const { - static_assert(kind<T>() == Kind::LIST, - "ListSchema::requireUsableAs<T>() requires T is a list type."); - requireUsableAs(Schema::from<T>()); -} - -inline void ListSchema::requireUsableAs(ListSchema expected) const { - elementType.requireUsableAs(expected.elementType); -} - -template <typename T> -struct ListSchema::FromImpl<List<T>> { - static inline ListSchema get() { return of(Schema::from<T>()); } -}; - -inline Type::Type(): baseType(schema::Type::VOID), listDepth(0), schema(nullptr) {} -inline Type::Type(schema::Type::Which primitive) - : baseType(primitive), listDepth(0), isImplicitParam(false) { - KJ_IREQUIRE(primitive != schema::Type::STRUCT && - primitive != schema::Type::ENUM && - primitive != schema::Type::INTERFACE && - primitive != schema::Type::LIST); - if (primitive == schema::Type::ANY_POINTER) { - scopeId = 0; - anyPointerKind = schema::Type::AnyPointer::Unconstrained::ANY_KIND; - } else { - schema = nullptr; - } -} -inline Type::Type(schema::Type::Which derived, const _::RawBrandedSchema* schema) - : baseType(derived), listDepth(0), isImplicitParam(false), schema(schema) { - KJ_IREQUIRE(derived == schema::Type::STRUCT || - derived == schema::Type::ENUM || - derived == schema::Type::INTERFACE); -} - -inline Type::Type(StructSchema schema) - : baseType(schema::Type::STRUCT), listDepth(0), schema(schema.raw) {} -inline Type::Type(EnumSchema schema) - : baseType(schema::Type::ENUM), listDepth(0), schema(schema.raw) {} -inline Type::Type(InterfaceSchema schema) - : baseType(schema::Type::INTERFACE), listDepth(0), schema(schema.raw) {} -inline Type::Type(ListSchema schema) - : Type(schema.getElementType()) { ++listDepth; } -inline Type::Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind) - : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false), - anyPointerKind(anyPointerKind), scopeId(0) {} -inline Type::Type(BrandParameter param) - : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false), - paramIndex(param.index), scopeId(param.scopeId) {} -inline Type::Type(ImplicitParameter param) - : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(true), - paramIndex(param.index), scopeId(0) {} - -inline schema::Type::Which Type::which() const { - return listDepth > 0 ? schema::Type::LIST : baseType; -} - -inline schema::Type::AnyPointer::Unconstrained::Which Type::whichAnyPointerKind() const { - KJ_IREQUIRE(baseType == schema::Type::ANY_POINTER); - return !isImplicitParam && scopeId == 0 ? anyPointerKind - : schema::Type::AnyPointer::Unconstrained::ANY_KIND; -} - -template <typename T> -inline Type Type::from() { return Type(Schema::from<T>()); } - -inline bool Type::isVoid () const { return baseType == schema::Type::VOID && listDepth == 0; } -inline bool Type::isBool () const { return baseType == schema::Type::BOOL && listDepth == 0; } -inline bool Type::isInt8 () const { return baseType == schema::Type::INT8 && listDepth == 0; } -inline bool Type::isInt16 () const { return baseType == schema::Type::INT16 && listDepth == 0; } -inline bool Type::isInt32 () const { return baseType == schema::Type::INT32 && listDepth == 0; } -inline bool Type::isInt64 () const { return baseType == schema::Type::INT64 && listDepth == 0; } -inline bool Type::isUInt8 () const { return baseType == schema::Type::UINT8 && listDepth == 0; } -inline bool Type::isUInt16 () const { return baseType == schema::Type::UINT16 && listDepth == 0; } -inline bool Type::isUInt32 () const { return baseType == schema::Type::UINT32 && listDepth == 0; } -inline bool Type::isUInt64 () const { return baseType == schema::Type::UINT64 && listDepth == 0; } -inline bool Type::isFloat32() const { return baseType == schema::Type::FLOAT32 && listDepth == 0; } -inline bool Type::isFloat64() const { return baseType == schema::Type::FLOAT64 && listDepth == 0; } -inline bool Type::isText () const { return baseType == schema::Type::TEXT && listDepth == 0; } -inline bool Type::isData () const { return baseType == schema::Type::DATA && listDepth == 0; } -inline bool Type::isList () const { return listDepth > 0; } -inline bool Type::isEnum () const { return baseType == schema::Type::ENUM && listDepth == 0; } -inline bool Type::isStruct () const { return baseType == schema::Type::STRUCT && listDepth == 0; } -inline bool Type::isInterface() const { - return baseType == schema::Type::INTERFACE && listDepth == 0; -} -inline bool Type::isAnyPointer() const { - return baseType == schema::Type::ANY_POINTER && listDepth == 0; -} - -inline Type Type::wrapInList(uint depth) const { - Type result = *this; - result.listDepth += depth; - return result; -} - -} // namespace capnp - -#endif // CAPNP_SCHEMA_H_ +// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors +// Licensed under the MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CAPNP_SCHEMA_H_ +#define CAPNP_SCHEMA_H_ + +#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) +#pragma GCC system_header +#endif + +#if CAPNP_LITE +#error "Reflection APIs, including this header, are not available in lite mode." +#endif + +#include <capnp/schema.capnp.h> + +namespace capnp { + +class Schema; +class StructSchema; +class EnumSchema; +class InterfaceSchema; +class ConstSchema; +class ListSchema; +class Type; + +template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; }; +template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Which Type; }; +template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Which Type; }; +template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; }; +template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; }; +template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; }; +template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; }; + +template <typename T> +using SchemaType = typename SchemaType_<T>::Type; +// SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct. + +namespace _ { // private +extern const RawSchema NULL_SCHEMA; +extern const RawSchema NULL_STRUCT_SCHEMA; +extern const RawSchema NULL_ENUM_SCHEMA; +extern const RawSchema NULL_INTERFACE_SCHEMA; +extern const RawSchema NULL_CONST_SCHEMA; +// The schema types default to these null (empty) schemas in case of error, especially when +// exceptions are disabled. +} // namespace _ (private) + +class Schema { + // Convenience wrapper around capnp::schema::Node. + +public: + inline Schema(): raw(&_::NULL_SCHEMA.defaultBrand) {} + + template <typename T> + static inline SchemaType<T> from() { return SchemaType<T>::template fromImpl<T>(); } + // Get the Schema for a particular compiled-in type. + + schema::Node::Reader getProto() const; + // Get the underlying Cap'n Proto representation of the schema node. (Note that this accessor + // has performance comparable to accessors of struct-typed fields on Reader classes.) + + kj::ArrayPtr<const word> asUncheckedMessage() const; + // Get the encoded schema node content as a single message segment. It is safe to read as an + // unchecked message. + + Schema getDependency(uint64_t id) const KJ_DEPRECATED("Does not handle generics correctly."); + // DEPRECATED: This method cannot correctly account for generic type parameter bindings that + // may apply to the dependency. Instead of using this method, use a method of the Schema API + // that corresponds to the exact kind of dependency. For example, to get a field type, use + // StructSchema::Field::getType(). + // + // Gets the Schema for one of this Schema's dependencies. For example, if this Schema is for a + // struct, you could look up the schema for one of its fields' types. Throws an exception if this + // schema doesn't actually depend on the given id. + // + // Note that not all type IDs found in the schema node are considered "dependencies" -- only the + // ones that are needed to implement the dynamic API are. That includes: + // - Field types. + // - Group types. + // - scopeId for group nodes, but NOT otherwise. + // - Method parameter and return types. + // + // The following are NOT considered dependencies: + // - Nested nodes. + // - scopeId for a non-group node. + // - Annotations. + // + // To obtain schemas for those, you would need a SchemaLoader. + + bool isBranded() const; + // Returns true if this schema represents a non-default parameterization of this type. + + Schema getGeneric() const; + // Get the version of this schema with any brands removed. + + class BrandArgumentList; + BrandArgumentList getBrandArgumentsAtScope(uint64_t scopeId) const; + // Gets the values bound to the brand parameters at the given scope. + + StructSchema asStruct() const; + EnumSchema asEnum() const; + InterfaceSchema asInterface() const; + ConstSchema asConst() const; + // Cast the Schema to a specific type. Throws an exception if the type doesn't match. Use + // getProto() to determine type, e.g. getProto().isStruct(). + + inline bool operator==(const Schema& other) const { return raw == other.raw; } + inline bool operator!=(const Schema& other) const { return raw != other.raw; } + // Determine whether two Schemas are wrapping the exact same underlying data, by identity. If + // you want to check if two Schemas represent the same type (but possibly different versions of + // it), compare their IDs instead. + + template <typename T> + void requireUsableAs() const; + // Throws an exception if a value with this Schema cannot safely be cast to a native value of + // the given type. This passes if either: + // - *this == from<T>() + // - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and + // loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader. + + kj::StringPtr getShortDisplayName() const; + // Get the short version of the node's display name. + +private: + const _::RawBrandedSchema* raw; + + inline explicit Schema(const _::RawBrandedSchema* raw): raw(raw) { + KJ_IREQUIRE(raw->lazyInitializer == nullptr, + "Must call ensureInitialized() on RawSchema before constructing Schema."); + } + + template <typename T> static inline Schema fromImpl() { + return Schema(&_::rawSchema<T>()); + } + + void requireUsableAs(const _::RawSchema* expected) const; + + uint32_t getSchemaOffset(const schema::Value::Reader& value) const; + + Type getBrandBinding(uint64_t scopeId, uint index) const; + // Look up the binding for a brand parameter used by this Schema. Returns `AnyPointer` if the + // parameter is not bound. + // + // TODO(someday): Public interface for iterating over all bindings? + + Schema getDependency(uint64_t id, uint location) const; + // Look up schema for a particular dependency of this schema. `location` is the dependency + // location number as defined in _::RawBrandedSchema. + + Type interpretType(schema::Type::Reader proto, uint location) const; + // Interpret a schema::Type in the given location within the schema, compiling it into a + // Type object. + + friend class StructSchema; + friend class EnumSchema; + friend class InterfaceSchema; + friend class ConstSchema; + friend class ListSchema; + friend class SchemaLoader; + friend class Type; + friend kj::StringTree _::structString( + _::StructReader reader, const _::RawBrandedSchema& schema); + friend kj::String _::enumString(uint16_t value, const _::RawBrandedSchema& schema); +}; + +kj::StringPtr KJ_STRINGIFY(const Schema& schema); + +class Schema::BrandArgumentList { + // A list of generic parameter bindings for parameters of some particular type. Note that since + // parameters on an outer type apply to all inner types as well, a deeply-nested type can have + // multiple BrandArgumentLists that apply to it. + // + // A BrandArgumentList only represents the arguments that the client of the type specified. Since + // new parameters can be added over time, this list may not cover all defined parameters for the + // type. Missing parameters should be treated as AnyPointer. This class's implementation of + // operator[] already does this for you; out-of-bounds access will safely return AnyPointer. + +public: + inline BrandArgumentList(): scopeId(0), size_(0), bindings(nullptr) {} + + inline uint size() const { return size_; } + Type operator[](uint index) const; + + typedef _::IndexingIterator<const BrandArgumentList, Type> Iterator; + inline Iterator begin() const { return Iterator(this, 0); } + inline Iterator end() const { return Iterator(this, size()); } + +private: + uint64_t scopeId; + uint size_; + bool isUnbound; + const _::RawBrandedSchema::Binding* bindings; + + inline BrandArgumentList(uint64_t scopeId, bool isUnbound) + : scopeId(scopeId), size_(0), isUnbound(isUnbound), bindings(nullptr) {} + inline BrandArgumentList(uint64_t scopeId, uint size, + const _::RawBrandedSchema::Binding* bindings) + : scopeId(scopeId), size_(size), isUnbound(false), bindings(bindings) {} + + friend class Schema; +}; + +// ------------------------------------------------------------------- + +class StructSchema: public Schema { +public: + inline StructSchema(): Schema(&_::NULL_STRUCT_SCHEMA.defaultBrand) {} + + class Field; + class FieldList; + class FieldSubset; + + FieldList getFields() const; + // List top-level fields of this struct. This list will contain top-level groups (including + // named unions) but not the members of those groups. The list does, however, contain the + // members of the unnamed union, if there is one. + + FieldSubset getUnionFields() const; + // If the field contains an unnamed union, get a list of fields in the union, ordered by + // ordinal. Since discriminant values are assigned sequentially by ordinal, you may index this + // list by discriminant value. + + FieldSubset getNonUnionFields() const; + // Get the fields of this struct which are not in an unnamed union, ordered by ordinal. + + kj::Maybe<Field> findFieldByName(kj::StringPtr name) const; + // Find the field with the given name, or return null if there is no such field. If the struct + // contains an unnamed union, then this will find fields of that union in addition to fields + // of the outer struct, since they exist in the same namespace. It will not, however, find + // members of groups (including named unions) -- you must first look up the group itself, + // then dig into its type. + + Field getFieldByName(kj::StringPtr name) const; + // Like findFieldByName() but throws an exception on failure. + + kj::Maybe<Field> getFieldByDiscriminant(uint16_t discriminant) const; + // Finds the field whose `discriminantValue` is equal to the given value, or returns null if + // there is no such field. (If the schema does not represent a union or a struct containing + // an unnamed union, then this always returns null.) + +private: + StructSchema(Schema base): Schema(base) {} + template <typename T> static inline StructSchema fromImpl() { + return StructSchema(Schema(&_::rawBrandedSchema<T>())); + } + friend class Schema; + friend class Type; +}; + +class StructSchema::Field { +public: + Field() = default; + + inline schema::Field::Reader getProto() const { return proto; } + inline StructSchema getContainingStruct() const { return parent; } + + inline uint getIndex() const { return index; } + // Get the index of this field within the containing struct or union. + + Type getType() const; + // Get the type of this field. Note that this is preferred over getProto().getType() as this + // method will apply generics. + + uint32_t getDefaultValueSchemaOffset() const; + // For struct, list, and object fields, returns the offset, in words, within the first segment of + // the struct's schema, where this field's default value pointer is located. The schema is + // always stored as a single-segment unchecked message, which in turn means that the default + // value pointer itself can be treated as the root of an unchecked message -- if you know where + // to find it, which is what this method helps you with. + // + // For blobs, returns the offset of the beginning of the blob's content within the first segment + // of the struct's schema. + // + // This is primarily useful for code generators. The C++ code generator, for example, embeds + // the entire schema as a raw word array within the generated code. Of course, to implement + // field accessors, it needs access to those fields' default values. Embedding separate copies + // of those default values would be redundant since they are already included in the schema, but + // seeking through the schema at runtime to find the default values would be ugly. Instead, + // the code generator can use getDefaultValueSchemaOffset() to find the offset of the default + // value within the schema, and can simply apply that offset at runtime. + // + // If the above does not make sense, you probably don't need this method. + + inline bool operator==(const Field& other) const; + inline bool operator!=(const Field& other) const { return !(*this == other); } + +private: + StructSchema parent; + uint index; + schema::Field::Reader proto; + + inline Field(StructSchema parent, uint index, schema::Field::Reader proto) + : parent(parent), index(index), proto(proto) {} + + friend class StructSchema; +}; + +kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field); + +class StructSchema::FieldList { +public: + FieldList() = default; // empty list + + inline uint size() const { return list.size(); } + inline Field operator[](uint index) const { return Field(parent, index, list[index]); } + + typedef _::IndexingIterator<const FieldList, Field> Iterator; + inline Iterator begin() const { return Iterator(this, 0); } + inline Iterator end() const { return Iterator(this, size()); } + +private: + StructSchema parent; + List<schema::Field>::Reader list; + + inline FieldList(StructSchema parent, List<schema::Field>::Reader list) + : parent(parent), list(list) {} + + friend class StructSchema; +}; + +class StructSchema::FieldSubset { +public: + FieldSubset() = default; // empty list + + inline uint size() const { return size_; } + inline Field operator[](uint index) const { + return Field(parent, indices[index], list[indices[index]]); + } + + typedef _::IndexingIterator<const FieldSubset, Field> Iterator; + inline Iterator begin() const { return Iterator(this, 0); } + inline Iterator end() const { return Iterator(this, size()); } + +private: + StructSchema parent; + List<schema::Field>::Reader list; + const uint16_t* indices; + uint size_; + + inline FieldSubset(StructSchema parent, List<schema::Field>::Reader list, + const uint16_t* indices, uint size) + : parent(parent), list(list), indices(indices), size_(size) {} + + friend class StructSchema; +}; + +// ------------------------------------------------------------------- + +class EnumSchema: public Schema { +public: + inline EnumSchema(): Schema(&_::NULL_ENUM_SCHEMA.defaultBrand) {} + + class Enumerant; + class EnumerantList; + + EnumerantList getEnumerants() const; + + kj::Maybe<Enumerant> findEnumerantByName(kj::StringPtr name) const; + + Enumerant getEnumerantByName(kj::StringPtr name) const; + // Like findEnumerantByName() but throws an exception on failure. + +private: + EnumSchema(Schema base): Schema(base) {} + template <typename T> static inline EnumSchema fromImpl() { + return EnumSchema(Schema(&_::rawBrandedSchema<T>())); + } + friend class Schema; + friend class Type; +}; + +class EnumSchema::Enumerant { +public: + Enumerant() = default; + + inline schema::Enumerant::Reader getProto() const { return proto; } + inline EnumSchema getContainingEnum() const { return parent; } + + inline uint16_t getOrdinal() const { return ordinal; } + inline uint getIndex() const { return ordinal; } + + inline bool operator==(const Enumerant& other) const; + inline bool operator!=(const Enumerant& other) const { return !(*this == other); } + +private: + EnumSchema parent; + uint16_t ordinal; + schema::Enumerant::Reader proto; + + inline Enumerant(EnumSchema parent, uint16_t ordinal, schema::Enumerant::Reader proto) + : parent(parent), ordinal(ordinal), proto(proto) {} + + friend class EnumSchema; +}; + +class EnumSchema::EnumerantList { +public: + EnumerantList() = default; // empty list + + inline uint size() const { return list.size(); } + inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); } + + typedef _::IndexingIterator<const EnumerantList, Enumerant> Iterator; + inline Iterator begin() const { return Iterator(this, 0); } + inline Iterator end() const { return Iterator(this, size()); } + +private: + EnumSchema parent; + List<schema::Enumerant>::Reader list; + + inline EnumerantList(EnumSchema parent, List<schema::Enumerant>::Reader list) + : parent(parent), list(list) {} + + friend class EnumSchema; +}; + +// ------------------------------------------------------------------- + +class InterfaceSchema: public Schema { +public: + inline InterfaceSchema(): Schema(&_::NULL_INTERFACE_SCHEMA.defaultBrand) {} + + class Method; + class MethodList; + + MethodList getMethods() const; + + kj::Maybe<Method> findMethodByName(kj::StringPtr name) const; + + Method getMethodByName(kj::StringPtr name) const; + // Like findMethodByName() but throws an exception on failure. + + class SuperclassList; + + SuperclassList getSuperclasses() const; + // Get the immediate superclasses of this type, after applying generics. + + bool extends(InterfaceSchema other) const; + // Returns true if `other` is a superclass of this interface (including if `other == *this`). + + kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const; + // Find the superclass of this interface with the given type ID. Returns null if the interface + // extends no such type. + +private: + InterfaceSchema(Schema base): Schema(base) {} + template <typename T> static inline InterfaceSchema fromImpl() { + return InterfaceSchema(Schema(&_::rawBrandedSchema<T>())); + } + friend class Schema; + friend class Type; + + kj::Maybe<Method> findMethodByName(kj::StringPtr name, uint& counter) const; + bool extends(InterfaceSchema other, uint& counter) const; + kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId, uint& counter) const; + // We protect against malicious schemas with large or cyclic hierarchies by cutting off the + // search when the counter reaches a threshold. +}; + +class InterfaceSchema::Method { +public: + Method() = default; + + inline schema::Method::Reader getProto() const { return proto; } + inline InterfaceSchema getContainingInterface() const { return parent; } + + inline uint16_t getOrdinal() const { return ordinal; } + inline uint getIndex() const { return ordinal; } + + StructSchema getParamType() const; + StructSchema getResultType() const; + // Get the parameter and result types, including substituting generic parameters. + + inline bool operator==(const Method& other) const; + inline bool operator!=(const Method& other) const { return !(*this == other); } + +private: + InterfaceSchema parent; + uint16_t ordinal; + schema::Method::Reader proto; + + inline Method(InterfaceSchema parent, uint16_t ordinal, + schema::Method::Reader proto) + : parent(parent), ordinal(ordinal), proto(proto) {} + + friend class InterfaceSchema; +}; + +class InterfaceSchema::MethodList { +public: + MethodList() = default; // empty list + + inline uint size() const { return list.size(); } + inline Method operator[](uint index) const { return Method(parent, index, list[index]); } + + typedef _::IndexingIterator<const MethodList, Method> Iterator; + inline Iterator begin() const { return Iterator(this, 0); } + inline Iterator end() const { return Iterator(this, size()); } + +private: + InterfaceSchema parent; + List<schema::Method>::Reader list; + + inline MethodList(InterfaceSchema parent, List<schema::Method>::Reader list) + : parent(parent), list(list) {} + + friend class InterfaceSchema; +}; + +class InterfaceSchema::SuperclassList { +public: + SuperclassList() = default; // empty list + + inline uint size() const { return list.size(); } + InterfaceSchema operator[](uint index) const; + + typedef _::IndexingIterator<const SuperclassList, InterfaceSchema> Iterator; + inline Iterator begin() const { return Iterator(this, 0); } + inline Iterator end() const { return Iterator(this, size()); } + +private: + InterfaceSchema parent; + List<schema::Superclass>::Reader list; + + inline SuperclassList(InterfaceSchema parent, List<schema::Superclass>::Reader list) + : parent(parent), list(list) {} + + friend class InterfaceSchema; +}; + +// ------------------------------------------------------------------- + +class ConstSchema: public Schema { + // Represents a constant declaration. + // + // `ConstSchema` can be implicitly cast to DynamicValue to read its value. + +public: + inline ConstSchema(): Schema(&_::NULL_CONST_SCHEMA.defaultBrand) {} + + template <typename T> + ReaderFor<T> as() const; + // Read the constant's value. This is a convenience method equivalent to casting the ConstSchema + // to a DynamicValue and then calling its `as<T>()` method. For dependency reasons, this method + // is defined in <capnp/dynamic.h>, which you must #include explicitly. + + uint32_t getValueSchemaOffset() const; + // Much like StructSchema::Field::getDefaultValueSchemaOffset(), if the constant has pointer + // type, this gets the offset from the beginning of the constant's schema node to a pointer + // representing the constant value. + + Type getType() const; + +private: + ConstSchema(Schema base): Schema(base) {} + friend class Schema; +}; + +// ------------------------------------------------------------------- + +class Type { +public: + struct BrandParameter { + uint64_t scopeId; + uint index; + }; + struct ImplicitParameter { + uint index; + }; + + inline Type(); + inline Type(schema::Type::Which primitive); + inline Type(StructSchema schema); + inline Type(EnumSchema schema); + inline Type(InterfaceSchema schema); + inline Type(ListSchema schema); + inline Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind); + inline Type(BrandParameter param); + inline Type(ImplicitParameter param); + + template <typename T> + inline static Type from(); + + inline schema::Type::Which which() const; + + StructSchema asStruct() const; + EnumSchema asEnum() const; + InterfaceSchema asInterface() const; + ListSchema asList() const; + // Each of these methods may only be called if which() returns the corresponding type. + + kj::Maybe<BrandParameter> getBrandParameter() const; + // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular + // AnyPointer and not a parameter. + + kj::Maybe<ImplicitParameter> getImplicitParameter() const; + // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular + // AnyPointer and not a parameter. "Implicit parameters" refer to type parameters on methods. + + inline schema::Type::AnyPointer::Unconstrained::Which whichAnyPointerKind() const; + // Only callable if which() returns ANY_POINTER. + + inline bool isVoid() const; + inline bool isBool() const; + inline bool isInt8() const; + inline bool isInt16() const; + inline bool isInt32() const; + inline bool isInt64() const; + inline bool isUInt8() const; + inline bool isUInt16() const; + inline bool isUInt32() const; + inline bool isUInt64() const; + inline bool isFloat32() const; + inline bool isFloat64() const; + inline bool isText() const; + inline bool isData() const; + inline bool isList() const; + inline bool isEnum() const; + inline bool isStruct() const; + inline bool isInterface() const; + inline bool isAnyPointer() const; + + bool operator==(const Type& other) const; + inline bool operator!=(const Type& other) const { return !(*this == other); } + + size_t hashCode() const; + + inline Type wrapInList(uint depth = 1) const; + // Return the Type formed by wrapping this type in List() `depth` times. + + inline Type(schema::Type::Which derived, const _::RawBrandedSchema* schema); + // For internal use. + +private: + schema::Type::Which baseType; // type not including applications of List() + uint8_t listDepth; // 0 for T, 1 for List(T), 2 for List(List(T)), ... + + bool isImplicitParam; + // If true, this refers to an implicit method parameter. baseType must be ANY_POINTER, scopeId + // must be zero, and paramIndex indicates the parameter index. + + union { + uint16_t paramIndex; + // If baseType is ANY_POINTER but this Type actually refers to a type parameter, this is the + // index of the parameter among the parameters at its scope, and `scopeId` below is the type ID + // of the scope where the parameter was defined. + + schema::Type::AnyPointer::Unconstrained::Which anyPointerKind; + // If scopeId is zero and isImplicitParam is false. + }; + + union { + const _::RawBrandedSchema* schema; // if type is struct, enum, interface... + uint64_t scopeId; // if type is AnyPointer but it's actually a type parameter... + }; + + Type(schema::Type::Which baseType, uint8_t listDepth, const _::RawBrandedSchema* schema) + : baseType(baseType), listDepth(listDepth), schema(schema) { + KJ_IREQUIRE(baseType != schema::Type::ANY_POINTER); + } + + void requireUsableAs(Type expected) const; + + friend class ListSchema; // only for requireUsableAs() +}; + +// ------------------------------------------------------------------- + +class ListSchema { + // ListSchema is a little different because list types are not described by schema nodes. So, + // ListSchema doesn't subclass Schema. + +public: + ListSchema() = default; + + static ListSchema of(schema::Type::Which primitiveType); + static ListSchema of(StructSchema elementType); + static ListSchema of(EnumSchema elementType); + static ListSchema of(InterfaceSchema elementType); + static ListSchema of(ListSchema elementType); + static ListSchema of(Type elementType); + // Construct the schema for a list of the given type. + + static ListSchema of(schema::Type::Reader elementType, Schema context) + KJ_DEPRECATED("Does not handle generics correctly."); + // DEPRECATED: This method cannot correctly account for generic type parameter bindings that + // may apply to the input type. Instead of using this method, use a method of the Schema API + // that corresponds to the exact kind of dependency. For example, to get a field type, use + // StructSchema::Field::getType(). + // + // Construct from an element type schema. Requires a context which can handle getDependency() + // requests for any type ID found in the schema. + + Type getElementType() const; + + inline schema::Type::Which whichElementType() const; + // Get the element type's "which()". ListSchema does not actually store a schema::Type::Reader + // describing the element type, but if it did, this would be equivalent to calling + // .getBody().which() on that type. + + StructSchema getStructElementType() const; + EnumSchema getEnumElementType() const; + InterfaceSchema getInterfaceElementType() const; + ListSchema getListElementType() const; + // Get the schema for complex element types. Each of these throws an exception if the element + // type is not of the requested kind. + + inline bool operator==(const ListSchema& other) const { return elementType == other.elementType; } + inline bool operator!=(const ListSchema& other) const { return elementType != other.elementType; } + + template <typename T> + void requireUsableAs() const; + +private: + Type elementType; + + inline explicit ListSchema(Type elementType): elementType(elementType) {} + + template <typename T> + struct FromImpl; + template <typename T> static inline ListSchema fromImpl() { + return FromImpl<T>::get(); + } + + void requireUsableAs(ListSchema expected) const; + + friend class Schema; +}; + +// ======================================================================================= +// inline implementation + +template <> inline schema::Type::Which Schema::from<Void>() { return schema::Type::VOID; } +template <> inline schema::Type::Which Schema::from<bool>() { return schema::Type::BOOL; } +template <> inline schema::Type::Which Schema::from<int8_t>() { return schema::Type::INT8; } +template <> inline schema::Type::Which Schema::from<int16_t>() { return schema::Type::INT16; } +template <> inline schema::Type::Which Schema::from<int32_t>() { return schema::Type::INT32; } +template <> inline schema::Type::Which Schema::from<int64_t>() { return schema::Type::INT64; } +template <> inline schema::Type::Which Schema::from<uint8_t>() { return schema::Type::UINT8; } +template <> inline schema::Type::Which Schema::from<uint16_t>() { return schema::Type::UINT16; } +template <> inline schema::Type::Which Schema::from<uint32_t>() { return schema::Type::UINT32; } +template <> inline schema::Type::Which Schema::from<uint64_t>() { return schema::Type::UINT64; } +template <> inline schema::Type::Which Schema::from<float>() { return schema::Type::FLOAT32; } +template <> inline schema::Type::Which Schema::from<double>() { return schema::Type::FLOAT64; } +template <> inline schema::Type::Which Schema::from<Text>() { return schema::Type::TEXT; } +template <> inline schema::Type::Which Schema::from<Data>() { return schema::Type::DATA; } + +inline Schema Schema::getDependency(uint64_t id) const { + return getDependency(id, 0); +} + +inline bool Schema::isBranded() const { + return raw != &raw->generic->defaultBrand; +} + +inline Schema Schema::getGeneric() const { + return Schema(&raw->generic->defaultBrand); +} + +template <typename T> +inline void Schema::requireUsableAs() const { + requireUsableAs(&_::rawSchema<T>()); +} + +inline bool StructSchema::Field::operator==(const Field& other) const { + return parent == other.parent && index == other.index; +} +inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const { + return parent == other.parent && ordinal == other.ordinal; +} +inline bool InterfaceSchema::Method::operator==(const Method& other) const { + return parent == other.parent && ordinal == other.ordinal; +} + +inline ListSchema ListSchema::of(StructSchema elementType) { + return ListSchema(Type(elementType)); +} +inline ListSchema ListSchema::of(EnumSchema elementType) { + return ListSchema(Type(elementType)); +} +inline ListSchema ListSchema::of(InterfaceSchema elementType) { + return ListSchema(Type(elementType)); +} +inline ListSchema ListSchema::of(ListSchema elementType) { + return ListSchema(Type(elementType)); +} +inline ListSchema ListSchema::of(Type elementType) { + return ListSchema(elementType); +} + +inline Type ListSchema::getElementType() const { + return elementType; +} + +inline schema::Type::Which ListSchema::whichElementType() const { + return elementType.which(); +} + +inline StructSchema ListSchema::getStructElementType() const { + return elementType.asStruct(); +} + +inline EnumSchema ListSchema::getEnumElementType() const { + return elementType.asEnum(); +} + +inline InterfaceSchema ListSchema::getInterfaceElementType() const { + return elementType.asInterface(); +} + +inline ListSchema ListSchema::getListElementType() const { + return elementType.asList(); +} + +template <typename T> +inline void ListSchema::requireUsableAs() const { + static_assert(kind<T>() == Kind::LIST, + "ListSchema::requireUsableAs<T>() requires T is a list type."); + requireUsableAs(Schema::from<T>()); +} + +inline void ListSchema::requireUsableAs(ListSchema expected) const { + elementType.requireUsableAs(expected.elementType); +} + +template <typename T> +struct ListSchema::FromImpl<List<T>> { + static inline ListSchema get() { return of(Schema::from<T>()); } +}; + +inline Type::Type(): baseType(schema::Type::VOID), listDepth(0), schema(nullptr) {} +inline Type::Type(schema::Type::Which primitive) + : baseType(primitive), listDepth(0), isImplicitParam(false) { + KJ_IREQUIRE(primitive != schema::Type::STRUCT && + primitive != schema::Type::ENUM && + primitive != schema::Type::INTERFACE && + primitive != schema::Type::LIST); + if (primitive == schema::Type::ANY_POINTER) { + scopeId = 0; + anyPointerKind = schema::Type::AnyPointer::Unconstrained::ANY_KIND; + } else { + schema = nullptr; + } +} +inline Type::Type(schema::Type::Which derived, const _::RawBrandedSchema* schema) + : baseType(derived), listDepth(0), isImplicitParam(false), schema(schema) { + KJ_IREQUIRE(derived == schema::Type::STRUCT || + derived == schema::Type::ENUM || + derived == schema::Type::INTERFACE); +} + +inline Type::Type(StructSchema schema) + : baseType(schema::Type::STRUCT), listDepth(0), schema(schema.raw) {} +inline Type::Type(EnumSchema schema) + : baseType(schema::Type::ENUM), listDepth(0), schema(schema.raw) {} +inline Type::Type(InterfaceSchema schema) + : baseType(schema::Type::INTERFACE), listDepth(0), schema(schema.raw) {} +inline Type::Type(ListSchema schema) + : Type(schema.getElementType()) { ++listDepth; } +inline Type::Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind) + : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false), + anyPointerKind(anyPointerKind), scopeId(0) {} +inline Type::Type(BrandParameter param) + : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false), + paramIndex(param.index), scopeId(param.scopeId) {} +inline Type::Type(ImplicitParameter param) + : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(true), + paramIndex(param.index), scopeId(0) {} + +inline schema::Type::Which Type::which() const { + return listDepth > 0 ? schema::Type::LIST : baseType; +} + +inline schema::Type::AnyPointer::Unconstrained::Which Type::whichAnyPointerKind() const { + KJ_IREQUIRE(baseType == schema::Type::ANY_POINTER); + return !isImplicitParam && scopeId == 0 ? anyPointerKind + : schema::Type::AnyPointer::Unconstrained::ANY_KIND; +} + +template <typename T> +inline Type Type::from() { return Type(Schema::from<T>()); } + +inline bool Type::isVoid () const { return baseType == schema::Type::VOID && listDepth == 0; } +inline bool Type::isBool () const { return baseType == schema::Type::BOOL && listDepth == 0; } +inline bool Type::isInt8 () const { return baseType == schema::Type::INT8 && listDepth == 0; } +inline bool Type::isInt16 () const { return baseType == schema::Type::INT16 && listDepth == 0; } +inline bool Type::isInt32 () const { return baseType == schema::Type::INT32 && listDepth == 0; } +inline bool Type::isInt64 () const { return baseType == schema::Type::INT64 && listDepth == 0; } +inline bool Type::isUInt8 () const { return baseType == schema::Type::UINT8 && listDepth == 0; } +inline bool Type::isUInt16 () const { return baseType == schema::Type::UINT16 && listDepth == 0; } +inline bool Type::isUInt32 () const { return baseType == schema::Type::UINT32 && listDepth == 0; } +inline bool Type::isUInt64 () const { return baseType == schema::Type::UINT64 && listDepth == 0; } +inline bool Type::isFloat32() const { return baseType == schema::Type::FLOAT32 && listDepth == 0; } +inline bool Type::isFloat64() const { return baseType == schema::Type::FLOAT64 && listDepth == 0; } +inline bool Type::isText () const { return baseType == schema::Type::TEXT && listDepth == 0; } +inline bool Type::isData () const { return baseType == schema::Type::DATA && listDepth == 0; } +inline bool Type::isList () const { return listDepth > 0; } +inline bool Type::isEnum () const { return baseType == schema::Type::ENUM && listDepth == 0; } +inline bool Type::isStruct () const { return baseType == schema::Type::STRUCT && listDepth == 0; } +inline bool Type::isInterface() const { + return baseType == schema::Type::INTERFACE && listDepth == 0; +} +inline bool Type::isAnyPointer() const { + return baseType == schema::Type::ANY_POINTER && listDepth == 0; +} + +inline Type Type::wrapInList(uint depth) const { + Type result = *this; + result.listDepth += depth; + return result; +} + +} // namespace capnp + +#endif // CAPNP_SCHEMA_H_