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