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#include "schema.h"
23#include "message.h"
24#include <kj/debug.h>
25
26namespace capnp {
27
28namespace schema {
29 uint KJ_HASHCODE(Type::Which w) { return kj::hashCode(static_cast<uint16_t>(w)); }
30 // TODO(cleanup): Cap'n Proto does not declare stringifiers nor hashers for `Which` enums, unlike
31 // all other enums. Fix that and remove this.
32}
33
34namespace _ { // private
35
36// Null schemas generated using the below schema file with:
37//
38// capnp eval -Isrc null-schemas.capnp node --flat |
39// hexdump -v -e '8/1 "0x%02x, "' -e '1/8 "\n"'; echo
40//
41// I totally don't understand hexdump format strings and came up with this command based on trial
42// and error.
43//
44// @0x879863d4b2cc4a1e;
45//
46// using Node = import "/capnp/schema.capnp".Node;
47//
48// const node :Node = (
49// id = 0x0000000000000000,
50// displayName = "(null schema)");
51//
52// const struct :Node = (
53// id = 0x0000000000000001,
54// displayName = "(null struct schema)",
55// struct = (
56// dataWordCount = 0,
57// pointerCount = 0,
58// preferredListEncoding = empty));
59//
60// const enum :Node = (
61// id = 0x0000000000000002,
62// displayName = "(null enum schema)",
63// enum = ());
64//
65// const interface :Node = (
66// id = 0x0000000000000003,
67// displayName = "(null interface schema)",
68// interface = ());
69//
70// const const :Node = (
71// id = 0x0000000000000004,
72// displayName = "(null const schema)",
73// const = (type = (void = void), value = (void = void)));
74
75static const AlignedData<13> NULL_SCHEMA_BYTES = {{
76 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, // union discriminant intentionally mangled
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x11, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x63,
88 0x68, 0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00,
89}};
90const RawSchema NULL_SCHEMA = {
91 0x0000000000000000, NULL_SCHEMA_BYTES.words, 13,
92 nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
93 { &NULL_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
94};
95
96static const AlignedData<14> NULL_STRUCT_SCHEMA_BYTES = {{
97 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
98 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x11, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x74,
109 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x63, 0x68,
110 0x65, 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00,
111}};
112const RawSchema NULL_STRUCT_SCHEMA = {
113 0x0000000000000001, NULL_STRUCT_SCHEMA_BYTES.words, 14,
114 nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
115 { &NULL_STRUCT_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
116};
117
118static const AlignedData<14> NULL_ENUM_SCHEMA_BYTES = {{
119 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
120 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x11, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x65, 0x6e,
131 0x75, 0x6d, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d,
132 0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133}};
134const RawSchema NULL_ENUM_SCHEMA = {
135 0x0000000000000002, NULL_ENUM_SCHEMA_BYTES.words, 14,
136 nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
137 { &NULL_ENUM_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
138};
139
140static const AlignedData<14> NULL_INTERFACE_SCHEMA_BYTES = {{
141 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
142 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x11, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x69, 0x6e,
153 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x20,
154 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x29, 0x00,
155}};
156const RawSchema NULL_INTERFACE_SCHEMA = {
157 0x0000000000000003, NULL_INTERFACE_SCHEMA_BYTES.words, 14,
158 nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
159 { &NULL_INTERFACE_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
160};
161
162static const AlignedData<20> NULL_CONST_SCHEMA_BYTES = {{
163 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
164 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x11, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
173 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,
174 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x63, 0x6f,
175 0x6e, 0x73, 0x74, 0x20, 0x73, 0x63, 0x68, 0x65,
176 0x6d, 0x61, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183}};
184const RawSchema NULL_CONST_SCHEMA = {
185 0x0000000000000004, NULL_CONST_SCHEMA_BYTES.words, 20,
186 nullptr, nullptr, 0, 0, nullptr, nullptr, nullptr,
187 { &NULL_CONST_SCHEMA, nullptr, nullptr, 0, 0, nullptr }
188};
189
190} // namespace _ (private)
191
192// =======================================================================================
193
194schema::Node::Reader Schema::getProto() const {
195 return readMessageUnchecked<schema::Node>(raw->generic->encodedNode);
196}
197
198kj::ArrayPtr<const word> Schema::asUncheckedMessage() const {
199 return kj::arrayPtr(raw->generic->encodedNode, raw->generic->encodedSize);
200}
201
202Schema Schema::getDependency(uint64_t id, uint location) const {
203 {
204 // Binary search dependency list.
205 uint lower = 0;
206 uint upper = raw->dependencyCount;
207
208 while (lower < upper) {
209 uint mid = (lower + upper) / 2;
210
211 auto candidate = raw->dependencies[mid];
212 if (candidate.location == location) {
213 candidate.schema->ensureInitialized();
214 return Schema(candidate.schema);
215 } else if (candidate.location < location) {
216 lower = mid + 1;
217 } else {
218 upper = mid;
219 }
220 }
221 }
222
223 {
224 uint lower = 0;
225 uint upper = raw->generic->dependencyCount;
226
227 while (lower < upper) {
228 uint mid = (lower + upper) / 2;
229
230 const _::RawSchema* candidate = raw->generic->dependencies[mid];
231
232 uint64_t candidateId = candidate->id;
233 if (candidateId == id) {
234 candidate->ensureInitialized();
235 return Schema(&candidate->defaultBrand);
236 } else if (candidateId < id) {
237 lower = mid + 1;
238 } else {
239 upper = mid;
240 }
241 }
242 }
243
244 KJ_FAIL_REQUIRE("Requested ID not found in dependency table.", kj::hex(id)) {
245 return Schema();
246 }
247}
248
249Schema::BrandArgumentList Schema::getBrandArgumentsAtScope(uint64_t scopeId) const {
250 KJ_REQUIRE(getProto().getIsGeneric(), "Not a generic type.", getProto().getDisplayName());
251
252 for (auto scope: kj::range(raw->scopes, raw->scopes + raw->scopeCount)) {
253 if (scope->typeId == scopeId) {
254 // OK, this scope matches the scope we're looking for.
255 if (scope->isUnbound) {
256 return BrandArgumentList(scopeId, true);
257 } else {
258 return BrandArgumentList(scopeId, scope->bindingCount, scope->bindings);
259 }
260 }
261 }
262
263 // This scope is not listed in the scopes list.
264 return BrandArgumentList(scopeId, raw->isUnbound());
265}
266
267StructSchema Schema::asStruct() const {
268 KJ_REQUIRE(getProto().isStruct(), "Tried to use non-struct schema as a struct.",
269 getProto().getDisplayName()) {
270 return StructSchema();
271 }
272 return StructSchema(*this);
273}
274
275EnumSchema Schema::asEnum() const {
276 KJ_REQUIRE(getProto().isEnum(), "Tried to use non-enum schema as an enum.",
277 getProto().getDisplayName()) {
278 return EnumSchema();
279 }
280 return EnumSchema(*this);
281}
282
283InterfaceSchema Schema::asInterface() const {
284 KJ_REQUIRE(getProto().isInterface(), "Tried to use non-interface schema as an interface.",
285 getProto().getDisplayName()) {
286 return InterfaceSchema();
287 }
288 return InterfaceSchema(*this);
289}
290
291ConstSchema Schema::asConst() const {
292 KJ_REQUIRE(getProto().isConst(), "Tried to use non-constant schema as a constant.",
293 getProto().getDisplayName()) {
294 return ConstSchema();
295 }
296 return ConstSchema(*this);
297}
298
299kj::StringPtr Schema::getShortDisplayName() const {
300 auto proto = getProto();
301 return proto.getDisplayName().slice(proto.getDisplayNamePrefixLength());
302}
303
304void Schema::requireUsableAs(const _::RawSchema* expected) const {
305 KJ_REQUIRE(raw->generic == expected ||
306 (expected != nullptr && raw->generic->canCastTo == expected),
307 "This schema is not compatible with the requested native type.");
308}
309
310uint32_t Schema::getSchemaOffset(const schema::Value::Reader& value) const {
311 const word* ptr;
312
313 switch (value.which()) {
314 case schema::Value::TEXT:
315 ptr = reinterpret_cast<const word*>(value.getText().begin());
316 break;
317 case schema::Value::DATA:
318 ptr = reinterpret_cast<const word*>(value.getData().begin());
319 break;
320 case schema::Value::STRUCT:
321 ptr = value.getStruct().getAs<_::UncheckedMessage>();
322 break;
323 case schema::Value::LIST:
324 ptr = value.getList().getAs<_::UncheckedMessage>();
325 break;
326 case schema::Value::ANY_POINTER:
327 ptr = value.getAnyPointer().getAs<_::UncheckedMessage>();
328 break;
329 default:
330 KJ_FAIL_ASSERT("getDefaultValueSchemaOffset() can only be called on struct, list, "
331 "and any-pointer fields.");
332 }
333
334 return ptr - raw->generic->encodedNode;
335}
336
337Type Schema::getBrandBinding(uint64_t scopeId, uint index) const {
338 return getBrandArgumentsAtScope(scopeId)[index];
339}
340
341Type Schema::interpretType(schema::Type::Reader proto, uint location) const {
342 switch (proto.which()) {
343 case schema::Type::VOID:
344 case schema::Type::BOOL:
345 case schema::Type::INT8:
346 case schema::Type::INT16:
347 case schema::Type::INT32:
348 case schema::Type::INT64:
349 case schema::Type::UINT8:
350 case schema::Type::UINT16:
351 case schema::Type::UINT32:
352 case schema::Type::UINT64:
353 case schema::Type::FLOAT32:
354 case schema::Type::FLOAT64:
355 case schema::Type::TEXT:
356 case schema::Type::DATA:
357 return proto.which();
358
359 case schema::Type::STRUCT: {
360 auto structType = proto.getStruct();
361 return getDependency(structType.getTypeId(), location).asStruct();
362 }
363
364 case schema::Type::ENUM: {
365 auto enumType = proto.getEnum();
366 return getDependency(enumType.getTypeId(), location).asEnum();
367 }
368
369 case schema::Type::INTERFACE: {
370 auto interfaceType = proto.getInterface();
371 return getDependency(interfaceType.getTypeId(), location).asInterface();
372 }
373
374 case schema::Type::LIST:
375 return ListSchema::of(interpretType(proto.getList().getElementType(), location));
376
377 case schema::Type::ANY_POINTER: {
378 auto anyPointer = proto.getAnyPointer();
379 switch (anyPointer.which()) {
380 case schema::Type::AnyPointer::UNCONSTRAINED:
381 return anyPointer.getUnconstrained().which();
382 case schema::Type::AnyPointer::PARAMETER: {
383 auto param = anyPointer.getParameter();
384 return getBrandBinding(param.getScopeId(), param.getParameterIndex());
385 }
386 case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER:
387 return Type(Type::ImplicitParameter {
388 anyPointer.getImplicitMethodParameter().getParameterIndex() });
389 }
390
391 KJ_UNREACHABLE;
392 }
393 }
394
395 KJ_UNREACHABLE;
396}
397
398Type Schema::BrandArgumentList::operator[](uint index) const {
399 if (isUnbound) {
400 return Type::BrandParameter { scopeId, index };
401 }
402
403 if (index >= size_) {
404 // Binding index out-of-range. Treat as AnyPointer. This is important to allow new
405 // type parameters to be added to existing types without breaking dependent
406 // schemas.
407 return schema::Type::ANY_POINTER;
408 }
409
410 auto& binding = bindings[index];
411 Type result;
412 if (binding.which == (uint)schema::Type::ANY_POINTER) {
413 if (binding.scopeId != 0) {
414 result = Type::BrandParameter { binding.scopeId, binding.paramIndex };
415 } else if (binding.isImplicitParameter) {
416 result = Type::ImplicitParameter { binding.paramIndex };
417 } else {
418 result = static_cast<schema::Type::AnyPointer::Unconstrained::Which>(binding.paramIndex);
419 }
420 } else if (binding.schema == nullptr) {
421 // Builtin / primitive type.
422 result = static_cast<schema::Type::Which>(binding.which);
423 } else {
424 binding.schema->ensureInitialized();
425 result = Type(static_cast<schema::Type::Which>(binding.which), binding.schema);
426 }
427
428 return result.wrapInList(binding.listDepth);
429}
430
431kj::StringPtr KJ_STRINGIFY(const Schema& schema) {
432 return schema.getProto().getDisplayName();
433}
434
435// =======================================================================================
436
437namespace {
438
439template <typename List>
440auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name, List&& list)
441 -> kj::Maybe<decltype(list[0])> {
442 uint lower = 0;
443 uint upper = raw->memberCount;
444
445 while (lower < upper) {
446 uint mid = (lower + upper) / 2;
447
448 uint16_t memberIndex = raw->membersByName[mid];
449
450 auto candidate = list[memberIndex];
451 kj::StringPtr candidateName = candidate.getProto().getName();
452 if (candidateName == name) {
453 return candidate;
454 } else if (candidateName < name) {
455 lower = mid + 1;
456 } else {
457 upper = mid;
458 }
459 }
460
461 return nullptr;
462}
463
464} // namespace
465
466StructSchema::FieldList StructSchema::getFields() const {
467 return FieldList(*this, getProto().getStruct().getFields());
468}
469
470StructSchema::FieldSubset StructSchema::getUnionFields() const {
471 auto proto = getProto().getStruct();
472 return FieldSubset(*this, proto.getFields(),
473 raw->generic->membersByDiscriminant, proto.getDiscriminantCount());
474}
475
476StructSchema::FieldSubset StructSchema::getNonUnionFields() const {
477 auto proto = getProto().getStruct();
478 auto fields = proto.getFields();
479 auto offset = proto.getDiscriminantCount();
480 auto size = fields.size() - offset;
481 return FieldSubset(*this, fields, raw->generic->membersByDiscriminant + offset, size);
482}
483
484kj::Maybe<StructSchema::Field> StructSchema::findFieldByName(kj::StringPtr name) const {
485 return findSchemaMemberByName(raw->generic, name, getFields());
486}
487
488StructSchema::Field StructSchema::getFieldByName(kj::StringPtr name) const {
489 KJ_IF_MAYBE(member, findFieldByName(name)) {
490 return *member;
491 } else {
492 KJ_FAIL_REQUIRE("struct has no such member", name);
493 }
494}
495
496kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t discriminant) const {
497 auto unionFields = getUnionFields();
498
499 if (discriminant >= unionFields.size()) {
500 return nullptr;
501 } else {
502 return unionFields[discriminant];
503 }
504}
505
506Type StructSchema::Field::getType() const {
507 auto proto = getProto();
508 uint location = _::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::FIELD, index);
509
510 switch (proto.which()) {
511 case schema::Field::SLOT:
512 return parent.interpretType(proto.getSlot().getType(), location);
513
514 case schema::Field::GROUP:
515 return parent.getDependency(proto.getGroup().getTypeId(), location).asStruct();
516 }
517 KJ_UNREACHABLE;
518}
519
520uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const {
521 return parent.getSchemaOffset(proto.getSlot().getDefaultValue());
522}
523
524kj::StringPtr KJ_STRINGIFY(const StructSchema::Field& field) {
525 return field.getProto().getName();
526}
527
528// -------------------------------------------------------------------
529
530EnumSchema::EnumerantList EnumSchema::getEnumerants() const {
531 return EnumerantList(*this, getProto().getEnum().getEnumerants());
532}
533
534kj::Maybe<EnumSchema::Enumerant> EnumSchema::findEnumerantByName(kj::StringPtr name) const {
535 return findSchemaMemberByName(raw->generic, name, getEnumerants());
536}
537
538EnumSchema::Enumerant EnumSchema::getEnumerantByName(kj::StringPtr name) const {
539 KJ_IF_MAYBE(enumerant, findEnumerantByName(name)) {
540 return *enumerant;
541 } else {
542 KJ_FAIL_REQUIRE("enum has no such enumerant", name);
543 }
544}
545
546// -------------------------------------------------------------------
547
548InterfaceSchema::MethodList InterfaceSchema::getMethods() const {
549 return MethodList(*this, getProto().getInterface().getMethods());
550}
551
552kj::Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(kj::StringPtr name) const {
553 uint counter = 0;
554 return findMethodByName(name, counter);
555}
556
557static constexpr uint MAX_SUPERCLASSES = 64;
558
559kj::Maybe<InterfaceSchema::Method> InterfaceSchema::findMethodByName(
560 kj::StringPtr name, uint& counter) const {
561 // Security: Don't let someone DOS us with a dynamic schema containing cyclic inheritance.
562 KJ_REQUIRE(counter++ < MAX_SUPERCLASSES, "Cyclic or absurdly-large inheritance graph detected.") {
563 return nullptr;
564 }
565
566 auto result = findSchemaMemberByName(raw->generic, name, getMethods());
567
568 if (result == nullptr) {
569 // Search superclasses.
570 // TODO(perf): This may be somewhat slow, and in the case of lots of diamond dependencies it
571 // could get pathological. Arguably we should generate a flat list of transitive
572 // superclasses to search and store it in the RawSchema. It's problematic, though, because
573 // this means that a dynamically-loaded RawSchema cannot be correctly constructed until all
574 // superclasses have been loaded, which imposes an ordering requirement on SchemaLoader or
575 // requires updating subclasses whenever a new superclass is loaded.
576 auto superclasses = getProto().getInterface().getSuperclasses();
577 for (auto i: kj::indices(superclasses)) {
578 auto superclass = superclasses[i];
579 uint location = _::RawBrandedSchema::makeDepLocation(
580 _::RawBrandedSchema::DepKind::SUPERCLASS, i);
581 result = getDependency(superclass.getId(), location)
582 .asInterface().findMethodByName(name, counter);
583 if (result != nullptr) {
584 break;
585 }
586 }
587 }
588
589 return result;
590}
591
592InterfaceSchema::Method InterfaceSchema::getMethodByName(kj::StringPtr name) const {
593 KJ_IF_MAYBE(method, findMethodByName(name)) {
594 return *method;
595 } else {
596 KJ_FAIL_REQUIRE("interface has no such method", name);
597 }
598}
599
600InterfaceSchema::SuperclassList InterfaceSchema::getSuperclasses() const {
601 return SuperclassList(*this, getProto().getInterface().getSuperclasses());
602}
603
604bool InterfaceSchema::extends(InterfaceSchema other) const {
605 if (other.raw->generic == &_::NULL_INTERFACE_SCHEMA) {
606 // We consider all interfaces to extend the null schema.
607 return true;
608 }
609 uint counter = 0;
610 return extends(other, counter);
611}
612
613bool InterfaceSchema::extends(InterfaceSchema other, uint& counter) const {
614 // Security: Don't let someone DOS us with a dynamic schema containing cyclic inheritance.
615 KJ_REQUIRE(counter++ < MAX_SUPERCLASSES, "Cyclic or absurdly-large inheritance graph detected.") {
616 return false;
617 }
618
619 if (other == *this) {
620 return true;
621 }
622
623 // TODO(perf): This may be somewhat slow. See findMethodByName() for discussion.
624 auto superclasses = getProto().getInterface().getSuperclasses();
625 for (auto i: kj::indices(superclasses)) {
626 auto superclass = superclasses[i];
627 uint location = _::RawBrandedSchema::makeDepLocation(
628 _::RawBrandedSchema::DepKind::SUPERCLASS, i);
629 if (getDependency(superclass.getId(), location).asInterface().extends(other, counter)) {
630 return true;
631 }
632 }
633
634 return false;
635}
636
637kj::Maybe<InterfaceSchema> InterfaceSchema::findSuperclass(uint64_t typeId) const {
638 if (typeId == _::NULL_INTERFACE_SCHEMA.id) {
639 // We consider all interfaces to extend the null schema.
640 return InterfaceSchema();
641 }
642 uint counter = 0;
643 return findSuperclass(typeId, counter);
644}
645
646kj::Maybe<InterfaceSchema> InterfaceSchema::findSuperclass(uint64_t typeId, uint& counter) const {
647 // Security: Don't let someone DOS us with a dynamic schema containing cyclic inheritance.
648 KJ_REQUIRE(counter++ < MAX_SUPERCLASSES, "Cyclic or absurdly-large inheritance graph detected.") {
649 return nullptr;
650 }
651
652 if (typeId == raw->generic->id) {
653 return *this;
654 }
655
656 // TODO(perf): This may be somewhat slow. See findMethodByName() for discussion.
657 auto superclasses = getProto().getInterface().getSuperclasses();
658 for (auto i: kj::indices(superclasses)) {
659 auto superclass = superclasses[i];
660 uint location = _::RawBrandedSchema::makeDepLocation(
661 _::RawBrandedSchema::DepKind::SUPERCLASS, i);
662 KJ_IF_MAYBE(result, getDependency(superclass.getId(), location).asInterface()
663 .findSuperclass(typeId, counter)) {
664 return *result;
665 }
666 }
667
668 return nullptr;
669}
670
671StructSchema InterfaceSchema::Method::getParamType() const {
672 auto proto = getProto();
673 uint location = _::RawBrandedSchema::makeDepLocation(
674 _::RawBrandedSchema::DepKind::METHOD_PARAMS, ordinal);
675 return parent.getDependency(proto.getParamStructType(), location).asStruct();
676}
677
678StructSchema InterfaceSchema::Method::getResultType() const {
679 auto proto = getProto();
680 uint location = _::RawBrandedSchema::makeDepLocation(
681 _::RawBrandedSchema::DepKind::METHOD_RESULTS, ordinal);
682 return parent.getDependency(proto.getResultStructType(), location).asStruct();
683}
684
685InterfaceSchema InterfaceSchema::SuperclassList::operator[](uint index) const {
686 auto superclass = list[index];
687 uint location = _::RawBrandedSchema::makeDepLocation(
688 _::RawBrandedSchema::DepKind::SUPERCLASS, index);
689 return parent.getDependency(superclass.getId(), location).asInterface();
690}
691
692// -------------------------------------------------------------------
693
694uint32_t ConstSchema::getValueSchemaOffset() const {
695 return getSchemaOffset(getProto().getConst().getValue());
696}
697
698Type ConstSchema::getType() const {
699 return interpretType(getProto().getConst().getType(),
700 _::RawBrandedSchema::makeDepLocation(_::RawBrandedSchema::DepKind::CONST_TYPE, 0));
701}
702
703// =======================================================================================
704
705ListSchema ListSchema::of(schema::Type::Which primitiveType) {
706 switch (primitiveType) {
707 case schema::Type::VOID:
708 case schema::Type::BOOL:
709 case schema::Type::INT8:
710 case schema::Type::INT16:
711 case schema::Type::INT32:
712 case schema::Type::INT64:
713 case schema::Type::UINT8:
714 case schema::Type::UINT16:
715 case schema::Type::UINT32:
716 case schema::Type::UINT64:
717 case schema::Type::FLOAT32:
718 case schema::Type::FLOAT64:
719 case schema::Type::TEXT:
720 case schema::Type::DATA:
721 break;
722
723 case schema::Type::STRUCT:
724 case schema::Type::ENUM:
725 case schema::Type::INTERFACE:
726 case schema::Type::LIST:
727 KJ_FAIL_REQUIRE("Must use one of the other ListSchema::of() overloads for complex types.");
728 break;
729
730 case schema::Type::ANY_POINTER:
731 KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
732 break;
733 }
734
735 return ListSchema(primitiveType);
736}
737
738ListSchema ListSchema::of(schema::Type::Reader elementType, Schema context) {
739 // This method is deprecated because it can only be implemented in terms of other deprecated
740 // methods. Temporarily disable warnings for those other deprecated methods.
741#pragma GCC diagnostic push
742#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
743
744 switch (elementType.which()) {
745 case schema::Type::VOID:
746 case schema::Type::BOOL:
747 case schema::Type::INT8:
748 case schema::Type::INT16:
749 case schema::Type::INT32:
750 case schema::Type::INT64:
751 case schema::Type::UINT8:
752 case schema::Type::UINT16:
753 case schema::Type::UINT32:
754 case schema::Type::UINT64:
755 case schema::Type::FLOAT32:
756 case schema::Type::FLOAT64:
757 case schema::Type::TEXT:
758 case schema::Type::DATA:
759 return of(elementType.which());
760
761 case schema::Type::STRUCT:
762 return of(context.getDependency(elementType.getStruct().getTypeId()).asStruct());
763
764 case schema::Type::ENUM:
765 return of(context.getDependency(elementType.getEnum().getTypeId()).asEnum());
766
767 case schema::Type::INTERFACE:
768 return of(context.getDependency(elementType.getInterface().getTypeId()).asInterface());
769
770 case schema::Type::LIST:
771 return of(of(elementType.getList().getElementType(), context));
772
773 case schema::Type::ANY_POINTER:
774 KJ_FAIL_REQUIRE("List(AnyPointer) not supported.");
775 return ListSchema();
776 }
777
778 // Unknown type is acceptable.
779 return ListSchema(elementType.which());
780#pragma GCC diagnostic pop
781}
782
783// =======================================================================================
784
785StructSchema Type::asStruct() const {
786 KJ_REQUIRE(isStruct(), "Tried to interpret a non-struct type as a struct.") {
787 return StructSchema();
788 }
789 KJ_ASSERT(schema != nullptr);
790 return StructSchema(Schema(schema));
791}
792EnumSchema Type::asEnum() const {
793 KJ_REQUIRE(isEnum(), "Tried to interpret a non-enum type as an enum.") {
794 return EnumSchema();
795 }
796 KJ_ASSERT(schema != nullptr);
797 return EnumSchema(Schema(schema));
798}
799InterfaceSchema Type::asInterface() const {
800 KJ_REQUIRE(isInterface(), "Tried to interpret a non-interface type as an interface.") {
801 return InterfaceSchema();
802 }
803 KJ_ASSERT(schema != nullptr);
804 return InterfaceSchema(Schema(schema));
805}
806ListSchema Type::asList() const {
807 KJ_REQUIRE(isList(), "Type::asList(): Not a list.") {
808 return ListSchema::of(schema::Type::VOID);
809 }
810 Type elementType = *this;
811 --elementType.listDepth;
812 return ListSchema::of(elementType);
813}
814
815kj::Maybe<Type::BrandParameter> Type::getBrandParameter() const {
816 KJ_REQUIRE(isAnyPointer(), "Type::getBrandParameter() can only be called on AnyPointer types.");
817
818 if (scopeId == 0) {
819 return nullptr;
820 } else {
821 return BrandParameter { scopeId, paramIndex };
822 }
823}
824
825kj::Maybe<Type::ImplicitParameter> Type::getImplicitParameter() const {
826 KJ_REQUIRE(isAnyPointer(),
827 "Type::getImplicitParameter() can only be called on AnyPointer types.");
828
829 if (isImplicitParam) {
830 return ImplicitParameter { paramIndex };
831 } else {
832 return nullptr;
833 }
834}
835
836bool Type::operator==(const Type& other) const {
837 if (baseType != other.baseType || listDepth != other.listDepth) {
838 return false;
839 }
840
841 switch (baseType) {
842 case schema::Type::VOID:
843 case schema::Type::BOOL:
844 case schema::Type::INT8:
845 case schema::Type::INT16:
846 case schema::Type::INT32:
847 case schema::Type::INT64:
848 case schema::Type::UINT8:
849 case schema::Type::UINT16:
850 case schema::Type::UINT32:
851 case schema::Type::UINT64:
852 case schema::Type::FLOAT32:
853 case schema::Type::FLOAT64:
854 case schema::Type::TEXT:
855 case schema::Type::DATA:
856 return true;
857
858 case schema::Type::STRUCT:
859 case schema::Type::ENUM:
860 case schema::Type::INTERFACE:
861 return schema == other.schema;
862
863 case schema::Type::LIST:
864 KJ_UNREACHABLE;
865
866 case schema::Type::ANY_POINTER:
867 return scopeId == other.scopeId && isImplicitParam == other.isImplicitParam &&
868 // Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
869 // both branches compile to the same instructions and can optimize it away.
870 (scopeId != 0 || isImplicitParam ? paramIndex == other.paramIndex
871 : anyPointerKind == other.anyPointerKind);
872 }
873
874 KJ_UNREACHABLE;
875}
876
877uint Type::hashCode() const {
878 switch (baseType) {
879 case schema::Type::VOID:
880 case schema::Type::BOOL:
881 case schema::Type::INT8:
882 case schema::Type::INT16:
883 case schema::Type::INT32:
884 case schema::Type::INT64:
885 case schema::Type::UINT8:
886 case schema::Type::UINT16:
887 case schema::Type::UINT32:
888 case schema::Type::UINT64:
889 case schema::Type::FLOAT32:
890 case schema::Type::FLOAT64:
891 case schema::Type::TEXT:
892 case schema::Type::DATA:
893 return kj::hashCode(baseType, listDepth);
894
895 case schema::Type::STRUCT:
896 case schema::Type::ENUM:
897 case schema::Type::INTERFACE:
898 return kj::hashCode(schema, listDepth);
899
900 case schema::Type::LIST:
901 KJ_UNREACHABLE;
902
903 case schema::Type::ANY_POINTER: {
904 // Trying to comply with strict aliasing rules. Hopefully the compiler realizes that
905 // both branches compile to the same instructions and can optimize it away.
906 uint16_t val = scopeId != 0 || isImplicitParam ?
907 paramIndex : static_cast<uint16_t>(anyPointerKind);
908 return kj::hashCode(val, isImplicitParam, scopeId);
909 }
910 }
911
912 KJ_UNREACHABLE;
913}
914
915void Type::requireUsableAs(Type expected) const {
916 KJ_REQUIRE(baseType == expected.baseType && listDepth == expected.listDepth,
917 "This type is not compatible with the requested native type.");
918
919 switch (baseType) {
920 case schema::Type::VOID:
921 case schema::Type::BOOL:
922 case schema::Type::INT8:
923 case schema::Type::INT16:
924 case schema::Type::INT32:
925 case schema::Type::INT64:
926 case schema::Type::UINT8:
927 case schema::Type::UINT16:
928 case schema::Type::UINT32:
929 case schema::Type::UINT64:
930 case schema::Type::FLOAT32:
931 case schema::Type::FLOAT64:
932 case schema::Type::TEXT:
933 case schema::Type::DATA:
934 case schema::Type::ANY_POINTER:
935 break;
936
937 case schema::Type::STRUCT:
938 case schema::Type::ENUM:
939 case schema::Type::INTERFACE:
940 Schema(schema).requireUsableAs(expected.schema->generic);
941 break;
942
943 case schema::Type::LIST:
944 KJ_UNREACHABLE;
945 }
946}
947
948} // namespace capnp
949