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
36namespace capnp {
37
38class Schema;
39class StructSchema;
40class EnumSchema;
41class InterfaceSchema;
42class ConstSchema;
43class ListSchema;
44class Type;
45
46template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; };
47template <typename T> struct SchemaType_<T, Kind::PRIMITIVE> { typedef schema::Type::Which Type; };
48template <typename T> struct SchemaType_<T, Kind::BLOB> { typedef schema::Type::Which Type; };
49template <typename T> struct SchemaType_<T, Kind::ENUM> { typedef EnumSchema Type; };
50template <typename T> struct SchemaType_<T, Kind::STRUCT> { typedef StructSchema Type; };
51template <typename T> struct SchemaType_<T, Kind::INTERFACE> { typedef InterfaceSchema Type; };
52template <typename T> struct SchemaType_<T, Kind::LIST> { typedef ListSchema Type; };
53
54template <typename T>
55using SchemaType = typename SchemaType_<T>::Type;
56// SchemaType<T> is the type of T's schema, e.g. StructSchema if T is a struct.
57
58namespace _ { // private
59extern const RawSchema NULL_SCHEMA;
60extern const RawSchema NULL_STRUCT_SCHEMA;
61extern const RawSchema NULL_ENUM_SCHEMA;
62extern const RawSchema NULL_INTERFACE_SCHEMA;
63extern 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
68class Schema {
69 // Convenience wrapper around capnp::schema::Node.
70
71public:
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
146private:
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
188kj::StringPtr KJ_STRINGIFY(const Schema& schema);
189
190class 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
200public:
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
210private:
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
227class StructSchema: public Schema {
228public:
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
263private:
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
272class StructSchema::Field {
273public:
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
310private:
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
321kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field);
322
323class StructSchema::FieldList {
324public:
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
334private:
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
344class StructSchema::FieldSubset {
345public:
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
357private:
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
372class EnumSchema: public Schema {
373public:
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
386private:
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
395class EnumSchema::Enumerant {
396public:
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
409private:
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
420class EnumSchema::EnumerantList {
421public:
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
431private:
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
443class InterfaceSchema: public Schema {
444public:
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
469private:
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
484class InterfaceSchema::Method {
485public:
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
502private:
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
514class InterfaceSchema::MethodList {
515public:
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
525private:
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
535class InterfaceSchema::SuperclassList {
536public:
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
546private:
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
558class ConstSchema: public Schema {
559 // Represents a constant declaration.
560 //
561 // `ConstSchema` can be implicitly cast to DynamicValue to read its value.
562
563public:
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
579private:
580 ConstSchema(Schema base): Schema(base) {}
581 friend class Schema;
582};
583
584// -------------------------------------------------------------------
585
586class Type {
587public:
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
661private:
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
699class ListSchema {
700 // ListSchema is a little different because list types are not described by schema nodes. So,
701 // ListSchema doesn't subclass Schema.
702
703public:
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
744private:
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
763template <> inline schema::Type::Which Schema::from<Void>() { return schema::Type::VOID; }
764template <> inline schema::Type::Which Schema::from<bool>() { return schema::Type::BOOL; }
765template <> inline schema::Type::Which Schema::from<int8_t>() { return schema::Type::INT8; }
766template <> inline schema::Type::Which Schema::from<int16_t>() { return schema::Type::INT16; }
767template <> inline schema::Type::Which Schema::from<int32_t>() { return schema::Type::INT32; }
768template <> inline schema::Type::Which Schema::from<int64_t>() { return schema::Type::INT64; }
769template <> inline schema::Type::Which Schema::from<uint8_t>() { return schema::Type::UINT8; }
770template <> inline schema::Type::Which Schema::from<uint16_t>() { return schema::Type::UINT16; }
771template <> inline schema::Type::Which Schema::from<uint32_t>() { return schema::Type::UINT32; }
772template <> inline schema::Type::Which Schema::from<uint64_t>() { return schema::Type::UINT64; }
773template <> inline schema::Type::Which Schema::from<float>() { return schema::Type::FLOAT32; }
774template <> inline schema::Type::Which Schema::from<double>() { return schema::Type::FLOAT64; }
775template <> inline schema::Type::Which Schema::from<Text>() { return schema::Type::TEXT; }
776template <> inline schema::Type::Which Schema::from<Data>() { return schema::Type::DATA; }
777
778inline Schema Schema::getDependency(uint64_t id) const {
779 return getDependency(id, 0);
780}
781
782inline bool Schema::isBranded() const {
783 return raw != &raw->generic->defaultBrand;
784}
785
786inline Schema Schema::getGeneric() const {
787 return Schema(&raw->generic->defaultBrand);
788}
789
790template <typename T>
791inline void Schema::requireUsableAs() const {
792 requireUsableAs(&_::rawSchema<T>());
793}
794
795inline bool StructSchema::Field::operator==(const Field& other) const {
796 return parent == other.parent && index == other.index;
797}
798inline bool EnumSchema::Enumerant::operator==(const Enumerant& other) const {
799 return parent == other.parent && ordinal == other.ordinal;
800}
801inline bool InterfaceSchema::Method::operator==(const Method& other) const {
802 return parent == other.parent && ordinal == other.ordinal;
803}
804
805inline uint StructSchema::Field::hashCode() const {
806 return kj::hashCode(parent, index);
807}
808inline uint EnumSchema::Enumerant::hashCode() const {
809 return kj::hashCode(parent, ordinal);
810}
811inline uint InterfaceSchema::Method::hashCode() const {
812 return kj::hashCode(parent, ordinal);
813}
814
815inline ListSchema ListSchema::of(StructSchema elementType) {
816 return ListSchema(Type(elementType));
817}
818inline ListSchema ListSchema::of(EnumSchema elementType) {
819 return ListSchema(Type(elementType));
820}
821inline ListSchema ListSchema::of(InterfaceSchema elementType) {
822 return ListSchema(Type(elementType));
823}
824inline ListSchema ListSchema::of(ListSchema elementType) {
825 return ListSchema(Type(elementType));
826}
827inline ListSchema ListSchema::of(Type elementType) {
828 return ListSchema(elementType);
829}
830
831inline Type ListSchema::getElementType() const {
832 return elementType;
833}
834
835inline schema::Type::Which ListSchema::whichElementType() const {
836 return elementType.which();
837}
838
839inline StructSchema ListSchema::getStructElementType() const {
840 return elementType.asStruct();
841}
842
843inline EnumSchema ListSchema::getEnumElementType() const {
844 return elementType.asEnum();
845}
846
847inline InterfaceSchema ListSchema::getInterfaceElementType() const {
848 return elementType.asInterface();
849}
850
851inline ListSchema ListSchema::getListElementType() const {
852 return elementType.asList();
853}
854
855template <typename T>
856inline 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
862inline void ListSchema::requireUsableAs(ListSchema expected) const {
863 elementType.requireUsableAs(expected.elementType);
864}
865
866template <typename T>
867struct ListSchema::FromImpl<List<T>> {
868 static inline ListSchema get() { return of(Schema::from<T>()); }
869};
870
871inline Type::Type(): baseType(schema::Type::VOID), listDepth(0), schema(nullptr) {}
872inline 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}
885inline 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
892inline Type::Type(StructSchema schema)
893 : baseType(schema::Type::STRUCT), listDepth(0), schema(schema.raw) {}
894inline Type::Type(EnumSchema schema)
895 : baseType(schema::Type::ENUM), listDepth(0), schema(schema.raw) {}
896inline Type::Type(InterfaceSchema schema)
897 : baseType(schema::Type::INTERFACE), listDepth(0), schema(schema.raw) {}
898inline Type::Type(ListSchema schema)
899 : Type(schema.getElementType()) { ++listDepth; }
900inline Type::Type(schema::Type::AnyPointer::Unconstrained::Which anyPointerKind)
901 : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false),
902 anyPointerKind(anyPointerKind), scopeId(0) {}
903inline Type::Type(BrandParameter param)
904 : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(false),
905 paramIndex(param.index), scopeId(param.scopeId) {}
906inline Type::Type(ImplicitParameter param)
907 : baseType(schema::Type::ANY_POINTER), listDepth(0), isImplicitParam(true),
908 paramIndex(param.index), scopeId(0) {}
909
910inline schema::Type::Which Type::which() const {
911 return listDepth > 0 ? schema::Type::LIST : baseType;
912}
913
914inline 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
920template <typename T>
921inline Type Type::from() { return Type(Schema::from<T>()); }
922
923template <typename T, Kind k>
924struct Type::FromValueImpl {
925 template <typename U>
926 static inline Type type(U&& value) {
927 return Type::from<T>();
928 }
929};
930
931template <typename T>
932struct 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
940template <typename T>
941inline 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
946inline bool Type::isVoid () const { return baseType == schema::Type::VOID && listDepth == 0; }
947inline bool Type::isBool () const { return baseType == schema::Type::BOOL && listDepth == 0; }
948inline bool Type::isInt8 () const { return baseType == schema::Type::INT8 && listDepth == 0; }
949inline bool Type::isInt16 () const { return baseType == schema::Type::INT16 && listDepth == 0; }
950inline bool Type::isInt32 () const { return baseType == schema::Type::INT32 && listDepth == 0; }
951inline bool Type::isInt64 () const { return baseType == schema::Type::INT64 && listDepth == 0; }
952inline bool Type::isUInt8 () const { return baseType == schema::Type::UINT8 && listDepth == 0; }
953inline bool Type::isUInt16 () const { return baseType == schema::Type::UINT16 && listDepth == 0; }
954inline bool Type::isUInt32 () const { return baseType == schema::Type::UINT32 && listDepth == 0; }
955inline bool Type::isUInt64 () const { return baseType == schema::Type::UINT64 && listDepth == 0; }
956inline bool Type::isFloat32() const { return baseType == schema::Type::FLOAT32 && listDepth == 0; }
957inline bool Type::isFloat64() const { return baseType == schema::Type::FLOAT64 && listDepth == 0; }
958inline bool Type::isText () const { return baseType == schema::Type::TEXT && listDepth == 0; }
959inline bool Type::isData () const { return baseType == schema::Type::DATA && listDepth == 0; }
960inline bool Type::isList () const { return listDepth > 0; }
961inline bool Type::isEnum () const { return baseType == schema::Type::ENUM && listDepth == 0; }
962inline bool Type::isStruct () const { return baseType == schema::Type::STRUCT && listDepth == 0; }
963inline bool Type::isInterface() const {
964 return baseType == schema::Type::INTERFACE && listDepth == 0;
965}
966inline bool Type::isAnyPointer() const {
967 return baseType == schema::Type::ANY_POINTER && listDepth == 0;
968}
969
970inline Type Type::wrapInList(uint depth) const {
971 Type result = *this;
972 result.listDepth += depth;
973 return result;
974}
975
976} // namespace capnp
977