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 | |