| 1 | // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors |
| 2 | // Licensed under the MIT License: |
| 3 | // |
| 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
| 5 | // of this software and associated documentation files (the "Software"), to deal |
| 6 | // in the Software without restriction, including without limitation the rights |
| 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 8 | // copies of the Software, and to permit persons to whom the Software is |
| 9 | // furnished to do so, subject to the following conditions: |
| 10 | // |
| 11 | // The above copyright notice and this permission notice shall be included in |
| 12 | // all copies or substantial portions of the Software. |
| 13 | // |
| 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 20 | // THE SOFTWARE. |
| 21 | |
| 22 | #pragma once |
| 23 | |
| 24 | #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) |
| 25 | #pragma GCC system_header |
| 26 | #endif |
| 27 | |
| 28 | #if CAPNP_LITE |
| 29 | #error "Reflection APIs, including this header, are not available in lite mode." |
| 30 | #endif |
| 31 | |
| 32 | #include <capnp/schema.capnp.h> |
| 33 | #include <kj/hash.h> |
| 34 | #include <kj/windows-sanity.h> // work-around macro conflict with `VOID` |
| 35 | |
| 36 | namespace capnp { |
| 37 | |
| 38 | class Schema; |
| 39 | class StructSchema; |
| 40 | class EnumSchema; |
| 41 | class InterfaceSchema; |
| 42 | class ConstSchema; |
| 43 | class ListSchema; |
| 44 | class Type; |
| 45 | |
| 46 | template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; }; |
| 47 | template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Which Type; }; |
| 48 | template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Which Type; }; |
| 49 | template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; }; |
| 50 | template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; }; |
| 51 | template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; }; |
| 52 | template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; }; |
| 53 | |
| 54 | template <typename T> |
| 55 | using SchemaType = typename SchemaType_<T>::Type; |
| 56 | // SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct. |
| 57 | |
| 58 | namespace _ { // private |
| 59 | extern const RawSchema NULL_SCHEMA; |
| 60 | extern const RawSchema NULL_STRUCT_SCHEMA; |
| 61 | extern const RawSchema NULL_ENUM_SCHEMA; |
| 62 | extern const RawSchema NULL_INTERFACE_SCHEMA; |
| 63 | extern const RawSchema NULL_CONST_SCHEMA; |
| 64 | // The schema types default to these null (empty) schemas in case of error, especially when |
| 65 | // exceptions are disabled. |
| 66 | } // namespace _ (private) |
| 67 | |
| 68 | class Schema { |
| 69 | // Convenience wrapper around capnp::schema::Node. |
| 70 | |
| 71 | public: |
| 72 | inline Schema(): raw(&_::NULL_SCHEMA.defaultBrand) {} |
| 73 | |
| 74 | template <typename T> |
| 75 | static inline SchemaType<T> from() { return SchemaType<T>::template fromImpl<T>(); } |
| 76 | // Get the Schema for a particular compiled-in type. |
| 77 | |
| 78 | schema::Node::Reader getProto() const; |
| 79 | // Get the underlying Cap'n Proto representation of the schema node. (Note that this accessor |
| 80 | // has performance comparable to accessors of struct-typed fields on Reader classes.) |
| 81 | |
| 82 | kj::ArrayPtr<const word> asUncheckedMessage() const; |
| 83 | // Get the encoded schema node content as a single message segment. It is safe to read as an |
| 84 | // unchecked message. |
| 85 | |
| 86 | Schema getDependency(uint64_t id) const CAPNP_DEPRECATED("Does not handle generics correctly." ); |
| 87 | // DEPRECATED: This method cannot correctly account for generic type parameter bindings that |
| 88 | // may apply to the dependency. Instead of using this method, use a method of the Schema API |
| 89 | // that corresponds to the exact kind of dependency. For example, to get a field type, use |
| 90 | // StructSchema::Field::getType(). |
| 91 | // |
| 92 | // Gets the Schema for one of this Schema's dependencies. For example, if this Schema is for a |
| 93 | // struct, you could look up the schema for one of its fields' types. Throws an exception if this |
| 94 | // schema doesn't actually depend on the given id. |
| 95 | // |
| 96 | // Note that not all type IDs found in the schema node are considered "dependencies" -- only the |
| 97 | // ones that are needed to implement the dynamic API are. That includes: |
| 98 | // - Field types. |
| 99 | // - Group types. |
| 100 | // - scopeId for group nodes, but NOT otherwise. |
| 101 | // - Method parameter and return types. |
| 102 | // |
| 103 | // The following are NOT considered dependencies: |
| 104 | // - Nested nodes. |
| 105 | // - scopeId for a non-group node. |
| 106 | // - Annotations. |
| 107 | // |
| 108 | // To obtain schemas for those, you would need a SchemaLoader. |
| 109 | |
| 110 | bool isBranded() const; |
| 111 | // Returns true if this schema represents a non-default parameterization of this type. |
| 112 | |
| 113 | Schema getGeneric() const; |
| 114 | // Get the version of this schema with any brands removed. |
| 115 | |
| 116 | class BrandArgumentList; |
| 117 | BrandArgumentList getBrandArgumentsAtScope(uint64_t scopeId) const; |
| 118 | // Gets the values bound to the brand parameters at the given scope. |
| 119 | |
| 120 | StructSchema asStruct() const; |
| 121 | EnumSchema asEnum() const; |
| 122 | InterfaceSchema asInterface() const; |
| 123 | ConstSchema asConst() const; |
| 124 | // Cast the Schema to a specific type. Throws an exception if the type doesn't match. Use |
| 125 | // getProto() to determine type, e.g. getProto().isStruct(). |
| 126 | |
| 127 | inline bool operator==(const Schema& other) const { return raw == other.raw; } |
| 128 | inline bool operator!=(const Schema& other) const { return raw != other.raw; } |
| 129 | // Determine whether two Schemas are wrapping the exact same underlying data, by identity. If |
| 130 | // you want to check if two Schemas represent the same type (but possibly different versions of |
| 131 | // it), compare their IDs instead. |
| 132 | |
| 133 | inline uint hashCode() const { return kj::hashCode(raw); } |
| 134 | |
| 135 | template <typename T> |
| 136 | void requireUsableAs() const; |
| 137 | // Throws an exception if a value with this Schema cannot safely be cast to a native value of |
| 138 | // the given type. This passes if either: |
| 139 | // - *this == from<T>() |
| 140 | // - This schema was loaded with SchemaLoader, the type ID matches typeId<T>(), and |
| 141 | // loadCompiledTypeAndDependencies<T>() was called on the SchemaLoader. |
| 142 | |
| 143 | kj::StringPtr getShortDisplayName() const; |
| 144 | // Get the short version of the node's display name. |
| 145 | |
| 146 | private: |
| 147 | const _::RawBrandedSchema* raw; |
| 148 | |
| 149 | inline explicit Schema(const _::RawBrandedSchema* raw): raw(raw) { |
| 150 | KJ_IREQUIRE(raw->lazyInitializer == nullptr, |
| 151 | "Must call ensureInitialized() on RawSchema before constructing Schema." ); |
| 152 | } |
| 153 | |
| 154 | template <typename T> static inline Schema fromImpl() { |
| 155 | return Schema(&_::rawSchema<T>()); |
| 156 | } |
| 157 | |
| 158 | void requireUsableAs(const _::RawSchema* expected) const; |
| 159 | |
| 160 | uint32_t getSchemaOffset(const schema::Value::Reader& value) const; |
| 161 | |
| 162 | Type getBrandBinding(uint64_t scopeId, uint index) const; |
| 163 | // Look up the binding for a brand parameter used by this Schema. Returns `AnyPointer` if the |
| 164 | // parameter is not bound. |
| 165 | // |
| 166 | // TODO(someday): Public interface for iterating over all bindings? |
| 167 | |
| 168 | Schema getDependency(uint64_t id, uint location) const; |
| 169 | // Look up schema for a particular dependency of this schema. `location` is the dependency |
| 170 | // location number as defined in _::RawBrandedSchema. |
| 171 | |
| 172 | Type interpretType(schema::Type::Reader proto, uint location) const; |
| 173 | // Interpret a schema::Type in the given location within the schema, compiling it into a |
| 174 | // Type object. |
| 175 | |
| 176 | friend class StructSchema; |
| 177 | friend class EnumSchema; |
| 178 | friend class InterfaceSchema; |
| 179 | friend class ConstSchema; |
| 180 | friend class ListSchema; |
| 181 | friend class SchemaLoader; |
| 182 | friend class Type; |
| 183 | friend kj::StringTree _::structString( |
| 184 | _::StructReader reader, const _::RawBrandedSchema& schema); |
| 185 | friend kj::String _::enumString(uint16_t value, const _::RawBrandedSchema& schema); |
| 186 | }; |
| 187 | |
| 188 | kj::StringPtr KJ_STRINGIFY(const Schema& schema); |
| 189 | |
| 190 | class Schema::BrandArgumentList { |
| 191 | // A list of generic parameter bindings for parameters of some particular type. Note that since |
| 192 | // parameters on an outer type apply to all inner types as well, a deeply-nested type can have |
| 193 | // multiple BrandArgumentLists that apply to it. |
| 194 | // |
| 195 | // A BrandArgumentList only represents the arguments that the client of the type specified. Since |
| 196 | // new parameters can be added over time, this list may not cover all defined parameters for the |
| 197 | // type. Missing parameters should be treated as AnyPointer. This class's implementation of |
| 198 | // operator[] already does this for you; out-of-bounds access will safely return AnyPointer. |
| 199 | |
| 200 | public: |
| 201 | inline BrandArgumentList(): scopeId(0), size_(0), bindings(nullptr) {} |
| 202 | |
| 203 | inline uint size() const { return size_; } |
| 204 | Type operator[](uint index) const; |
| 205 | |
| 206 | typedef _::IndexingIterator<const BrandArgumentList, Type> Iterator; |
| 207 | inline Iterator begin() const { return Iterator(this, 0); } |
| 208 | inline Iterator end() const { return Iterator(this, size()); } |
| 209 | |
| 210 | private: |
| 211 | uint64_t scopeId; |
| 212 | uint size_; |
| 213 | bool isUnbound; |
| 214 | const _::RawBrandedSchema::Binding* bindings; |
| 215 | |
| 216 | inline BrandArgumentList(uint64_t scopeId, bool isUnbound) |
| 217 | : scopeId(scopeId), size_(0), isUnbound(isUnbound), bindings(nullptr) {} |
| 218 | inline BrandArgumentList(uint64_t scopeId, uint size, |
| 219 | const _::RawBrandedSchema::Binding* bindings) |
| 220 | : scopeId(scopeId), size_(size), isUnbound(false), bindings(bindings) {} |
| 221 | |
| 222 | friend class Schema; |
| 223 | }; |
| 224 | |
| 225 | // ------------------------------------------------------------------- |
| 226 | |
| 227 | class StructSchema: public Schema { |
| 228 | public: |
| 229 | inline StructSchema(): Schema(&_::NULL_STRUCT_SCHEMA.defaultBrand) {} |
| 230 | |
| 231 | class Field; |
| 232 | class FieldList; |
| 233 | class FieldSubset; |
| 234 | |
| 235 | FieldList getFields() const; |
| 236 | // List top-level fields of this struct. This list will contain top-level groups (including |
| 237 | // named unions) but not the members of those groups. The list does, however, contain the |
| 238 | // members of the unnamed union, if there is one. |
| 239 | |
| 240 | FieldSubset getUnionFields() const; |
| 241 | // If the field contains an unnamed union, get a list of fields in the union, ordered by |
| 242 | // ordinal. Since discriminant values are assigned sequentially by ordinal, you may index this |
| 243 | // list by discriminant value. |
| 244 | |
| 245 | FieldSubset getNonUnionFields() const; |
| 246 | // Get the fields of this struct which are not in an unnamed union, ordered by ordinal. |
| 247 | |
| 248 | kj::Maybe<Field> findFieldByName(kj::StringPtr name) const; |
| 249 | // Find the field with the given name, or return null if there is no such field. If the struct |
| 250 | // contains an unnamed union, then this will find fields of that union in addition to fields |
| 251 | // of the outer struct, since they exist in the same namespace. It will not, however, find |
| 252 | // members of groups (including named unions) -- you must first look up the group itself, |
| 253 | // then dig into its type. |
| 254 | |
| 255 | Field getFieldByName(kj::StringPtr name) const; |
| 256 | // Like findFieldByName() but throws an exception on failure. |
| 257 | |
| 258 | kj::Maybe<Field> getFieldByDiscriminant(uint16_t discriminant) const; |
| 259 | // Finds the field whose `discriminantValue` is equal to the given value, or returns null if |
| 260 | // there is no such field. (If the schema does not represent a union or a struct containing |
| 261 | // an unnamed union, then this always returns null.) |
| 262 | |
| 263 | private: |
| 264 | StructSchema(Schema base): Schema(base) {} |
| 265 | template <typename T> static inline StructSchema fromImpl() { |
| 266 | return StructSchema(Schema(&_::rawBrandedSchema<T>())); |
| 267 | } |
| 268 | friend class Schema; |
| 269 | friend class Type; |
| 270 | }; |
| 271 | |
| 272 | class StructSchema::Field { |
| 273 | public: |
| 274 | Field() = default; |
| 275 | |
| 276 | inline schema::Field::Reader getProto() const { return proto; } |
| 277 | inline StructSchema getContainingStruct() const { return parent; } |
| 278 | |
| 279 | inline uint getIndex() const { return index; } |
| 280 | // Get the index of this field within the containing struct or union. |
| 281 | |
| 282 | Type getType() const; |
| 283 | // Get the type of this field. Note that this is preferred over getProto().getType() as this |
| 284 | // method will apply generics. |
| 285 | |
| 286 | uint32_t getDefaultValueSchemaOffset() const; |
| 287 | // For struct, list, and object fields, returns the offset, in words, within the first segment of |
| 288 | // the struct's schema, where this field's default value pointer is located. The schema is |
| 289 | // always stored as a single-segment unchecked message, which in turn means that the default |
| 290 | // value pointer itself can be treated as the root of an unchecked message -- if you know where |
| 291 | // to find it, which is what this method helps you with. |
| 292 | // |
| 293 | // For blobs, returns the offset of the beginning of the blob's content within the first segment |
| 294 | // of the struct's schema. |
| 295 | // |
| 296 | // This is primarily useful for code generators. The C++ code generator, for example, embeds |
| 297 | // the entire schema as a raw word array within the generated code. Of course, to implement |
| 298 | // field accessors, it needs access to those fields' default values. Embedding separate copies |
| 299 | // of those default values would be redundant since they are already included in the schema, but |
| 300 | // seeking through the schema at runtime to find the default values would be ugly. Instead, |
| 301 | // the code generator can use getDefaultValueSchemaOffset() to find the offset of the default |
| 302 | // value within the schema, and can simply apply that offset at runtime. |
| 303 | // |
| 304 | // If the above does not make sense, you probably don't need this method. |
| 305 | |
| 306 | inline bool operator==(const Field& other) const; |
| 307 | inline bool operator!=(const Field& other) const { return !(*this == other); } |
| 308 | inline uint hashCode() const; |
| 309 | |
| 310 | private: |
| 311 | StructSchema parent; |
| 312 | uint index; |
| 313 | schema::Field::Reader proto; |
| 314 | |
| 315 | inline Field(StructSchema parent, uint index, schema::Field::Reader proto) |
| 316 | : parent(parent), index(index), proto(proto) {} |
| 317 | |
| 318 | friend class StructSchema; |
| 319 | }; |
| 320 | |
| 321 | kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field); |
| 322 | |
| 323 | class StructSchema::FieldList { |
| 324 | public: |
| 325 | FieldList() = default; // empty list |
| 326 | |
| 327 | inline uint size() const { return list.size(); } |
| 328 | inline Field operator[](uint index) const { return Field(parent, index, list[index]); } |
| 329 | |
| 330 | typedef _::IndexingIterator<const FieldList, Field> Iterator; |
| 331 | inline Iterator begin() const { return Iterator(this, 0); } |
| 332 | inline Iterator end() const { return Iterator(this, size()); } |
| 333 | |
| 334 | private: |
| 335 | StructSchema parent; |
| 336 | List<schema::Field>::Reader list; |
| 337 | |
| 338 | inline FieldList(StructSchema parent, List<schema::Field>::Reader list) |
| 339 | : parent(parent), list(list) {} |
| 340 | |
| 341 | friend class StructSchema; |
| 342 | }; |
| 343 | |
| 344 | class StructSchema::FieldSubset { |
| 345 | public: |
| 346 | FieldSubset() = default; // empty list |
| 347 | |
| 348 | inline uint size() const { return size_; } |
| 349 | inline Field operator[](uint index) const { |
| 350 | return Field(parent, indices[index], list[indices[index]]); |
| 351 | } |
| 352 | |
| 353 | typedef _::IndexingIterator<const FieldSubset, Field> Iterator; |
| 354 | inline Iterator begin() const { return Iterator(this, 0); } |
| 355 | inline Iterator end() const { return Iterator(this, size()); } |
| 356 | |
| 357 | private: |
| 358 | StructSchema parent; |
| 359 | List<schema::Field>::Reader list; |
| 360 | const uint16_t* indices; |
| 361 | uint size_; |
| 362 | |
| 363 | inline FieldSubset(StructSchema parent, List<schema::Field>::Reader list, |
| 364 | const uint16_t* indices, uint size) |
| 365 | : parent(parent), list(list), indices(indices), size_(size) {} |
| 366 | |
| 367 | friend class StructSchema; |
| 368 | }; |
| 369 | |
| 370 | // ------------------------------------------------------------------- |
| 371 | |
| 372 | class EnumSchema: public Schema { |
| 373 | public: |
| 374 | inline EnumSchema(): Schema(&_::NULL_ENUM_SCHEMA.defaultBrand) {} |
| 375 | |
| 376 | class Enumerant; |
| 377 | class EnumerantList; |
| 378 | |
| 379 | EnumerantList getEnumerants() const; |
| 380 | |
| 381 | kj::Maybe<Enumerant> findEnumerantByName(kj::StringPtr name) const; |
| 382 | |
| 383 | Enumerant getEnumerantByName(kj::StringPtr name) const; |
| 384 | // Like findEnumerantByName() but throws an exception on failure. |
| 385 | |
| 386 | private: |
| 387 | EnumSchema(Schema base): Schema(base) {} |
| 388 | template <typename T> static inline EnumSchema fromImpl() { |
| 389 | return EnumSchema(Schema(&_::rawBrandedSchema<T>())); |
| 390 | } |
| 391 | friend class Schema; |
| 392 | friend class Type; |
| 393 | }; |
| 394 | |
| 395 | class EnumSchema::Enumerant { |
| 396 | public: |
| 397 | Enumerant() = default; |
| 398 | |
| 399 | inline schema::Enumerant::Reader getProto() const { return proto; } |
| 400 | inline EnumSchema getContainingEnum() const { return parent; } |
| 401 | |
| 402 | inline uint16_t getOrdinal() const { return ordinal; } |
| 403 | inline uint getIndex() const { return ordinal; } |
| 404 | |
| 405 | inline bool operator==(const Enumerant& other) const; |
| 406 | inline bool operator!=(const Enumerant& other) const { return !(*this == other); } |
| 407 | inline uint hashCode() const; |
| 408 | |
| 409 | private: |
| 410 | EnumSchema parent; |
| 411 | uint16_t ordinal; |
| 412 | schema::Enumerant::Reader proto; |
| 413 | |
| 414 | inline Enumerant(EnumSchema parent, uint16_t ordinal, schema::Enumerant::Reader proto) |
| 415 | : parent(parent), ordinal(ordinal), proto(proto) {} |
| 416 | |
| 417 | friend class EnumSchema; |
| 418 | }; |
| 419 | |
| 420 | class EnumSchema::EnumerantList { |
| 421 | public: |
| 422 | EnumerantList() = default; // empty list |
| 423 | |
| 424 | inline uint size() const { return list.size(); } |
| 425 | inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); } |
| 426 | |
| 427 | typedef _::IndexingIterator<const EnumerantList, Enumerant> Iterator; |
| 428 | inline Iterator begin() const { return Iterator(this, 0); } |
| 429 | inline Iterator end() const { return Iterator(this, size()); } |
| 430 | |
| 431 | private: |
| 432 | EnumSchema parent; |
| 433 | List<schema::Enumerant>::Reader list; |
| 434 | |
| 435 | inline EnumerantList(EnumSchema parent, List<schema::Enumerant>::Reader list) |
| 436 | : parent(parent), list(list) {} |
| 437 | |
| 438 | friend class EnumSchema; |
| 439 | }; |
| 440 | |
| 441 | // ------------------------------------------------------------------- |
| 442 | |
| 443 | class InterfaceSchema: public Schema { |
| 444 | public: |
| 445 | inline InterfaceSchema(): Schema(&_::NULL_INTERFACE_SCHEMA.defaultBrand) {} |
| 446 | |
| 447 | class Method; |
| 448 | class MethodList; |
| 449 | |
| 450 | MethodList getMethods() const; |
| 451 | |
| 452 | kj::Maybe<Method> findMethodByName(kj::StringPtr name) const; |
| 453 | |
| 454 | Method getMethodByName(kj::StringPtr name) const; |
| 455 | // Like findMethodByName() but throws an exception on failure. |
| 456 | |
| 457 | class SuperclassList; |
| 458 | |
| 459 | SuperclassList getSuperclasses() const; |
| 460 | // Get the immediate superclasses of this type, after applying generics. |
| 461 | |
| 462 | bool extends(InterfaceSchema other) const; |
| 463 | // Returns true if `other` is a superclass of this interface (including if `other == *this`). |
| 464 | |
| 465 | kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const; |
| 466 | // Find the superclass of this interface with the given type ID. Returns null if the interface |
| 467 | // extends no such type. |
| 468 | |
| 469 | private: |
| 470 | InterfaceSchema(Schema base): Schema(base) {} |
| 471 | template <typename T> static inline InterfaceSchema fromImpl() { |
| 472 | return InterfaceSchema(Schema(&_::rawBrandedSchema<T>())); |
| 473 | } |
| 474 | friend class Schema; |
| 475 | friend class Type; |
| 476 | |
| 477 | kj::Maybe<Method> findMethodByName(kj::StringPtr name, uint& counter) const; |
| 478 | bool extends(InterfaceSchema other, uint& counter) const; |
| 479 | kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId, uint& counter) const; |
| 480 | // We protect against malicious schemas with large or cyclic hierarchies by cutting off the |
| 481 | // search when the counter reaches a threshold. |
| 482 | }; |
| 483 | |
| 484 | class InterfaceSchema::Method { |
| 485 | public: |
| 486 | Method() = default; |
| 487 | |
| 488 | inline schema::Method::Reader getProto() const { return proto; } |
| 489 | inline InterfaceSchema getContainingInterface() const { return parent; } |
| 490 | |
| 491 | inline uint16_t getOrdinal() const { return ordinal; } |
| 492 | inline uint getIndex() const { return ordinal; } |
| 493 | |
| 494 | StructSchema getParamType() const; |
| 495 | StructSchema getResultType() const; |
| 496 | // Get the parameter and result types, including substituting generic parameters. |
| 497 | |
| 498 | inline bool operator==(const Method& other) const; |
| 499 | inline bool operator!=(const Method& other) const { return !(*this == other); } |
| 500 | inline uint hashCode() const; |
| 501 | |
| 502 | private: |
| 503 | InterfaceSchema parent; |
| 504 | uint16_t ordinal; |
| 505 | schema::Method::Reader proto; |
| 506 | |
| 507 | inline Method(InterfaceSchema parent, uint16_t ordinal, |
| 508 | schema::Method::Reader proto) |
| 509 | : parent(parent), ordinal(ordinal), proto(proto) {} |
| 510 | |
| 511 | friend class InterfaceSchema; |
| 512 | }; |
| 513 | |
| 514 | class InterfaceSchema::MethodList { |
| 515 | public: |
| 516 | MethodList() = default; // empty list |
| 517 | |
| 518 | inline uint size() const { return list.size(); } |
| 519 | inline Method operator[](uint index) const { return Method(parent, index, list[index]); } |
| 520 | |
| 521 | typedef _::IndexingIterator<const MethodList, Method> Iterator; |
| 522 | inline Iterator begin() const { return Iterator(this, 0); } |
| 523 | inline Iterator end() const { return Iterator(this, size()); } |
| 524 | |
| 525 | private: |
| 526 | InterfaceSchema parent; |
| 527 | List<schema::Method>::Reader list; |
| 528 | |
| 529 | inline MethodList(InterfaceSchema parent, List<schema::Method>::Reader list) |
| 530 | : parent(parent), list(list) {} |
| 531 | |
| 532 | friend class InterfaceSchema; |
| 533 | }; |
| 534 | |
| 535 | class InterfaceSchema::SuperclassList { |
| 536 | public: |
| 537 | SuperclassList() = default; // empty list |
| 538 | |
| 539 | inline uint size() const { return list.size(); } |
| 540 | InterfaceSchema operator[](uint index) const; |
| 541 | |
| 542 | typedef _::IndexingIterator<const SuperclassList, InterfaceSchema> Iterator; |
| 543 | inline Iterator begin() const { return Iterator(this, 0); } |
| 544 | inline Iterator end() const { return Iterator(this, size()); } |
| 545 | |
| 546 | private: |
| 547 | InterfaceSchema parent; |
| 548 | List<schema::Superclass>::Reader list; |
| 549 | |
| 550 | inline SuperclassList(InterfaceSchema parent, List<schema::Superclass>::Reader list) |
| 551 | : parent(parent), list(list) {} |
| 552 | |
| 553 | friend class InterfaceSchema; |
| 554 | }; |
| 555 | |
| 556 | // ------------------------------------------------------------------- |
| 557 | |
| 558 | class ConstSchema: public Schema { |
| 559 | // Represents a constant declaration. |
| 560 | // |
| 561 | // `ConstSchema` can be implicitly cast to DynamicValue to read its value. |
| 562 | |
| 563 | public: |
| 564 | inline ConstSchema(): Schema(&_::NULL_CONST_SCHEMA.defaultBrand) {} |
| 565 | |
| 566 | template <typename T> |
| 567 | ReaderFor<T> as() const; |
| 568 | // Read the constant's value. This is a convenience method equivalent to casting the ConstSchema |
| 569 | // to a DynamicValue and then calling its `as<T>()` method. For dependency reasons, this method |
| 570 | // is defined in <capnp/dynamic.h>, which you must #include explicitly. |
| 571 | |
| 572 | uint32_t getValueSchemaOffset() const; |
| 573 | // Much like StructSchema::Field::getDefaultValueSchemaOffset(), if the constant has pointer |
| 574 | // type, this gets the offset from the beginning of the constant's schema node to a pointer |
| 575 | // representing the constant value. |
| 576 | |
| 577 | Type getType() const; |
| 578 | |
| 579 | private: |
| 580 | ConstSchema(Schema base): Schema(base) {} |
| 581 | friend class Schema; |
| 582 | }; |
| 583 | |
| 584 | // ------------------------------------------------------------------- |
| 585 | |
| 586 | class Type { |
| 587 | public: |
| 588 | struct BrandParameter { |
| 589 | uint64_t scopeId; |
| 590 | uint index; |
| 591 | }; |
| 592 | struct ImplicitParameter { |
| 593 | uint index; |
| 594 | }; |
| 595 | |
| 596 | inline Type(); |
| 597 | inline Type(schema::Type::Which primitive); |
| 598 | inline Type(StructSchema schema); |
| 599 | inline Type(EnumSchema schema); |
| 600 | inline Type(InterfaceSchema schema); |
| 601 | inline Type(ListSchema schema); |
| 602 | inline Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind); |
| 603 | inline Type(BrandParameter param); |
| 604 | inline Type(ImplicitParameter param); |
| 605 | |
| 606 | template <typename T> |
| 607 | inline static Type from(); |
| 608 | template <typename T> |
| 609 | inline static Type from(T&& value); |
| 610 | |
| 611 | inline schema::Type::Which which() const; |
| 612 | |
| 613 | StructSchema asStruct() const; |
| 614 | EnumSchema asEnum() const; |
| 615 | InterfaceSchema asInterface() const; |
| 616 | ListSchema asList() const; |
| 617 | // Each of these methods may only be called if which() returns the corresponding type. |
| 618 | |
| 619 | kj::Maybe<BrandParameter> getBrandParameter() const; |
| 620 | // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular |
| 621 | // AnyPointer and not a parameter. |
| 622 | |
| 623 | kj::Maybe<ImplicitParameter> getImplicitParameter() const; |
| 624 | // Only callable if which() returns ANY_POINTER. Returns null if the type is just a regular |
| 625 | // AnyPointer and not a parameter. "Implicit parameters" refer to type parameters on methods. |
| 626 | |
| 627 | inline schema::Type::AnyPointer::Unconstrained::Which whichAnyPointerKind() const; |
| 628 | // Only callable if which() returns ANY_POINTER. |
| 629 | |
| 630 | inline bool isVoid() const; |
| 631 | inline bool isBool() const; |
| 632 | inline bool isInt8() const; |
| 633 | inline bool isInt16() const; |
| 634 | inline bool isInt32() const; |
| 635 | inline bool isInt64() const; |
| 636 | inline bool isUInt8() const; |
| 637 | inline bool isUInt16() const; |
| 638 | inline bool isUInt32() const; |
| 639 | inline bool isUInt64() const; |
| 640 | inline bool isFloat32() const; |
| 641 | inline bool isFloat64() const; |
| 642 | inline bool isText() const; |
| 643 | inline bool isData() const; |
| 644 | inline bool isList() const; |
| 645 | inline bool isEnum() const; |
| 646 | inline bool isStruct() const; |
| 647 | inline bool isInterface() const; |
| 648 | inline bool isAnyPointer() const; |
| 649 | |
| 650 | bool operator==(const Type& other) const; |
| 651 | inline bool operator!=(const Type& other) const { return !(*this == other); } |
| 652 | |
| 653 | uint hashCode() const; |
| 654 | |
| 655 | inline Type wrapInList(uint depth = 1) const; |
| 656 | // Return the Type formed by wrapping this type in List() `depth` times. |
| 657 | |
| 658 | inline Type(schema::Type::Which derived, const _::RawBrandedSchema* schema); |
| 659 | // For internal use. |
| 660 | |
| 661 | private: |
| 662 | schema::Type::Which baseType; // type not including applications of List() |
| 663 | uint8_t listDepth; // 0 for T, 1 for List(T), 2 for List(List(T)), ... |
| 664 | |
| 665 | bool isImplicitParam; |
| 666 | // If true, this refers to an implicit method parameter. baseType must be ANY_POINTER, scopeId |
| 667 | // must be zero, and paramIndex indicates the parameter index. |
| 668 | |
| 669 | union { |
| 670 | uint16_t paramIndex; |
| 671 | // If baseType is ANY_POINTER but this Type actually refers to a type parameter, this is the |
| 672 | // index of the parameter among the parameters at its scope, and `scopeId` below is the type ID |
| 673 | // of the scope where the parameter was defined. |
| 674 | |
| 675 | schema::Type::AnyPointer::Unconstrained::Which anyPointerKind; |
| 676 | // If scopeId is zero and isImplicitParam is false. |
| 677 | }; |
| 678 | |
| 679 | union { |
| 680 | const _::RawBrandedSchema* schema; // if type is struct, enum, interface... |
| 681 | uint64_t scopeId; // if type is AnyPointer but it's actually a type parameter... |
| 682 | }; |
| 683 | |
| 684 | Type(schema::Type::Which baseType, uint8_t listDepth, const _::RawBrandedSchema* schema) |
| 685 | : baseType(baseType), listDepth(listDepth), schema(schema) { |
| 686 | KJ_IREQUIRE(baseType != schema::Type::ANY_POINTER); |
| 687 | } |
| 688 | |
| 689 | void requireUsableAs(Type expected) const; |
| 690 | |
| 691 | template <typename T, Kind k> |
| 692 | struct FromValueImpl; |
| 693 | |
| 694 | friend class ListSchema; // only for requireUsableAs() |
| 695 | }; |
| 696 | |
| 697 | // ------------------------------------------------------------------- |
| 698 | |
| 699 | class ListSchema { |
| 700 | // ListSchema is a little different because list types are not described by schema nodes. So, |
| 701 | // ListSchema doesn't subclass Schema. |
| 702 | |
| 703 | public: |
| 704 | ListSchema() = default; |
| 705 | |
| 706 | static ListSchema of(schema::Type::Which primitiveType); |
| 707 | static ListSchema of(StructSchema elementType); |
| 708 | static ListSchema of(EnumSchema elementType); |
| 709 | static ListSchema of(InterfaceSchema elementType); |
| 710 | static ListSchema of(ListSchema elementType); |
| 711 | static ListSchema of(Type elementType); |
| 712 | // Construct the schema for a list of the given type. |
| 713 | |
| 714 | static ListSchema of(schema::Type::Reader elementType, Schema context) |
| 715 | CAPNP_DEPRECATED("Does not handle generics correctly." ); |
| 716 | // DEPRECATED: This method cannot correctly account for generic type parameter bindings that |
| 717 | // may apply to the input type. Instead of using this method, use a method of the Schema API |
| 718 | // that corresponds to the exact kind of dependency. For example, to get a field type, use |
| 719 | // StructSchema::Field::getType(). |
| 720 | // |
| 721 | // Construct from an element type schema. Requires a context which can handle getDependency() |
| 722 | // requests for any type ID found in the schema. |
| 723 | |
| 724 | Type getElementType() const; |
| 725 | |
| 726 | inline schema::Type::Which whichElementType() const; |
| 727 | // Get the element type's "which()". ListSchema does not actually store a schema::Type::Reader |
| 728 | // describing the element type, but if it did, this would be equivalent to calling |
| 729 | // .getBody().which() on that type. |
| 730 | |
| 731 | StructSchema getStructElementType() const; |
| 732 | EnumSchema getEnumElementType() const; |
| 733 | InterfaceSchema getInterfaceElementType() const; |
| 734 | ListSchema getListElementType() const; |
| 735 | // Get the schema for complex element types. Each of these throws an exception if the element |
| 736 | // type is not of the requested kind. |
| 737 | |
| 738 | inline bool operator==(const ListSchema& other) const { return elementType == other.elementType; } |
| 739 | inline bool operator!=(const ListSchema& other) const { return elementType != other.elementType; } |
| 740 | |
| 741 | template <typename T> |
| 742 | void requireUsableAs() const; |
| 743 | |
| 744 | private: |
| 745 | Type elementType; |
| 746 | |
| 747 | inline explicit ListSchema(Type elementType): elementType(elementType) {} |
| 748 | |
| 749 | template <typename T> |
| 750 | struct FromImpl; |
| 751 | template <typename T> static inline ListSchema fromImpl() { |
| 752 | return FromImpl<T>::get(); |
| 753 | } |
| 754 | |
| 755 | void requireUsableAs(ListSchema expected) const; |
| 756 | |
| 757 | friend class Schema; |
| 758 | }; |
| 759 | |
| 760 | // ======================================================================================= |
| 761 | // inline implementation |
| 762 | |
| 763 | template <> inline schema::Type::Which Schema::from<Void>() { return schema::Type::VOID; } |
| 764 | template <> inline schema::Type::Which Schema::from<bool>() { return schema::Type::BOOL; } |
| 765 | template <> inline schema::Type::Which Schema::from<int8_t>() { return schema::Type::INT8; } |
| 766 | template <> inline schema::Type::Which Schema::from<int16_t>() { return schema::Type::INT16; } |
| 767 | template <> inline schema::Type::Which Schema::from<int32_t>() { return schema::Type::INT32; } |
| 768 | template <> inline schema::Type::Which Schema::from<int64_t>() { return schema::Type::INT64; } |
| 769 | template <> inline schema::Type::Which Schema::from<uint8_t>() { return schema::Type::UINT8; } |
| 770 | template <> inline schema::Type::Which Schema::from<uint16_t>() { return schema::Type::UINT16; } |
| 771 | template <> inline schema::Type::Which Schema::from<uint32_t>() { return schema::Type::UINT32; } |
| 772 | template <> inline schema::Type::Which Schema::from<uint64_t>() { return schema::Type::UINT64; } |
| 773 | template <> inline schema::Type::Which Schema::from<float>() { return schema::Type::FLOAT32; } |
| 774 | template <> inline schema::Type::Which Schema::from<double>() { return schema::Type::FLOAT64; } |
| 775 | template <> inline schema::Type::Which Schema::from<Text>() { return schema::Type::TEXT; } |
| 776 | template <> inline schema::Type::Which Schema::from<Data>() { return schema::Type::DATA; } |
| 777 | |
| 778 | inline Schema Schema::getDependency(uint64_t id) const { |
| 779 | return getDependency(id, 0); |
| 780 | } |
| 781 | |
| 782 | inline bool Schema::isBranded() const { |
| 783 | return raw != &raw->generic->defaultBrand; |
| 784 | } |
| 785 | |
| 786 | inline Schema Schema::getGeneric() const { |
| 787 | return Schema(&raw->generic->defaultBrand); |
| 788 | } |
| 789 | |
| 790 | template <typename T> |
| 791 | inline void Schema::requireUsableAs() const { |
| 792 | requireUsableAs(&_::rawSchema<T>()); |
| 793 | } |
| 794 | |
| 795 | inline bool StructSchema::Field::operator==(const Field& other) const { |
| 796 | return parent == other.parent && index == other.index; |
| 797 | } |
| 798 | inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const { |
| 799 | return parent == other.parent && ordinal == other.ordinal; |
| 800 | } |
| 801 | inline bool InterfaceSchema::Method::operator==(const Method& other) const { |
| 802 | return parent == other.parent && ordinal == other.ordinal; |
| 803 | } |
| 804 | |
| 805 | inline uint StructSchema::Field::hashCode() const { |
| 806 | return kj::hashCode(parent, index); |
| 807 | } |
| 808 | inline uint EnumSchema::Enumerant::hashCode() const { |
| 809 | return kj::hashCode(parent, ordinal); |
| 810 | } |
| 811 | inline uint InterfaceSchema::Method::hashCode() const { |
| 812 | return kj::hashCode(parent, ordinal); |
| 813 | } |
| 814 | |
| 815 | inline ListSchema ListSchema::of(StructSchema elementType) { |
| 816 | return ListSchema(Type(elementType)); |
| 817 | } |
| 818 | inline ListSchema ListSchema::of(EnumSchema elementType) { |
| 819 | return ListSchema(Type(elementType)); |
| 820 | } |
| 821 | inline ListSchema ListSchema::of(InterfaceSchema elementType) { |
| 822 | return ListSchema(Type(elementType)); |
| 823 | } |
| 824 | inline ListSchema ListSchema::of(ListSchema elementType) { |
| 825 | return ListSchema(Type(elementType)); |
| 826 | } |
| 827 | inline ListSchema ListSchema::of(Type elementType) { |
| 828 | return ListSchema(elementType); |
| 829 | } |
| 830 | |
| 831 | inline Type ListSchema::getElementType() const { |
| 832 | return elementType; |
| 833 | } |
| 834 | |
| 835 | inline schema::Type::Which ListSchema::whichElementType() const { |
| 836 | return elementType.which(); |
| 837 | } |
| 838 | |
| 839 | inline StructSchema ListSchema::getStructElementType() const { |
| 840 | return elementType.asStruct(); |
| 841 | } |
| 842 | |
| 843 | inline EnumSchema ListSchema::getEnumElementType() const { |
| 844 | return elementType.asEnum(); |
| 845 | } |
| 846 | |
| 847 | inline InterfaceSchema ListSchema::getInterfaceElementType() const { |
| 848 | return elementType.asInterface(); |
| 849 | } |
| 850 | |
| 851 | inline ListSchema ListSchema::getListElementType() const { |
| 852 | return elementType.asList(); |
| 853 | } |
| 854 | |
| 855 | template <typename T> |
| 856 | inline void ListSchema::requireUsableAs() const { |
| 857 | static_assert(kind<T>() == Kind::LIST, |
| 858 | "ListSchema::requireUsableAs<T>() requires T is a list type." ); |
| 859 | requireUsableAs(Schema::from<T>()); |
| 860 | } |
| 861 | |
| 862 | inline void ListSchema::requireUsableAs(ListSchema expected) const { |
| 863 | elementType.requireUsableAs(expected.elementType); |
| 864 | } |
| 865 | |
| 866 | template <typename T> |
| 867 | struct ListSchema::FromImpl<List<T>> { |
| 868 | static inline ListSchema get() { return of(Schema::from<T>()); } |
| 869 | }; |
| 870 | |
| 871 | inline Type::Type(): baseType(schema::Type::VOID), listDepth(0), schema(nullptr) {} |
| 872 | inline Type::Type(schema::Type::Which primitive) |
| 873 | : baseType(primitive), listDepth(0), isImplicitParam(false) { |
| 874 | KJ_IREQUIRE(primitive != schema::Type::STRUCT && |
| 875 | primitive != schema::Type::ENUM && |
| 876 | primitive != schema::Type::INTERFACE && |
| 877 | primitive != schema::Type::LIST); |
| 878 | if (primitive == schema::Type::ANY_POINTER) { |
| 879 | scopeId = 0; |
| 880 | anyPointerKind = schema::Type::AnyPointer::Unconstrained::ANY_KIND; |
| 881 | } else { |
| 882 | schema = nullptr; |
| 883 | } |
| 884 | } |
| 885 | inline Type::Type(schema::Type::Which derived, const _::RawBrandedSchema* schema) |
| 886 | : baseType(derived), listDepth(0), isImplicitParam(false), schema(schema) { |
| 887 | KJ_IREQUIRE(derived == schema::Type::STRUCT || |
| 888 | derived == schema::Type::ENUM || |
| 889 | derived == schema::Type::INTERFACE); |
| 890 | } |
| 891 | |
| 892 | inline Type::Type(StructSchema schema) |
| 893 | : baseType(schema::Type::STRUCT), listDepth(0), schema(schema.raw) {} |
| 894 | inline Type::Type(EnumSchema schema) |
| 895 | : baseType(schema::Type::ENUM), listDepth(0), schema(schema.raw) {} |
| 896 | inline Type::Type(InterfaceSchema schema) |
| 897 | : baseType(schema::Type::INTERFACE), listDepth(0), schema(schema.raw) {} |
| 898 | inline Type::Type(ListSchema schema) |
| 899 | : Type(schema.getElementType()) { ++listDepth; } |
| 900 | inline Type::Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind) |
| 901 | : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false), |
| 902 | anyPointerKind(anyPointerKind), scopeId(0) {} |
| 903 | inline Type::Type(BrandParameter param) |
| 904 | : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false), |
| 905 | paramIndex(param.index), scopeId(param.scopeId) {} |
| 906 | inline Type::Type(ImplicitParameter param) |
| 907 | : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(true), |
| 908 | paramIndex(param.index), scopeId(0) {} |
| 909 | |
| 910 | inline schema::Type::Which Type::which() const { |
| 911 | return listDepth > 0 ? schema::Type::LIST : baseType; |
| 912 | } |
| 913 | |
| 914 | inline schema::Type::AnyPointer::Unconstrained::Which Type::whichAnyPointerKind() const { |
| 915 | KJ_IREQUIRE(baseType == schema::Type::ANY_POINTER); |
| 916 | return !isImplicitParam && scopeId == 0 ? anyPointerKind |
| 917 | : schema::Type::AnyPointer::Unconstrained::ANY_KIND; |
| 918 | } |
| 919 | |
| 920 | template <typename T> |
| 921 | inline Type Type::from() { return Type(Schema::from<T>()); } |
| 922 | |
| 923 | template <typename T, Kind k> |
| 924 | struct Type::FromValueImpl { |
| 925 | template <typename U> |
| 926 | static inline Type type(U&& value) { |
| 927 | return Type::from<T>(); |
| 928 | } |
| 929 | }; |
| 930 | |
| 931 | template <typename T> |
| 932 | struct Type::FromValueImpl<T, Kind::OTHER> { |
| 933 | template <typename U> |
| 934 | static inline Type type(U&& value) { |
| 935 | // All dynamic types have getSchema(). |
| 936 | return value.getSchema(); |
| 937 | } |
| 938 | }; |
| 939 | |
| 940 | template <typename T> |
| 941 | inline Type Type::from(T&& value) { |
| 942 | typedef FromAny<kj::Decay<T>> Base; |
| 943 | return Type::FromValueImpl<Base, kind<Base>()>::type(kj::fwd<T>(value)); |
| 944 | } |
| 945 | |
| 946 | inline bool Type::isVoid () const { return baseType == schema::Type::VOID && listDepth == 0; } |
| 947 | inline bool Type::isBool () const { return baseType == schema::Type::BOOL && listDepth == 0; } |
| 948 | inline bool Type::isInt8 () const { return baseType == schema::Type::INT8 && listDepth == 0; } |
| 949 | inline bool Type::isInt16 () const { return baseType == schema::Type::INT16 && listDepth == 0; } |
| 950 | inline bool Type::isInt32 () const { return baseType == schema::Type::INT32 && listDepth == 0; } |
| 951 | inline bool Type::isInt64 () const { return baseType == schema::Type::INT64 && listDepth == 0; } |
| 952 | inline bool Type::isUInt8 () const { return baseType == schema::Type::UINT8 && listDepth == 0; } |
| 953 | inline bool Type::isUInt16 () const { return baseType == schema::Type::UINT16 && listDepth == 0; } |
| 954 | inline bool Type::isUInt32 () const { return baseType == schema::Type::UINT32 && listDepth == 0; } |
| 955 | inline bool Type::isUInt64 () const { return baseType == schema::Type::UINT64 && listDepth == 0; } |
| 956 | inline bool Type::isFloat32() const { return baseType == schema::Type::FLOAT32 && listDepth == 0; } |
| 957 | inline bool Type::isFloat64() const { return baseType == schema::Type::FLOAT64 && listDepth == 0; } |
| 958 | inline bool Type::isText () const { return baseType == schema::Type::TEXT && listDepth == 0; } |
| 959 | inline bool Type::isData () const { return baseType == schema::Type::DATA && listDepth == 0; } |
| 960 | inline bool Type::isList () const { return listDepth > 0; } |
| 961 | inline bool Type::isEnum () const { return baseType == schema::Type::ENUM && listDepth == 0; } |
| 962 | inline bool Type::isStruct () const { return baseType == schema::Type::STRUCT && listDepth == 0; } |
| 963 | inline bool Type::isInterface() const { |
| 964 | return baseType == schema::Type::INTERFACE && listDepth == 0; |
| 965 | } |
| 966 | inline bool Type::isAnyPointer() const { |
| 967 | return baseType == schema::Type::ANY_POINTER && listDepth == 0; |
| 968 | } |
| 969 | |
| 970 | inline Type Type::wrapInList(uint depth) const { |
| 971 | Type result = *this; |
| 972 | result.listDepth += depth; |
| 973 | return result; |
| 974 | } |
| 975 | |
| 976 | } // namespace capnp |
| 977 | |