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