capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

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