capnpc-capnp.c++ (28007B)
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 writes the schema back to 23 // stdout in roughly capnpc format. 24 25 #ifndef _GNU_SOURCE 26 #define _GNU_SOURCE 27 #endif 28 29 #include <capnp/schema.capnp.h> 30 #include "../serialize.h" 31 #include <kj/debug.h> 32 #include <kj/io.h> 33 #include <kj/string-tree.h> 34 #include <kj/vector.h> 35 #include "../schema-loader.h" 36 #include "../dynamic.h" 37 #include <kj/miniposix.h> 38 #include <unordered_map> 39 #include <kj/main.h> 40 #include <algorithm> 41 #include <map> 42 #include <capnp/stream.capnp.h> 43 44 #if HAVE_CONFIG_H 45 #include "config.h" 46 #endif 47 48 #ifndef VERSION 49 #define VERSION "(unknown)" 50 #endif 51 52 namespace capnp { 53 namespace { 54 55 bool hasDiscriminantValue(const schema::Field::Reader& reader) { 56 return reader.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT; 57 } 58 59 struct Indent { 60 uint amount; 61 Indent() = default; 62 inline Indent(int amount): amount(amount) {} 63 64 Indent next() { 65 return Indent(amount + 2); 66 } 67 68 struct Iterator { 69 uint i; 70 Iterator() = default; 71 inline Iterator(uint i): i(i) {} 72 inline char operator*() const { return ' '; } 73 inline Iterator& operator++() { ++i; return *this; } 74 inline Iterator operator++(int) { Iterator result = *this; ++i; return result; } 75 inline bool operator==(const Iterator& other) const { return i == other.i; } 76 inline bool operator!=(const Iterator& other) const { return i != other.i; } 77 }; 78 79 inline size_t size() const { return amount; } 80 81 inline Iterator begin() const { return Iterator(0); } 82 inline Iterator end() const { return Iterator(amount); } 83 }; 84 85 inline Indent KJ_STRINGIFY(const Indent& indent) { 86 return indent; 87 } 88 89 // ======================================================================================= 90 91 class CapnpcCapnpMain { 92 public: 93 CapnpcCapnpMain(kj::ProcessContext& context): context(context) {} 94 95 kj::MainFunc getMain() { 96 return kj::MainBuilder(context, "Cap'n Proto loopback plugin version " VERSION, 97 "This is a Cap'n Proto compiler plugin which \"de-compiles\" the schema back into " 98 "Cap'n Proto schema language format, with comments showing the offsets chosen by the " 99 "compiler. This is meant to be run using the Cap'n Proto compiler, e.g.:\n" 100 " capnp compile -ocapnp foo.capnp") 101 .callAfterParsing(KJ_BIND_METHOD(*this, run)) 102 .build(); 103 } 104 105 private: 106 kj::ProcessContext& context; 107 SchemaLoader schemaLoader; 108 109 Text::Reader getUnqualifiedName(Schema schema) { 110 auto proto = schema.getProto(); 111 KJ_CONTEXT(proto.getDisplayName()); 112 auto parent = schemaLoader.get(proto.getScopeId()); 113 for (auto nested: parent.getProto().getNestedNodes()) { 114 if (nested.getId() == proto.getId()) { 115 return nested.getName(); 116 } 117 } 118 KJ_FAIL_REQUIRE("A schema Node's supposed scope did not contain the node as a NestedNode."); 119 return "(?)"; 120 } 121 122 kj::StringTree nodeName(Schema target, Schema scope, schema::Brand::Reader brand, 123 kj::Maybe<InterfaceSchema::Method> method) { 124 kj::Vector<Schema> targetPath; 125 kj::Vector<Schema> scopeParts; 126 127 targetPath.add(target); 128 129 std::map<uint64_t, List<schema::Brand::Binding>::Reader> scopeBindings; 130 for (auto scopeBrand: brand.getScopes()) { 131 switch (scopeBrand.which()) { 132 case schema::Brand::Scope::BIND: 133 scopeBindings[scopeBrand.getScopeId()] = scopeBrand.getBind(); 134 break; 135 case schema::Brand::Scope::INHERIT: 136 // TODO(someday): We need to pay attention to INHERIT and be sure to explicitly override 137 // any bindings that are not inherited. This requires a way to determine which of our 138 // parent scopes have a non-empty parameter list. 139 break; 140 } 141 } 142 143 { 144 Schema parent = target; 145 while (parent.getProto().getScopeId() != 0) { 146 parent = schemaLoader.get(parent.getProto().getScopeId()); 147 targetPath.add(parent); 148 } 149 } 150 151 { 152 Schema parent = scope; 153 scopeParts.add(parent); 154 while (parent.getProto().getScopeId() != 0) { 155 parent = schemaLoader.get(parent.getProto().getScopeId()); 156 scopeParts.add(parent); 157 } 158 } 159 160 // Remove common scope (unless it has been reparameterized). 161 // TODO(someday): This is broken in that we aren't checking for shadowing. 162 while (!scopeParts.empty() && targetPath.size() > 1 && 163 scopeParts.back() == targetPath.back() && 164 scopeBindings.count(scopeParts.back().getProto().getId()) == 0) { 165 scopeParts.removeLast(); 166 targetPath.removeLast(); 167 } 168 169 auto parts = kj::heapArrayBuilder<kj::StringTree>(targetPath.size()); 170 while (!targetPath.empty()) { 171 auto part = targetPath.back(); 172 auto proto = part.getProto(); 173 kj::StringTree partStr; 174 if (proto.getScopeId() == 0) { 175 partStr = kj::strTree("import \"/", proto.getDisplayName(), '\"'); 176 } else { 177 partStr = kj::strTree(getUnqualifiedName(part)); 178 } 179 180 auto iter = scopeBindings.find(proto.getId()); 181 if (iter != scopeBindings.end()) { 182 auto bindings = KJ_MAP(binding, iter->second) { 183 switch (binding.which()) { 184 case schema::Brand::Binding::UNBOUND: 185 return kj::strTree("AnyPointer"); 186 case schema::Brand::Binding::TYPE: 187 return genType(binding.getType(), scope, method); 188 } 189 return kj::strTree("<unknown binding>"); 190 }; 191 partStr = kj::strTree(kj::mv(partStr), "(", kj::StringTree(kj::mv(bindings), ", "), ")"); 192 } 193 194 parts.add(kj::mv(partStr)); 195 targetPath.removeLast(); 196 } 197 198 return kj::StringTree(parts.finish(), "."); 199 } 200 201 kj::StringTree genType(schema::Type::Reader type, Schema scope, 202 kj::Maybe<InterfaceSchema::Method> method) { 203 switch (type.which()) { 204 case schema::Type::VOID: return kj::strTree("Void"); 205 case schema::Type::BOOL: return kj::strTree("Bool"); 206 case schema::Type::INT8: return kj::strTree("Int8"); 207 case schema::Type::INT16: return kj::strTree("Int16"); 208 case schema::Type::INT32: return kj::strTree("Int32"); 209 case schema::Type::INT64: return kj::strTree("Int64"); 210 case schema::Type::UINT8: return kj::strTree("UInt8"); 211 case schema::Type::UINT16: return kj::strTree("UInt16"); 212 case schema::Type::UINT32: return kj::strTree("UInt32"); 213 case schema::Type::UINT64: return kj::strTree("UInt64"); 214 case schema::Type::FLOAT32: return kj::strTree("Float32"); 215 case schema::Type::FLOAT64: return kj::strTree("Float64"); 216 case schema::Type::TEXT: return kj::strTree("Text"); 217 case schema::Type::DATA: return kj::strTree("Data"); 218 case schema::Type::LIST: 219 return kj::strTree("List(", genType(type.getList().getElementType(), scope, method), ")"); 220 case schema::Type::ENUM: 221 return nodeName(schemaLoader.get(type.getEnum().getTypeId()), scope, 222 type.getEnum().getBrand(), method); 223 case schema::Type::STRUCT: 224 return nodeName(schemaLoader.get(type.getStruct().getTypeId()), scope, 225 type.getStruct().getBrand(), method); 226 case schema::Type::INTERFACE: 227 return nodeName(schemaLoader.get(type.getInterface().getTypeId()), scope, 228 type.getInterface().getBrand(), method); 229 case schema::Type::ANY_POINTER: { 230 auto anyPointer = type.getAnyPointer(); 231 switch (anyPointer.which()) { 232 case schema::Type::AnyPointer::UNCONSTRAINED: 233 switch (anyPointer.getUnconstrained().which()) { 234 case schema::Type::AnyPointer::Unconstrained::ANY_KIND: 235 return kj::strTree("AnyPointer"); 236 case schema::Type::AnyPointer::Unconstrained::STRUCT: 237 return kj::strTree("AnyStruct"); 238 case schema::Type::AnyPointer::Unconstrained::LIST: 239 return kj::strTree("AnyList"); 240 case schema::Type::AnyPointer::Unconstrained::CAPABILITY: 241 return kj::strTree("Capability"); 242 } 243 KJ_UNREACHABLE; 244 case schema::Type::AnyPointer::PARAMETER: { 245 auto param = anyPointer.getParameter(); 246 auto scopeProto = scope.getProto(); 247 auto targetScopeId = param.getScopeId(); 248 while (scopeProto.getId() != targetScopeId) { 249 scopeProto = schemaLoader.get(param.getScopeId()).getProto(); 250 } 251 auto params = scopeProto.getParameters(); 252 KJ_REQUIRE(param.getParameterIndex() < params.size()); 253 return kj::strTree(params[param.getParameterIndex()].getName()); 254 } 255 case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER: { 256 auto params = KJ_REQUIRE_NONNULL(method).getProto().getImplicitParameters(); 257 uint index = anyPointer.getImplicitMethodParameter().getParameterIndex(); 258 KJ_REQUIRE(index < params.size()); 259 return kj::strTree(params[index].getName()); 260 } 261 } 262 KJ_UNREACHABLE; 263 } 264 } 265 return kj::strTree(); 266 } 267 268 int typeSizeBits(schema::Type::Reader type) { 269 switch (type.which()) { 270 case schema::Type::VOID: return 0; 271 case schema::Type::BOOL: return 1; 272 case schema::Type::INT8: return 8; 273 case schema::Type::INT16: return 16; 274 case schema::Type::INT32: return 32; 275 case schema::Type::INT64: return 64; 276 case schema::Type::UINT8: return 8; 277 case schema::Type::UINT16: return 16; 278 case schema::Type::UINT32: return 32; 279 case schema::Type::UINT64: return 64; 280 case schema::Type::FLOAT32: return 32; 281 case schema::Type::FLOAT64: return 64; 282 case schema::Type::TEXT: return -1; 283 case schema::Type::DATA: return -1; 284 case schema::Type::LIST: return -1; 285 case schema::Type::ENUM: return 16; 286 case schema::Type::STRUCT: return -1; 287 case schema::Type::INTERFACE: return -1; 288 case schema::Type::ANY_POINTER: return -1; 289 } 290 return 0; 291 } 292 293 bool isEmptyValue(schema::Value::Reader value) { 294 switch (value.which()) { 295 case schema::Value::VOID: return true; 296 case schema::Value::BOOL: return value.getBool() == false; 297 case schema::Value::INT8: return value.getInt8() == 0; 298 case schema::Value::INT16: return value.getInt16() == 0; 299 case schema::Value::INT32: return value.getInt32() == 0; 300 case schema::Value::INT64: return value.getInt64() == 0; 301 case schema::Value::UINT8: return value.getUint8() == 0; 302 case schema::Value::UINT16: return value.getUint16() == 0; 303 case schema::Value::UINT32: return value.getUint32() == 0; 304 case schema::Value::UINT64: return value.getUint64() == 0; 305 case schema::Value::FLOAT32: return value.getFloat32() == 0; 306 case schema::Value::FLOAT64: return value.getFloat64() == 0; 307 case schema::Value::TEXT: return !value.hasText(); 308 case schema::Value::DATA: return !value.hasData(); 309 case schema::Value::LIST: return !value.hasList(); 310 case schema::Value::ENUM: return value.getEnum() == 0; 311 case schema::Value::STRUCT: return !value.hasStruct(); 312 case schema::Value::INTERFACE: return true; 313 case schema::Value::ANY_POINTER: return true; 314 } 315 return true; 316 } 317 318 kj::StringTree genValue(Type type, schema::Value::Reader value) { 319 switch (value.which()) { 320 case schema::Value::VOID: return kj::strTree("void"); 321 case schema::Value::BOOL: 322 return kj::strTree(value.getBool() ? "true" : "false"); 323 case schema::Value::INT8: return kj::strTree((int)value.getInt8()); 324 case schema::Value::INT16: return kj::strTree(value.getInt16()); 325 case schema::Value::INT32: return kj::strTree(value.getInt32()); 326 case schema::Value::INT64: return kj::strTree(value.getInt64()); 327 case schema::Value::UINT8: return kj::strTree((uint)value.getUint8()); 328 case schema::Value::UINT16: return kj::strTree(value.getUint16()); 329 case schema::Value::UINT32: return kj::strTree(value.getUint32()); 330 case schema::Value::UINT64: return kj::strTree(value.getUint64()); 331 case schema::Value::FLOAT32: return kj::strTree(value.getFloat32()); 332 case schema::Value::FLOAT64: return kj::strTree(value.getFloat64()); 333 case schema::Value::TEXT: 334 return kj::strTree(DynamicValue::Reader(value.getText())); 335 case schema::Value::DATA: 336 return kj::strTree(DynamicValue::Reader(value.getData())); 337 case schema::Value::LIST: { 338 auto listValue = value.getList().getAs<DynamicList>(type.asList()); 339 return kj::strTree(listValue); 340 } 341 case schema::Value::ENUM: { 342 auto enumNode = type.asEnum().getProto(); 343 auto enumerants = enumNode.getEnum().getEnumerants(); 344 KJ_REQUIRE(value.getEnum() < enumerants.size(), 345 "Enum value out-of-range.", value.getEnum(), enumNode.getDisplayName()); 346 return kj::strTree(enumerants[value.getEnum()].getName()); 347 } 348 case schema::Value::STRUCT: { 349 KJ_REQUIRE(type.which() == schema::Type::STRUCT, "type/value mismatch"); 350 auto structValue = value.getStruct().getAs<DynamicStruct>(type.asStruct()); 351 return kj::strTree(structValue); 352 } 353 case schema::Value::INTERFACE: { 354 return kj::strTree(""); 355 } 356 case schema::Value::ANY_POINTER: { 357 return kj::strTree(""); 358 } 359 } 360 return kj::strTree(""); 361 } 362 363 kj::StringTree genGenericParams(List<schema::Node::Parameter>::Reader params, Schema scope) { 364 if (params.size() == 0) { 365 return kj::strTree(); 366 } 367 368 return kj::strTree(" (", kj::StringTree( 369 KJ_MAP(param, params) { return kj::strTree(param.getName()); }, ", "), ')'); 370 } 371 kj::StringTree genGenericParams(Schema schema) { 372 auto proto = schema.getProto(); 373 return genGenericParams(proto.getParameters(), schemaLoader.get(proto.getScopeId())); 374 } 375 376 kj::StringTree genAnnotation(schema::Annotation::Reader annotation, 377 Schema scope, 378 const char* prefix = " ", const char* suffix = "") { 379 auto decl = schemaLoader.get(annotation.getId(), annotation.getBrand(), scope); 380 auto proto = decl.getProto(); 381 KJ_REQUIRE(proto.isAnnotation()); 382 auto annDecl = proto.getAnnotation(); 383 384 auto value = genValue(schemaLoader.getType(annDecl.getType(), decl), 385 annotation.getValue()).flatten(); 386 if (value.startsWith("(")) { 387 return kj::strTree(prefix, "$", nodeName(decl, scope, annotation.getBrand(), nullptr), 388 value, suffix); 389 } else { 390 return kj::strTree(prefix, "$", nodeName(decl, scope, annotation.getBrand(), nullptr), 391 "(", value, ")", suffix); 392 } 393 } 394 395 kj::StringTree genAnnotations(List<schema::Annotation>::Reader list, Schema scope) { 396 return kj::strTree(KJ_MAP(ann, list) { return genAnnotation(ann, scope); }); 397 } 398 kj::StringTree genAnnotations(Schema schema) { 399 auto proto = schema.getProto(); 400 return genAnnotations(proto.getAnnotations(), schemaLoader.get(proto.getScopeId())); 401 } 402 403 const char* elementSizeName(schema::ElementSize size) { 404 switch (size) { 405 case schema::ElementSize::EMPTY: return "void"; 406 case schema::ElementSize::BIT: return "1-bit"; 407 case schema::ElementSize::BYTE: return "8-bit"; 408 case schema::ElementSize::TWO_BYTES: return "16-bit"; 409 case schema::ElementSize::FOUR_BYTES: return "32-bit"; 410 case schema::ElementSize::EIGHT_BYTES: return "64-bit"; 411 case schema::ElementSize::POINTER: return "pointer"; 412 case schema::ElementSize::INLINE_COMPOSITE: return "inline composite"; 413 } 414 return ""; 415 } 416 417 struct OrderByCodeOrder { 418 template <typename T> 419 inline bool operator()(const T& a, const T& b) const { 420 return a.getProto().getCodeOrder() < b.getProto().getCodeOrder(); 421 } 422 }; 423 424 template <typename MemberList> 425 kj::Array<decltype(kj::instance<MemberList>()[0])> sortByCodeOrder(MemberList&& list) { 426 auto sorted = KJ_MAP(item, list) { return item; }; 427 std::sort(sorted.begin(), sorted.end(), OrderByCodeOrder()); 428 return kj::mv(sorted); 429 } 430 431 kj::Array<kj::StringTree> genStructFields(StructSchema schema, Indent indent) { 432 // Slightly hacky: We want to print in code order, but we also need to print the union in one 433 // chunk. Its fields should be together in code order anyway, but it's easier to simply 434 // output the whole union in place of the first union field, and then output nothing for the 435 // subsequent fields. 436 437 bool seenUnion = false; 438 return KJ_MAP(field, sortByCodeOrder(schema.getFields())) { 439 if (hasDiscriminantValue(field.getProto())) { 440 if (seenUnion) { 441 return kj::strTree(); 442 } else { 443 seenUnion = true; 444 uint offset = schema.getProto().getStruct().getDiscriminantOffset(); 445 446 // GCC 4.7.3 crashes if you inline unionFields. 447 auto unionFields = sortByCodeOrder(schema.getUnionFields()); 448 return kj::strTree( 449 indent, "union { # tag bits [", offset * 16, ", ", offset * 16 + 16, ")\n", 450 KJ_MAP(uField, unionFields) { 451 return genStructField(uField, schema, indent.next()); 452 }, 453 indent, "}\n"); 454 } 455 } else { 456 return genStructField(field, schema, indent); 457 } 458 }; 459 } 460 461 kj::StringTree genStructField(StructSchema::Field field, Schema scope, Indent indent) { 462 auto proto = field.getProto(); 463 switch (proto.which()) { 464 case schema::Field::SLOT: { 465 auto slot = proto.getSlot(); 466 int size = typeSizeBits(slot.getType()); 467 return kj::strTree( 468 indent, proto.getName(), " @", proto.getOrdinal().getExplicit(), 469 " :", genType(slot.getType(), scope, nullptr), 470 isEmptyValue(slot.getDefaultValue()) ? kj::strTree("") : 471 kj::strTree(" = ", genValue(field.getType(), slot.getDefaultValue())), 472 genAnnotations(proto.getAnnotations(), scope), 473 "; # ", size == -1 ? kj::strTree("ptr[", slot.getOffset(), "]") 474 : kj::strTree("bits[", slot.getOffset() * size, ", ", 475 (slot.getOffset() + 1) * size, ")"), 476 hasDiscriminantValue(proto) 477 ? kj::strTree(", union tag = ", proto.getDiscriminantValue()) : kj::strTree(), 478 "\n"); 479 } 480 case schema::Field::GROUP: { 481 auto group = field.getType().asStruct(); 482 return kj::strTree( 483 indent, proto.getName(), 484 " :group", genAnnotations(proto.getAnnotations(), scope), " {", 485 hasDiscriminantValue(proto) 486 ? kj::strTree(" # union tag = ", proto.getDiscriminantValue()) : kj::strTree(), 487 "\n", 488 genStructFields(group, indent.next()), 489 indent, "}\n"); 490 } 491 } 492 return kj::strTree(); 493 } 494 495 kj::StringTree genParamList(InterfaceSchema interface, StructSchema schema, 496 schema::Brand::Reader brand, InterfaceSchema::Method method) { 497 if (schema.getProto().getId() == typeId<StreamResult>()) { 498 return kj::strTree("stream"); 499 } else if (schema.getProto().getScopeId() == 0) { 500 // A named parameter list. 501 return kj::strTree("(", kj::StringTree( 502 KJ_MAP(field, schema.getFields()) { 503 auto proto = field.getProto(); 504 auto slot = proto.getSlot(); 505 506 return kj::strTree( 507 proto.getName(), " :", genType(slot.getType(), interface, nullptr), 508 isEmptyValue(slot.getDefaultValue()) ? kj::strTree("") : 509 kj::strTree(" = ", genValue(field.getType(), slot.getDefaultValue())), 510 genAnnotations(proto.getAnnotations(), interface)); 511 }, ", "), ")"); 512 } else { 513 return nodeName(schema, interface, brand, method); 514 } 515 } 516 517 kj::StringTree genSuperclasses(InterfaceSchema interface) { 518 auto superclasses = interface.getProto().getInterface().getSuperclasses(); 519 if (superclasses.size() == 0) { 520 return kj::strTree(); 521 } else { 522 return kj::strTree(" superclasses(", kj::StringTree( 523 KJ_MAP(superclass, superclasses) { 524 return nodeName(schemaLoader.get(superclass.getId()), interface, 525 superclass.getBrand(), nullptr); 526 }, ", "), ")"); 527 } 528 } 529 530 kj::StringTree genDecl(Schema schema, Text::Reader name, uint64_t scopeId, Indent indent) { 531 auto proto = schema.getProto(); 532 if (proto.getScopeId() != scopeId) { 533 // This appears to be an alias for something declared elsewhere. 534 KJ_FAIL_REQUIRE("Aliases not implemented."); 535 } 536 537 switch (proto.which()) { 538 case schema::Node::FILE: 539 KJ_FAIL_REQUIRE("Encountered nested file node."); 540 break; 541 case schema::Node::STRUCT: { 542 auto structProto = proto.getStruct(); 543 return kj::strTree( 544 indent, "struct ", name, 545 " @0x", kj::hex(proto.getId()), genGenericParams(schema), 546 genAnnotations(schema), " { # ", 547 structProto.getDataWordCount() * 8, " bytes, ", 548 structProto.getPointerCount(), " ptrs", 549 structProto.getPreferredListEncoding() == schema::ElementSize::INLINE_COMPOSITE 550 ? kj::strTree() 551 : kj::strTree(", packed as ", 552 elementSizeName(structProto.getPreferredListEncoding())), 553 "\n", 554 genStructFields(schema.asStruct(), indent.next()), 555 genNestedDecls(schema, indent.next()), 556 indent, "}\n"); 557 } 558 case schema::Node::ENUM: { 559 return kj::strTree( 560 indent, "enum ", name, " @0x", kj::hex(proto.getId()), genAnnotations(schema), " {\n", 561 KJ_MAP(enumerant, sortByCodeOrder(schema.asEnum().getEnumerants())) { 562 return kj::strTree(indent.next(), enumerant.getProto().getName(), " @", 563 enumerant.getIndex(), 564 genAnnotations(enumerant.getProto().getAnnotations(), schema), 565 ";\n"); 566 }, 567 genNestedDecls(schema, indent.next()), 568 indent, "}\n"); 569 } 570 case schema::Node::INTERFACE: { 571 auto interface = schema.asInterface(); 572 return kj::strTree( 573 indent, "interface ", name, " @0x", kj::hex(proto.getId()), genGenericParams(schema), 574 genSuperclasses(interface), 575 genAnnotations(schema), " {\n", 576 KJ_MAP(method, sortByCodeOrder(interface.getMethods())) { 577 auto methodProto = method.getProto(); 578 579 auto implicits = methodProto.getImplicitParameters(); 580 kj::StringTree implicitsStr; 581 if (implicits.size() > 0) { 582 implicitsStr = kj::strTree( 583 "[", kj::StringTree(KJ_MAP(implicit, implicits) { 584 return kj::strTree(implicit.getName()); 585 }, ", "), "] "); 586 } 587 588 auto params = schemaLoader.get(methodProto.getParamStructType()).asStruct(); 589 auto results = schemaLoader.get(methodProto.getResultStructType()).asStruct(); 590 return kj::strTree( 591 indent.next(), methodProto.getName(), 592 " @", method.getIndex(), " ", kj::mv(implicitsStr), 593 genParamList(interface, params, methodProto.getParamBrand(), method), " -> ", 594 genParamList(interface, results, methodProto.getResultBrand(), method), 595 genAnnotations(methodProto.getAnnotations(), interface), ";\n"); 596 }, 597 genNestedDecls(schema, indent.next()), 598 indent, "}\n"); 599 } 600 case schema::Node::CONST: { 601 auto constProto = proto.getConst(); 602 return kj::strTree( 603 indent, "const ", name, " @0x", kj::hex(proto.getId()), " :", 604 genType(constProto.getType(), schema, nullptr), " = ", 605 genValue(schema.asConst().getType(), constProto.getValue()), 606 genAnnotations(schema), ";\n"); 607 } 608 case schema::Node::ANNOTATION: { 609 auto annotationProto = proto.getAnnotation(); 610 611 kj::Vector<kj::String> targets(8); 612 bool targetsAll = true; 613 614 auto dynamic = toDynamic(annotationProto); 615 for (auto field: dynamic.getSchema().getFields()) { 616 auto fieldName = field.getProto().getName(); 617 if (fieldName.startsWith("targets")) { 618 if (dynamic.get(field).as<bool>()) { 619 auto target = kj::str(fieldName.slice(strlen("targets"))); 620 target[0] = target[0] - 'A' + 'a'; 621 targets.add(kj::mv(target)); 622 } else { 623 targetsAll = false; 624 } 625 } 626 } 627 628 if (targetsAll) { 629 targets = kj::Vector<kj::String>(1); 630 targets.add(kj::heapString("*")); 631 } 632 633 return kj::strTree( 634 indent, "annotation ", name, " @0x", kj::hex(proto.getId()), 635 " (", strArray(targets, ", "), ") :", 636 genType(annotationProto.getType(), schema, nullptr), genAnnotations(schema), ";\n"); 637 } 638 } 639 640 return kj::strTree(); 641 } 642 643 kj::StringTree genNestedDecls(Schema schema, Indent indent) { 644 uint64_t id = schema.getProto().getId(); 645 return kj::strTree(KJ_MAP(nested, schema.getProto().getNestedNodes()) { 646 return genDecl(schemaLoader.get(nested.getId()), nested.getName(), id, indent); 647 }); 648 } 649 650 kj::StringTree genFile(Schema file) { 651 auto proto = file.getProto(); 652 KJ_REQUIRE(proto.isFile(), "Expected a file node.", (uint)proto.which()); 653 654 return kj::strTree( 655 "# ", proto.getDisplayName(), "\n", 656 "@0x", kj::hex(proto.getId()), ";\n", 657 KJ_MAP(ann, proto.getAnnotations()) { return genAnnotation(ann, file, "", ";\n"); }, 658 genNestedDecls(file, Indent(0))); 659 } 660 661 kj::MainBuilder::Validity run() { 662 ReaderOptions options; 663 options.traversalLimitInWords = 1 << 30; // Don't limit. 664 StreamFdMessageReader reader(STDIN_FILENO, options); 665 auto request = reader.getRoot<schema::CodeGeneratorRequest>(); 666 667 for (auto node: request.getNodes()) { 668 schemaLoader.load(node); 669 } 670 671 kj::FdOutputStream rawOut(STDOUT_FILENO); 672 kj::BufferedOutputStreamWrapper out(rawOut); 673 674 for (auto requestedFile: request.getRequestedFiles()) { 675 genFile(schemaLoader.get(requestedFile.getId())).visit( 676 [&](kj::ArrayPtr<const char> text) { 677 out.write(text.begin(), text.size()); 678 }); 679 } 680 681 return true; 682 } 683 }; 684 685 } // namespace 686 } // namespace capnp 687 688 KJ_MAIN(capnp::CapnpcCapnpMain);