blob: 3660c827df58296bcce4bf9656bd011ec8efcaeb [file] [log] [blame]
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This program is a code generator plugin for `capnp compile` which writes the schema back to
// stdout in roughly capnpc format.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <capnp/schema.capnp.h>
#include "../serialize.h"
#include <kj/debug.h>
#include <kj/io.h>
#include <kj/string-tree.h>
#include <kj/vector.h>
#include "../schema-loader.h"
#include "../dynamic.h"
#include <kj/miniposix.h>
#include <unordered_map>
#include <kj/main.h>
#include <algorithm>
#include <map>
#include <capnp/stream.capnp.h>
#if HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef VERSION
#define VERSION "(unknown)"
#endif
namespace capnp {
namespace {
bool hasDiscriminantValue(const schema::Field::Reader& reader) {
return reader.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT;
}
struct Indent {
uint amount;
Indent() = default;
inline Indent(int amount): amount(amount) {}
Indent next() {
return Indent(amount + 2);
}
struct Iterator {
uint i;
Iterator() = default;
inline Iterator(uint i): i(i) {}
inline char operator*() const { return ' '; }
inline Iterator& operator++() { ++i; return *this; }
inline Iterator operator++(int) { Iterator result = *this; ++i; return result; }
inline bool operator==(const Iterator& other) const { return i == other.i; }
inline bool operator!=(const Iterator& other) const { return i != other.i; }
};
inline size_t size() const { return amount; }
inline Iterator begin() const { return Iterator(0); }
inline Iterator end() const { return Iterator(amount); }
};
inline Indent KJ_STRINGIFY(const Indent& indent) {
return indent;
}
// =======================================================================================
class CapnpcCapnpMain {
public:
CapnpcCapnpMain(kj::ProcessContext& context): context(context) {}
kj::MainFunc getMain() {
return kj::MainBuilder(context, "Cap'n Proto loopback plugin version " VERSION,
"This is a Cap'n Proto compiler plugin which \"de-compiles\" the schema back into "
"Cap'n Proto schema language format, with comments showing the offsets chosen by the "
"compiler. This is meant to be run using the Cap'n Proto compiler, e.g.:\n"
" capnp compile -ocapnp foo.capnp")
.callAfterParsing(KJ_BIND_METHOD(*this, run))
.build();
}
private:
kj::ProcessContext& context;
SchemaLoader schemaLoader;
Text::Reader getUnqualifiedName(Schema schema) {
auto proto = schema.getProto();
KJ_CONTEXT(proto.getDisplayName());
auto parent = schemaLoader.get(proto.getScopeId());
for (auto nested: parent.getProto().getNestedNodes()) {
if (nested.getId() == proto.getId()) {
return nested.getName();
}
}
KJ_FAIL_REQUIRE("A schema Node's supposed scope did not contain the node as a NestedNode.");
return "(?)";
}
kj::StringTree nodeName(Schema target, Schema scope, schema::Brand::Reader brand,
kj::Maybe<InterfaceSchema::Method> method) {
kj::Vector<Schema> targetPath;
kj::Vector<Schema> scopeParts;
targetPath.add(target);
std::map<uint64_t, List<schema::Brand::Binding>::Reader> scopeBindings;
for (auto scopeBrand: brand.getScopes()) {
switch (scopeBrand.which()) {
case schema::Brand::Scope::BIND:
scopeBindings[scopeBrand.getScopeId()] = scopeBrand.getBind();
break;
case schema::Brand::Scope::INHERIT:
// TODO(someday): We need to pay attention to INHERIT and be sure to explicitly override
// any bindings that are not inherited. This requires a way to determine which of our
// parent scopes have a non-empty parameter list.
break;
}
}
{
Schema parent = target;
while (parent.getProto().getScopeId() != 0) {
parent = schemaLoader.get(parent.getProto().getScopeId());
targetPath.add(parent);
}
}
{
Schema parent = scope;
scopeParts.add(parent);
while (parent.getProto().getScopeId() != 0) {
parent = schemaLoader.get(parent.getProto().getScopeId());
scopeParts.add(parent);
}
}
// Remove common scope (unless it has been reparameterized).
// TODO(someday): This is broken in that we aren't checking for shadowing.
while (!scopeParts.empty() && targetPath.size() > 1 &&
scopeParts.back() == targetPath.back() &&
scopeBindings.count(scopeParts.back().getProto().getId()) == 0) {
scopeParts.removeLast();
targetPath.removeLast();
}
auto parts = kj::heapArrayBuilder<kj::StringTree>(targetPath.size());
while (!targetPath.empty()) {
auto part = targetPath.back();
auto proto = part.getProto();
kj::StringTree partStr;
if (proto.getScopeId() == 0) {
partStr = kj::strTree("import \"/", proto.getDisplayName(), '\"');
} else {
partStr = kj::strTree(getUnqualifiedName(part));
}
auto iter = scopeBindings.find(proto.getId());
if (iter != scopeBindings.end()) {
auto bindings = KJ_MAP(binding, iter->second) {
switch (binding.which()) {
case schema::Brand::Binding::UNBOUND:
return kj::strTree("AnyPointer");
case schema::Brand::Binding::TYPE:
return genType(binding.getType(), scope, method);
}
return kj::strTree("<unknown binding>");
};
partStr = kj::strTree(kj::mv(partStr), "(", kj::StringTree(kj::mv(bindings), ", "), ")");
}
parts.add(kj::mv(partStr));
targetPath.removeLast();
}
return kj::StringTree(parts.finish(), ".");
}
kj::StringTree genType(schema::Type::Reader type, Schema scope,
kj::Maybe<InterfaceSchema::Method> method) {
switch (type.which()) {
case schema::Type::VOID: return kj::strTree("Void");
case schema::Type::BOOL: return kj::strTree("Bool");
case schema::Type::INT8: return kj::strTree("Int8");
case schema::Type::INT16: return kj::strTree("Int16");
case schema::Type::INT32: return kj::strTree("Int32");
case schema::Type::INT64: return kj::strTree("Int64");
case schema::Type::UINT8: return kj::strTree("UInt8");
case schema::Type::UINT16: return kj::strTree("UInt16");
case schema::Type::UINT32: return kj::strTree("UInt32");
case schema::Type::UINT64: return kj::strTree("UInt64");
case schema::Type::FLOAT32: return kj::strTree("Float32");
case schema::Type::FLOAT64: return kj::strTree("Float64");
case schema::Type::TEXT: return kj::strTree("Text");
case schema::Type::DATA: return kj::strTree("Data");
case schema::Type::LIST:
return kj::strTree("List(", genType(type.getList().getElementType(), scope, method), ")");
case schema::Type::ENUM:
return nodeName(schemaLoader.get(type.getEnum().getTypeId()), scope,
type.getEnum().getBrand(), method);
case schema::Type::STRUCT:
return nodeName(schemaLoader.get(type.getStruct().getTypeId()), scope,
type.getStruct().getBrand(), method);
case schema::Type::INTERFACE:
return nodeName(schemaLoader.get(type.getInterface().getTypeId()), scope,
type.getInterface().getBrand(), method);
case schema::Type::ANY_POINTER: {
auto anyPointer = type.getAnyPointer();
switch (anyPointer.which()) {
case schema::Type::AnyPointer::UNCONSTRAINED:
switch (anyPointer.getUnconstrained().which()) {
case schema::Type::AnyPointer::Unconstrained::ANY_KIND:
return kj::strTree("AnyPointer");
case schema::Type::AnyPointer::Unconstrained::STRUCT:
return kj::strTree("AnyStruct");
case schema::Type::AnyPointer::Unconstrained::LIST:
return kj::strTree("AnyList");
case schema::Type::AnyPointer::Unconstrained::CAPABILITY:
return kj::strTree("Capability");
}
KJ_UNREACHABLE;
case schema::Type::AnyPointer::PARAMETER: {
auto param = anyPointer.getParameter();
auto scopeProto = scope.getProto();
auto targetScopeId = param.getScopeId();
while (scopeProto.getId() != targetScopeId) {
scopeProto = schemaLoader.get(param.getScopeId()).getProto();
}
auto params = scopeProto.getParameters();
KJ_REQUIRE(param.getParameterIndex() < params.size());
return kj::strTree(params[param.getParameterIndex()].getName());
}
case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER: {
auto params = KJ_REQUIRE_NONNULL(method).getProto().getImplicitParameters();
uint index = anyPointer.getImplicitMethodParameter().getParameterIndex();
KJ_REQUIRE(index < params.size());
return kj::strTree(params[index].getName());
}
}
KJ_UNREACHABLE;
}
}
return kj::strTree();
}
int typeSizeBits(schema::Type::Reader type) {
switch (type.which()) {
case schema::Type::VOID: return 0;
case schema::Type::BOOL: return 1;
case schema::Type::INT8: return 8;
case schema::Type::INT16: return 16;
case schema::Type::INT32: return 32;
case schema::Type::INT64: return 64;
case schema::Type::UINT8: return 8;
case schema::Type::UINT16: return 16;
case schema::Type::UINT32: return 32;
case schema::Type::UINT64: return 64;
case schema::Type::FLOAT32: return 32;
case schema::Type::FLOAT64: return 64;
case schema::Type::TEXT: return -1;
case schema::Type::DATA: return -1;
case schema::Type::LIST: return -1;
case schema::Type::ENUM: return 16;
case schema::Type::STRUCT: return -1;
case schema::Type::INTERFACE: return -1;
case schema::Type::ANY_POINTER: return -1;
}
return 0;
}
bool isEmptyValue(schema::Value::Reader value) {
switch (value.which()) {
case schema::Value::VOID: return true;
case schema::Value::BOOL: return value.getBool() == false;
case schema::Value::INT8: return value.getInt8() == 0;
case schema::Value::INT16: return value.getInt16() == 0;
case schema::Value::INT32: return value.getInt32() == 0;
case schema::Value::INT64: return value.getInt64() == 0;
case schema::Value::UINT8: return value.getUint8() == 0;
case schema::Value::UINT16: return value.getUint16() == 0;
case schema::Value::UINT32: return value.getUint32() == 0;
case schema::Value::UINT64: return value.getUint64() == 0;
case schema::Value::FLOAT32: return value.getFloat32() == 0;
case schema::Value::FLOAT64: return value.getFloat64() == 0;
case schema::Value::TEXT: return !value.hasText();
case schema::Value::DATA: return !value.hasData();
case schema::Value::LIST: return !value.hasList();
case schema::Value::ENUM: return value.getEnum() == 0;
case schema::Value::STRUCT: return !value.hasStruct();
case schema::Value::INTERFACE: return true;
case schema::Value::ANY_POINTER: return true;
}
return true;
}
kj::StringTree genValue(Type type, schema::Value::Reader value) {
switch (value.which()) {
case schema::Value::VOID: return kj::strTree("void");
case schema::Value::BOOL:
return kj::strTree(value.getBool() ? "true" : "false");
case schema::Value::INT8: return kj::strTree((int)value.getInt8());
case schema::Value::INT16: return kj::strTree(value.getInt16());
case schema::Value::INT32: return kj::strTree(value.getInt32());
case schema::Value::INT64: return kj::strTree(value.getInt64());
case schema::Value::UINT8: return kj::strTree((uint)value.getUint8());
case schema::Value::UINT16: return kj::strTree(value.getUint16());
case schema::Value::UINT32: return kj::strTree(value.getUint32());
case schema::Value::UINT64: return kj::strTree(value.getUint64());
case schema::Value::FLOAT32: return kj::strTree(value.getFloat32());
case schema::Value::FLOAT64: return kj::strTree(value.getFloat64());
case schema::Value::TEXT:
return kj::strTree(DynamicValue::Reader(value.getText()));
case schema::Value::DATA:
return kj::strTree(DynamicValue::Reader(value.getData()));
case schema::Value::LIST: {
auto listValue = value.getList().getAs<DynamicList>(type.asList());
return kj::strTree(listValue);
}
case schema::Value::ENUM: {
auto enumNode = type.asEnum().getProto();
auto enumerants = enumNode.getEnum().getEnumerants();
KJ_REQUIRE(value.getEnum() < enumerants.size(),
"Enum value out-of-range.", value.getEnum(), enumNode.getDisplayName());
return kj::strTree(enumerants[value.getEnum()].getName());
}
case schema::Value::STRUCT: {
KJ_REQUIRE(type.which() == schema::Type::STRUCT, "type/value mismatch");
auto structValue = value.getStruct().getAs<DynamicStruct>(type.asStruct());
return kj::strTree(structValue);
}
case schema::Value::INTERFACE: {
return kj::strTree("");
}
case schema::Value::ANY_POINTER: {
return kj::strTree("");
}
}
return kj::strTree("");
}
kj::StringTree genGenericParams(List<schema::Node::Parameter>::Reader params, Schema scope) {
if (params.size() == 0) {
return kj::strTree();
}
return kj::strTree(" (", kj::StringTree(
KJ_MAP(param, params) { return kj::strTree(param.getName()); }, ", "), ')');
}
kj::StringTree genGenericParams(Schema schema) {
auto proto = schema.getProto();
return genGenericParams(proto.getParameters(), schemaLoader.get(proto.getScopeId()));
}
kj::StringTree genAnnotation(schema::Annotation::Reader annotation,
Schema scope,
const char* prefix = " ", const char* suffix = "") {
auto decl = schemaLoader.get(annotation.getId(), annotation.getBrand(), scope);
auto proto = decl.getProto();
KJ_REQUIRE(proto.isAnnotation());
auto annDecl = proto.getAnnotation();
auto value = genValue(schemaLoader.getType(annDecl.getType(), decl),
annotation.getValue()).flatten();
if (value.startsWith("(")) {
return kj::strTree(prefix, "$", nodeName(decl, scope, annotation.getBrand(), nullptr),
value, suffix);
} else {
return kj::strTree(prefix, "$", nodeName(decl, scope, annotation.getBrand(), nullptr),
"(", value, ")", suffix);
}
}
kj::StringTree genAnnotations(List<schema::Annotation>::Reader list, Schema scope) {
return kj::strTree(KJ_MAP(ann, list) { return genAnnotation(ann, scope); });
}
kj::StringTree genAnnotations(Schema schema) {
auto proto = schema.getProto();
return genAnnotations(proto.getAnnotations(), schemaLoader.get(proto.getScopeId()));
}
const char* elementSizeName(schema::ElementSize size) {
switch (size) {
case schema::ElementSize::EMPTY: return "void";
case schema::ElementSize::BIT: return "1-bit";
case schema::ElementSize::BYTE: return "8-bit";
case schema::ElementSize::TWO_BYTES: return "16-bit";
case schema::ElementSize::FOUR_BYTES: return "32-bit";
case schema::ElementSize::EIGHT_BYTES: return "64-bit";
case schema::ElementSize::POINTER: return "pointer";
case schema::ElementSize::INLINE_COMPOSITE: return "inline composite";
}
return "";
}
struct OrderByCodeOrder {
template <typename T>
inline bool operator()(const T& a, const T& b) const {
return a.getProto().getCodeOrder() < b.getProto().getCodeOrder();
}
};
template <typename MemberList>
kj::Array<decltype(kj::instance<MemberList>()[0])> sortByCodeOrder(MemberList&& list) {
auto sorted = KJ_MAP(item, list) { return item; };
std::sort(sorted.begin(), sorted.end(), OrderByCodeOrder());
return kj::mv(sorted);
}
kj::Array<kj::StringTree> genStructFields(StructSchema schema, Indent indent) {
// Slightly hacky: We want to print in code order, but we also need to print the union in one
// chunk. Its fields should be together in code order anyway, but it's easier to simply
// output the whole union in place of the first union field, and then output nothing for the
// subsequent fields.
bool seenUnion = false;
return KJ_MAP(field, sortByCodeOrder(schema.getFields())) {
if (hasDiscriminantValue(field.getProto())) {
if (seenUnion) {
return kj::strTree();
} else {
seenUnion = true;
uint offset = schema.getProto().getStruct().getDiscriminantOffset();
// GCC 4.7.3 crashes if you inline unionFields.
auto unionFields = sortByCodeOrder(schema.getUnionFields());
return kj::strTree(
indent, "union { # tag bits [", offset * 16, ", ", offset * 16 + 16, ")\n",
KJ_MAP(uField, unionFields) {
return genStructField(uField, schema, indent.next());
},
indent, "}\n");
}
} else {
return genStructField(field, schema, indent);
}
};
}
kj::StringTree genStructField(StructSchema::Field field, Schema scope, Indent indent) {
auto proto = field.getProto();
switch (proto.which()) {
case schema::Field::SLOT: {
auto slot = proto.getSlot();
int size = typeSizeBits(slot.getType());
return kj::strTree(
indent, proto.getName(), " @", proto.getOrdinal().getExplicit(),
" :", genType(slot.getType(), scope, nullptr),
isEmptyValue(slot.getDefaultValue()) ? kj::strTree("") :
kj::strTree(" = ", genValue(field.getType(), slot.getDefaultValue())),
genAnnotations(proto.getAnnotations(), scope),
"; # ", size == -1 ? kj::strTree("ptr[", slot.getOffset(), "]")
: kj::strTree("bits[", slot.getOffset() * size, ", ",
(slot.getOffset() + 1) * size, ")"),
hasDiscriminantValue(proto)
? kj::strTree(", union tag = ", proto.getDiscriminantValue()) : kj::strTree(),
"\n");
}
case schema::Field::GROUP: {
auto group = field.getType().asStruct();
return kj::strTree(
indent, proto.getName(),
" :group", genAnnotations(proto.getAnnotations(), scope), " {",
hasDiscriminantValue(proto)
? kj::strTree(" # union tag = ", proto.getDiscriminantValue()) : kj::strTree(),
"\n",
genStructFields(group, indent.next()),
indent, "}\n");
}
}
return kj::strTree();
}
kj::StringTree genParamList(InterfaceSchema interface, StructSchema schema,
schema::Brand::Reader brand, InterfaceSchema::Method method) {
if (schema.getProto().getId() == typeId<StreamResult>()) {
return kj::strTree("stream");
} else if (schema.getProto().getScopeId() == 0) {
// A named parameter list.
return kj::strTree("(", kj::StringTree(
KJ_MAP(field, schema.getFields()) {
auto proto = field.getProto();
auto slot = proto.getSlot();
return kj::strTree(
proto.getName(), " :", genType(slot.getType(), interface, nullptr),
isEmptyValue(slot.getDefaultValue()) ? kj::strTree("") :
kj::strTree(" = ", genValue(field.getType(), slot.getDefaultValue())),
genAnnotations(proto.getAnnotations(), interface));
}, ", "), ")");
} else {
return nodeName(schema, interface, brand, method);
}
}
kj::StringTree genSuperclasses(InterfaceSchema interface) {
auto superclasses = interface.getProto().getInterface().getSuperclasses();
if (superclasses.size() == 0) {
return kj::strTree();
} else {
return kj::strTree(" superclasses(", kj::StringTree(
KJ_MAP(superclass, superclasses) {
return nodeName(schemaLoader.get(superclass.getId()), interface,
superclass.getBrand(), nullptr);
}, ", "), ")");
}
}
kj::StringTree genDecl(Schema schema, Text::Reader name, uint64_t scopeId, Indent indent) {
auto proto = schema.getProto();
if (proto.getScopeId() != scopeId) {
// This appears to be an alias for something declared elsewhere.
KJ_FAIL_REQUIRE("Aliases not implemented.");
}
switch (proto.which()) {
case schema::Node::FILE:
KJ_FAIL_REQUIRE("Encountered nested file node.");
break;
case schema::Node::STRUCT: {
auto structProto = proto.getStruct();
return kj::strTree(
indent, "struct ", name,
" @0x", kj::hex(proto.getId()), genGenericParams(schema),
genAnnotations(schema), " { # ",
structProto.getDataWordCount() * 8, " bytes, ",
structProto.getPointerCount(), " ptrs",
structProto.getPreferredListEncoding() == schema::ElementSize::INLINE_COMPOSITE
? kj::strTree()
: kj::strTree(", packed as ",
elementSizeName(structProto.getPreferredListEncoding())),
"\n",
genStructFields(schema.asStruct(), indent.next()),
genNestedDecls(schema, indent.next()),
indent, "}\n");
}
case schema::Node::ENUM: {
return kj::strTree(
indent, "enum ", name, " @0x", kj::hex(proto.getId()), genAnnotations(schema), " {\n",
KJ_MAP(enumerant, sortByCodeOrder(schema.asEnum().getEnumerants())) {
return kj::strTree(indent.next(), enumerant.getProto().getName(), " @",
enumerant.getIndex(),
genAnnotations(enumerant.getProto().getAnnotations(), schema),
";\n");
},
genNestedDecls(schema, indent.next()),
indent, "}\n");
}
case schema::Node::INTERFACE: {
auto interface = schema.asInterface();
return kj::strTree(
indent, "interface ", name, " @0x", kj::hex(proto.getId()), genGenericParams(schema),
genSuperclasses(interface),
genAnnotations(schema), " {\n",
KJ_MAP(method, sortByCodeOrder(interface.getMethods())) {
auto methodProto = method.getProto();
auto implicits = methodProto.getImplicitParameters();
kj::StringTree implicitsStr;
if (implicits.size() > 0) {
implicitsStr = kj::strTree(
"[", kj::StringTree(KJ_MAP(implicit, implicits) {
return kj::strTree(implicit.getName());
}, ", "), "] ");
}
auto params = schemaLoader.get(methodProto.getParamStructType()).asStruct();
auto results = schemaLoader.get(methodProto.getResultStructType()).asStruct();
return kj::strTree(
indent.next(), methodProto.getName(),
" @", method.getIndex(), " ", kj::mv(implicitsStr),
genParamList(interface, params, methodProto.getParamBrand(), method), " -> ",
genParamList(interface, results, methodProto.getResultBrand(), method),
genAnnotations(methodProto.getAnnotations(), interface), ";\n");
},
genNestedDecls(schema, indent.next()),
indent, "}\n");
}
case schema::Node::CONST: {
auto constProto = proto.getConst();
return kj::strTree(
indent, "const ", name, " @0x", kj::hex(proto.getId()), " :",
genType(constProto.getType(), schema, nullptr), " = ",
genValue(schema.asConst().getType(), constProto.getValue()),
genAnnotations(schema), ";\n");
}
case schema::Node::ANNOTATION: {
auto annotationProto = proto.getAnnotation();
kj::Vector<kj::String> targets(8);
bool targetsAll = true;
auto dynamic = toDynamic(annotationProto);
for (auto field: dynamic.getSchema().getFields()) {
auto fieldName = field.getProto().getName();
if (fieldName.startsWith("targets")) {
if (dynamic.get(field).as<bool>()) {
auto target = kj::str(fieldName.slice(strlen("targets")));
target[0] = target[0] - 'A' + 'a';
targets.add(kj::mv(target));
} else {
targetsAll = false;
}
}
}
if (targetsAll) {
targets = kj::Vector<kj::String>(1);
targets.add(kj::heapString("*"));
}
return kj::strTree(
indent, "annotation ", name, " @0x", kj::hex(proto.getId()),
" (", strArray(targets, ", "), ") :",
genType(annotationProto.getType(), schema, nullptr), genAnnotations(schema), ";\n");
}
}
return kj::strTree();
}
kj::StringTree genNestedDecls(Schema schema, Indent indent) {
uint64_t id = schema.getProto().getId();
return kj::strTree(KJ_MAP(nested, schema.getProto().getNestedNodes()) {
return genDecl(schemaLoader.get(nested.getId()), nested.getName(), id, indent);
});
}
kj::StringTree genFile(Schema file) {
auto proto = file.getProto();
KJ_REQUIRE(proto.isFile(), "Expected a file node.", (uint)proto.which());
return kj::strTree(
"# ", proto.getDisplayName(), "\n",
"@0x", kj::hex(proto.getId()), ";\n",
KJ_MAP(ann, proto.getAnnotations()) { return genAnnotation(ann, file, "", ";\n"); },
genNestedDecls(file, Indent(0)));
}
kj::MainBuilder::Validity run() {
ReaderOptions options;
options.traversalLimitInWords = 1 << 30; // Don't limit.
StreamFdMessageReader reader(STDIN_FILENO, options);
auto request = reader.getRoot<schema::CodeGeneratorRequest>();
for (auto node: request.getNodes()) {
schemaLoader.load(node);
}
kj::FdOutputStream rawOut(STDOUT_FILENO);
kj::BufferedOutputStreamWrapper out(rawOut);
for (auto requestedFile: request.getRequestedFiles()) {
genFile(schemaLoader.get(requestedFile.getId())).visit(
[&](kj::ArrayPtr<const char> text) {
out.write(text.begin(), text.size());
});
}
return true;
}
};
} // namespace
} // namespace capnp
KJ_MAIN(capnp::CapnpcCapnpMain);