capnpc-c++.c++ (126998B)
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 // This program is a code generator plugin for `capnp compile` which generates C++ code. 23 24 #if _WIN32 25 #include <kj/win32-api-version.h> 26 #endif 27 28 #include <capnp/schema.capnp.h> 29 #include "../serialize.h" 30 #include <kj/debug.h> 31 #include <kj/io.h> 32 #include <kj/string-tree.h> 33 #include <kj/tuple.h> 34 #include <kj/vector.h> 35 #include <kj/filesystem.h> 36 #include "../schema-loader.h" 37 #include "../dynamic.h" 38 #include <unordered_map> 39 #include <unordered_set> 40 #include <map> 41 #include <set> 42 #include <kj/main.h> 43 #include <algorithm> 44 #include <capnp/stream.capnp.h> 45 46 #if _WIN32 47 #include <windows.h> 48 #include <kj/windows-sanity.h> 49 #undef CONST 50 #else 51 #include <sys/time.h> 52 #endif 53 54 #if HAVE_CONFIG_H 55 #include "config.h" 56 #endif 57 58 #ifndef VERSION 59 #define VERSION "(unknown)" 60 #endif 61 62 namespace capnp { 63 namespace { 64 65 static constexpr uint64_t NAMESPACE_ANNOTATION_ID = 0xb9c6f99ebf805f2cull; 66 static constexpr uint64_t NAME_ANNOTATION_ID = 0xf264a779fef191ceull; 67 68 bool hasDiscriminantValue(const schema::Field::Reader& reader) { 69 return reader.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT; 70 } 71 72 void enumerateDeps(schema::Type::Reader type, std::set<uint64_t>& deps) { 73 switch (type.which()) { 74 case schema::Type::STRUCT: 75 deps.insert(type.getStruct().getTypeId()); 76 break; 77 case schema::Type::ENUM: 78 deps.insert(type.getEnum().getTypeId()); 79 break; 80 case schema::Type::INTERFACE: 81 deps.insert(type.getInterface().getTypeId()); 82 break; 83 case schema::Type::LIST: 84 enumerateDeps(type.getList().getElementType(), deps); 85 break; 86 default: 87 break; 88 } 89 } 90 91 void enumerateDeps(schema::Node::Reader node, std::set<uint64_t>& deps) { 92 switch (node.which()) { 93 case schema::Node::STRUCT: { 94 auto structNode = node.getStruct(); 95 for (auto field: structNode.getFields()) { 96 switch (field.which()) { 97 case schema::Field::SLOT: 98 enumerateDeps(field.getSlot().getType(), deps); 99 break; 100 case schema::Field::GROUP: 101 deps.insert(field.getGroup().getTypeId()); 102 break; 103 } 104 } 105 if (structNode.getIsGroup()) { 106 deps.insert(node.getScopeId()); 107 } 108 break; 109 } 110 case schema::Node::INTERFACE: { 111 auto interfaceNode = node.getInterface(); 112 for (auto superclass: interfaceNode.getSuperclasses()) { 113 deps.insert(superclass.getId()); 114 } 115 for (auto method: interfaceNode.getMethods()) { 116 deps.insert(method.getParamStructType()); 117 deps.insert(method.getResultStructType()); 118 } 119 break; 120 } 121 default: 122 break; 123 } 124 } 125 126 struct OrderByName { 127 template <typename T> 128 inline bool operator()(const T& a, const T& b) const { 129 return a.getProto().getName() < b.getProto().getName(); 130 } 131 }; 132 133 template <typename MemberList> 134 kj::Array<uint> makeMembersByName(MemberList&& members) { 135 auto sorted = KJ_MAP(member, members) { return member; }; 136 std::sort(sorted.begin(), sorted.end(), OrderByName()); 137 return KJ_MAP(member, sorted) { return member.getIndex(); }; 138 } 139 140 kj::StringPtr baseName(kj::StringPtr path) { 141 KJ_IF_MAYBE(slashPos, path.findLast('/')) { 142 return path.slice(*slashPos + 1); 143 } else { 144 return path; 145 } 146 } 147 148 kj::String safeIdentifier(kj::StringPtr identifier) { 149 // Given a desired identifier name, munge it to make it safe for use in generated code. 150 // 151 // If the identifier is a keyword, this adds an underscore to the end. 152 153 static const std::set<kj::StringPtr> keywords({ 154 "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", 155 "case", "catch", "char", "char16_t", "char32_t", "class", "compl", "const", "constexpr", 156 "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", 157 "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", 158 "if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not", "not_eq", 159 "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register", 160 "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_assert", 161 "static_cast", "struct", "switch", "template", "this", "thread_local", "throw", "true", 162 "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", 163 "volatile", "wchar_t", "while", "xor", "xor_eq" 164 }); 165 166 if (keywords.count(identifier) > 0) { 167 return kj::str(identifier, '_'); 168 } else { 169 return kj::heapString(identifier); 170 } 171 } 172 173 // ======================================================================================= 174 175 class CppTypeName { 176 // Used to build a C++ type name string. This is complicated in the presence of templates, 177 // because we must add the "typename" and "template" disambiguator keywords as needed. 178 179 public: 180 inline CppTypeName(): isArgDependent(false), needsTypename(false), 181 hasInterfaces_(false), hasDisambiguatedTemplate_(false) {} 182 CppTypeName(CppTypeName&& other) = default; 183 CppTypeName(const CppTypeName& other) 184 : name(kj::strTree(other.name.flatten())), 185 isArgDependent(other.isArgDependent), 186 needsTypename(other.needsTypename), 187 hasInterfaces_(other.hasInterfaces_), 188 hasDisambiguatedTemplate_(other.hasDisambiguatedTemplate_) {} 189 190 CppTypeName& operator=(CppTypeName&& other) = default; 191 CppTypeName& operator=(const CppTypeName& other) { 192 name = kj::strTree(other.name.flatten()); 193 isArgDependent = other.isArgDependent; 194 needsTypename = other.needsTypename; 195 return *this; 196 } 197 198 static CppTypeName makeRoot() { 199 return CppTypeName(kj::strTree(" "), false); 200 } 201 202 static CppTypeName makeNamespace(kj::StringPtr name) { 203 return CppTypeName(kj::strTree(" ::", name), false); 204 } 205 206 static CppTypeName makeTemplateParam(kj::StringPtr name) { 207 return CppTypeName(kj::strTree(name), true); 208 } 209 210 static CppTypeName makePrimitive(kj::StringPtr name) { 211 return CppTypeName(kj::strTree(name), false); 212 } 213 214 void addMemberType(kj::StringPtr innerName) { 215 // Append "::innerName" to refer to a member. 216 217 name = kj::strTree(kj::mv(name), "::", innerName); 218 needsTypename = isArgDependent; 219 } 220 221 void addMemberValue(kj::StringPtr innerName) { 222 // Append "::innerName" to refer to a member. 223 224 name = kj::strTree(kj::mv(name), "::", innerName); 225 needsTypename = false; 226 } 227 228 bool hasDisambiguatedTemplate() { 229 return hasDisambiguatedTemplate_; 230 } 231 232 void addMemberTemplate(kj::StringPtr innerName, kj::Array<CppTypeName>&& params) { 233 // Append "::innerName<params, ...>". 234 // 235 // If necessary, add the "template" disambiguation keyword in front of `innerName`. 236 237 bool parentIsArgDependent = isArgDependent; 238 needsTypename = parentIsArgDependent; 239 hasDisambiguatedTemplate_ = hasDisambiguatedTemplate_ || parentIsArgDependent; 240 241 name = kj::strTree(kj::mv(name), 242 parentIsArgDependent ? "::template " : "::", 243 innerName, '<', 244 kj::StringTree(KJ_MAP(p, params) { 245 if (p.isArgDependent) isArgDependent = true; 246 if (p.hasInterfaces_) hasInterfaces_ = true; 247 if (p.hasDisambiguatedTemplate_) hasDisambiguatedTemplate_ = true; 248 return kj::strTree(kj::mv(p)); 249 }, ", "), 250 '>'); 251 } 252 253 void setHasInterfaces() { 254 hasInterfaces_ = true; 255 } 256 257 bool hasInterfaces() { 258 return hasInterfaces_; 259 } 260 261 kj::StringTree strNoTypename() const & { return name.flatten(); } 262 kj::StringTree strNoTypename() && { return kj::mv(name); } 263 // Stringify but never prefix with `typename`. Use in contexts where `typename` is implicit. 264 265 private: 266 kj::StringTree name; 267 268 bool isArgDependent; 269 // Does the name contain any template-argument-dependent types? 270 271 bool needsTypename; 272 // Does the name require a prefix of "typename"? 273 274 bool hasInterfaces_; 275 // Does this type name refer to any interface types? If so it may need to be #ifdefed out in 276 // lite mode. 277 278 bool hasDisambiguatedTemplate_; 279 // Whether the type name contains a template type that had to be disambiguated using the 280 // "template" keyword, e.g. "Foo<T>::template Bar<U>". 281 // 282 // TODO(msvc): We only track this because MSVC seems to get confused by it in some weird cases. 283 284 inline CppTypeName(kj::StringTree&& name, bool isArgDependent) 285 : name(kj::mv(name)), isArgDependent(isArgDependent), needsTypename(false), 286 hasInterfaces_(false), hasDisambiguatedTemplate_(false) {} 287 288 friend kj::StringTree KJ_STRINGIFY(CppTypeName&& typeName); 289 friend kj::String KJ_STRINGIFY(const CppTypeName& typeName); 290 }; 291 292 kj::StringTree KJ_STRINGIFY(CppTypeName&& typeName) { 293 if (typeName.needsTypename) { 294 return kj::strTree("typename ", kj::mv(typeName.name)); 295 } else { 296 return kj::mv(typeName.name); 297 } 298 } 299 kj::String KJ_STRINGIFY(const CppTypeName& typeName) { 300 if (typeName.needsTypename) { 301 return kj::str("typename ", typeName.name); 302 } else { 303 return typeName.name.flatten(); 304 } 305 } 306 307 CppTypeName whichKind(Type type) { 308 // Make a CppTypeName representing the capnp::Kind value for the given schema type. This makes 309 // CppTypeName conflate types and values, but this is all just a hack for MSVC's benefit. Its 310 // primary use is as a non-type template parameter to `capnp::List<T, K>` -- normally the Kind K 311 // is deduced via SFINAE, but MSVC just can't do it in certain cases, such as when a nested type 312 // of `capnp::List<T, K>` is the return type of a function, and the element type T is a template 313 // instantiation. 314 315 switch (type.which()) { 316 case schema::Type::VOID: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 317 318 case schema::Type::BOOL: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 319 case schema::Type::INT8: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 320 case schema::Type::INT16: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 321 case schema::Type::INT32: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 322 case schema::Type::INT64: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 323 case schema::Type::UINT8: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 324 case schema::Type::UINT16: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 325 case schema::Type::UINT32: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 326 case schema::Type::UINT64: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 327 case schema::Type::FLOAT32: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 328 case schema::Type::FLOAT64: return CppTypeName::makePrimitive(" ::capnp::Kind::PRIMITIVE"); 329 330 case schema::Type::TEXT: return CppTypeName::makePrimitive(" ::capnp::Kind::BLOB"); 331 case schema::Type::DATA: return CppTypeName::makePrimitive(" ::capnp::Kind::BLOB"); 332 333 case schema::Type::ENUM: return CppTypeName::makePrimitive(" ::capnp::Kind::ENUM"); 334 case schema::Type::STRUCT: return CppTypeName::makePrimitive(" ::capnp::Kind::STRUCT"); 335 case schema::Type::INTERFACE: return CppTypeName::makePrimitive(" ::capnp::Kind::INTERFACE"); 336 337 case schema::Type::LIST: return CppTypeName::makePrimitive(" ::capnp::Kind::LIST"); 338 case schema::Type::ANY_POINTER: { 339 switch (type.whichAnyPointerKind()) { 340 case schema::Type::AnyPointer::Unconstrained::CAPABILITY: 341 return CppTypeName::makePrimitive(" ::capnp::Kind::INTERFACE"); 342 default: 343 return CppTypeName::makePrimitive(" ::capnp::Kind::OTHER"); 344 } 345 } 346 } 347 348 KJ_UNREACHABLE; 349 } 350 351 // ======================================================================================= 352 353 class CapnpcCppMain { 354 public: 355 CapnpcCppMain(kj::ProcessContext& context): context(context) {} 356 357 kj::MainFunc getMain() { 358 return kj::MainBuilder(context, "Cap'n Proto C++ plugin version " VERSION, 359 "This is a Cap'n Proto compiler plugin which generates C++ code. " 360 "It is meant to be run using the Cap'n Proto compiler, e.g.:\n" 361 " capnp compile -oc++ foo.capnp") 362 .callAfterParsing(KJ_BIND_METHOD(*this, run)) 363 .build(); 364 } 365 366 private: 367 kj::ProcessContext& context; 368 SchemaLoader schemaLoader; 369 std::unordered_set<uint64_t> usedImports; 370 bool hasInterfaces = false; 371 372 CppTypeName cppFullName(Schema schema, kj::Maybe<InterfaceSchema::Method> method) { 373 return cppFullName(schema, schema, method); 374 } 375 376 CppTypeName cppFullName(Schema schema, Schema brand, kj::Maybe<InterfaceSchema::Method> method) { 377 auto node = schema.getProto(); 378 379 if (node.getScopeId() == 0) { 380 // This is the file-level scope. Search for the namespace annotation. 381 KJ_REQUIRE(node.isFile(), 382 "Non-file had scopeId zero; perhaps it's a method param / result struct?"); 383 usedImports.insert(node.getId()); 384 KJ_IF_MAYBE(ns, annotationValue(node, NAMESPACE_ANNOTATION_ID)) { 385 return CppTypeName::makeNamespace(ns->getText()); 386 } else { 387 return CppTypeName::makeRoot(); 388 } 389 } else { 390 // This is a named type. 391 392 // Figure out what name to use. 393 Schema parent = schemaLoader.get(node.getScopeId()); 394 kj::StringPtr unqualifiedName; 395 kj::String ownUnqualifiedName; 396 KJ_IF_MAYBE(annotatedName, annotationValue(node, NAME_ANNOTATION_ID)) { 397 // The node's name has been overridden for C++ by an annotation. 398 unqualifiedName = annotatedName->getText(); 399 } else { 400 // Search among the parent's nested nodes to for this node, in order to determine its name. 401 auto parentProto = parent.getProto(); 402 for (auto nested: parentProto.getNestedNodes()) { 403 if (nested.getId() == node.getId()) { 404 unqualifiedName = nested.getName(); 405 break; 406 } 407 } 408 if (unqualifiedName == nullptr) { 409 // Hmm, maybe it's a group node? 410 if (parentProto.isStruct()) { 411 for (auto field: parentProto.getStruct().getFields()) { 412 if (field.isGroup() && field.getGroup().getTypeId() == node.getId()) { 413 ownUnqualifiedName = toTitleCase(protoName(field)); 414 unqualifiedName = ownUnqualifiedName; 415 break; 416 } 417 } 418 } 419 } 420 KJ_REQUIRE(unqualifiedName != nullptr, 421 "A schema Node's supposed scope did not contain the node as a NestedNode."); 422 } 423 424 auto result = cppFullName(parent, brand, method); 425 426 // Construct the generic arguments. 427 auto params = node.getParameters(); 428 if (params.size() > 0) { 429 auto args = brand.getBrandArgumentsAtScope(node.getId()); 430 431 #if 0 432 // Figure out exactly how many params are not bound to AnyPointer. 433 // TODO(msvc): In a few obscure cases, MSVC does not like empty template pramater lists, 434 // even if all parameters have defaults. So, we give in and explicitly list all 435 // parameters in our generated code for now. Try again later. 436 uint paramCount = 0; 437 for (uint i: kj::indices(params)) { 438 auto arg = args[i]; 439 if (arg.which() != schema::Type::ANY_POINTER || arg.getBrandParameter() != nullptr || 440 (arg.getImplicitParameter() != nullptr && method != nullptr)) { 441 paramCount = i + 1; 442 } 443 } 444 #else 445 uint paramCount = params.size(); 446 #endif 447 448 result.addMemberTemplate(unqualifiedName, 449 KJ_MAP(i, kj::range(0u, paramCount)) { 450 return typeName(args[i], method); 451 }); 452 } else { 453 result.addMemberType(unqualifiedName); 454 } 455 456 return result; 457 } 458 } 459 460 kj::String toUpperCase(kj::StringPtr name) { 461 kj::Vector<char> result(name.size() + 4); 462 463 for (char c: name) { 464 if ('a' <= c && c <= 'z') { 465 result.add(c - 'a' + 'A'); 466 } else if (result.size() > 0 && 'A' <= c && c <= 'Z') { 467 result.add('_'); 468 result.add(c); 469 } else { 470 result.add(c); 471 } 472 } 473 474 if (result.size() == 4 && memcmp(result.begin(), "NULL", 4) == 0) { 475 // NULL probably collides with a macro. 476 result.add('_'); 477 } 478 479 result.add('\0'); 480 481 return kj::String(result.releaseAsArray()); 482 } 483 484 kj::String toTitleCase(kj::StringPtr name) { 485 kj::String result = kj::heapString(name); 486 if ('a' <= result[0] && result[0] <= 'z') { 487 result[0] = result[0] - 'a' + 'A'; 488 } 489 return kj::mv(result); 490 } 491 492 CppTypeName typeName(Type type, kj::Maybe<InterfaceSchema::Method> method) { 493 switch (type.which()) { 494 case schema::Type::VOID: return CppTypeName::makePrimitive(" ::capnp::Void"); 495 496 case schema::Type::BOOL: return CppTypeName::makePrimitive("bool"); 497 case schema::Type::INT8: return CppTypeName::makePrimitive(" ::int8_t"); 498 case schema::Type::INT16: return CppTypeName::makePrimitive(" ::int16_t"); 499 case schema::Type::INT32: return CppTypeName::makePrimitive(" ::int32_t"); 500 case schema::Type::INT64: return CppTypeName::makePrimitive(" ::int64_t"); 501 case schema::Type::UINT8: return CppTypeName::makePrimitive(" ::uint8_t"); 502 case schema::Type::UINT16: return CppTypeName::makePrimitive(" ::uint16_t"); 503 case schema::Type::UINT32: return CppTypeName::makePrimitive(" ::uint32_t"); 504 case schema::Type::UINT64: return CppTypeName::makePrimitive(" ::uint64_t"); 505 case schema::Type::FLOAT32: return CppTypeName::makePrimitive("float"); 506 case schema::Type::FLOAT64: return CppTypeName::makePrimitive("double"); 507 508 case schema::Type::TEXT: return CppTypeName::makePrimitive(" ::capnp::Text"); 509 case schema::Type::DATA: return CppTypeName::makePrimitive(" ::capnp::Data"); 510 511 case schema::Type::ENUM: 512 return cppFullName(type.asEnum(), method); 513 case schema::Type::STRUCT: 514 return cppFullName(type.asStruct(), method); 515 case schema::Type::INTERFACE: { 516 auto result = cppFullName(type.asInterface(), method); 517 result.setHasInterfaces(); 518 return result; 519 } 520 521 case schema::Type::LIST: { 522 CppTypeName result = CppTypeName::makeNamespace("capnp"); 523 auto params = kj::heapArrayBuilder<CppTypeName>(2); 524 auto list = type.asList(); 525 params.add(typeName(list.getElementType(), method)); 526 params.add(whichKind(list.getElementType())); 527 result.addMemberTemplate("List", params.finish()); 528 return result; 529 } 530 531 case schema::Type::ANY_POINTER: 532 KJ_IF_MAYBE(param, type.getBrandParameter()) { 533 return CppTypeName::makeTemplateParam(schemaLoader.get(param->scopeId).getProto() 534 .getParameters()[param->index].getName()); 535 } else KJ_IF_MAYBE(param, type.getImplicitParameter()) { 536 KJ_IF_MAYBE(m, method) { 537 auto params = m->getProto().getImplicitParameters(); 538 KJ_REQUIRE(param->index < params.size()); 539 return CppTypeName::makeTemplateParam(params[param->index].getName()); 540 } else { 541 return CppTypeName::makePrimitive(" ::capnp::AnyPointer"); 542 } 543 } else { 544 switch (type.whichAnyPointerKind()) { 545 case schema::Type::AnyPointer::Unconstrained::ANY_KIND: 546 return CppTypeName::makePrimitive(" ::capnp::AnyPointer"); 547 case schema::Type::AnyPointer::Unconstrained::STRUCT: 548 return CppTypeName::makePrimitive(" ::capnp::AnyStruct"); 549 case schema::Type::AnyPointer::Unconstrained::LIST: 550 return CppTypeName::makePrimitive(" ::capnp::AnyList"); 551 case schema::Type::AnyPointer::Unconstrained::CAPABILITY: { 552 hasInterfaces = true; // Probably need to #include <capnp/capability.h>. 553 auto result = CppTypeName::makePrimitive(" ::capnp::Capability"); 554 result.setHasInterfaces(); 555 return result; 556 } 557 } 558 KJ_UNREACHABLE; 559 } 560 } 561 KJ_UNREACHABLE; 562 } 563 564 template <typename P> 565 kj::Maybe<schema::Value::Reader> annotationValue(P proto, uint64_t annotationId) { 566 for (auto annotation: proto.getAnnotations()) { 567 if (annotation.getId() == annotationId) { 568 return annotation.getValue(); 569 } 570 } 571 return nullptr; 572 } 573 574 template <typename P> 575 kj::StringPtr protoName(P proto) { 576 KJ_IF_MAYBE(name, annotationValue(proto, NAME_ANNOTATION_ID)) { 577 return name->getText(); 578 } else { 579 return proto.getName(); 580 } 581 } 582 583 kj::StringTree literalValue(Type type, schema::Value::Reader value) { 584 switch (value.which()) { 585 case schema::Value::VOID: return kj::strTree(" ::capnp::VOID"); 586 case schema::Value::BOOL: return kj::strTree(value.getBool() ? "true" : "false"); 587 case schema::Value::INT8: return kj::strTree(value.getInt8()); 588 case schema::Value::INT16: return kj::strTree(value.getInt16()); 589 case schema::Value::INT32: return kj::strTree(value.getInt32()); 590 case schema::Value::INT64: return kj::strTree(value.getInt64(), "ll"); 591 case schema::Value::UINT8: return kj::strTree(value.getUint8(), "u"); 592 case schema::Value::UINT16: return kj::strTree(value.getUint16(), "u"); 593 case schema::Value::UINT32: return kj::strTree(value.getUint32(), "u"); 594 case schema::Value::UINT64: return kj::strTree(value.getUint64(), "llu"); 595 case schema::Value::FLOAT32: { 596 auto text = kj::str(value.getFloat32()); 597 if (text.findFirst('.') == nullptr && 598 text.findFirst('e') == nullptr && 599 text.findFirst('E') == nullptr) { 600 text = kj::str(text, ".0"); 601 } 602 return kj::strTree(kj::mv(text), "f"); 603 } 604 case schema::Value::FLOAT64: return kj::strTree(value.getFloat64()); 605 case schema::Value::ENUM: { 606 EnumSchema schema = type.asEnum(); 607 if (value.getEnum() < schema.getEnumerants().size()) { 608 return kj::strTree( 609 cppFullName(schema, nullptr), "::", 610 toUpperCase(protoName(schema.getEnumerants()[value.getEnum()].getProto()))); 611 } else { 612 return kj::strTree("static_cast<", cppFullName(schema, nullptr), 613 ">(", value.getEnum(), ")"); 614 } 615 } 616 617 case schema::Value::TEXT: 618 case schema::Value::DATA: 619 case schema::Value::STRUCT: 620 case schema::Value::INTERFACE: 621 case schema::Value::LIST: 622 case schema::Value::ANY_POINTER: 623 KJ_FAIL_REQUIRE("literalValue() can only be used on primitive types."); 624 } 625 KJ_UNREACHABLE; 626 } 627 628 // ----------------------------------------------------------------- 629 630 class TemplateContext { 631 public: 632 TemplateContext(): parent(nullptr) {} 633 explicit TemplateContext(schema::Node::Reader node) 634 : parent(nullptr), node(node) {} 635 TemplateContext(const TemplateContext& parent, kj::StringPtr name, schema::Node::Reader node) 636 : parent(parent), name(name), node(node) {} 637 638 bool hasParams() const { 639 return node.getParameters().size() > 0; 640 } 641 bool isGeneric() const { 642 return node.getIsGeneric(); 643 } 644 kj::Maybe<const TemplateContext&> getParent() const { 645 return parent; 646 } 647 kj::StringPtr getName() const { return name; } 648 649 kj::StringTree decl(bool withDefaults, kj::StringPtr suffix = nullptr) const { 650 // "template <typename T, typename U>" for this type. Includes default assignments 651 // ("= ::capnp::AnyPointer") if `withDefaults` is true. Returns empty string if this type 652 // is not parameterized. 653 654 auto params = node.getParameters(); 655 656 if (params.size() == 0) { 657 return kj::strTree(); 658 } else { 659 return kj::strTree( 660 "template <", kj::StringTree(KJ_MAP(p, params) { 661 return kj::strTree("typename ", p.getName(), suffix, 662 withDefaults ? " = ::capnp::AnyPointer" : ""); 663 }, ", "), ">\n"); 664 } 665 } 666 667 kj::StringTree allDecls() const { 668 // Decl for each generic parent plus this type, one per line. 669 return kj::strTree(parentDecls(), decl(false)); 670 } 671 672 kj::StringTree parentDecls() const { 673 // Decls just for the parents. 674 KJ_IF_MAYBE(p, parent) { 675 return p->allDecls(); 676 } else { 677 return kj::strTree(); 678 } 679 } 680 681 kj::StringTree args(kj::StringPtr suffix = nullptr) const { 682 // "<T, U>" for this type. 683 auto params = node.getParameters(); 684 685 if (params.size() == 0) { 686 return kj::strTree(); 687 } else { 688 return kj::strTree( 689 "<", kj::StringTree(KJ_MAP(p, params) { 690 return kj::strTree(p.getName(), suffix); 691 }, ", "), ">"); 692 } 693 } 694 695 kj::StringTree allArgs() const { 696 // "S, T, U, V" -- listing all args for all enclosing scopes starting from the outermost. 697 698 auto params = node.getParameters(); 699 700 kj::StringTree self(KJ_MAP(p, params) { return kj::strTree(p.getName()); }, ", "); 701 kj::StringTree up; 702 KJ_IF_MAYBE(p, parent) { 703 up = p->allArgs(); 704 } 705 706 if (self.size() == 0) { 707 return up; 708 } else if (up.size() == 0) { 709 return self; 710 } else { 711 return kj::strTree(kj::mv(up), ", ", kj::mv(self)); 712 } 713 } 714 715 std::map<uint64_t, List<schema::Node::Parameter>::Reader> getScopeMap() const { 716 std::map<uint64_t, List<schema::Node::Parameter>::Reader> result; 717 const TemplateContext* current = this; 718 for (;;) { 719 auto params = current->node.getParameters(); 720 if (params.size() > 0) { 721 result[current->node.getId()] = params; 722 } 723 KJ_IF_MAYBE(p, current->parent) { 724 current = p; 725 } else { 726 return result; 727 } 728 } 729 } 730 731 private: 732 kj::Maybe<const TemplateContext&> parent; 733 kj::StringPtr name; 734 schema::Node::Reader node; 735 }; 736 737 struct BrandInitializerText { 738 kj::StringTree scopes; 739 kj::StringTree bindings; 740 kj::StringTree dependencies; 741 size_t dependencyCount; 742 // TODO(msvc): `dependencyCount` is the number of individual dependency definitions in 743 // `dependencies`. It's a hack to allow makeGenericDefinitions to hard-code the size of the 744 // `_capnpPrivate::brandDependencies` array into the definition of 745 // `_capnpPrivate::specificBrand::dependencyCount`. This is necessary because MSVC cannot 746 // deduce the size of `brandDependencies` if it is nested under a class template. It's 747 // probably this demoralizingly deferred bug: 748 // https://connect.microsoft.com/VisualStudio/feedback/details/759407/can-not-get-size-of-static-array-defined-in-class-template 749 }; 750 751 BrandInitializerText makeBrandInitializers( 752 const TemplateContext& templateContext, Schema schema) { 753 auto scopeMap = templateContext.getScopeMap(); 754 755 auto scopes = kj::heapArrayBuilder<kj::StringTree>(scopeMap.size()); 756 kj::Vector<kj::StringTree> bindings(scopeMap.size() * 2); // (estimate two params per scope) 757 758 for (auto& scope: scopeMap) { 759 scopes.add(kj::strTree(" { ", 760 "0x", kj::hex(scope.first), ", " 761 "brandBindings + ", bindings.size(), ", ", 762 scope.second.size(), ", " 763 "false" 764 "},\n")); 765 766 for (auto param: scope.second) { 767 bindings.add(kj::strTree(" ::capnp::_::brandBindingFor<", param.getName(), ">(),\n")); 768 } 769 } 770 771 auto depMap = makeBrandDepMap(templateContext, schema); 772 auto dependencyCount = depMap.size(); 773 return { 774 kj::strTree("{\n", scopes.finish(), "}"), 775 kj::strTree("{\n", bindings.releaseAsArray(), "}"), 776 makeBrandDepInitializers(kj::mv(depMap)), 777 dependencyCount 778 }; 779 } 780 781 std::map<uint, kj::StringTree> 782 makeBrandDepMap(const TemplateContext& templateContext, Schema schema) { 783 // Build deps. This is separate from makeBrandDepInitializers to give calling code the 784 // opportunity to count the number of dependencies, to calculate array sizes. 785 std::map<uint, kj::StringTree> depMap; 786 787 #define ADD_DEP(kind, index, ...) \ 788 { \ 789 uint location = _::RawBrandedSchema::makeDepLocation( \ 790 _::RawBrandedSchema::DepKind::kind, index); \ 791 KJ_IF_MAYBE(dep, makeBrandDepInitializer(__VA_ARGS__)) { \ 792 depMap[location] = kj::mv(*dep); \ 793 } \ 794 } 795 796 switch (schema.getProto().which()) { 797 case schema::Node::FILE: 798 case schema::Node::ENUM: 799 case schema::Node::ANNOTATION: 800 break; 801 802 case schema::Node::STRUCT: 803 for (auto field: schema.asStruct().getFields()) { 804 ADD_DEP(FIELD, field.getIndex(), field.getType()); 805 } 806 break; 807 case schema::Node::INTERFACE: { 808 auto interface = schema.asInterface(); 809 auto superclasses = interface.getSuperclasses(); 810 for (auto i: kj::indices(superclasses)) { 811 ADD_DEP(SUPERCLASS, i, superclasses[i]); 812 } 813 auto methods = interface.getMethods(); 814 for (auto i: kj::indices(methods)) { 815 auto method = methods[i]; 816 ADD_DEP(METHOD_PARAMS, i, method, method.getParamType(), "Params"); 817 ADD_DEP(METHOD_RESULTS, i, method, method.getResultType(), "Results"); 818 } 819 break; 820 } 821 case schema::Node::CONST: 822 ADD_DEP(CONST_TYPE, 0, schema.asConst().getType()); 823 break; 824 } 825 #undef ADD_DEP 826 return depMap; 827 } 828 829 kj::StringTree makeBrandDepInitializers(std::map<uint, kj::StringTree>&& depMap) { 830 // Process depMap. Returns a braced initialiser list, or an empty string if there are no 831 // dependencies. 832 if (!depMap.size()) { 833 return kj::strTree(); 834 } 835 836 auto deps = kj::heapArrayBuilder<kj::StringTree>(depMap.size()); 837 for (auto& entry: depMap) { 838 deps.add(kj::strTree(" { ", entry.first, ", ", kj::mv(entry.second), " },\n")); 839 } 840 841 return kj::strTree("{\n", kj::StringTree(deps.finish(), ""), "}"); 842 } 843 844 kj::Maybe<kj::StringTree> makeBrandDepInitializer(Schema type) { 845 // Be careful not to invoke cppFullName() if it would just be thrown away, as doing so will 846 // add the type's declaring file to `usedImports`. In particular, this causes `stream.capnp.h` 847 // to be #included unnecessarily. 848 if (type.isBranded()) { 849 return makeBrandDepInitializer(type, cppFullName(type, nullptr)); 850 } else { 851 return nullptr; 852 } 853 } 854 855 kj::Maybe<kj::StringTree> makeBrandDepInitializer( 856 InterfaceSchema::Method method, StructSchema type, kj::StringPtr suffix) { 857 auto typeProto = type.getProto(); 858 if (typeProto.getScopeId() == 0) { 859 // This is an auto-generated params or results type. 860 auto name = cppFullName(method.getContainingInterface(), nullptr); 861 auto memberTypeName = kj::str(toTitleCase(protoName(method.getProto())), suffix); 862 863 if (typeProto.getParameters().size() == 0) { 864 name.addMemberType(memberTypeName); 865 } else { 866 // The method has implicit parameters (i.e. it's generic). For the purpose of the brand 867 // dep initializer, we only want to supply the default AnyPointer variant, so just don't 868 // pass any parameters here. 869 name.addMemberTemplate(memberTypeName, nullptr); 870 } 871 return makeBrandDepInitializer(type, kj::mv(name)); 872 } else { 873 return makeBrandDepInitializer(type); 874 } 875 } 876 877 kj::Maybe<kj::StringTree> makeBrandDepInitializer(Schema type, CppTypeName name) { 878 if (type.isBranded()) { 879 name.addMemberType("_capnpPrivate"); 880 name.addMemberValue("brand"); 881 return kj::strTree(name, "()"); 882 } else { 883 return nullptr; 884 } 885 } 886 887 kj::Maybe<kj::StringTree> makeBrandDepInitializer(Type type) { 888 switch (type.which()) { 889 case schema::Type::VOID: 890 case schema::Type::BOOL: 891 case schema::Type::INT8: 892 case schema::Type::INT16: 893 case schema::Type::INT32: 894 case schema::Type::INT64: 895 case schema::Type::UINT8: 896 case schema::Type::UINT16: 897 case schema::Type::UINT32: 898 case schema::Type::UINT64: 899 case schema::Type::FLOAT32: 900 case schema::Type::FLOAT64: 901 case schema::Type::TEXT: 902 case schema::Type::DATA: 903 case schema::Type::ENUM: 904 case schema::Type::ANY_POINTER: 905 return nullptr; 906 907 case schema::Type::STRUCT: 908 return makeBrandDepInitializer(type.asStruct()); 909 case schema::Type::INTERFACE: 910 return makeBrandDepInitializer(type.asInterface()); 911 case schema::Type::LIST: 912 return makeBrandDepInitializer(type.asList().getElementType()); 913 } 914 915 KJ_UNREACHABLE; 916 } 917 918 // ----------------------------------------------------------------- 919 // Code to deal with "slots" -- determines what to zero out when we clear a group. 920 921 static uint typeSizeBits(schema::Type::Which whichType) { 922 switch (whichType) { 923 case schema::Type::BOOL: return 1; 924 case schema::Type::INT8: return 8; 925 case schema::Type::INT16: return 16; 926 case schema::Type::INT32: return 32; 927 case schema::Type::INT64: return 64; 928 case schema::Type::UINT8: return 8; 929 case schema::Type::UINT16: return 16; 930 case schema::Type::UINT32: return 32; 931 case schema::Type::UINT64: return 64; 932 case schema::Type::FLOAT32: return 32; 933 case schema::Type::FLOAT64: return 64; 934 case schema::Type::ENUM: return 16; 935 936 case schema::Type::VOID: 937 case schema::Type::TEXT: 938 case schema::Type::DATA: 939 case schema::Type::LIST: 940 case schema::Type::STRUCT: 941 case schema::Type::INTERFACE: 942 case schema::Type::ANY_POINTER: 943 KJ_FAIL_REQUIRE("Should only be called for data types."); 944 } 945 KJ_UNREACHABLE; 946 } 947 948 enum class Section { 949 NONE, 950 DATA, 951 POINTERS 952 }; 953 954 static Section sectionFor(schema::Type::Which whichType) { 955 switch (whichType) { 956 case schema::Type::VOID: 957 return Section::NONE; 958 case schema::Type::BOOL: 959 case schema::Type::INT8: 960 case schema::Type::INT16: 961 case schema::Type::INT32: 962 case schema::Type::INT64: 963 case schema::Type::UINT8: 964 case schema::Type::UINT16: 965 case schema::Type::UINT32: 966 case schema::Type::UINT64: 967 case schema::Type::FLOAT32: 968 case schema::Type::FLOAT64: 969 case schema::Type::ENUM: 970 return Section::DATA; 971 case schema::Type::TEXT: 972 case schema::Type::DATA: 973 case schema::Type::LIST: 974 case schema::Type::STRUCT: 975 case schema::Type::INTERFACE: 976 case schema::Type::ANY_POINTER: 977 return Section::POINTERS; 978 } 979 KJ_UNREACHABLE; 980 } 981 982 static kj::StringPtr maskType(schema::Type::Which whichType) { 983 switch (whichType) { 984 case schema::Type::BOOL: return "bool"; 985 case schema::Type::INT8: return " ::uint8_t"; 986 case schema::Type::INT16: return " ::uint16_t"; 987 case schema::Type::INT32: return " ::uint32_t"; 988 case schema::Type::INT64: return " ::uint64_t"; 989 case schema::Type::UINT8: return " ::uint8_t"; 990 case schema::Type::UINT16: return " ::uint16_t"; 991 case schema::Type::UINT32: return " ::uint32_t"; 992 case schema::Type::UINT64: return " ::uint64_t"; 993 case schema::Type::FLOAT32: return " ::uint32_t"; 994 case schema::Type::FLOAT64: return " ::uint64_t"; 995 case schema::Type::ENUM: return " ::uint16_t"; 996 997 case schema::Type::VOID: 998 case schema::Type::TEXT: 999 case schema::Type::DATA: 1000 case schema::Type::LIST: 1001 case schema::Type::STRUCT: 1002 case schema::Type::INTERFACE: 1003 case schema::Type::ANY_POINTER: 1004 KJ_FAIL_REQUIRE("Should only be called for data types."); 1005 } 1006 KJ_UNREACHABLE; 1007 } 1008 1009 struct Slot { 1010 schema::Type::Which whichType; 1011 uint offset; 1012 1013 bool isSupersetOf(Slot other) const { 1014 auto section = sectionFor(whichType); 1015 if (section != sectionFor(other.whichType)) return false; 1016 switch (section) { 1017 case Section::NONE: 1018 return true; // all voids overlap 1019 case Section::DATA: { 1020 auto bits = typeSizeBits(whichType); 1021 auto start = offset * bits; 1022 auto otherBits = typeSizeBits(other.whichType); 1023 auto otherStart = other.offset * otherBits; 1024 return start <= otherStart && otherStart + otherBits <= start + bits; 1025 } 1026 case Section::POINTERS: 1027 return offset == other.offset; 1028 } 1029 KJ_UNREACHABLE; 1030 } 1031 1032 bool operator<(Slot other) const { 1033 // Sort by section, then start position, and finally size. 1034 1035 auto section = sectionFor(whichType); 1036 auto otherSection = sectionFor(other.whichType); 1037 if (section < otherSection) { 1038 return true; 1039 } else if (section > otherSection) { 1040 return false; 1041 } 1042 1043 switch (section) { 1044 case Section::NONE: 1045 return false; 1046 case Section::DATA: { 1047 auto bits = typeSizeBits(whichType); 1048 auto start = offset * bits; 1049 auto otherBits = typeSizeBits(other.whichType); 1050 auto otherStart = other.offset * otherBits; 1051 if (start < otherStart) { 1052 return true; 1053 } else if (start > otherStart) { 1054 return false; 1055 } 1056 1057 // Sort larger sizes before smaller. 1058 return bits > otherBits; 1059 } 1060 case Section::POINTERS: 1061 return offset < other.offset; 1062 } 1063 KJ_UNREACHABLE; 1064 } 1065 }; 1066 1067 void getSlots(StructSchema schema, kj::Vector<Slot>& slots) { 1068 auto structProto = schema.getProto().getStruct(); 1069 if (structProto.getDiscriminantCount() > 0) { 1070 slots.add(Slot { schema::Type::UINT16, structProto.getDiscriminantOffset() }); 1071 } 1072 1073 for (auto field: schema.getFields()) { 1074 auto proto = field.getProto(); 1075 switch (proto.which()) { 1076 case schema::Field::SLOT: { 1077 auto slot = proto.getSlot(); 1078 slots.add(Slot { slot.getType().which(), slot.getOffset() }); 1079 break; 1080 } 1081 case schema::Field::GROUP: 1082 getSlots(field.getType().asStruct(), slots); 1083 break; 1084 } 1085 } 1086 } 1087 1088 kj::Array<Slot> getSortedSlots(StructSchema schema) { 1089 // Get a representation of all of the field locations owned by this schema, e.g. so that they 1090 // can be zero'd out. 1091 1092 kj::Vector<Slot> slots(schema.getFields().size()); 1093 getSlots(schema, slots); 1094 std::sort(slots.begin(), slots.end()); 1095 1096 kj::Vector<Slot> result(slots.size()); 1097 1098 // All void slots are redundant, and they sort towards the front of the list. By starting out 1099 // with `prevSlot` = void, we will end up skipping them all, which is what we want. 1100 Slot prevSlot = { schema::Type::VOID, 0 }; 1101 for (auto slot: slots) { 1102 if (prevSlot.isSupersetOf(slot)) { 1103 // This slot is redundant as prevSlot is a superset of it. 1104 continue; 1105 } 1106 1107 // Since all sizes are power-of-two, if two slots overlap at all, one must be a superset of 1108 // the other. Since we sort slots by starting position, we know that the only way `slot` 1109 // could be a superset of `prevSlot` is if they have the same starting position. However, 1110 // since we sort slots with the same starting position by descending size, this is not 1111 // possible. 1112 KJ_DASSERT(!slot.isSupersetOf(prevSlot)); 1113 1114 result.add(slot); 1115 1116 prevSlot = slot; 1117 } 1118 1119 return result.releaseAsArray(); 1120 } 1121 1122 // ----------------------------------------------------------------- 1123 1124 struct DiscriminantChecks { 1125 kj::String has; 1126 kj::String check; 1127 kj::String set; 1128 kj::StringTree readerIsDecl; 1129 kj::StringTree builderIsDecl; 1130 kj::StringTree isDefs; 1131 }; 1132 1133 DiscriminantChecks makeDiscriminantChecks(kj::StringPtr scope, 1134 kj::StringPtr memberName, 1135 StructSchema containingStruct, 1136 const TemplateContext& templateContext) { 1137 auto discrimOffset = containingStruct.getProto().getStruct().getDiscriminantOffset(); 1138 1139 kj::String titleCase = toTitleCase(memberName); 1140 kj::String upperCase = toUpperCase(memberName); 1141 1142 return DiscriminantChecks { 1143 kj::str( 1144 " if (which() != ", scope, upperCase, ") return false;\n"), 1145 kj::str( 1146 // Extra parens around the condition are needed for when we're compiling a multi-arg 1147 // generic type, which will have a comma, which would otherwise mess up the macro. 1148 // Ah, C++. 1149 " KJ_IREQUIRE((which() == ", scope, upperCase, "),\n" 1150 " \"Must check which() before get()ing a union member.\");\n"), 1151 kj::str( 1152 " _builder.setDataField<", scope, "Which>(\n" 1153 " ::capnp::bounded<", discrimOffset, ">() * ::capnp::ELEMENTS, ", 1154 scope, upperCase, ");\n"), 1155 kj::strTree(" inline bool is", titleCase, "() const;\n"), 1156 kj::strTree(" inline bool is", titleCase, "();\n"), 1157 kj::strTree( 1158 templateContext.allDecls(), 1159 "inline bool ", scope, "Reader::is", titleCase, "() const {\n" 1160 " return which() == ", scope, upperCase, ";\n" 1161 "}\n", 1162 templateContext.allDecls(), 1163 "inline bool ", scope, "Builder::is", titleCase, "() {\n" 1164 " return which() == ", scope, upperCase, ";\n" 1165 "}\n") 1166 }; 1167 } 1168 1169 // ----------------------------------------------------------------- 1170 1171 struct FieldText { 1172 kj::StringTree readerMethodDecls; 1173 kj::StringTree builderMethodDecls; 1174 kj::StringTree pipelineMethodDecls; 1175 kj::StringTree inlineMethodDefs; 1176 }; 1177 1178 enum class FieldKind { 1179 PRIMITIVE, 1180 BLOB, 1181 STRUCT, 1182 LIST, 1183 INTERFACE, 1184 ANY_POINTER, 1185 BRAND_PARAMETER 1186 }; 1187 1188 FieldText makeFieldText(kj::StringPtr scope, StructSchema::Field field, 1189 const TemplateContext& templateContext) { 1190 auto proto = field.getProto(); 1191 auto typeSchema = field.getType(); 1192 auto baseName = protoName(proto); 1193 kj::String titleCase = toTitleCase(baseName); 1194 1195 DiscriminantChecks unionDiscrim; 1196 if (hasDiscriminantValue(proto)) { 1197 unionDiscrim = makeDiscriminantChecks(scope, baseName, field.getContainingStruct(), 1198 templateContext); 1199 } 1200 1201 switch (proto.which()) { 1202 case schema::Field::SLOT: 1203 // Continue below. 1204 break; 1205 1206 case schema::Field::GROUP: { 1207 auto slots = getSortedSlots(field.getType().asStruct()); 1208 return FieldText { 1209 kj::strTree( 1210 kj::mv(unionDiscrim.readerIsDecl), 1211 " inline typename ", titleCase, "::Reader get", titleCase, "() const;\n" 1212 "\n"), 1213 1214 kj::strTree( 1215 kj::mv(unionDiscrim.builderIsDecl), 1216 " inline typename ", titleCase, "::Builder get", titleCase, "();\n" 1217 " inline typename ", titleCase, "::Builder init", titleCase, "();\n" 1218 "\n"), 1219 1220 hasDiscriminantValue(proto) ? kj::strTree() : 1221 kj::strTree(" inline typename ", titleCase, "::Pipeline get", titleCase, "();\n"), 1222 1223 kj::strTree( 1224 kj::mv(unionDiscrim.isDefs), 1225 templateContext.allDecls(), 1226 "inline typename ", scope, titleCase, "::Reader ", scope, "Reader::get", titleCase, "() const {\n", 1227 unionDiscrim.check, 1228 " return typename ", scope, titleCase, "::Reader(_reader);\n" 1229 "}\n", 1230 templateContext.allDecls(), 1231 "inline typename ", scope, titleCase, "::Builder ", scope, "Builder::get", titleCase, "() {\n", 1232 unionDiscrim.check, 1233 " return typename ", scope, titleCase, "::Builder(_builder);\n" 1234 "}\n", 1235 hasDiscriminantValue(proto) ? kj::strTree() : kj::strTree( 1236 "#if !CAPNP_LITE\n", 1237 templateContext.allDecls(), 1238 "inline typename ", scope, titleCase, "::Pipeline ", scope, "Pipeline::get", titleCase, "() {\n", 1239 " return typename ", scope, titleCase, "::Pipeline(_typeless.noop());\n" 1240 "}\n" 1241 "#endif // !CAPNP_LITE\n"), 1242 templateContext.allDecls(), 1243 "inline typename ", scope, titleCase, "::Builder ", scope, "Builder::init", titleCase, "() {\n", 1244 unionDiscrim.set, 1245 KJ_MAP(slot, slots) { 1246 switch (sectionFor(slot.whichType)) { 1247 case Section::NONE: 1248 return kj::strTree(); 1249 case Section::DATA: 1250 return kj::strTree( 1251 " _builder.setDataField<", maskType(slot.whichType), ">(::capnp::bounded<", 1252 slot.offset, ">() * ::capnp::ELEMENTS, 0);\n"); 1253 case Section::POINTERS: 1254 return kj::strTree( 1255 " _builder.getPointerField(::capnp::bounded<", slot.offset, 1256 ">() * ::capnp::POINTERS).clear();\n"); 1257 } 1258 KJ_UNREACHABLE; 1259 }, 1260 " return typename ", scope, titleCase, "::Builder(_builder);\n" 1261 "}\n") 1262 }; 1263 } 1264 } 1265 1266 auto slot = proto.getSlot(); 1267 1268 FieldKind kind = FieldKind::PRIMITIVE; 1269 kj::String ownedType; 1270 CppTypeName type = typeName(typeSchema, nullptr); 1271 kj::StringPtr setterDefault; // only for void 1272 kj::String defaultMask; // primitives only 1273 size_t defaultOffset = 0; // pointers only: offset of the default value within the schema. 1274 size_t defaultSize = 0; // blobs only: byte size of the default value. 1275 1276 auto defaultBody = slot.getDefaultValue(); 1277 switch (typeSchema.which()) { 1278 case schema::Type::VOID: 1279 kind = FieldKind::PRIMITIVE; 1280 setterDefault = " = ::capnp::VOID"; 1281 break; 1282 1283 #define HANDLE_PRIMITIVE(discrim, typeName, defaultName, suffix) \ 1284 case schema::Type::discrim: \ 1285 kind = FieldKind::PRIMITIVE; \ 1286 if (defaultBody.get##defaultName() != 0) { \ 1287 defaultMask = kj::str(defaultBody.get##defaultName(), #suffix); \ 1288 } \ 1289 break; 1290 1291 HANDLE_PRIMITIVE(BOOL, bool, Bool, ); 1292 HANDLE_PRIMITIVE(INT8 , ::int8_t , Int8 , ); 1293 HANDLE_PRIMITIVE(INT16, ::int16_t, Int16, ); 1294 HANDLE_PRIMITIVE(INT32, ::int32_t, Int32, ); 1295 HANDLE_PRIMITIVE(INT64, ::int64_t, Int64, ll); 1296 HANDLE_PRIMITIVE(UINT8 , ::uint8_t , Uint8 , u); 1297 HANDLE_PRIMITIVE(UINT16, ::uint16_t, Uint16, u); 1298 HANDLE_PRIMITIVE(UINT32, ::uint32_t, Uint32, u); 1299 HANDLE_PRIMITIVE(UINT64, ::uint64_t, Uint64, ull); 1300 #undef HANDLE_PRIMITIVE 1301 1302 case schema::Type::FLOAT32: 1303 kind = FieldKind::PRIMITIVE; 1304 if (defaultBody.getFloat32() != 0) { 1305 uint32_t mask; 1306 float value = defaultBody.getFloat32(); 1307 static_assert(sizeof(mask) == sizeof(value), "bug"); 1308 memcpy(&mask, &value, sizeof(mask)); 1309 defaultMask = kj::str(mask, "u"); 1310 } 1311 break; 1312 1313 case schema::Type::FLOAT64: 1314 kind = FieldKind::PRIMITIVE; 1315 if (defaultBody.getFloat64() != 0) { 1316 uint64_t mask; 1317 double value = defaultBody.getFloat64(); 1318 static_assert(sizeof(mask) == sizeof(value), "bug"); 1319 memcpy(&mask, &value, sizeof(mask)); 1320 defaultMask = kj::str(mask, "ull"); 1321 } 1322 break; 1323 1324 case schema::Type::TEXT: 1325 kind = FieldKind::BLOB; 1326 if (defaultBody.hasText()) { 1327 defaultOffset = field.getDefaultValueSchemaOffset(); 1328 defaultSize = defaultBody.getText().size(); 1329 } 1330 break; 1331 case schema::Type::DATA: 1332 kind = FieldKind::BLOB; 1333 if (defaultBody.hasData()) { 1334 defaultOffset = field.getDefaultValueSchemaOffset(); 1335 defaultSize = defaultBody.getData().size(); 1336 } 1337 break; 1338 1339 case schema::Type::ENUM: 1340 kind = FieldKind::PRIMITIVE; 1341 if (defaultBody.getEnum() != 0) { 1342 defaultMask = kj::str(defaultBody.getEnum(), "u"); 1343 } 1344 break; 1345 1346 case schema::Type::STRUCT: 1347 kind = FieldKind::STRUCT; 1348 if (defaultBody.hasStruct()) { 1349 defaultOffset = field.getDefaultValueSchemaOffset(); 1350 } 1351 break; 1352 case schema::Type::LIST: 1353 kind = FieldKind::LIST; 1354 if (defaultBody.hasList()) { 1355 defaultOffset = field.getDefaultValueSchemaOffset(); 1356 } 1357 break; 1358 case schema::Type::INTERFACE: 1359 kind = FieldKind::INTERFACE; 1360 break; 1361 case schema::Type::ANY_POINTER: 1362 if (defaultBody.hasAnyPointer()) { 1363 defaultOffset = field.getDefaultValueSchemaOffset(); 1364 } 1365 if (typeSchema.getBrandParameter() != nullptr) { 1366 kind = FieldKind::BRAND_PARAMETER; 1367 } else { 1368 kind = FieldKind::ANY_POINTER; 1369 switch (typeSchema.whichAnyPointerKind()) { 1370 case schema::Type::AnyPointer::Unconstrained::ANY_KIND: 1371 kind = FieldKind::ANY_POINTER; 1372 break; 1373 case schema::Type::AnyPointer::Unconstrained::STRUCT: 1374 kind = FieldKind::STRUCT; 1375 break; 1376 case schema::Type::AnyPointer::Unconstrained::LIST: 1377 kind = FieldKind::LIST; 1378 break; 1379 case schema::Type::AnyPointer::Unconstrained::CAPABILITY: 1380 kind = FieldKind::INTERFACE; 1381 break; 1382 } 1383 } 1384 break; 1385 } 1386 1387 kj::String defaultMaskParam; 1388 if (defaultMask.size() > 0) { 1389 defaultMaskParam = kj::str(", ", defaultMask); 1390 } 1391 1392 uint offset = slot.getOffset(); 1393 1394 if (kind == FieldKind::PRIMITIVE) { 1395 return FieldText { 1396 kj::strTree( 1397 kj::mv(unionDiscrim.readerIsDecl), 1398 " inline ", type, " get", titleCase, "() const;\n" 1399 "\n"), 1400 1401 kj::strTree( 1402 kj::mv(unionDiscrim.builderIsDecl), 1403 " inline ", type, " get", titleCase, "();\n" 1404 " inline void set", titleCase, "(", type, " value", setterDefault, ");\n" 1405 "\n"), 1406 1407 kj::strTree(), 1408 1409 kj::strTree( 1410 kj::mv(unionDiscrim.isDefs), 1411 templateContext.allDecls(), 1412 "inline ", type, " ", scope, "Reader::get", titleCase, "() const {\n", 1413 unionDiscrim.check, 1414 " return _reader.getDataField<", type, ">(\n" 1415 " ::capnp::bounded<", offset, ">() * ::capnp::ELEMENTS", defaultMaskParam, ");\n", 1416 "}\n" 1417 "\n", 1418 templateContext.allDecls(), 1419 "inline ", type, " ", scope, "Builder::get", titleCase, "() {\n", 1420 unionDiscrim.check, 1421 " return _builder.getDataField<", type, ">(\n" 1422 " ::capnp::bounded<", offset, ">() * ::capnp::ELEMENTS", defaultMaskParam, ");\n", 1423 "}\n", 1424 templateContext.allDecls(), 1425 "inline void ", scope, "Builder::set", titleCase, "(", type, " value) {\n", 1426 unionDiscrim.set, 1427 " _builder.setDataField<", type, ">(\n" 1428 " ::capnp::bounded<", offset, ">() * ::capnp::ELEMENTS, value", defaultMaskParam, ");\n", 1429 "}\n" 1430 "\n") 1431 }; 1432 1433 } else if (kind == FieldKind::INTERFACE) { 1434 CppTypeName clientType = type; 1435 clientType.addMemberType("Client"); 1436 1437 return FieldText { 1438 kj::strTree( 1439 kj::mv(unionDiscrim.readerIsDecl), 1440 " inline bool has", titleCase, "() const;\n" 1441 "#if !CAPNP_LITE\n" 1442 " inline ", clientType, " get", titleCase, "() const;\n" 1443 "#endif // !CAPNP_LITE\n" 1444 "\n"), 1445 1446 kj::strTree( 1447 kj::mv(unionDiscrim.builderIsDecl), 1448 " inline bool has", titleCase, "();\n" 1449 "#if !CAPNP_LITE\n" 1450 " inline ", clientType, " get", titleCase, "();\n" 1451 " inline void set", titleCase, "(", clientType, "&& value);\n", 1452 " inline void set", titleCase, "(", clientType, "& value);\n", 1453 " inline void adopt", titleCase, "(::capnp::Orphan<", type, ">&& value);\n" 1454 " inline ::capnp::Orphan<", type, "> disown", titleCase, "();\n" 1455 "#endif // !CAPNP_LITE\n" 1456 "\n"), 1457 1458 kj::strTree( 1459 hasDiscriminantValue(proto) ? kj::strTree() : kj::strTree( 1460 " inline ", clientType, " get", titleCase, "();\n")), 1461 1462 kj::strTree( 1463 kj::mv(unionDiscrim.isDefs), 1464 templateContext.allDecls(), 1465 "inline bool ", scope, "Reader::has", titleCase, "() const {\n", 1466 unionDiscrim.has, 1467 " return !_reader.getPointerField(\n" 1468 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS).isNull();\n" 1469 "}\n", 1470 templateContext.allDecls(), 1471 "inline bool ", scope, "Builder::has", titleCase, "() {\n", 1472 unionDiscrim.has, 1473 " return !_builder.getPointerField(\n" 1474 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS).isNull();\n" 1475 "}\n" 1476 "#if !CAPNP_LITE\n", 1477 templateContext.allDecls(), 1478 "inline ", clientType, " ", scope, "Reader::get", titleCase, "() const {\n", 1479 unionDiscrim.check, 1480 " return ::capnp::_::PointerHelpers<", type, ">::get(_reader.getPointerField(\n" 1481 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1482 "}\n", 1483 templateContext.allDecls(), 1484 "inline ", clientType, " ", scope, "Builder::get", titleCase, "() {\n", 1485 unionDiscrim.check, 1486 " return ::capnp::_::PointerHelpers<", type, ">::get(_builder.getPointerField(\n" 1487 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1488 "}\n", 1489 hasDiscriminantValue(proto) ? kj::strTree() : kj::strTree( 1490 templateContext.allDecls(), 1491 "inline ", clientType, " ", scope, "Pipeline::get", titleCase, "() {\n", 1492 " return ", clientType, "(_typeless.getPointerField(", offset, ").asCap());\n" 1493 "}\n"), 1494 templateContext.allDecls(), 1495 "inline void ", scope, "Builder::set", titleCase, "(", clientType, "&& cap) {\n", 1496 unionDiscrim.set, 1497 " ::capnp::_::PointerHelpers<", type, ">::set(_builder.getPointerField(\n" 1498 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), kj::mv(cap));\n" 1499 "}\n", 1500 templateContext.allDecls(), 1501 "inline void ", scope, "Builder::set", titleCase, "(", clientType, "& cap) {\n", 1502 unionDiscrim.set, 1503 " ::capnp::_::PointerHelpers<", type, ">::set(_builder.getPointerField(\n" 1504 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), cap);\n" 1505 "}\n", 1506 templateContext.allDecls(), 1507 "inline void ", scope, "Builder::adopt", titleCase, "(\n" 1508 " ::capnp::Orphan<", type, ">&& value) {\n", 1509 unionDiscrim.set, 1510 " ::capnp::_::PointerHelpers<", type, ">::adopt(_builder.getPointerField(\n" 1511 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), kj::mv(value));\n" 1512 "}\n", 1513 templateContext.allDecls(), 1514 "inline ::capnp::Orphan<", type, "> ", scope, "Builder::disown", titleCase, "() {\n", 1515 unionDiscrim.check, 1516 " return ::capnp::_::PointerHelpers<", type, ">::disown(_builder.getPointerField(\n" 1517 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1518 "}\n" 1519 "#endif // !CAPNP_LITE\n" 1520 "\n") 1521 }; 1522 1523 } else if (kind == FieldKind::ANY_POINTER) { 1524 return FieldText { 1525 kj::strTree( 1526 kj::mv(unionDiscrim.readerIsDecl), 1527 " inline bool has", titleCase, "() const;\n" 1528 " inline ::capnp::AnyPointer::Reader get", titleCase, "() const;\n" 1529 "\n"), 1530 1531 kj::strTree( 1532 kj::mv(unionDiscrim.builderIsDecl), 1533 " inline bool has", titleCase, "();\n" 1534 " inline ::capnp::AnyPointer::Builder get", titleCase, "();\n" 1535 " inline ::capnp::AnyPointer::Builder init", titleCase, "();\n" 1536 "\n"), 1537 1538 kj::strTree(), 1539 1540 kj::strTree( 1541 kj::mv(unionDiscrim.isDefs), 1542 templateContext.allDecls(), 1543 "inline bool ", scope, "Reader::has", titleCase, "() const {\n", 1544 unionDiscrim.has, 1545 " return !_reader.getPointerField(\n" 1546 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS).isNull();\n" 1547 "}\n", 1548 templateContext.allDecls(), 1549 "inline bool ", scope, "Builder::has", titleCase, "() {\n", 1550 unionDiscrim.has, 1551 " return !_builder.getPointerField(\n" 1552 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS).isNull();\n" 1553 "}\n", 1554 templateContext.allDecls(), 1555 "inline ::capnp::AnyPointer::Reader ", scope, "Reader::get", titleCase, "() const {\n", 1556 unionDiscrim.check, 1557 " return ::capnp::AnyPointer::Reader(_reader.getPointerField(\n" 1558 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1559 "}\n", 1560 templateContext.allDecls(), 1561 "inline ::capnp::AnyPointer::Builder ", scope, "Builder::get", titleCase, "() {\n", 1562 unionDiscrim.check, 1563 " return ::capnp::AnyPointer::Builder(_builder.getPointerField(\n" 1564 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1565 "}\n", 1566 templateContext.allDecls(), 1567 "inline ::capnp::AnyPointer::Builder ", scope, "Builder::init", titleCase, "() {\n", 1568 unionDiscrim.set, 1569 " auto result = ::capnp::AnyPointer::Builder(_builder.getPointerField(\n" 1570 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1571 " result.clear();\n" 1572 " return result;\n" 1573 "}\n" 1574 "\n") 1575 }; 1576 1577 } else { 1578 // Blob, struct, list, or template param. These have only minor differences. 1579 1580 uint64_t typeId = field.getContainingStruct().getProto().getId(); 1581 kj::String defaultParam = defaultOffset == 0 ? kj::str() : kj::str( 1582 ",\n ::capnp::schemas::bp_", kj::hex(typeId), " + ", defaultOffset, 1583 defaultSize == 0 ? kj::strTree() : kj::strTree(", ", defaultSize)); 1584 1585 bool shouldIncludeStructInit = 1586 kind == FieldKind::STRUCT || kind == FieldKind::BRAND_PARAMETER; 1587 bool shouldIncludeSizedInit = 1588 kind != FieldKind::STRUCT || kind == FieldKind::BRAND_PARAMETER; 1589 bool shouldIncludePipelineGetter = !hasDiscriminantValue(proto) && 1590 (kind == FieldKind::STRUCT || kind == FieldKind::BRAND_PARAMETER); 1591 bool shouldIncludeArrayInitializer = false; 1592 bool shouldExcludeInLiteMode = type.hasInterfaces(); 1593 bool shouldTemplatizeInit = typeSchema.which() == schema::Type::ANY_POINTER && 1594 kind != FieldKind::BRAND_PARAMETER; 1595 1596 CppTypeName elementReaderType; 1597 if (typeSchema.isList()) { 1598 bool primitiveElement = false; 1599 bool interface = false; 1600 auto elementType = typeSchema.asList().getElementType(); 1601 switch (elementType.which()) { 1602 case schema::Type::VOID: 1603 case schema::Type::BOOL: 1604 case schema::Type::INT8: 1605 case schema::Type::INT16: 1606 case schema::Type::INT32: 1607 case schema::Type::INT64: 1608 case schema::Type::UINT8: 1609 case schema::Type::UINT16: 1610 case schema::Type::UINT32: 1611 case schema::Type::UINT64: 1612 case schema::Type::FLOAT32: 1613 case schema::Type::FLOAT64: 1614 case schema::Type::ENUM: 1615 primitiveElement = true; 1616 shouldIncludeArrayInitializer = true; 1617 break; 1618 1619 case schema::Type::TEXT: 1620 case schema::Type::DATA: 1621 case schema::Type::LIST: 1622 primitiveElement = false; 1623 shouldIncludeArrayInitializer = true; 1624 break; 1625 1626 case schema::Type::ANY_POINTER: 1627 primitiveElement = false; 1628 shouldIncludeArrayInitializer = elementType.getBrandParameter() != nullptr; 1629 break; 1630 1631 case schema::Type::INTERFACE: 1632 primitiveElement = false; 1633 interface = true; 1634 break; 1635 1636 case schema::Type::STRUCT: 1637 primitiveElement = false; 1638 break; 1639 } 1640 elementReaderType = typeName(elementType, nullptr); 1641 if (!primitiveElement) { 1642 if (interface) { 1643 elementReaderType.addMemberType("Client"); 1644 } else { 1645 elementReaderType.addMemberType("Reader"); 1646 } 1647 } 1648 }; 1649 1650 CppTypeName readerType; 1651 CppTypeName builderType; 1652 CppTypeName pipelineType; 1653 if (kind == FieldKind::BRAND_PARAMETER) { 1654 readerType = CppTypeName::makeNamespace("capnp"); 1655 readerType.addMemberTemplate("ReaderFor", kj::heapArray(&type, 1)); 1656 builderType = CppTypeName::makeNamespace("capnp"); 1657 builderType.addMemberTemplate("BuilderFor", kj::heapArray(&type, 1)); 1658 pipelineType = CppTypeName::makeNamespace("capnp"); 1659 pipelineType.addMemberTemplate("PipelineFor", kj::heapArray(&type, 1)); 1660 } else { 1661 readerType = type; 1662 readerType.addMemberType("Reader"); 1663 builderType = type; 1664 builderType.addMemberType("Builder"); 1665 pipelineType = type; 1666 pipelineType.addMemberType("Pipeline"); 1667 } 1668 1669 #define COND(cond, ...) ((cond) ? kj::strTree(__VA_ARGS__) : kj::strTree()) 1670 1671 return FieldText { 1672 kj::strTree( 1673 kj::mv(unionDiscrim.readerIsDecl), 1674 " inline bool has", titleCase, "() const;\n", 1675 COND(shouldExcludeInLiteMode, "#if !CAPNP_LITE\n"), 1676 " inline ", readerType, " get", titleCase, "() const;\n", 1677 COND(shouldExcludeInLiteMode, "#endif // !CAPNP_LITE\n"), 1678 "\n"), 1679 1680 kj::strTree( 1681 kj::mv(unionDiscrim.builderIsDecl), 1682 " inline bool has", titleCase, "();\n", 1683 COND(shouldExcludeInLiteMode, "#if !CAPNP_LITE\n"), 1684 " inline ", builderType, " get", titleCase, "();\n" 1685 " inline void set", titleCase, "(", readerType, " value);\n", 1686 COND(shouldIncludeArrayInitializer, 1687 " inline void set", titleCase, "(::kj::ArrayPtr<const ", elementReaderType, "> value);\n"), 1688 COND(shouldIncludeStructInit, 1689 COND(shouldTemplatizeInit, 1690 " template <typename T_>\n" 1691 " inline ::capnp::BuilderFor<T_> init", titleCase, "As();\n"), 1692 COND(!shouldTemplatizeInit, 1693 " inline ", builderType, " init", titleCase, "();\n")), 1694 COND(shouldIncludeSizedInit, 1695 COND(shouldTemplatizeInit, 1696 " template <typename T_>\n" 1697 " inline ::capnp::BuilderFor<T_> init", titleCase, "As(unsigned int size);\n"), 1698 COND(!shouldTemplatizeInit, 1699 " inline ", builderType, " init", titleCase, "(unsigned int size);\n")), 1700 " inline void adopt", titleCase, "(::capnp::Orphan<", type, ">&& value);\n" 1701 " inline ::capnp::Orphan<", type, "> disown", titleCase, "();\n", 1702 COND(shouldExcludeInLiteMode, "#endif // !CAPNP_LITE\n"), 1703 "\n"), 1704 1705 kj::strTree( 1706 COND(shouldIncludePipelineGetter, 1707 " inline ", pipelineType, " get", titleCase, "();\n")), 1708 1709 kj::strTree( 1710 kj::mv(unionDiscrim.isDefs), 1711 templateContext.allDecls(), 1712 "inline bool ", scope, "Reader::has", titleCase, "() const {\n", 1713 unionDiscrim.has, 1714 " return !_reader.getPointerField(\n" 1715 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS).isNull();\n" 1716 "}\n", 1717 templateContext.allDecls(), 1718 "inline bool ", scope, "Builder::has", titleCase, "() {\n", 1719 unionDiscrim.has, 1720 " return !_builder.getPointerField(\n" 1721 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS).isNull();\n" 1722 "}\n", 1723 COND(shouldExcludeInLiteMode, "#if !CAPNP_LITE\n"), 1724 templateContext.allDecls(), 1725 "inline ", readerType, " ", scope, "Reader::get", titleCase, "() const {\n", 1726 unionDiscrim.check, 1727 " return ::capnp::_::PointerHelpers<", type, ">::get(_reader.getPointerField(\n" 1728 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS)", defaultParam, ");\n" 1729 "}\n", 1730 templateContext.allDecls(), 1731 "inline ", builderType, " ", scope, "Builder::get", titleCase, "() {\n", 1732 unionDiscrim.check, 1733 " return ::capnp::_::PointerHelpers<", type, ">::get(_builder.getPointerField(\n" 1734 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS)", defaultParam, ");\n" 1735 "}\n", 1736 COND(shouldIncludePipelineGetter, 1737 "#if !CAPNP_LITE\n", 1738 templateContext.allDecls(), 1739 "inline ", pipelineType, " ", scope, "Pipeline::get", titleCase, "() {\n", 1740 " return ", pipelineType, "(_typeless.getPointerField(", offset, "));\n" 1741 "}\n" 1742 "#endif // !CAPNP_LITE\n"), 1743 templateContext.allDecls(), 1744 "inline void ", scope, "Builder::set", titleCase, "(", readerType, " value) {\n", 1745 unionDiscrim.set, 1746 " ::capnp::_::PointerHelpers<", type, ">::set(_builder.getPointerField(\n" 1747 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), value);\n" 1748 "}\n", 1749 COND(shouldIncludeArrayInitializer, 1750 templateContext.allDecls(), 1751 "inline void ", scope, "Builder::set", titleCase, "(::kj::ArrayPtr<const ", elementReaderType, "> value) {\n", 1752 unionDiscrim.set, 1753 " ::capnp::_::PointerHelpers<", type, ">::set(_builder.getPointerField(\n" 1754 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), value);\n" 1755 "}\n"), 1756 COND(shouldIncludeStructInit, 1757 COND(shouldTemplatizeInit, 1758 templateContext.allDecls(), 1759 "template <typename T_>\n" 1760 "inline ::capnp::BuilderFor<T_> ", scope, "Builder::init", titleCase, "As() {\n", 1761 " static_assert(::capnp::kind<T_>() == ::capnp::Kind::STRUCT,\n" 1762 " \"", proto.getName(), " must be a struct\");\n", 1763 unionDiscrim.set, 1764 " return ::capnp::_::PointerHelpers<T_>::init(_builder.getPointerField(\n" 1765 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1766 "}\n"), 1767 COND(!shouldTemplatizeInit, 1768 templateContext.allDecls(), 1769 "inline ", builderType, " ", scope, "Builder::init", titleCase, "() {\n", 1770 unionDiscrim.set, 1771 " return ::capnp::_::PointerHelpers<", type, ">::init(_builder.getPointerField(\n" 1772 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1773 "}\n")), 1774 COND(shouldIncludeSizedInit, 1775 COND(shouldTemplatizeInit, 1776 templateContext.allDecls(), 1777 "template <typename T_>\n" 1778 "inline ::capnp::BuilderFor<T_> ", scope, "Builder::init", titleCase, "As(unsigned int size) {\n", 1779 " static_assert(::capnp::kind<T_>() == ::capnp::Kind::LIST,\n" 1780 " \"", proto.getName(), " must be a list\");\n", 1781 unionDiscrim.set, 1782 " return ::capnp::_::PointerHelpers<T_>::init(_builder.getPointerField(\n" 1783 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), size);\n" 1784 "}\n"), 1785 COND(!shouldTemplatizeInit, 1786 templateContext.allDecls(), 1787 "inline ", builderType, " ", scope, "Builder::init", titleCase, "(unsigned int size) {\n", 1788 unionDiscrim.set, 1789 " return ::capnp::_::PointerHelpers<", type, ">::init(_builder.getPointerField(\n" 1790 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), size);\n" 1791 "}\n")), 1792 templateContext.allDecls(), 1793 "inline void ", scope, "Builder::adopt", titleCase, "(\n" 1794 " ::capnp::Orphan<", type, ">&& value) {\n", 1795 unionDiscrim.set, 1796 " ::capnp::_::PointerHelpers<", type, ">::adopt(_builder.getPointerField(\n" 1797 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS), kj::mv(value));\n" 1798 "}\n", 1799 COND(type.hasDisambiguatedTemplate(), 1800 "#if !defined(_MSC_VER) || defined(__clang__)\n" 1801 "// Excluded under MSVC because bugs may make it unable to compile this method.\n"), 1802 templateContext.allDecls(), 1803 "inline ::capnp::Orphan<", type, "> ", scope, "Builder::disown", titleCase, "() {\n", 1804 unionDiscrim.check, 1805 " return ::capnp::_::PointerHelpers<", type, ">::disown(_builder.getPointerField(\n" 1806 " ::capnp::bounded<", offset, ">() * ::capnp::POINTERS));\n" 1807 "}\n", 1808 COND(type.hasDisambiguatedTemplate(), "#endif // !_MSC_VER || __clang__\n"), 1809 COND(shouldExcludeInLiteMode, "#endif // !CAPNP_LITE\n"), 1810 "\n") 1811 }; 1812 1813 #undef COND 1814 } 1815 } 1816 1817 // ----------------------------------------------------------------- 1818 1819 enum class AsGenericRole { READER, BUILDER, CLIENT }; 1820 1821 kj::StringTree makeAsGenericDef(AsGenericRole role, const TemplateContext& templateContext, 1822 kj::StringPtr type, kj::String innerType = kj::String()) { 1823 if (!templateContext.isGeneric()) { 1824 return kj::StringTree(); 1825 } 1826 kj::StringTree self, up; 1827 if (templateContext.hasParams()) { 1828 auto returnType = [&]() { 1829 return kj::strTree(type, templateContext.args("2"), innerType); 1830 }; 1831 kj::StringTree asGeneric = kj::strTree("as", innerType.size() ? type : "", "Generic()"); 1832 switch (role) { 1833 case AsGenericRole::READER: 1834 self = kj::strTree( 1835 " ", templateContext.decl(true, "2"), 1836 " typename ", returnType(), "::Reader ", kj::mv(asGeneric), " {\n" 1837 " return typename ", returnType(), "::Reader(_reader);\n" 1838 " }\n" 1839 "\n"); 1840 break; 1841 case AsGenericRole::BUILDER: 1842 self = kj::strTree( 1843 " ", templateContext.decl(true, "2"), 1844 " typename ", returnType(), "::Builder ", kj::mv(asGeneric), " {\n" 1845 " return typename ", returnType(), "::Builder(_builder);\n" 1846 " }\n" 1847 "\n"); 1848 break; 1849 case AsGenericRole::CLIENT: 1850 self = kj::strTree( 1851 " ", templateContext.decl(true, "2"), 1852 " typename ", returnType(), "::Client ", kj::mv(asGeneric), " {\n" 1853 " return castAs<", innerType.size() ? "typename " : "", returnType(), ">();\n" 1854 " }\n" 1855 "\n"); 1856 break; 1857 } 1858 } 1859 KJ_IF_MAYBE(p, templateContext.getParent()) { 1860 up = makeAsGenericDef(role, *p, p->getName(), kj::strTree( 1861 templateContext.hasParams() ? "::template " : "::", type, 1862 templateContext.args()).flatten()); 1863 } 1864 return kj::strTree(kj::mv(self), kj::mv(up)); 1865 } 1866 1867 // ----------------------------------------------------------------- 1868 1869 struct StructText { 1870 kj::StringTree outerTypeDecl; 1871 kj::StringTree outerTypeDef; 1872 kj::StringTree readerBuilderDefs; 1873 kj::StringTree inlineMethodDefs; 1874 kj::StringTree sourceDefs; 1875 }; 1876 1877 kj::StringTree makeReaderDef(kj::StringPtr fullName, kj::StringPtr unqualifiedParentType, 1878 const TemplateContext& templateContext, bool isUnion, 1879 kj::Array<kj::StringTree>&& methodDecls) { 1880 return kj::strTree( 1881 templateContext.allDecls(), 1882 "class ", fullName, "::Reader {\n" 1883 "public:\n" 1884 " typedef ", unqualifiedParentType, " Reads;\n" 1885 "\n" 1886 " Reader() = default;\n" 1887 " inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}\n" 1888 "\n" 1889 " inline ::capnp::MessageSize totalSize() const {\n" 1890 " return _reader.totalSize().asPublic();\n" 1891 " }\n" 1892 "\n" 1893 "#if !CAPNP_LITE\n" 1894 " inline ::kj::StringTree toString() const {\n" 1895 " return ::capnp::_::structString(_reader, *_capnpPrivate::brand());\n" 1896 " }\n" 1897 "#endif // !CAPNP_LITE\n" 1898 "\n", 1899 makeAsGenericDef(AsGenericRole::READER, templateContext, unqualifiedParentType), 1900 isUnion ? kj::strTree(" inline Which which() const;\n") : kj::strTree(), 1901 kj::mv(methodDecls), 1902 "private:\n" 1903 " ::capnp::_::StructReader _reader;\n" 1904 " template <typename, ::capnp::Kind>\n" 1905 " friend struct ::capnp::ToDynamic_;\n" 1906 " template <typename, ::capnp::Kind>\n" 1907 " friend struct ::capnp::_::PointerHelpers;\n" 1908 " template <typename, ::capnp::Kind>\n" 1909 " friend struct ::capnp::List;\n" 1910 " friend class ::capnp::MessageBuilder;\n" 1911 " friend class ::capnp::Orphanage;\n" 1912 "};\n" 1913 "\n"); 1914 } 1915 1916 kj::StringTree makeBuilderDef(kj::StringPtr fullName, kj::StringPtr unqualifiedParentType, 1917 const TemplateContext& templateContext, bool isUnion, 1918 kj::Array<kj::StringTree>&& methodDecls) { 1919 return kj::strTree( 1920 templateContext.allDecls(), 1921 "class ", fullName, "::Builder {\n" 1922 "public:\n" 1923 " typedef ", unqualifiedParentType, " Builds;\n" 1924 "\n" 1925 " Builder() = delete; // Deleted to discourage incorrect usage.\n" 1926 " // You can explicitly initialize to nullptr instead.\n" 1927 " inline Builder(decltype(nullptr)) {}\n" 1928 " inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}\n" 1929 " inline operator Reader() const { return Reader(_builder.asReader()); }\n" 1930 " inline Reader asReader() const { return *this; }\n" 1931 "\n" 1932 " inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }\n" 1933 "#if !CAPNP_LITE\n" 1934 " inline ::kj::StringTree toString() const { return asReader().toString(); }\n" 1935 "#endif // !CAPNP_LITE\n" 1936 "\n", 1937 makeAsGenericDef(AsGenericRole::BUILDER, templateContext, unqualifiedParentType), 1938 isUnion ? kj::strTree(" inline Which which();\n") : kj::strTree(), 1939 kj::mv(methodDecls), 1940 "private:\n" 1941 " ::capnp::_::StructBuilder _builder;\n" 1942 " template <typename, ::capnp::Kind>\n" 1943 " friend struct ::capnp::ToDynamic_;\n" 1944 " friend class ::capnp::Orphanage;\n", 1945 " template <typename, ::capnp::Kind>\n" 1946 " friend struct ::capnp::_::PointerHelpers;\n" 1947 "};\n" 1948 "\n"); 1949 } 1950 1951 kj::StringTree makePipelineDef(kj::StringPtr fullName, kj::StringPtr unqualifiedParentType, 1952 const TemplateContext& templateContext, bool isUnion, 1953 kj::Array<kj::StringTree>&& methodDecls) { 1954 return kj::strTree( 1955 "#if !CAPNP_LITE\n", 1956 templateContext.allDecls(), 1957 "class ", fullName, "::Pipeline {\n" 1958 "public:\n" 1959 " typedef ", unqualifiedParentType, " Pipelines;\n" 1960 "\n" 1961 " inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}\n" 1962 " inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)\n" 1963 " : _typeless(kj::mv(typeless)) {}\n" 1964 "\n", 1965 kj::mv(methodDecls), 1966 "private:\n" 1967 " ::capnp::AnyPointer::Pipeline _typeless;\n" 1968 " friend class ::capnp::PipelineHook;\n" 1969 " template <typename, ::capnp::Kind>\n" 1970 " friend struct ::capnp::ToDynamic_;\n" 1971 "};\n" 1972 "#endif // !CAPNP_LITE\n" 1973 "\n"); 1974 } 1975 1976 kj::StringTree makeGenericDeclarations(const TemplateContext& templateContext, 1977 bool hasBrandDependencies) { 1978 // Returns the declarations for the private members of a generic struct/interface; 1979 // paired with the definitions from makeGenericDefinitions(). 1980 return kj::strTree( 1981 " static const ::capnp::_::RawBrandedSchema::Scope brandScopes[];\n" 1982 " static const ::capnp::_::RawBrandedSchema::Binding brandBindings[];\n", 1983 (!hasBrandDependencies ? "" : 1984 " static const ::capnp::_::RawBrandedSchema::Dependency brandDependencies[];\n"), 1985 " static const ::capnp::_::RawBrandedSchema specificBrand;\n" 1986 " static constexpr ::capnp::_::RawBrandedSchema const* brand() { " 1987 "return ::capnp::_::ChooseBrand<_capnpPrivate, ", templateContext.allArgs(), ">::brand(); }\n"); 1988 } 1989 1990 kj::StringTree makeGenericDefinitions( 1991 const TemplateContext& templateContext, kj::StringPtr fullName, kj::StringPtr hexId, 1992 BrandInitializerText brandInitializers) { 1993 // Returns the definitions for the members from makeGenericDeclarations(). 1994 bool hasBrandDependencies = (brandInitializers.dependencies.size() != 0); 1995 1996 auto scopeCount = templateContext.getScopeMap().size(); 1997 auto dependencyCount = brandInitializers.dependencyCount; 1998 1999 kj::String templates = kj::str(templateContext.allDecls()); 2000 2001 return kj::strTree( 2002 templates, "const ::capnp::_::RawBrandedSchema::Scope ", fullName, 2003 "::_capnpPrivate::brandScopes[] = ", kj::mv(brandInitializers.scopes), ";\n", 2004 2005 templates, "const ::capnp::_::RawBrandedSchema::Binding ", fullName, 2006 "::_capnpPrivate::brandBindings[] = ", kj::mv(brandInitializers.bindings), ";\n", 2007 2008 (!hasBrandDependencies ? kj::strTree("") : kj::strTree( 2009 templates, "const ::capnp::_::RawBrandedSchema::Dependency ", fullName, 2010 "::_capnpPrivate::brandDependencies[] = ", kj::mv(brandInitializers.dependencies), 2011 ";\n")), 2012 2013 templates, "const ::capnp::_::RawBrandedSchema ", fullName, "::_capnpPrivate::specificBrand = {\n", 2014 " &::capnp::schemas::s_", hexId, ", brandScopes, ", 2015 (!hasBrandDependencies ? "nullptr" : "brandDependencies"), ",\n", 2016 " ", scopeCount, ", ", dependencyCount, 2017 ", nullptr\n" 2018 "};\n"); 2019 } 2020 2021 StructText makeStructText(kj::StringPtr scope, kj::StringPtr name, StructSchema schema, 2022 kj::Array<kj::StringTree> nestedTypeDecls, 2023 const TemplateContext& templateContext) { 2024 auto proto = schema.getProto(); 2025 KJ_IF_MAYBE(annotatedName, annotationValue(proto, NAME_ANNOTATION_ID)) { 2026 name = annotatedName->getText(); 2027 } 2028 auto fullName = kj::str(scope, name, templateContext.args()); 2029 auto subScope = kj::str(fullName, "::"); 2030 auto fieldTexts = KJ_MAP(f, schema.getFields()) { 2031 return makeFieldText(subScope, f, templateContext); 2032 }; 2033 2034 auto structNode = proto.getStruct(); 2035 uint discrimOffset = structNode.getDiscriminantOffset(); 2036 auto hexId = kj::hex(proto.getId()); 2037 2038 kj::String templates = kj::str(templateContext.allDecls()); // Ends with a newline 2039 2040 // Private members struct 2041 kj::StringTree declareText = kj::strTree( 2042 " struct _capnpPrivate {\n" 2043 " CAPNP_DECLARE_STRUCT_HEADER(", hexId, ", ", structNode.getDataWordCount(), ", ", 2044 structNode.getPointerCount(), ")\n"); 2045 2046 kj::StringTree defineText = kj::strTree( 2047 "// ", fullName, "\n", 2048 templates, "constexpr uint16_t ", fullName, "::_capnpPrivate::dataWordSize;\n", 2049 templates, "constexpr uint16_t ", fullName, "::_capnpPrivate::pointerCount;\n" 2050 "#if !CAPNP_LITE\n", 2051 templates, "constexpr ::capnp::Kind ", fullName, "::_capnpPrivate::kind;\n", 2052 templates, "constexpr ::capnp::_::RawSchema const* ", fullName, "::_capnpPrivate::schema;\n"); 2053 2054 if (templateContext.isGeneric()) { 2055 auto brandInitializers = makeBrandInitializers(templateContext, schema); 2056 bool hasDeps = (brandInitializers.dependencies.size() != 0); 2057 2058 declareText = kj::strTree(kj::mv(declareText), 2059 " #if !CAPNP_LITE\n", 2060 makeGenericDeclarations(templateContext, hasDeps), 2061 " #endif // !CAPNP_LITE\n"); 2062 2063 defineText = kj::strTree(kj::mv(defineText), 2064 makeGenericDefinitions( 2065 templateContext, fullName, kj::str(hexId), kj::mv(brandInitializers))); 2066 } else { 2067 declareText = kj::strTree(kj::mv(declareText), 2068 " #if !CAPNP_LITE\n" 2069 " static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }\n" 2070 " #endif // !CAPNP_LITE\n"); 2071 } 2072 2073 declareText = kj::strTree(kj::mv(declareText), " };"); 2074 defineText = kj::strTree(kj::mv(defineText), "#endif // !CAPNP_LITE\n\n"); 2075 2076 // Name of the ::Which type, when applicable. 2077 CppTypeName whichName; 2078 if (structNode.getDiscriminantCount() != 0) { 2079 whichName = cppFullName(schema, nullptr); 2080 whichName.addMemberType("Which"); 2081 } 2082 2083 return StructText { 2084 kj::strTree( 2085 templateContext.hasParams() ? " " : "", templateContext.decl(true), 2086 " struct ", name, ";\n"), 2087 2088 kj::strTree( 2089 templateContext.parentDecls(), 2090 templateContext.decl(scope == nullptr), 2091 "struct ", scope, name, " {\n", 2092 " ", name, "() = delete;\n" 2093 "\n" 2094 " class Reader;\n" 2095 " class Builder;\n" 2096 " class Pipeline;\n", 2097 structNode.getDiscriminantCount() == 0 ? kj::strTree() : kj::strTree( 2098 " enum Which: uint16_t {\n", 2099 KJ_MAP(f, structNode.getFields()) { 2100 if (hasDiscriminantValue(f)) { 2101 return kj::strTree(" ", toUpperCase(protoName(f)), ",\n"); 2102 } else { 2103 return kj::strTree(); 2104 } 2105 }, 2106 " };\n"), 2107 KJ_MAP(n, nestedTypeDecls) { return kj::mv(n); }, 2108 "\n", 2109 kj::mv(declareText), "\n", 2110 "};\n" 2111 "\n"), 2112 2113 kj::strTree( 2114 makeReaderDef(fullName, name, templateContext, structNode.getDiscriminantCount() != 0, 2115 KJ_MAP(f, fieldTexts) { return kj::mv(f.readerMethodDecls); }), 2116 makeBuilderDef(fullName, name, templateContext, structNode.getDiscriminantCount() != 0, 2117 KJ_MAP(f, fieldTexts) { return kj::mv(f.builderMethodDecls); }), 2118 makePipelineDef(fullName, name, templateContext, structNode.getDiscriminantCount() != 0, 2119 KJ_MAP(f, fieldTexts) { return kj::mv(f.pipelineMethodDecls); })), 2120 2121 kj::strTree( 2122 structNode.getDiscriminantCount() == 0 ? kj::strTree() : kj::strTree( 2123 templateContext.allDecls(), 2124 "inline ", whichName, " ", fullName, "::Reader::which() const {\n" 2125 " return _reader.getDataField<Which>(\n" 2126 " ::capnp::bounded<", discrimOffset, ">() * ::capnp::ELEMENTS);\n" 2127 "}\n", 2128 templateContext.allDecls(), 2129 "inline ", whichName, " ", fullName, "::Builder::which() {\n" 2130 " return _builder.getDataField<Which>(\n" 2131 " ::capnp::bounded<", discrimOffset, ">() * ::capnp::ELEMENTS);\n" 2132 "}\n" 2133 "\n"), 2134 KJ_MAP(f, fieldTexts) { return kj::mv(f.inlineMethodDefs); }), 2135 2136 kj::mv(defineText) 2137 }; 2138 } 2139 2140 // ----------------------------------------------------------------- 2141 2142 struct MethodText { 2143 kj::StringTree clientDecls; 2144 kj::StringTree serverDecls; 2145 kj::StringTree inlineDefs; 2146 kj::StringTree sourceDefs; 2147 kj::StringTree dispatchCase; 2148 }; 2149 2150 MethodText makeMethodText(kj::StringPtr interfaceName, InterfaceSchema::Method method, 2151 const TemplateContext& templateContext) { 2152 auto proto = method.getProto(); 2153 auto name = protoName(proto); 2154 auto titleCase = toTitleCase(name); 2155 auto paramSchema = method.getParamType(); 2156 auto resultSchema = method.getResultType(); 2157 auto identifierName = safeIdentifier(name); 2158 2159 auto paramProto = paramSchema.getProto(); 2160 auto resultProto = resultSchema.getProto(); 2161 2162 bool isStreaming = method.isStreaming(); 2163 2164 auto implicitParamsReader = proto.getImplicitParameters(); 2165 auto implicitParamsBuilder = kj::heapArrayBuilder<CppTypeName>(implicitParamsReader.size()); 2166 for (auto param: implicitParamsReader) { 2167 implicitParamsBuilder.add(CppTypeName::makeTemplateParam(param.getName())); 2168 } 2169 auto implicitParams = implicitParamsBuilder.finish(); 2170 2171 kj::String implicitParamsTemplateDecl; 2172 if (implicitParams.size() > 0) { 2173 implicitParamsTemplateDecl = kj::str( 2174 "template <", kj::StringTree(KJ_MAP(p, implicitParams) { 2175 return kj::strTree("typename ", p); 2176 }, ", "), ">\n"); 2177 } 2178 2179 2180 CppTypeName interfaceTypeName = cppFullName(method.getContainingInterface(), nullptr); 2181 CppTypeName paramType; 2182 CppTypeName genericParamType; 2183 if (paramProto.getScopeId() == 0) { 2184 paramType = interfaceTypeName; 2185 if (implicitParams.size() == 0) { 2186 paramType.addMemberType(kj::str(titleCase, "Params")); 2187 genericParamType = paramType; 2188 } else { 2189 genericParamType = paramType; 2190 genericParamType.addMemberTemplate(kj::str(titleCase, "Params"), nullptr); 2191 paramType.addMemberTemplate(kj::str(titleCase, "Params"), 2192 kj::heapArray(implicitParams.asPtr())); 2193 } 2194 } else { 2195 paramType = cppFullName(paramSchema, method); 2196 genericParamType = cppFullName(paramSchema, nullptr); 2197 } 2198 CppTypeName resultType; 2199 CppTypeName genericResultType; 2200 if (isStreaming) { 2201 // We don't use resultType or genericResultType in this case. We want to avoid computing them 2202 // at all so that we don't end up marking stream.capnp.h in usedImports. 2203 } else if (resultProto.getScopeId() == 0) { 2204 resultType = interfaceTypeName; 2205 if (implicitParams.size() == 0) { 2206 resultType.addMemberType(kj::str(titleCase, "Results")); 2207 genericResultType = resultType; 2208 } else { 2209 genericResultType = resultType; 2210 genericResultType.addMemberTemplate(kj::str(titleCase, "Results"), nullptr); 2211 resultType.addMemberTemplate(kj::str(titleCase, "Results"), 2212 kj::heapArray(implicitParams.asPtr())); 2213 } 2214 } else { 2215 resultType = cppFullName(resultSchema, method); 2216 genericResultType = cppFullName(resultSchema, nullptr); 2217 } 2218 2219 kj::String shortParamType = paramProto.getScopeId() == 0 ? 2220 kj::str(titleCase, "Params") : kj::str(genericParamType); 2221 kj::String shortResultType = resultProto.getScopeId() == 0 || isStreaming ? 2222 kj::str(titleCase, "Results") : kj::str(genericResultType); 2223 2224 auto interfaceProto = method.getContainingInterface().getProto(); 2225 uint64_t interfaceId = interfaceProto.getId(); 2226 auto interfaceIdHex = kj::hex(interfaceId); 2227 uint16_t methodId = method.getIndex(); 2228 2229 // TODO(msvc): Notice that the return type of this method's request function is supposed to be 2230 // `::capnp::Request<param, result>`. If the first template parameter to ::capnp::Request is a 2231 // template instantiation, MSVC will sometimes complain that it's unspecialized and can't be 2232 // used as a parameter in the return type (error C3203). It is not clear to me under what exact 2233 // conditions this bug occurs, but it commonly crops up in test.capnp.h. 2234 // 2235 // The easiest (and only) workaround I found is to use C++14's return type deduction here, thus 2236 // the `CAPNP_AUTO_IF_MSVC()` hackery in the return type declarations below. We're depending on 2237 // the fact that that this function has an inline implementation for the deduction to work. 2238 2239 auto requestMethodImpl = kj::strTree( 2240 templateContext.allDecls(), 2241 implicitParamsTemplateDecl, 2242 templateContext.isGeneric() ? "CAPNP_AUTO_IF_MSVC(" : "", 2243 isStreaming ? kj::strTree("::capnp::StreamingRequest<", paramType, ">") 2244 : kj::strTree("::capnp::Request<", paramType, ", ", resultType, ">"), 2245 templateContext.isGeneric() ? ")\n" : "\n", 2246 interfaceName, "::Client::", name, "Request(::kj::Maybe< ::capnp::MessageSize> sizeHint) {\n", 2247 isStreaming 2248 ? kj::strTree(" return newStreamingCall<", paramType, ">(\n") 2249 : kj::strTree(" return newCall<", paramType, ", ", resultType, ">(\n"), 2250 " 0x", interfaceIdHex, "ull, ", methodId, ", sizeHint);\n" 2251 "}\n"); 2252 2253 return MethodText { 2254 kj::strTree( 2255 implicitParamsTemplateDecl.size() == 0 ? "" : " ", implicitParamsTemplateDecl, 2256 templateContext.isGeneric() ? " CAPNP_AUTO_IF_MSVC(" : " ", 2257 isStreaming ? kj::strTree("::capnp::StreamingRequest<", paramType, ">") 2258 : kj::strTree("::capnp::Request<", paramType, ", ", resultType, ">"), 2259 templateContext.isGeneric() ? ")" : "", 2260 " ", name, "Request(\n" 2261 " ::kj::Maybe< ::capnp::MessageSize> sizeHint = nullptr);\n"), 2262 2263 kj::strTree( 2264 paramProto.getScopeId() != 0 ? kj::strTree() : kj::strTree( 2265 " typedef ", genericParamType, " ", titleCase, "Params;\n"), 2266 resultProto.getScopeId() != 0 ? kj::strTree() : kj::strTree( 2267 " typedef ", genericResultType, " ", titleCase, "Results;\n"), 2268 isStreaming 2269 ? kj::strTree(" typedef ::capnp::StreamingCallContext<", shortParamType, "> ") 2270 : kj::strTree( 2271 " typedef ::capnp::CallContext<", shortParamType, ", ", shortResultType, "> "), 2272 titleCase, "Context;\n" 2273 " virtual ::kj::Promise<void> ", identifierName, "(", titleCase, "Context context);\n"), 2274 2275 implicitParams.size() == 0 ? kj::strTree() : kj::mv(requestMethodImpl), 2276 2277 kj::strTree( 2278 implicitParams.size() == 0 ? kj::mv(requestMethodImpl) : kj::strTree(), 2279 templateContext.allDecls(), 2280 "::kj::Promise<void> ", interfaceName, "::Server::", identifierName, "(", titleCase, "Context) {\n" 2281 " return ::capnp::Capability::Server::internalUnimplemented(\n" 2282 " \"", interfaceProto.getDisplayName(), "\", \"", name, "\",\n" 2283 " 0x", interfaceIdHex, "ull, ", methodId, ");\n" 2284 "}\n"), 2285 2286 kj::strTree( 2287 " case ", methodId, ":\n", 2288 isStreaming 2289 ? kj::strTree( 2290 // For streaming calls, we need to add an evalNow() here so that exceptions thrown 2291 // directly from the call can propagate to later calls. If we don't capture the 2292 // exception properly then the caller will never find out that this is a streaming 2293 // call (indicated by the boolean in the return value) so won't know to propagate 2294 // the exception. 2295 " return {\n" 2296 " kj::evalNow([&]() {\n" 2297 " return ", identifierName, "(::capnp::Capability::Server::internalGetTypedStreamingContext<\n" 2298 " ", genericParamType, ">(context));\n" 2299 " }),\n" 2300 " true\n" 2301 " };\n") 2302 : kj::strTree( 2303 // For non-streaming calls we let exceptions just flow through for a little more 2304 // efficiency. 2305 " return {\n" 2306 " ", identifierName, "(::capnp::Capability::Server::internalGetTypedContext<\n" 2307 " ", genericParamType, ", ", genericResultType, ">(context)),\n" 2308 " false\n" 2309 " };\n")) 2310 }; 2311 } 2312 2313 struct InterfaceText { 2314 kj::StringTree outerTypeDecl; 2315 kj::StringTree outerTypeDef; 2316 kj::StringTree clientServerDefs; 2317 kj::StringTree inlineMethodDefs; 2318 kj::StringTree sourceDefs; 2319 }; 2320 2321 struct ExtendInfo { 2322 CppTypeName typeName; 2323 uint64_t id; 2324 }; 2325 2326 void getTransitiveSuperclasses(InterfaceSchema schema, std::map<uint64_t, InterfaceSchema>& map) { 2327 if (map.insert(std::make_pair(schema.getProto().getId(), schema)).second) { 2328 for (auto sup: schema.getSuperclasses()) { 2329 getTransitiveSuperclasses(sup, map); 2330 } 2331 } 2332 } 2333 2334 InterfaceText makeInterfaceText(kj::StringPtr scope, kj::StringPtr name, InterfaceSchema schema, 2335 kj::Array<kj::StringTree> nestedTypeDecls, 2336 const TemplateContext& templateContext) { 2337 auto fullName = kj::str(scope, name, templateContext.args()); 2338 auto methods = KJ_MAP(m, schema.getMethods()) { 2339 return makeMethodText(fullName, m, templateContext); 2340 }; 2341 2342 auto proto = schema.getProto(); 2343 auto hexId = kj::hex(proto.getId()); 2344 2345 auto superclasses = KJ_MAP(superclass, schema.getSuperclasses()) { 2346 return ExtendInfo { cppFullName(superclass, nullptr), superclass.getProto().getId() }; 2347 }; 2348 2349 kj::Array<ExtendInfo> transitiveSuperclasses; 2350 { 2351 std::map<uint64_t, InterfaceSchema> map; 2352 getTransitiveSuperclasses(schema, map); 2353 map.erase(schema.getProto().getId()); 2354 transitiveSuperclasses = KJ_MAP(entry, map) { 2355 return ExtendInfo { cppFullName(entry.second, nullptr), entry.second.getProto().getId() }; 2356 }; 2357 } 2358 2359 CppTypeName typeName = cppFullName(schema, nullptr); 2360 2361 CppTypeName clientName = typeName; 2362 clientName.addMemberType("Client"); 2363 2364 kj::String templates = kj::str(templateContext.allDecls()); // Ends with a newline 2365 2366 // Private members struct 2367 kj::StringTree declareText = kj::strTree( 2368 " #if !CAPNP_LITE\n" 2369 " struct _capnpPrivate {\n" 2370 " CAPNP_DECLARE_INTERFACE_HEADER(", hexId, ")\n"); 2371 2372 kj::StringTree defineText = kj::strTree( 2373 "// ", fullName, "\n", 2374 "#if !CAPNP_LITE\n", 2375 templates, "constexpr ::capnp::Kind ", fullName, "::_capnpPrivate::kind;\n", 2376 templates, "constexpr ::capnp::_::RawSchema const* ", fullName, "::_capnpPrivate::schema;\n"); 2377 2378 if (templateContext.isGeneric()) { 2379 auto brandInitializers = makeBrandInitializers(templateContext, schema); 2380 bool hasDeps = (brandInitializers.dependencies.size() != 0); 2381 2382 declareText = kj::strTree(kj::mv(declareText), 2383 makeGenericDeclarations(templateContext, hasDeps)); 2384 2385 defineText = kj::strTree(kj::mv(defineText), 2386 makeGenericDefinitions( 2387 templateContext, fullName, kj::str(hexId), kj::mv(brandInitializers))); 2388 } else { 2389 declareText = kj::strTree(kj::mv(declareText), 2390 " static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }\n"); 2391 } 2392 2393 declareText = kj::strTree(kj::mv(declareText), " };\n #endif // !CAPNP_LITE"); 2394 defineText = kj::strTree(kj::mv(defineText), "#endif // !CAPNP_LITE\n\n"); 2395 2396 return InterfaceText { 2397 kj::strTree( 2398 templateContext.hasParams() ? " " : "", templateContext.decl(true), 2399 " struct ", name, ";\n"), 2400 2401 kj::strTree( 2402 templateContext.parentDecls(), 2403 templateContext.decl(scope == nullptr), 2404 "struct ", scope, name, " {\n", 2405 " ", name, "() = delete;\n" 2406 "\n" 2407 "#if !CAPNP_LITE\n" 2408 " class Client;\n" 2409 " class Server;\n" 2410 "#endif // !CAPNP_LITE\n" 2411 "\n", 2412 KJ_MAP(n, nestedTypeDecls) { return kj::mv(n); }, 2413 "\n", 2414 kj::mv(declareText), "\n" 2415 "};\n" 2416 "\n"), 2417 2418 kj::strTree( 2419 "#if !CAPNP_LITE\n", 2420 templateContext.allDecls(), 2421 "class ", fullName, "::Client\n" 2422 " : public virtual ::capnp::Capability::Client", 2423 KJ_MAP(s, superclasses) { 2424 return kj::strTree(",\n public virtual ", s.typeName.strNoTypename(), "::Client"); 2425 }, " {\n" 2426 "public:\n" 2427 " typedef ", name, " Calls;\n" 2428 " typedef ", name, " Reads;\n" 2429 "\n" 2430 " Client(decltype(nullptr));\n" 2431 " explicit Client(::kj::Own< ::capnp::ClientHook>&& hook);\n" 2432 " template <typename _t, typename = ::kj::EnableIf< ::kj::canConvert<_t*, Server*>()>>\n" 2433 " Client(::kj::Own<_t>&& server);\n" 2434 " template <typename _t, typename = ::kj::EnableIf< ::kj::canConvert<_t*, Client*>()>>\n" 2435 " Client(::kj::Promise<_t>&& promise);\n" 2436 " Client(::kj::Exception&& exception);\n" 2437 " Client(Client&) = default;\n" 2438 " Client(Client&&) = default;\n" 2439 " Client& operator=(Client& other);\n" 2440 " Client& operator=(Client&& other);\n" 2441 "\n", 2442 makeAsGenericDef(AsGenericRole::CLIENT, templateContext, name), 2443 KJ_MAP(m, methods) { return kj::mv(m.clientDecls); }, 2444 "\n" 2445 "protected:\n" 2446 " Client() = default;\n" 2447 "};\n" 2448 "\n", 2449 templateContext.allDecls(), 2450 "class ", fullName, "::Server\n" 2451 " : public virtual ::capnp::Capability::Server", 2452 KJ_MAP(s, superclasses) { 2453 return kj::strTree(",\n public virtual ", s.typeName.strNoTypename(), "::Server"); 2454 }, " {\n" 2455 "public:\n", 2456 " typedef ", name, " Serves;\n" 2457 "\n" 2458 " ::capnp::Capability::Server::DispatchCallResult dispatchCall(\n" 2459 " uint64_t interfaceId, uint16_t methodId,\n" 2460 " ::capnp::CallContext< ::capnp::AnyPointer, ::capnp::AnyPointer> context)\n" 2461 " override;\n" 2462 "\n" 2463 "protected:\n", 2464 KJ_MAP(m, methods) { return kj::mv(m.serverDecls); }, 2465 "\n" 2466 " inline ", clientName, " thisCap() {\n" 2467 " return ::capnp::Capability::Server::thisCap()\n" 2468 " .template castAs<", typeName, ">();\n" 2469 " }\n" 2470 "\n" 2471 " ::capnp::Capability::Server::DispatchCallResult dispatchCallInternal(\n" 2472 " uint16_t methodId,\n" 2473 " ::capnp::CallContext< ::capnp::AnyPointer, ::capnp::AnyPointer> context);\n" 2474 "};\n" 2475 "#endif // !CAPNP_LITE\n" 2476 "\n"), 2477 2478 kj::strTree( 2479 "#if !CAPNP_LITE\n", 2480 templateContext.allDecls(), 2481 "inline ", fullName, "::Client::Client(decltype(nullptr))\n" 2482 " : ::capnp::Capability::Client(nullptr) {}\n", 2483 templateContext.allDecls(), 2484 "inline ", fullName, "::Client::Client(\n" 2485 " ::kj::Own< ::capnp::ClientHook>&& hook)\n" 2486 " : ::capnp::Capability::Client(::kj::mv(hook)) {}\n", 2487 templateContext.allDecls(), 2488 "template <typename _t, typename>\n" 2489 "inline ", fullName, "::Client::Client(::kj::Own<_t>&& server)\n" 2490 " : ::capnp::Capability::Client(::kj::mv(server)) {}\n", 2491 templateContext.allDecls(), 2492 "template <typename _t, typename>\n" 2493 "inline ", fullName, "::Client::Client(::kj::Promise<_t>&& promise)\n" 2494 " : ::capnp::Capability::Client(::kj::mv(promise)) {}\n", 2495 templateContext.allDecls(), 2496 "inline ", fullName, "::Client::Client(::kj::Exception&& exception)\n" 2497 " : ::capnp::Capability::Client(::kj::mv(exception)) {}\n", 2498 templateContext.allDecls(), 2499 "inline ", clientName, "& ", fullName, "::Client::operator=(Client& other) {\n" 2500 " ::capnp::Capability::Client::operator=(other);\n" 2501 " return *this;\n" 2502 "}\n", 2503 templateContext.allDecls(), 2504 "inline ", clientName, "& ", fullName, "::Client::operator=(Client&& other) {\n" 2505 " ::capnp::Capability::Client::operator=(kj::mv(other));\n" 2506 " return *this;\n" 2507 "}\n" 2508 "\n", 2509 KJ_MAP(m, methods) { return kj::mv(m.inlineDefs); }, 2510 "#endif // !CAPNP_LITE\n"), 2511 2512 kj::strTree( 2513 "#if !CAPNP_LITE\n", 2514 KJ_MAP(m, methods) { return kj::mv(m.sourceDefs); }, 2515 templateContext.allDecls(), 2516 "::capnp::Capability::Server::DispatchCallResult ", fullName, "::Server::dispatchCall(\n" 2517 " uint64_t interfaceId, uint16_t methodId,\n" 2518 " ::capnp::CallContext< ::capnp::AnyPointer, ::capnp::AnyPointer> context) {\n" 2519 " switch (interfaceId) {\n" 2520 " case 0x", kj::hex(proto.getId()), "ull:\n" 2521 " return dispatchCallInternal(methodId, context);\n", 2522 KJ_MAP(s, transitiveSuperclasses) { 2523 return kj::strTree( 2524 " case 0x", kj::hex(s.id), "ull:\n" 2525 " return ", s.typeName.strNoTypename(), 2526 "::Server::dispatchCallInternal(methodId, context);\n"); 2527 }, 2528 " default:\n" 2529 " return internalUnimplemented(\"", proto.getDisplayName(), "\", interfaceId);\n" 2530 " }\n" 2531 "}\n", 2532 templateContext.allDecls(), 2533 "::capnp::Capability::Server::DispatchCallResult ", fullName, "::Server::dispatchCallInternal(\n" 2534 " uint16_t methodId,\n" 2535 " ::capnp::CallContext< ::capnp::AnyPointer, ::capnp::AnyPointer> context) {\n" 2536 " switch (methodId) {\n", 2537 KJ_MAP(m, methods) { return kj::mv(m.dispatchCase); }, 2538 " default:\n" 2539 " (void)context;\n" 2540 " return ::capnp::Capability::Server::internalUnimplemented(\n" 2541 " \"", proto.getDisplayName(), "\",\n" 2542 " 0x", kj::hex(proto.getId()), "ull, methodId);\n" 2543 " }\n" 2544 "}\n" 2545 "#endif // !CAPNP_LITE\n" 2546 "\n", 2547 kj::mv(defineText)) 2548 }; 2549 } 2550 2551 // ----------------------------------------------------------------- 2552 2553 struct ConstText { 2554 bool needsSchema; 2555 kj::StringTree decl; 2556 kj::StringTree def; 2557 }; 2558 2559 ConstText makeConstText(kj::StringPtr scope, kj::StringPtr name, ConstSchema schema, 2560 const TemplateContext& templateContext) { 2561 auto proto = schema.getProto(); 2562 auto constProto = proto.getConst(); 2563 auto type = schema.getType(); 2564 auto typeName_ = typeName(type, nullptr); 2565 auto upperCase = toUpperCase(name); 2566 2567 // Linkage qualifier for non-primitive types. 2568 const char* linkage = scope.size() == 0 ? "extern " : "static "; 2569 2570 switch (type.which()) { 2571 case schema::Type::BOOL: 2572 case schema::Type::INT8: 2573 case schema::Type::INT16: 2574 case schema::Type::INT32: 2575 case schema::Type::INT64: 2576 case schema::Type::UINT8: 2577 case schema::Type::UINT16: 2578 case schema::Type::UINT32: 2579 case schema::Type::UINT64: 2580 case schema::Type::ENUM: 2581 return ConstText { 2582 false, 2583 kj::strTree("static constexpr ", typeName_, ' ', upperCase, " = ", 2584 literalValue(schema.getType(), constProto.getValue()), ";\n"), 2585 scope.size() == 0 ? kj::strTree() : kj::strTree( 2586 // TODO(msvc): MSVC doesn't like definitions of constexprs, but other compilers and 2587 // the standard require them. 2588 "#if !defined(_MSC_VER) || defined(__clang__)\n" 2589 "constexpr ", typeName_, ' ', scope, upperCase, ";\n" 2590 "#endif\n") 2591 }; 2592 2593 case schema::Type::VOID: 2594 case schema::Type::FLOAT32: 2595 case schema::Type::FLOAT64: { 2596 // TODO(msvc): MSVC doesn't like float- or class-typed constexprs. As soon as this is fixed, 2597 // treat VOID, FLOAT32, and FLOAT64 the same as the other primitives. 2598 kj::String value = literalValue(schema.getType(), constProto.getValue()).flatten(); 2599 return ConstText { 2600 false, 2601 kj::strTree("static KJ_CONSTEXPR(const) ", typeName_, ' ', upperCase, 2602 " CAPNP_NON_INT_CONSTEXPR_DECL_INIT(", value, ");\n"), 2603 scope.size() == 0 ? kj::strTree() : kj::strTree( 2604 "KJ_CONSTEXPR(const) ", typeName_, ' ', scope, upperCase, 2605 " CAPNP_NON_INT_CONSTEXPR_DEF_INIT(", value, ");\n") 2606 }; 2607 } 2608 2609 case schema::Type::TEXT: { 2610 kj::String constType = kj::strTree( 2611 "::capnp::_::ConstText<", schema.as<Text>().size(), ">").flatten(); 2612 return ConstText { 2613 true, 2614 kj::strTree(linkage, "const ", constType, ' ', upperCase, ";\n"), 2615 kj::strTree("const ", constType, ' ', scope, upperCase, "(::capnp::schemas::b_", 2616 kj::hex(proto.getId()), ".words + ", schema.getValueSchemaOffset(), ");\n") 2617 }; 2618 } 2619 2620 case schema::Type::DATA: { 2621 kj::String constType = kj::strTree( 2622 "::capnp::_::ConstData<", schema.as<Data>().size(), ">").flatten(); 2623 return ConstText { 2624 true, 2625 kj::strTree(linkage, "const ", constType, ' ', upperCase, ";\n"), 2626 kj::strTree("const ", constType, ' ', scope, upperCase, "(::capnp::schemas::b_", 2627 kj::hex(proto.getId()), ".words + ", schema.getValueSchemaOffset(), ");\n") 2628 }; 2629 } 2630 2631 case schema::Type::STRUCT: { 2632 kj::String constType = kj::strTree( 2633 "::capnp::_::ConstStruct<", typeName_, ">").flatten(); 2634 return ConstText { 2635 true, 2636 kj::strTree(linkage, "const ", constType, ' ', upperCase, ";\n"), 2637 kj::strTree("const ", constType, ' ', scope, upperCase, "(::capnp::schemas::b_", 2638 kj::hex(proto.getId()), ".words + ", schema.getValueSchemaOffset(), ");\n") 2639 }; 2640 } 2641 2642 case schema::Type::LIST: { 2643 kj::String constType = kj::strTree( 2644 "::capnp::_::ConstList<", typeName(type.asList().getElementType(), nullptr), ">") 2645 .flatten(); 2646 return ConstText { 2647 true, 2648 kj::strTree(linkage, "const ", constType, ' ', upperCase, ";\n"), 2649 kj::strTree("const ", constType, ' ', scope, upperCase, "(::capnp::schemas::b_", 2650 kj::hex(proto.getId()), ".words + ", schema.getValueSchemaOffset(), ");\n") 2651 }; 2652 } 2653 2654 case schema::Type::ANY_POINTER: 2655 case schema::Type::INTERFACE: 2656 return ConstText { false, kj::strTree(), kj::strTree() }; 2657 } 2658 2659 KJ_UNREACHABLE; 2660 } 2661 2662 // ----------------------------------------------------------------- 2663 2664 struct NodeText { 2665 kj::StringTree outerTypeDecl; 2666 kj::StringTree outerTypeDef; 2667 kj::StringTree readerBuilderDefs; 2668 kj::StringTree inlineMethodDefs; 2669 kj::StringTree capnpSchemaDecls; 2670 kj::StringTree capnpSchemaDefs; 2671 kj::StringTree sourceFileDefs; 2672 }; 2673 2674 NodeText makeNodeText(kj::StringPtr namespace_, kj::StringPtr scope, 2675 kj::StringPtr name, Schema schema, 2676 const TemplateContext& parentTemplateContext) { 2677 // `templateContext` is something like "template <typename T>\ntemplate <typename U>\n" 2678 // declaring template parameters for all parent scopes. 2679 2680 auto proto = schema.getProto(); 2681 KJ_IF_MAYBE(annotatedName, annotationValue(proto, NAME_ANNOTATION_ID)) { 2682 name = annotatedName->getText(); 2683 } 2684 auto hexId = kj::hex(proto.getId()); 2685 2686 TemplateContext templateContext(parentTemplateContext, name, proto); 2687 2688 auto subScope = kj::str(scope, name, templateContext.args(), "::"); 2689 2690 // Compute nested nodes, including groups. 2691 kj::Vector<NodeText> nestedTexts(proto.getNestedNodes().size()); 2692 for (auto nested: proto.getNestedNodes()) { 2693 nestedTexts.add(makeNodeText( 2694 namespace_, subScope, nested.getName(), schemaLoader.getUnbound(nested.getId()),\ 2695 templateContext)); 2696 }; 2697 2698 if (proto.isStruct()) { 2699 for (auto field: proto.getStruct().getFields()) { 2700 if (field.isGroup()) { 2701 nestedTexts.add(makeNodeText( 2702 namespace_, subScope, toTitleCase(protoName(field)), 2703 schemaLoader.getUnbound(field.getGroup().getTypeId()), 2704 templateContext)); 2705 } 2706 } 2707 } else if (proto.isInterface()) { 2708 for (auto method: proto.getInterface().getMethods()) { 2709 { 2710 Schema params = schemaLoader.getUnbound(method.getParamStructType()); 2711 auto paramsProto = schemaLoader.getUnbound(method.getParamStructType()).getProto(); 2712 if (paramsProto.getScopeId() == 0) { 2713 nestedTexts.add(makeNodeText(namespace_, subScope, 2714 toTitleCase(kj::str(protoName(method), "Params")), params, templateContext)); 2715 } 2716 } 2717 { 2718 Schema results = schemaLoader.getUnbound(method.getResultStructType()); 2719 auto resultsProto = schemaLoader.getUnbound(method.getResultStructType()).getProto(); 2720 if (resultsProto.getScopeId() == 0) { 2721 nestedTexts.add(makeNodeText(namespace_, subScope, 2722 toTitleCase(kj::str(protoName(method), "Results")), results, templateContext)); 2723 } 2724 } 2725 } 2726 } 2727 2728 // Convert the encoded schema to a literal byte array. 2729 kj::ArrayPtr<const word> rawSchema = schema.asUncheckedMessage(); 2730 auto schemaLiteral = kj::StringTree(KJ_MAP(w, rawSchema) { 2731 const byte* bytes = reinterpret_cast<const byte*>(&w); 2732 2733 return kj::strTree(KJ_MAP(i, kj::range<uint>(0, sizeof(word))) { 2734 auto text = kj::toCharSequence(kj::implicitCast<uint>(bytes[i])); 2735 return kj::strTree(kj::repeat(' ', 4 - text.size()), text, ","); 2736 }); 2737 }, "\n "); 2738 2739 auto schemaDecl = kj::strTree( 2740 "CAPNP_DECLARE_SCHEMA(", hexId, ");\n"); 2741 2742 std::set<uint64_t> deps; 2743 enumerateDeps(proto, deps); 2744 2745 kj::Array<uint> membersByName; 2746 kj::Array<uint> membersByDiscrim; 2747 switch (proto.which()) { 2748 case schema::Node::STRUCT: { 2749 auto structSchema = schema.asStruct(); 2750 membersByName = makeMembersByName(structSchema.getFields()); 2751 auto builder = kj::heapArrayBuilder<uint>(structSchema.getFields().size()); 2752 for (auto field: structSchema.getUnionFields()) { 2753 builder.add(field.getIndex()); 2754 } 2755 for (auto field: structSchema.getNonUnionFields()) { 2756 builder.add(field.getIndex()); 2757 } 2758 membersByDiscrim = builder.finish(); 2759 break; 2760 } 2761 case schema::Node::ENUM: 2762 membersByName = makeMembersByName(schema.asEnum().getEnumerants()); 2763 break; 2764 case schema::Node::INTERFACE: 2765 membersByName = makeMembersByName(schema.asInterface().getMethods()); 2766 break; 2767 default: 2768 break; 2769 } 2770 2771 auto brandDeps = makeBrandDepInitializers( 2772 makeBrandDepMap(templateContext, schema.getGeneric())); 2773 2774 auto schemaDef = kj::strTree( 2775 "static const ::capnp::_::AlignedData<", rawSchema.size(), "> b_", hexId, " = {\n" 2776 " {", kj::mv(schemaLiteral), " }\n" 2777 "};\n" 2778 "::capnp::word const* const bp_", hexId, " = b_", hexId, ".words;\n" 2779 "#if !CAPNP_LITE\n", 2780 deps.size() == 0 ? kj::strTree() : kj::strTree( 2781 "static const ::capnp::_::RawSchema* const d_", hexId, "[] = {\n", 2782 KJ_MAP(depId, deps) { 2783 return kj::strTree(" &s_", kj::hex(depId), ",\n"); 2784 }, 2785 "};\n"), 2786 membersByName.size() == 0 ? kj::strTree() : kj::strTree( 2787 "static const uint16_t m_", hexId, "[] = {", 2788 kj::StringTree(KJ_MAP(index, membersByName) { return kj::strTree(index); }, ", "), 2789 "};\n"), 2790 membersByDiscrim.size() == 0 ? kj::strTree() : kj::strTree( 2791 "static const uint16_t i_", hexId, "[] = {", 2792 kj::StringTree(KJ_MAP(index, membersByDiscrim) { return kj::strTree(index); }, ", "), 2793 "};\n"), 2794 brandDeps.size() == 0 ? kj::strTree() : kj::strTree( 2795 "KJ_CONSTEXPR(const) ::capnp::_::RawBrandedSchema::Dependency bd_", hexId, "[] = ", 2796 kj::mv(brandDeps), ";\n"), 2797 "const ::capnp::_::RawSchema s_", hexId, " = {\n" 2798 " 0x", hexId, ", b_", hexId, ".words, ", rawSchema.size(), ", ", 2799 deps.size() == 0 ? kj::strTree("nullptr") : kj::strTree("d_", hexId), ", ", 2800 membersByName.size() == 0 ? kj::strTree("nullptr") : kj::strTree("m_", hexId), ",\n", 2801 " ", deps.size(), ", ", membersByName.size(), ", ", 2802 membersByDiscrim.size() == 0 ? kj::strTree("nullptr") : kj::strTree("i_", hexId), 2803 ", nullptr, nullptr, { &s_", hexId, ", nullptr, ", 2804 brandDeps.size() == 0 ? kj::strTree("nullptr, 0, 0") : kj::strTree( 2805 "bd_", hexId, ", 0, " "sizeof(bd_", hexId, ") / sizeof(bd_", hexId, "[0])"), 2806 ", nullptr }\n" 2807 "};\n" 2808 "#endif // !CAPNP_LITE\n"); 2809 2810 NodeText top = makeNodeTextWithoutNested( 2811 namespace_, scope, name, schema, 2812 KJ_MAP(n, nestedTexts) { return kj::mv(n.outerTypeDecl); }, 2813 templateContext); 2814 2815 NodeText result = { 2816 kj::mv(top.outerTypeDecl), 2817 2818 kj::strTree( 2819 kj::mv(top.outerTypeDef), 2820 KJ_MAP(n, nestedTexts) { return kj::mv(n.outerTypeDef); }), 2821 2822 kj::strTree( 2823 kj::mv(top.readerBuilderDefs), 2824 KJ_MAP(n, nestedTexts) { return kj::mv(n.readerBuilderDefs); }), 2825 2826 kj::strTree( 2827 kj::mv(top.inlineMethodDefs), 2828 KJ_MAP(n, nestedTexts) { return kj::mv(n.inlineMethodDefs); }), 2829 2830 kj::strTree( 2831 kj::mv(schemaDecl), 2832 kj::mv(top.capnpSchemaDecls), 2833 KJ_MAP(n, nestedTexts) { return kj::mv(n.capnpSchemaDecls); }), 2834 2835 kj::strTree( 2836 kj::mv(schemaDef), 2837 kj::mv(top.capnpSchemaDefs), 2838 KJ_MAP(n, nestedTexts) { return kj::mv(n.capnpSchemaDefs); }), 2839 2840 kj::strTree( 2841 kj::mv(top.sourceFileDefs), 2842 KJ_MAP(n, nestedTexts) { return kj::mv(n.sourceFileDefs); }), 2843 }; 2844 2845 if (templateContext.isGeneric()) { 2846 // This is a template, so move all source declarations into the header. 2847 result.inlineMethodDefs = kj::strTree( 2848 kj::mv(result.inlineMethodDefs), kj::mv(result.sourceFileDefs)); 2849 result.sourceFileDefs = kj::strTree(); 2850 } 2851 2852 return result; 2853 } 2854 2855 NodeText makeNodeTextWithoutNested(kj::StringPtr namespace_, kj::StringPtr scope, 2856 kj::StringPtr name, Schema schema, 2857 kj::Array<kj::StringTree> nestedTypeDecls, 2858 const TemplateContext& templateContext) { 2859 auto proto = schema.getProto(); 2860 KJ_IF_MAYBE(annotatedName, annotationValue(proto, NAME_ANNOTATION_ID)) { 2861 name = annotatedName->getText(); 2862 } 2863 auto hexId = kj::hex(proto.getId()); 2864 2865 switch (proto.which()) { 2866 case schema::Node::FILE: 2867 KJ_FAIL_REQUIRE("This method shouldn't be called on file nodes."); 2868 2869 case schema::Node::STRUCT: { 2870 StructText structText = 2871 makeStructText(scope, name, schema.asStruct(), kj::mv(nestedTypeDecls), 2872 templateContext); 2873 2874 return NodeText { 2875 kj::mv(structText.outerTypeDecl), 2876 kj::mv(structText.outerTypeDef), 2877 kj::mv(structText.readerBuilderDefs), 2878 kj::mv(structText.inlineMethodDefs), 2879 2880 kj::strTree(), 2881 kj::strTree(), 2882 2883 kj::mv(structText.sourceDefs), 2884 }; 2885 } 2886 2887 case schema::Node::ENUM: { 2888 auto enumerants = schema.asEnum().getEnumerants(); 2889 2890 return NodeText { 2891 scope.size() == 0 ? kj::strTree() : kj::strTree( 2892 " typedef ::capnp::schemas::", name, "_", hexId, " ", name, ";\n" 2893 "\n"), 2894 2895 scope.size() > 0 ? kj::strTree() : kj::strTree( 2896 "typedef ::capnp::schemas::", name, "_", hexId, " ", name, ";\n" 2897 "\n"), 2898 2899 kj::strTree(), 2900 kj::strTree(), 2901 2902 kj::strTree( 2903 // We declare enums in the capnp::schemas namespace and then typedef them into 2904 // place because we don't want them to be parameterized for generics. 2905 "enum class ", name, "_", hexId, ": uint16_t {\n", 2906 KJ_MAP(e, enumerants) { 2907 return kj::strTree(" ", toUpperCase(protoName(e.getProto())), ",\n"); 2908 }, 2909 "};\n" 2910 "CAPNP_DECLARE_ENUM(", name, ", ", hexId, ");\n"), 2911 kj::strTree( 2912 "CAPNP_DEFINE_ENUM(", name, "_", hexId, ", ", hexId, ");\n"), 2913 2914 kj::strTree(), 2915 }; 2916 } 2917 2918 case schema::Node::INTERFACE: { 2919 hasInterfaces = true; 2920 2921 InterfaceText interfaceText = 2922 makeInterfaceText(scope, name, schema.asInterface(), kj::mv(nestedTypeDecls), 2923 templateContext); 2924 2925 return NodeText { 2926 kj::mv(interfaceText.outerTypeDecl), 2927 kj::mv(interfaceText.outerTypeDef), 2928 kj::mv(interfaceText.clientServerDefs), 2929 kj::mv(interfaceText.inlineMethodDefs), 2930 2931 kj::strTree(), 2932 kj::strTree(), 2933 2934 kj::mv(interfaceText.sourceDefs), 2935 }; 2936 } 2937 2938 case schema::Node::CONST: { 2939 auto constText = makeConstText(scope, name, schema.asConst(), templateContext); 2940 2941 return NodeText { 2942 scope.size() == 0 ? kj::strTree() : kj::strTree(" ", kj::mv(constText.decl)), 2943 scope.size() > 0 ? kj::strTree() : kj::mv(constText.decl), 2944 kj::strTree(), 2945 kj::strTree(), 2946 2947 kj::strTree(), 2948 kj::strTree(), 2949 2950 kj::mv(constText.def), 2951 }; 2952 } 2953 2954 case schema::Node::ANNOTATION: { 2955 return NodeText { 2956 kj::strTree(), 2957 kj::strTree(), 2958 kj::strTree(), 2959 kj::strTree(), 2960 2961 kj::strTree(), 2962 kj::strTree(), 2963 2964 kj::strTree(), 2965 }; 2966 } 2967 } 2968 2969 KJ_UNREACHABLE; 2970 } 2971 2972 // ----------------------------------------------------------------- 2973 2974 struct FileText { 2975 kj::StringTree header; 2976 kj::StringTree source; 2977 }; 2978 2979 FileText makeFileText(Schema schema, 2980 schema::CodeGeneratorRequest::RequestedFile::Reader request) { 2981 usedImports.clear(); 2982 2983 auto node = schema.getProto(); 2984 auto displayName = node.getDisplayName(); 2985 2986 kj::Vector<kj::ArrayPtr<const char>> namespaceParts; 2987 kj::String namespacePrefix; 2988 2989 for (auto annotation: node.getAnnotations()) { 2990 if (annotation.getId() == NAMESPACE_ANNOTATION_ID) { 2991 kj::StringPtr ns = annotation.getValue().getText(); 2992 kj::StringPtr ns2 = ns; 2993 namespacePrefix = kj::str("::", ns); 2994 2995 for (;;) { 2996 KJ_IF_MAYBE(colonPos, ns.findFirst(':')) { 2997 namespaceParts.add(ns.slice(0, *colonPos)); 2998 ns = ns.slice(*colonPos); 2999 if (!ns.startsWith("::")) { 3000 context.exitError(kj::str(displayName, ": invalid namespace spec: ", ns2)); 3001 } 3002 ns = ns.slice(2); 3003 } else { 3004 namespaceParts.add(ns); 3005 break; 3006 } 3007 } 3008 3009 break; 3010 } 3011 } 3012 3013 auto nodeTexts = KJ_MAP(nested, node.getNestedNodes()) { 3014 return makeNodeText(namespacePrefix, "", nested.getName(), 3015 schemaLoader.getUnbound(nested.getId()), TemplateContext()); 3016 }; 3017 3018 kj::String separator = kj::str("// ", kj::repeat('=', 87), "\n"); 3019 3020 kj::Vector<kj::StringPtr> includes; 3021 for (auto import: request.getImports()) { 3022 if (usedImports.count(import.getId()) > 0) { 3023 includes.add(import.getName()); 3024 } 3025 } 3026 3027 kj::StringTree sourceDefs = kj::strTree( 3028 KJ_MAP(n, nodeTexts) { return kj::mv(n.sourceFileDefs); }); 3029 3030 return FileText { 3031 kj::strTree( 3032 "// Generated by Cap'n Proto compiler, DO NOT EDIT\n" 3033 "// source: ", baseName(displayName), "\n" 3034 "\n" 3035 "#pragma once\n" 3036 "\n" 3037 "#include <capnp/generated-header-support.h>\n" 3038 "#include <kj/windows-sanity.h>\n", // work-around macro conflict with VOID 3039 hasInterfaces ? kj::strTree( 3040 "#if !CAPNP_LITE\n" 3041 "#include <capnp/capability.h>\n" 3042 "#endif // !CAPNP_LITE\n" 3043 ) : kj::strTree(), 3044 "\n" 3045 "#if CAPNP_VERSION != ", CAPNP_VERSION, "\n" 3046 "#error \"Version mismatch between generated code and library headers. You must " 3047 "use the same version of the Cap'n Proto compiler and library.\"\n" 3048 "#endif\n" 3049 "\n", 3050 KJ_MAP(path, includes) { 3051 if (path.startsWith("/")) { 3052 return kj::strTree("#include <", path.slice(1), ".h>\n"); 3053 } else { 3054 return kj::strTree("#include \"", path, ".h\"\n"); 3055 } 3056 }, 3057 "\n" 3058 "CAPNP_BEGIN_HEADER\n" 3059 "\n" 3060 "namespace capnp {\n" 3061 "namespace schemas {\n" 3062 "\n", 3063 KJ_MAP(n, nodeTexts) { return kj::mv(n.capnpSchemaDecls); }, 3064 "\n" 3065 "} // namespace schemas\n" 3066 "} // namespace capnp\n" 3067 "\n", 3068 3069 KJ_MAP(n, namespaceParts) { return kj::strTree("namespace ", n, " {\n"); }, "\n", 3070 KJ_MAP(n, nodeTexts) { return kj::mv(n.outerTypeDef); }, 3071 separator, "\n", 3072 KJ_MAP(n, nodeTexts) { return kj::mv(n.readerBuilderDefs); }, 3073 separator, "\n", 3074 KJ_MAP(n, nodeTexts) { return kj::mv(n.inlineMethodDefs); }, 3075 KJ_MAP(n, namespaceParts) { return kj::strTree("} // namespace\n"); }, 3076 "\n" 3077 "CAPNP_END_HEADER\n" 3078 "\n"), 3079 3080 kj::strTree( 3081 "// Generated by Cap'n Proto compiler, DO NOT EDIT\n" 3082 "// source: ", baseName(displayName), "\n" 3083 "\n" 3084 "#include \"", baseName(displayName), ".h\"\n" 3085 "\n" 3086 "namespace capnp {\n" 3087 "namespace schemas {\n", 3088 KJ_MAP(n, nodeTexts) { return kj::mv(n.capnpSchemaDefs); }, 3089 "} // namespace schemas\n" 3090 "} // namespace capnp\n", 3091 sourceDefs.size() == 0 ? kj::strTree() : kj::strTree( 3092 "\n", separator, "\n", 3093 KJ_MAP(n, namespaceParts) { return kj::strTree("namespace ", n, " {\n"); }, "\n", 3094 kj::mv(sourceDefs), "\n", 3095 KJ_MAP(n, namespaceParts) { return kj::strTree("} // namespace\n"); }, "\n")) 3096 }; 3097 } 3098 3099 // ----------------------------------------------------------------- 3100 3101 kj::Own<kj::Filesystem> fs = kj::newDiskFilesystem(); 3102 3103 void writeFile(kj::StringPtr filename, const kj::StringTree& text) { 3104 // We don't use replaceFile() here because atomic replacements are actually detrimental for 3105 // build tools: 3106 // - It's the responsibility of the build pipeline to ensure that no one else is concurrently 3107 // reading the file when we write it, so atomicity brings no benefit. 3108 // - Atomic replacements force disk syncs which could slow us down for no benefit at all. 3109 // - Opening the existing file and overwriting it may allow the filesystem to reuse 3110 // already-allocated blocks, or maybe even notice that no actual changes occurred. 3111 // - In a power outage scenario, the user would obviously restart the build from scratch 3112 // anyway. 3113 // 3114 // At one point, in a fit of over-engineering, we used writable mmap() here. That turned out 3115 // to be a bad idea: writable mmap() is not implemented on some filesystems, especially shared 3116 // folders in VirtualBox. Oh well. 3117 3118 auto path = kj::Path::parse(filename); 3119 auto file = fs->getCurrent().openFile(path, 3120 kj::WriteMode::CREATE | kj::WriteMode::MODIFY | kj::WriteMode::CREATE_PARENT); 3121 file->writeAll(text.flatten()); 3122 } 3123 3124 kj::MainBuilder::Validity run() { 3125 ReaderOptions options; 3126 options.traversalLimitInWords = 1 << 30; // Don't limit. 3127 StreamFdMessageReader reader(0, options); 3128 auto request = reader.getRoot<schema::CodeGeneratorRequest>(); 3129 3130 auto capnpVersion = request.getCapnpVersion(); 3131 3132 if (capnpVersion.getMajor() != CAPNP_VERSION_MAJOR || 3133 capnpVersion.getMinor() != CAPNP_VERSION_MINOR || 3134 capnpVersion.getMicro() != CAPNP_VERSION_MICRO) { 3135 auto compilerVersion = request.hasCapnpVersion() 3136 ? kj::str(capnpVersion.getMajor(), '.', capnpVersion.getMinor(), '.', 3137 capnpVersion.getMicro()) 3138 : kj::str("pre-0.6"); // pre-0.6 didn't send the version. 3139 auto generatorVersion = kj::str( 3140 CAPNP_VERSION_MAJOR, '.', CAPNP_VERSION_MINOR, '.', CAPNP_VERSION_MICRO); 3141 3142 KJ_LOG(WARNING, 3143 "You appear to be using different versions of 'capnp' (the compiler) and " 3144 "'capnpc-c++' (the code generator). This can happen, for example, if you built " 3145 "a custom version of 'capnp' but then ran it with '-oc++', which invokes " 3146 "'capnpc-c++' from your PATH (i.e. the installed version). To specify an alternate " 3147 "'capnpc-c++' executable, try something like '-o/path/to/capnpc-c++' instead.", 3148 compilerVersion, generatorVersion); 3149 } 3150 3151 for (auto node: request.getNodes()) { 3152 schemaLoader.load(node); 3153 } 3154 3155 for (auto requestedFile: request.getRequestedFiles()) { 3156 auto schema = schemaLoader.get(requestedFile.getId()); 3157 auto fileText = makeFileText(schema, requestedFile); 3158 3159 writeFile(kj::str(schema.getProto().getDisplayName(), ".h"), fileText.header); 3160 writeFile(kj::str(schema.getProto().getDisplayName(), ".c++"), fileText.source); 3161 } 3162 3163 return true; 3164 } 3165 }; 3166 3167 } // namespace 3168 } // namespace capnp 3169 3170 KJ_MAIN(capnp::CapnpcCppMain);