blob: 5cc20b5e2eaf56e7a55e6a887ed3c1a7e4e7088a [file] [log] [blame]
// 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.
#pragma once
#if CAPNP_LITE
#error "Reflection APIs, including this header, are not available in lite mode."
#endif
#undef CONST
// For some ridiculous reason, Windows defines CONST to const. We have an enum value called CONST
// in schema.capnp.h, so if this is defined, compilation is gonna fail. So we undef it because
// that seems strictly better than failing entirely. But this could cause trouble for people later
// on if they, say, include windows.h, then include schema.h, then include another windows API
// header that uses CONST. I suppose they may have to re-#define CONST in between, or change the
// header ordering. Sorry.
//
// Please don't file a bug report telling us to change our enum naming style. You are at least
// seven years too late.
#include <capnp/schema.capnp.h>
#include <kj/hash.h>
#include <kj/windows-sanity.h> // work-around macro conflict with `VOID`
CAPNP_BEGIN_HEADER
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 CAPNP_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.
inline uint hashCode() const { return kj::hashCode(raw); }
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.
const kj::StringPtr getUnqualifiedName() const;
// Get the display name "nickname" of this node minus the prefix
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.)
bool isStreamResult() const;
// Convenience method to check if this is the result type of a streaming RPC method.
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); }
inline uint hashCode() const;
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); }
inline uint hashCode() const;
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; }
bool isStreaming() const { return getResultType().isStreamResult(); }
// Check if this is a streaming method.
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); }
inline uint hashCode() const;
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();
template <typename T>
inline static Type from(T&& value);
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); }
uint 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;
template <typename T, Kind k>
struct FromValueImpl;
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)
CAPNP_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 uint StructSchema::Field::hashCode() const {
return kj::hashCode(parent, index);
}
inline uint EnumSchema::Enumerant::hashCode() const {
return kj::hashCode(parent, ordinal);
}
inline uint InterfaceSchema::Method::hashCode() const {
return kj::hashCode(parent, 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>()); }
template <typename T, Kind k>
struct Type::FromValueImpl {
template <typename U>
static inline Type type(U&& value) {
return Type::from<T>();
}
};
template <typename T>
struct Type::FromValueImpl<T, Kind::OTHER> {
template <typename U>
static inline Type type(U&& value) {
// All dynamic types have getSchema().
return value.getSchema();
}
};
template <typename T>
inline Type Type::from(T&& value) {
typedef FromAny<kj::Decay<T>> Base;
return Type::FromValueImpl<Base, kind<Base>()>::type(kj::fwd<T>(value));
}
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
CAPNP_END_HEADER