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