Commit de742607 authored by Kenton Varda's avatar Kenton Varda

Implement code generator plugins. The Cap'n Proto compiler, when given -o foo,…

Implement code generator plugins.  The Cap'n Proto compiler, when given -o foo, invokes the binary capnp-foo and passes the schema, in Cap'n Proto format, to its stdin.  The binary is executed with the current working directory set to the desired output directory, so all it has to do is write the files.
parent 0fb40a47
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This program is a code generator plugin for capnpc which writes the schema back to stdout in
// roughly capnpc format.
#define CAPNPROTO_PRIVATE
#include "../schema.capnp.h"
#include "../serialize.h"
#include "../logging.h"
#include "../io.h"
#include <unistd.h>
#include <unordered_map>
#include <vector>
namespace capnproto {
namespace {
class TextBlob {
public:
TextBlob() = default;
template <typename... Params>
TextBlob(Params&&... params);
TextBlob(Array<TextBlob>&& params);
void writeTo(OutputStream& out) const;
private:
Array<char> text;
struct Branch;
Array<Branch> branches;
void allocate(size_t textSize, size_t branchCount);
template <typename First, typename... Rest>
void allocate(size_t textSize, size_t branchCount, const First& first, Rest&&... rest);
template <typename... Rest>
void allocate(size_t textSize, size_t branchCount, const TextBlob& first, Rest&&... rest);
void fill(char* textPos, Branch* branchesPos);
template <typename First, typename... Rest>
void fill(char* textPos, Branch* branchesPos, First&& first, Rest&&... rest);
template <typename... Rest>
void fill(char* textPos, Branch* branchesPos, TextBlob&& first, Rest&&... rest);
template <typename T>
auto toContainer(T&& t) -> decltype(STR * t) {
return STR * t;
}
TextBlob&& toContainer(TextBlob&& t) {
return move(t);
}
TextBlob& toContainer(TextBlob& t) {
return t;
}
const TextBlob& toContainer(const TextBlob& t) {
return t;
}
template <typename... Params>
void init(Params&&... params);
};
struct TextBlob::Branch {
char* pos;
TextBlob content;
};
template <typename... Params>
TextBlob::TextBlob(Params&&... params) {
init(toContainer(capnproto::forward<Params>(params))...);
}
TextBlob::TextBlob(Array<TextBlob>&& params) {
branches = newArray<Branch>(params.size());
for (size_t i = 0; i < params.size(); i++) {
branches[i].pos = nullptr;
branches[i].content = move(params[i]);
}
}
void TextBlob::writeTo(OutputStream& out) const {
const char* pos = text.begin();
for (auto& branch: branches) {
out.write(pos, branch.pos - pos);
pos = branch.pos;
branch.content.writeTo(out);
}
out.write(pos, text.end() - pos);
}
void TextBlob::allocate(size_t textSize, size_t branchCount) {
text = newArray<char>(textSize);
branches = newArray<Branch>(branchCount);
}
template <typename First, typename... Rest>
void TextBlob::allocate(size_t textSize, size_t branchCount, const First& first, Rest&&... rest) {
allocate(textSize + first.size(), branchCount, capnproto::forward<Rest>(rest)...);
}
template <typename... Rest>
void TextBlob::allocate(size_t textSize, size_t branchCount,
const TextBlob& first, Rest&&... rest) {
allocate(textSize, branchCount + 1, capnproto::forward<Rest>(rest)...);
}
void TextBlob::fill(char* textPos, Branch* branchesPos) {
CHECK(textPos == text.end(), textPos - text.end());
CHECK(branchesPos == branches.end(), branchesPos - branches.end());
}
template <typename First, typename... Rest>
void TextBlob::fill(char* textPos, Branch* branchesPos, First&& first, Rest&&... rest) {
textPos = capnproto::fill(textPos, capnproto::forward<First>(first));
fill(textPos, branchesPos, capnproto::forward<Rest>(rest)...);
}
template <typename... Rest>
void TextBlob::fill(char* textPos, Branch* branchesPos, TextBlob&& first, Rest&&... rest) {
branchesPos->pos = textPos;
branchesPos->content = move(first);
++branchesPos;
fill(textPos, branchesPos, capnproto::forward<Rest>(rest)...);
}
template <typename... Params>
void TextBlob::init(Params&&... params) {
allocate(0, 0, params...);
fill(text.begin(), branches.begin(), capnproto::forward<Params>(params)...);
}
template <typename... Params>
TextBlob text(Params&&... params) {
return TextBlob(capnproto::forward<Params>(params)...);
}
template <typename List, typename Func>
TextBlob forText(List&& list, Func&& func) {
Array<TextBlob> items = newArray<TextBlob>(list.size());
for (size_t i = 0; i < list.size(); i++) {
items[i] = func(list[i]);
}
return TextBlob(capnproto::move(items));
}
template <typename T>
struct ForTextHack {
T list;
ForTextHack(T list): list(capnproto::forward<T>(list)) {}
template <typename Func>
TextBlob operator*(Func&& func) {
return forText(list, func);
}
};
#define FOR_EACH(list, name) ForTextHack<decltype(list)>(list) * \
[&](decltype((list)[0]) name) -> TextBlob
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 operator*(Stringifier, Indent i) { return i; }
// =======================================================================================
std::unordered_map<uint64_t, schema::Node::Reader> schemaMap;
schema::Node::Reader findNode(uint64_t id) {
auto iter = schemaMap.find(id);
PRECOND(iter != schemaMap.end(), "Missing schema node.", hex(id));
return iter->second;
}
Text::Reader getUnqualifiedName(schema::Node::Reader node) {
auto parent = findNode(node.getScopeId());
for (auto nested: parent.getNestedNodes()) {
if (nested.getId() == node.getId()) {
return nested.getName();
}
}
FAIL_PRECOND("A schema Node's supposed scope did not contain the node as a NestedNode.");
return "(?)";
}
TextBlob nodeName(schema::Node::Reader target, schema::Node::Reader scope) {
std::vector<schema::Node::Reader> targetParents;
std::vector<schema::Node::Reader> scopeParts;
{
schema::Node::Reader parent = target;
while (parent.getScopeId() != 0) {
parent = findNode(parent.getScopeId());
targetParents.push_back(parent);
}
}
{
schema::Node::Reader parent = scope;
scopeParts.push_back(scope);
while (parent.getScopeId() != 0) {
parent = findNode(parent.getScopeId());
scopeParts.push_back(parent);
}
}
// Remove common scope.
while (!scopeParts.empty() && !targetParents.empty() &&
scopeParts.back().getId() == targetParents.back().getId()) {
scopeParts.pop_back();
targetParents.pop_back();
}
TextBlob path = text();
while (!targetParents.empty()) {
auto part = targetParents.back();
if (part.getScopeId() == 0) {
path = text(move(path), "import \"", part.getDisplayName(), "\".");
} else {
path = text(move(path), getUnqualifiedName(part), ".");
}
targetParents.pop_back();
}
return text(move(path), getUnqualifiedName(target));
}
TextBlob genType(schema::Type::Reader type, schema::Node::Reader scope) {
auto body = type.getBody();
switch (body.which()) {
case schema::Type::Body::VOID_TYPE: return text("Void");
case schema::Type::Body::BOOL_TYPE: return text("Bool");
case schema::Type::Body::INT8_TYPE: return text("Int8");
case schema::Type::Body::INT16_TYPE: return text("Int16");
case schema::Type::Body::INT32_TYPE: return text("Int32");
case schema::Type::Body::INT64_TYPE: return text("Int64");
case schema::Type::Body::UINT8_TYPE: return text("UInt8");
case schema::Type::Body::UINT16_TYPE: return text("UInt16");
case schema::Type::Body::UINT32_TYPE: return text("UInt32");
case schema::Type::Body::UINT64_TYPE: return text("UInt64");
case schema::Type::Body::FLOAT32_TYPE: return text("Float32");
case schema::Type::Body::FLOAT64_TYPE: return text("Float64");
case schema::Type::Body::TEXT_TYPE: return text("Text");
case schema::Type::Body::DATA_TYPE: return text("Data");
case schema::Type::Body::LIST_TYPE:
return text("List(", genType(body.getListType(), scope), ")");
case schema::Type::Body::ENUM_TYPE: return nodeName(findNode(body.getEnumType()), scope);
case schema::Type::Body::STRUCT_TYPE: return nodeName(findNode(body.getStructType()), scope);
case schema::Type::Body::INTERFACE_TYPE:
return nodeName(findNode(body.getInterfaceType()), scope);
case schema::Type::Body::OBJECT_TYPE: return text("Object");
}
return text();
}
int typeSizeBits(schema::Type::Reader type) {
switch (type.getBody().which()) {
case schema::Type::Body::VOID_TYPE: return 0;
case schema::Type::Body::BOOL_TYPE: return 1;
case schema::Type::Body::INT8_TYPE: return 8;
case schema::Type::Body::INT16_TYPE: return 16;
case schema::Type::Body::INT32_TYPE: return 32;
case schema::Type::Body::INT64_TYPE: return 64;
case schema::Type::Body::UINT8_TYPE: return 8;
case schema::Type::Body::UINT16_TYPE: return 16;
case schema::Type::Body::UINT32_TYPE: return 32;
case schema::Type::Body::UINT64_TYPE: return 64;
case schema::Type::Body::FLOAT32_TYPE: return 32;
case schema::Type::Body::FLOAT64_TYPE: return 64;
case schema::Type::Body::TEXT_TYPE: return -1;
case schema::Type::Body::DATA_TYPE: return -1;
case schema::Type::Body::LIST_TYPE: return -1;
case schema::Type::Body::ENUM_TYPE: return 16;
case schema::Type::Body::STRUCT_TYPE: return -1;
case schema::Type::Body::INTERFACE_TYPE: return -1;
case schema::Type::Body::OBJECT_TYPE: return -1;
}
return 0;
}
bool isEmptyValue(schema::Value::Reader value) {
auto body = value.getBody();
switch (body.which()) {
case schema::Value::Body::VOID_VALUE: return true;
case schema::Value::Body::BOOL_VALUE: return body.getBoolValue() == false;
case schema::Value::Body::INT8_VALUE: return body.getInt8Value() == 0;
case schema::Value::Body::INT16_VALUE: return body.getInt16Value() == 0;
case schema::Value::Body::INT32_VALUE: return body.getInt32Value() == 0;
case schema::Value::Body::INT64_VALUE: return body.getInt64Value() == 0;
case schema::Value::Body::UINT8_VALUE: return body.getUint8Value() == 0;
case schema::Value::Body::UINT16_VALUE: return body.getUint16Value() == 0;
case schema::Value::Body::UINT32_VALUE: return body.getUint32Value() == 0;
case schema::Value::Body::UINT64_VALUE: return body.getUint64Value() == 0;
case schema::Value::Body::FLOAT32_VALUE: return body.getFloat32Value() == 0;
case schema::Value::Body::FLOAT64_VALUE: return body.getFloat64Value() == 0;
case schema::Value::Body::TEXT_VALUE: return body.getTextValue().size() == 0;
case schema::Value::Body::DATA_VALUE: return body.getDataValue().size() == 0;
case schema::Value::Body::LIST_VALUE: return true; // TODO(soon): list values
case schema::Value::Body::ENUM_VALUE: return body.getEnumValue() == 0;
case schema::Value::Body::STRUCT_VALUE: return true; // TODO(soon): struct values
case schema::Value::Body::INTERFACE_VALUE: return true;
case schema::Value::Body::OBJECT_VALUE: return true;
}
return true;
}
TextBlob genValue(schema::Type::Reader type, schema::Value::Reader value) {
auto body = value.getBody();
switch (body.which()) {
case schema::Value::Body::VOID_VALUE: return text("void");
case schema::Value::Body::BOOL_VALUE: return text(body.getBoolValue() ? "true" : "false");
case schema::Value::Body::INT8_VALUE: return text((int)body.getInt8Value());
case schema::Value::Body::INT16_VALUE: return text(body.getInt16Value());
case schema::Value::Body::INT32_VALUE: return text(body.getInt32Value());
case schema::Value::Body::INT64_VALUE: return text(body.getInt64Value());
case schema::Value::Body::UINT8_VALUE: return text((uint)body.getUint8Value());
case schema::Value::Body::UINT16_VALUE: return text(body.getUint16Value());
case schema::Value::Body::UINT32_VALUE: return text(body.getUint32Value());
case schema::Value::Body::UINT64_VALUE: return text(body.getUint64Value());
case schema::Value::Body::FLOAT32_VALUE: return text(body.getFloat32Value());
case schema::Value::Body::FLOAT64_VALUE: return text(body.getFloat64Value());
case schema::Value::Body::TEXT_VALUE: return text("TODO"); // TODO(soon): escape strings
case schema::Value::Body::DATA_VALUE: return text("TODO"); // TODO(soon): escape strings
case schema::Value::Body::LIST_VALUE: {
PRECOND(type.getBody().which() == schema::Type::Body::LIST_TYPE, "type/value mismatch");
// TODO(soon): Requires dynamic message reading.
return text("TODO");
// int i = 0;
// return text("[",
// FOR_EACH(body.getListValue(), element) {
// return text(i++ > 0 ? ", " : "",
// genValue(type.getBody().getListType(), element));
// },
// "]");
}
case schema::Value::Body::ENUM_VALUE: {
PRECOND(type.getBody().which() == schema::Type::Body::ENUM_TYPE, "type/value mismatch");
auto enumNode = findNode(type.getBody().getEnumType());
PRECOND(enumNode.getBody().which() == schema::Node::Body::ENUM_NODE,
"schema.Type claimed to be an enum, but referred to some other node type.");
auto enumType = enumNode.getBody().getEnumNode();
auto enumerants = enumType.getEnumerants();
PRECOND(body.getEnumValue() < enumerants.size(),
"Enum value out-of-range.", body.getEnumValue(), enumNode.getDisplayName());
return text(enumerants[body.getEnumValue()].getName());
}
case schema::Value::Body::STRUCT_VALUE: {
PRECOND(type.getBody().which() == schema::Type::Body::STRUCT_TYPE, "type/value mismatch");
// TODO(soon): Requires dynamic message reading.
return text("TODO");
}
case schema::Value::Body::INTERFACE_VALUE: {
return text("");
}
case schema::Value::Body::OBJECT_VALUE: {
return text("");
}
}
return 0;
}
TextBlob genAnnotation(schema::Annotation::Reader annotation,
schema::Node::Reader scope,
const char* prefix = " ", const char* suffix = "") {
auto decl = findNode(annotation.getId());
auto body = decl.getBody();
PRECOND(body.which() == schema::Node::Body::ANNOTATION_NODE);
auto annDecl = body.getAnnotationNode();
// TODO: Don't use displayName.
return text(prefix, "$", nodeName(decl, scope), "(",
genValue(annDecl.getType(), annotation.getValue()), ")", suffix);
}
TextBlob genAnnotations(List<schema::Annotation>::Reader list, schema::Node::Reader scope) {
return FOR_EACH(list, ann) { return genAnnotation(ann, scope); };
}
TextBlob genAnnotations(schema::Node::Reader node) {
return genAnnotations(node.getAnnotations(), findNode(node.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 "";
}
TextBlob genStructMember(schema::StructNode::Member::Reader member,
schema::Node::Reader scope, Indent indent, int unionTag = -1) {
switch (member.getBody().which()) {
case schema::StructNode::Member::Body::FIELD_MEMBER: {
auto field = member.getBody().getFieldMember();
int size = typeSizeBits(field.getType());
return text(indent, member.getName(), " @", member.getOrdinal(),
" :", genType(field.getType(), scope),
isEmptyValue(field.getDefaultValue()) ? text("") :
text(" = ", genValue(field.getType(), field.getDefaultValue())),
genAnnotations(member.getAnnotations(), scope),
"; # ", size == -1 ? text("ptr[", field.getOffset(), "]")
: text("bits[", field.getOffset() * size, ", ",
(field.getOffset() + 1) * size, ")"),
unionTag != -1 ? text(", union tag = ", unionTag) : text(),
"\n");
}
case schema::StructNode::Member::Body::UNION_MEMBER: {
auto un = member.getBody().getUnionMember();
int i = 0;
return text(indent, member.getName(), " @", member.getOrdinal(),
" union", genAnnotations(member.getAnnotations(), scope),
" { # tag bits[", un.getDiscriminantOffset(), ", ",
un.getDiscriminantOffset() + 16, ")\n",
FOR_EACH(un.getMembers(), member) {
return genStructMember(member, scope, indent.next(), i++);
},
indent, "}\n");
}
}
return text();
}
TextBlob genNestedDecls(schema::Node::Reader node, Indent indent);
TextBlob genDecl(schema::Node::Reader node, Text::Reader name, uint64_t scopeId, Indent indent) {
if (node.getScopeId() != scopeId) {
// This appears to be an alias for something declared elsewhere.
FAIL_PRECOND("Aliases not implemented.");
}
switch (node.getBody().which()) {
case schema::Node::Body::FILE_NODE:
FAIL_PRECOND("Encountered nested file node.");
break;
case schema::Node::Body::STRUCT_NODE: {
auto body = node.getBody().getStructNode();
return text(
indent, "struct ", name, " @0x", hex(node.getId()), genAnnotations(node), " { # ",
body.getDataSectionWordSize() * 8, " bytes, ",
body.getPointerSectionSize(), " ptrs",
body.getPreferredListEncoding() == schema::ElementSize::INLINE_COMPOSITE
? text()
: text(", packed as ", elementSizeName(body.getPreferredListEncoding())),
"\n",
FOR_EACH(body.getMembers(), member) {
return genStructMember(member, node, indent.next());
},
genNestedDecls(node, indent.next()),
indent, "}\n");
}
case schema::Node::Body::ENUM_NODE: {
auto body = node.getBody().getEnumNode();
uint i = 0;
return text(
indent, "enum ", name, " @0x", hex(node.getId()), genAnnotations(node), " {\n",
FOR_EACH(body.getEnumerants(), enumerant) {
return text(indent.next(), enumerant.getName(), " @", i++,
genAnnotations(enumerant.getAnnotations(), node), ";\n");
},
genNestedDecls(node, indent.next()),
indent, "}\n");
}
case schema::Node::Body::INTERFACE_NODE: {
auto body = node.getBody().getInterfaceNode();
uint i = 0;
return text(
indent, "interface ", name, " @0x", hex(node.getId()), genAnnotations(node), " {\n",
FOR_EACH(body.getMethods(), method) {
int j = 0;
return text(
indent.next(), method.getName(), " @", i++, "(",
FOR_EACH(method.getParams(), param) {
bool hasDefault = j >= method.getRequiredParamCount() ||
!isEmptyValue(param.getDefaultValue());
return text(
j++ > 0 ? ", " : "",
param.getName(), ": ", genType(param.getType(), node),
hasDefault
? text(" = ", genValue(param.getType(), param.getDefaultValue()))
: text(),
genAnnotations(param.getAnnotations(), node));
},
") :", genType(method.getReturnType(), node),
genAnnotations(method.getAnnotations(), node), ";\n");
},
genNestedDecls(node, indent.next()),
indent, "}\n");
}
case schema::Node::Body::CONST_NODE: {
auto body = node.getBody().getConstNode();
return text(
indent, "const ", name, " @0x", hex(node.getId()), " :", genType(body.getType(), node),
" = ", genValue(body.getType(), body.getValue()), ";\n");
}
case schema::Node::Body::ANNOTATION_NODE: {
auto body = node.getBody().getAnnotationNode();
CappedArray<const char*, 11> targets;
uint i = 0;
if (body.getTargetsFile()) targets[i++] = "file";
if (body.getTargetsConst()) targets[i++] = "const";
if (body.getTargetsEnum()) targets[i++] = "enum";
if (body.getTargetsEnumerant()) targets[i++] = "enumerant";
if (body.getTargetsStruct()) targets[i++] = "struct";
if (body.getTargetsField()) targets[i++] = "field";
if (body.getTargetsUnion()) targets[i++] = "union";
if (body.getTargetsInterface()) targets[i++] = "interface";
if (body.getTargetsMethod()) targets[i++] = "method";
if (body.getTargetsParam()) targets[i++] = "param";
if (body.getTargetsAnnotation()) targets[i++] = "annotation";
if (i == targets.size()) {
targets[0] = "*";
targets.setSize(1);
} else {
targets.setSize(i);
}
return text(
indent, "annotation ", name, " @0x", hex(node.getId()),
" (", strArray(targets, ", "), ") :",
genType(body.getType(), node), genAnnotations(node), ";\n");
}
}
return text();
}
TextBlob genNestedDecls(schema::Node::Reader node, Indent indent) {
return FOR_EACH(node.getNestedNodes(), nested) {
return genDecl(findNode(nested.getId()), nested.getName(), node.getId(), indent);
};
}
TextBlob genFile(schema::Node::Reader file) {
auto body = file.getBody();
PRECOND(body.which() == schema::Node::Body::FILE_NODE, "Expected a file node.", body.which());
return text(
"# ", file.getDisplayName(), "\n",
"@0x", hex(file.getId()), ";\n",
FOR_EACH(file.getAnnotations(), ann) { return genAnnotation(ann, file, "", ";\n"); },
genNestedDecls(file, Indent(0)));
}
int main(int argc, char* argv[]) {
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()) {
schemaMap[node.getId()] = node;
}
FdOutputStream rawOut(STDOUT_FILENO);
BufferedOutputStreamWrapper out(rawOut);
for (auto fileId: request.getRequestedFiles()) {
genFile(findNode(fileId)).writeTo(out);
}
return 0;
}
} // namespace
} // namespace capnproto
int main(int argc, char* argv[]) {
return capnproto::main(argc, argv);
}
...@@ -167,6 +167,12 @@ static typename RootType::Reader readMessageTrusted(const word* data); ...@@ -167,6 +167,12 @@ static typename RootType::Reader readMessageTrusted(const word* data);
// a message MyMessage, you can read its default value like so: // a message MyMessage, you can read its default value like so:
// MyMessage::Reader reader = Message<MyMessage>::ReadTrusted(MyMessage::DEFAULT.words); // MyMessage::Reader reader = Message<MyMessage>::ReadTrusted(MyMessage::DEFAULT.words);
template <typename Type>
static typename Type::Reader defaultValue();
// Get a default instance of the given struct or list type.
//
// TODO(cleanup): Find a better home for this function?
// ======================================================================================= // =======================================================================================
class SegmentArrayMessageReader: public MessageReader { class SegmentArrayMessageReader: public MessageReader {
...@@ -282,6 +288,12 @@ typename RootType::Reader readMessageTrusted(const word* data) { ...@@ -282,6 +288,12 @@ typename RootType::Reader readMessageTrusted(const word* data) {
return typename RootType::Reader(internal::StructReader::readRootTrusted(data)); return typename RootType::Reader(internal::StructReader::readRootTrusted(data));
} }
template <typename Type>
static typename Type::Reader defaultValue() {
// TODO(soon): Correctly handle lists. Maybe primitives too?
return typename Type::Reader(internal::StructReader());
}
} // namespace capnproto } // namespace capnproto
#endif // CAPNPROTO_MESSAGE_H_ #endif // CAPNPROTO_MESSAGE_H_
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using Cxx = import "c++.capnp";
@0xb471df2f45ca32c7;
$Cxx.namespace("capnproto::schema");
# WARNING: This protocol is still subject to backwards-incompatible change.
using Id = UInt64;
# The globally-unique ID of a file, type, or annotation.
struct Node {
id @0 :Id;
displayName @1 :Text;
# Name to present to humans to identify this Node. You should not attempt to parse this. Its
# format could change. It is not guaranteed to be unique.
#
# (On Zooko's triangle, this is the node's nickname.)
scopeId @2 :Id = 0;
# ID of the lexical parent node. Typically, the scope node will have a NestedNode pointing back
# at this node, but robust code should avoid relying on this. `scopeId` is zero if the node has
# no parent, which is normally only the case with files, but should be allowed for any kind of
# node (in order to make runtime type generation easier).
nestedNodes @3 :List(NestedNode);
# List of nodes nested within this node, along with the names under which they were declared.
struct NestedNode {
name @0 :Text;
# Unqualified symbol name. Unlike Node.name, this *can* be used programmatically.
#
# (On Zooko's triangle, this is the node's petname according to its parent scope.)
id @1 :Id;
# ID of the nested node. Typically, the target node's scopeId points back to this node, but
# robust code should avoid relying on this.
}
annotations @4 :List(Annotation);
# Annotations applied to this node.
body @5 union {
# Info specific to each kind of node.
fileNode @6 :FileNode;
structNode @7 :StructNode;
enumNode @8 :EnumNode;
interfaceNode @9 :InterfaceNode;
constNode @10 :ConstNode;
annotationNode @11 :AnnotationNode;
}
}
struct Type {
# Represents a type expression.
body @0 union {
voidType @1 :Void;
boolType @2 :Void;
int8Type @3 :Void;
int16Type @4 :Void;
int32Type @5 :Void;
int64Type @6 :Void;
uint8Type @7 :Void;
uint16Type @8 :Void;
uint32Type @9 :Void;
uint64Type @10 :Void;
float32Type @11 :Void;
float64Type @12 :Void;
textType @13 :Void;
dataType @14 :Void;
listType @15 :Type; # Value = the element type.
enumType @16 :Id;
structType @17 :Id;
interfaceType @18 :Id;
objectType @19 :Void;
}
}
struct Value {
# Represents a value, e.g. a field default value, constant value, or annotation value.
body @0 union {
# Note ordinals 1 and 10 are intentionally swapped to improve union layout.
voidValue @10 :Void;
boolValue @2 :Bool;
int8Value @3 :Int8;
int16Value @4 :Int16;
int32Value @5 :Int32;
int64Value @6 :Int64;
uint8Value @7 :UInt8;
uint16Value @8 :UInt16;
uint32Value @9 :UInt32;
uint64Value @1 :UInt64;
float32Value @11 :Float32;
float64Value @12 :Float64;
textValue @13 :Text;
dataValue @14 :Data;
listValue @15 :Object;
enumValue @16 :UInt16;
structValue @17 :Object;
interfaceValue @18 :Void;
# The only interface value that can be represented statically is "null", whose methods always
# throw exceptions.
objectValue @19 :Object;
}
}
struct Annotation {
# Describes an annotation applied to a declaration. Note AnnotationNode describes the
# annotation's declaration, while this describes a use of the annotation.
id @0 :Id;
# ID of the annotation node.
value @1 :Value;
}
struct FileNode {
imports @0 :List(Import);
struct Import {
id @0 :Id;
# ID of the imported file.
name @1 :Text;
# Name which *this* file used to refer to the foreign file. This may be a relative name.
# This information is provided because it might be useful for code generation, e.g. to generate
# #include directives in C++.
#
# (On Zooko's triangle, this is the import's petname according to the importing file.)
}
}
enum ElementSize {
# Possible element sizes for encoded lists. These correspond exactly to the possible values of
# the 3-bit element size component of a list pointer.
empty @0; # aka "void", but that's a keyword.
bit @1;
byte @2;
twoBytes @3;
fourBytes @4;
eightBytes @5;
pointer @6;
inlineComposite @7;
}
struct StructNode {
dataSectionWordSize @0 :UInt16;
pointerSectionSize @1 :UInt16;
preferredListEncoding @2 :ElementSize;
# The preferred element size to use when encoding a list of this struct. If this is anything
# other than `inlineComposite` then the struct is one word or less in size and is a candidate for
# list packing optimization.
members @3 :List(Member);
# Top-level fields and unions of the struct, ordered by ordinal number, except that members of
# unions are not included in this list (because they are nested inside the union declaration).
# Note that this ordering is stable as the protocol evolves -- new members can only be added to
# the end. So, when encoding a struct as tag/value pairs with numeric tags, it actually may make
# sense to use the field's position in this list rather than the original ordinal number to
# identify fields.
struct Member {
name @0 :Text;
ordinal @1 :UInt16;
codeOrder @2 :UInt16;
# Indicates where this member appeared in the code, relative to other members.
# Code ordering may have semantic relevance -- programmers tend to place related fields
# together. So, using code ordering makes sense in human-readable formats where ordering is
# otherwise irrelevant, like JSON. The values of codeOrder are tightly-packed, so for unions
# and non-union fields the maximum value of codeOrder is count(fields) + count(unions).
# Fields that are members of a union are only ordered relative to the other members of that
# union, so the maximum value there is count(union.fields).
annotations @3 :List(Annotation);
body @4 union {
# More member types could be added over time. Consumers should skip those that they
# don't understand.
fieldMember @5 :Field;
unionMember @6 :Union;
}
}
struct Field {
offset @0 :UInt32;
# Offset, in units of the field's size, from the beginning of the section in which the field
# resides. E.g. for a UInt32 field, multiply this by 4 to get the byte offset from the
# beginning of the data section.
type @1 :Type;
defaultValue @2 :Value;
}
struct Union {
discriminantOffset @0 :UInt32;
# Offset of the union's 16-bit discriminant within the struct's data section, in 16-bit units.
members @1 :List(Member);
# Fields of this union, ordered by ordinal. Currently all members are fields, but
# consumers should skip member types that they don't understand. The first member in this list
# gets discriminant value zero, the next gets one, and so on.
#
# TODO(soon): Discriminant zero should be reserved to mean "unset", unless the first field in
# the union actually predates the union (it was retroactively unionized), in which case it
# gets discriminant zero.
}
}
struct EnumNode {
enumerants @0 :List(Enumerant);
# Enumerants, in order by ordinal.
struct Enumerant {
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the enumerants were declared in the code.
# Like Struct.Field.codeOrder.
annotations @2 :List(Annotation);
}
}
struct InterfaceNode {
methods @0 :List(Method);
# Methods, in order by ordinal.
struct Method {
name @0 :Text;
codeOrder @1 :UInt16;
# Specifies order in which the methods were declared in the code.
# Like Struct.Field.codeOrder.
params @2 :List(Param);
struct Param {
name @0 :Text;
type @1 :Type;
defaultValue @2 :Value;
annotations @3 :List(Annotation);
}
requiredParamCount @3 :UInt16;
# One plus the index of the last parameter that has no default value. In languages where
# method calls look like function calls, this is the minimum number of parameters that must
# always be specified, while subsequent parameters are optional.
returnType @4 :Type;
annotations @5 :List(Annotation);
}
}
struct ConstNode {
type @0 :Type;
value @1 :Value;
}
struct AnnotationNode {
type @0 :Type;
targetsFile @1 :Bool;
targetsConst @2 :Bool;
targetsEnum @3 :Bool;
targetsEnumerant @4 :Bool;
targetsStruct @5 :Bool;
targetsField @6 :Bool;
targetsUnion @7 :Bool;
targetsInterface @8 :Bool;
targetsMethod @9 :Bool;
targetsParam @10 :Bool;
targetsAnnotation @11 :Bool;
}
struct CodeGeneratorRequest {
nodes @0 :List(Node);
# All nodes parsed by the compiler, including for the files on the command line and their
# imports.
requestedFiles @1 :List(Id);
# IDs of files which were listed on the command line.
}
...@@ -212,7 +212,19 @@ inline Array<T> newArray(size_t size) { ...@@ -212,7 +212,19 @@ inline Array<T> newArray(size_t size) {
template <typename T> template <typename T>
class ArrayBuilder { class ArrayBuilder {
union Slot { T value; char dummy; }; // TODO(cleanup): This class doesn't work for non-primitive types because Slot is not
// constructable. Giving Slot a constructor/destructor means arrays of it have to be tagged
// so operator delete can run the destructors. If we reinterpret_cast the array to an array
// of T and delete it as that type, operator delete gets very upset.
//
// Perhaps we should bite the bullet and make the Array family do manual memory allocation,
// bypassing the rather-stupid C++ array new/delete operators which store a redundant copy of
// the size anyway.
union Slot {
T value;
char dummy;
};
static_assert(sizeof(Slot) == sizeof(T), "union is bigger than content?"); static_assert(sizeof(Slot) == sizeof(T), "union is bigger than content?");
public: public:
......
...@@ -51,6 +51,20 @@ STRINGIFY_INT(const void*, "%p"); ...@@ -51,6 +51,20 @@ STRINGIFY_INT(const void*, "%p");
#undef STRINGIFY_INT #undef STRINGIFY_INT
#define HEXIFY_INT(type, format) \
CappedArray<char, sizeof(type) * 4> hex(type i) { \
CappedArray<char, sizeof(type) * 4> result; \
result.setSize(sprintf(result.begin(), format, i)); \
return result; \
}
HEXIFY_INT(unsigned short, "%x");
HEXIFY_INT(unsigned int, "%x");
HEXIFY_INT(unsigned long, "%lx");
HEXIFY_INT(unsigned long long, "%llx");
#undef HEXIFY_INT
namespace { namespace {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <utility> #include <utility>
#include <type_traits> #include <type_traits>
#include "type-safety.h" #include "type-safety.h"
#include "blob.h"
#include <string.h> #include <string.h>
namespace capnproto { namespace capnproto {
...@@ -82,9 +83,15 @@ public: ...@@ -82,9 +83,15 @@ public:
inline const T* begin() const { return content; } inline const T* begin() const { return content; }
inline const T* end() const { return content + currentSize; } inline const T* end() const { return content + currentSize; }
inline operator ArrayPtr<T>() const { inline operator ArrayPtr<T>() {
return arrayPtr(content, fixedSize); return arrayPtr(content, currentSize);
} }
inline operator ArrayPtr<const T>() const {
return arrayPtr(content, currentSize);
}
inline T& operator[](size_t index) { return content[index]; }
inline const T& operator[](size_t index) const { return content[index]; }
private: private:
size_t currentSize; size_t currentSize;
...@@ -168,6 +175,10 @@ struct Stringifier { ...@@ -168,6 +175,10 @@ struct Stringifier {
// anything. // anything.
inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; } inline ArrayPtr<const char> operator*(ArrayPtr<const char> s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<const char>& s) const { return s; }
inline ArrayPtr<const char> operator*(const Array<char>& s) const { return s; }
template<size_t n>
inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); } inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
inline FixedArray<char, 1> operator*(char c) const { inline FixedArray<char, 1> operator*(char c) const {
...@@ -176,6 +187,10 @@ struct Stringifier { ...@@ -176,6 +187,10 @@ struct Stringifier {
return result; return result;
} }
inline ArrayPtr<const char> operator*(Text::Reader text) const {
return arrayPtr(text.data(), text.size());
}
CappedArray<char, sizeof(short) * 4> operator*(short i) const; CappedArray<char, sizeof(short) * 4> operator*(short i) const;
CappedArray<char, sizeof(unsigned short) * 4> operator*(unsigned short i) const; CappedArray<char, sizeof(unsigned short) * 4> operator*(unsigned short i) const;
CappedArray<char, sizeof(int) * 4> operator*(int i) const; CappedArray<char, sizeof(int) * 4> operator*(int i) const;
...@@ -190,9 +205,16 @@ struct Stringifier { ...@@ -190,9 +205,16 @@ struct Stringifier {
template <typename T> template <typename T>
Array<char> operator*(ArrayPtr<T> arr) const; Array<char> operator*(ArrayPtr<T> arr) const;
template <typename T>
Array<char> operator*(const Array<T>& arr) const;
}; };
static constexpr Stringifier STR; static constexpr Stringifier STR;
CappedArray<char, sizeof(unsigned short) * 4> hex(unsigned short i);
CappedArray<char, sizeof(unsigned int) * 4> hex(unsigned int i);
CappedArray<char, sizeof(unsigned long) * 4> hex(unsigned long i);
CappedArray<char, sizeof(unsigned long long) * 4> hex(unsigned long long i);
template <typename... Params> template <typename... Params>
Array<char> str(Params&&... params) { Array<char> str(Params&&... params) {
// Magic function which builds a string from a bunch of arbitrary values. Example: // Magic function which builds a string from a bunch of arbitrary values. Example:
...@@ -205,7 +227,7 @@ Array<char> str(Params&&... params) { ...@@ -205,7 +227,7 @@ Array<char> str(Params&&... params) {
} }
template <typename T> template <typename T>
Array<char> strArray(ArrayPtr<T> arr, const char* delim) { Array<char> strArray(T&& arr, const char* delim) {
size_t delimLen = strlen(delim); size_t delimLen = strlen(delim);
decltype(STR * arr[0]) pieces[arr.size()]; decltype(STR * arr[0]) pieces[arr.size()];
size_t size = 0; size_t size = 0;
...@@ -232,6 +254,22 @@ inline Array<char> Stringifier::operator*(ArrayPtr<T> arr) const { ...@@ -232,6 +254,22 @@ inline Array<char> Stringifier::operator*(ArrayPtr<T> arr) const {
return strArray(arr, ", "); return strArray(arr, ", ");
} }
template <typename T>
inline Array<char> Stringifier::operator*(const Array<T>& arr) const {
return strArray(arr, ", ");
}
template <typename T, typename Func>
auto mapArray(T&& arr, Func&& func) -> Array<decltype(func(arr[0]))> {
// TODO(cleanup): Use ArrayBuilder.
Array<decltype(func(arr[0]))> result = newArray<decltype(func(arr[0]))>(arr.size());
size_t pos = 0;
for (auto& element: arr) {
result[pos++] = func(element);
}
return result;
}
} // namespace capnproto } // namespace capnproto
#endif // CAPNPROTO_UTIL_H_ #endif // CAPNPROTO_UTIL_H_
...@@ -30,7 +30,8 @@ executable capnpc ...@@ -30,7 +30,8 @@ executable capnpc
directory, directory,
syb, syb,
transformers, transformers,
entropy entropy,
process
ghc-options: -Wall -fno-warn-missing-signatures ghc-options: -Wall -fno-warn-missing-signatures
other-modules: other-modules:
Lexer, Lexer,
......
...@@ -800,6 +800,7 @@ compileDecl scope (ConstantDecl (Located _ name) t annotations (Located valuePos ...@@ -800,6 +800,7 @@ compileDecl scope (ConstantDecl (Located _ name) t annotations (Located valuePos
compiledAnnotations <- compileAnnotations scope ConstantAnnotation annotations compiledAnnotations <- compileAnnotations scope ConstantAnnotation annotations
return (DescConstant ConstantDesc return (DescConstant ConstantDesc
{ constantName = name { constantName = name
, constantId = childId name Nothing scope
, constantParent = scope , constantParent = scope
, constantType = typeDesc , constantType = typeDesc
, constantValue = valueDesc , constantValue = valueDesc
......
...@@ -449,7 +449,10 @@ hastacheConfig = MuConfig ...@@ -449,7 +449,10 @@ hastacheConfig = MuConfig
generateCxxHeader file = hastacheStr hastacheConfig (encodeStr headerTemplate) (fileContext file) generateCxxHeader file = hastacheStr hastacheConfig (encodeStr headerTemplate) (fileContext file)
generateCxxSource file = hastacheStr hastacheConfig (encodeStr srcTemplate) (fileContext file) generateCxxSource file = hastacheStr hastacheConfig (encodeStr srcTemplate) (fileContext file)
generateCxx file = do generateCxx files _ = do
let handleFile file = do
header <- generateCxxHeader file header <- generateCxxHeader file
source <- generateCxxSource file source <- generateCxxSource file
return [(fileName file ++ ".h", header), (fileName file ++ ".c++", source)] return [(fileName file ++ ".h", header), (fileName file ++ ".c++", source)]
results <- mapM handleFile files
return $ concat results
...@@ -25,15 +25,17 @@ module Main ( main ) where ...@@ -25,15 +25,17 @@ module Main ( main ) where
import System.Environment import System.Environment
import System.Console.GetOpt import System.Console.GetOpt
import System.Exit(exitFailure, exitSuccess) import System.Exit(exitFailure, exitSuccess, ExitCode(..))
import System.IO(hPutStr, stderr) import System.IO(hPutStr, stderr, hSetBinaryMode, hClose)
import System.FilePath(takeDirectory) import System.FilePath(takeDirectory)
import System.Directory(createDirectoryIfMissing, doesDirectoryExist, doesFileExist) import System.Directory(createDirectoryIfMissing, doesDirectoryExist, doesFileExist)
import System.Entropy(getEntropy) import System.Entropy(getEntropy)
import System.Process(createProcess, proc, std_in, cwd, StdStream(CreatePipe), waitForProcess)
import Control.Monad import Control.Monad
import Control.Monad.IO.Class(MonadIO, liftIO) import Control.Monad.IO.Class(MonadIO, liftIO)
import Control.Exception(IOException, catch) import Control.Exception(IOException, catch)
import Control.Monad.Trans.State(StateT, state, modify, execStateT) import Control.Monad.Trans.State(StateT, state, modify, evalStateT)
import qualified Control.Monad.Trans.State as State
import Prelude hiding (catch) import Prelude hiding (catch)
import Compiler import Compiler
import Util(delimit) import Util(delimit)
...@@ -43,18 +45,21 @@ import Text.Printf(printf) ...@@ -43,18 +45,21 @@ import Text.Printf(printf)
import qualified Data.List as List import qualified Data.List as List
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.ByteString.Lazy.Char8 as LZ import qualified Data.ByteString.Lazy.Char8 as LZ
import Data.ByteString(unpack) import Data.ByteString(unpack, pack, hPut)
import Data.Word(Word64, Word8) import Data.Word(Word64, Word8)
import Data.Maybe(fromMaybe, catMaybes)
import Semantics import Semantics
import WireFormat(encodeSchema)
import CxxGenerator(generateCxx) import CxxGenerator(generateCxx)
type GeneratorFn = FileDesc -> IO [(FilePath, LZ.ByteString)] type GeneratorFn = [FileDesc] -> [FileDesc] -> IO [(FilePath, LZ.ByteString)]
generatorFns :: Map.Map String GeneratorFn
generatorFns = Map.fromList [ ("c++", generateCxx) ] generatorFns = Map.fromList [ ("c++", generateCxx) ]
data Opt = SearchPathOpt FilePath data Opt = SearchPathOpt FilePath
| OutputOpt String (Maybe GeneratorFn) FilePath | OutputOpt String GeneratorFn FilePath
| VerboseOpt | VerboseOpt
| HelpOpt | HelpOpt
| GenIdOpt | GenIdOpt
...@@ -78,10 +83,7 @@ main = do ...@@ -78,10 +83,7 @@ main = do
\Generate source code based on Cap'n Proto definition FILEs.\n" \Generate source code based on Cap'n Proto definition FILEs.\n"
optionDescs optionDescs
args <- getArgs args <- getArgs
let (options, files, optErrs) = getOpt Permute optionDescs args let (options, files, errs) = getOpt Permute optionDescs args
let langErrs = map (printf "Unknown output language: %s\n")
[lang | OutputOpt lang Nothing _ <- options]
let errs = optErrs ++ langErrs
unless (null errs) (do unless (null errs) (do
mapM_ (hPutStr stderr) errs mapM_ (hPutStr stderr) errs
hPutStr stderr usage hPutStr stderr usage
...@@ -104,7 +106,7 @@ main = do ...@@ -104,7 +106,7 @@ main = do
exitSuccess) exitSuccess)
let isVerbose = not $ null [opt | opt@VerboseOpt <- options] let isVerbose = not $ null [opt | opt@VerboseOpt <- options]
let outputs = [(fn, dir) | OutputOpt _ (Just fn) dir <- options] let outputs = [(fn, dir) | OutputOpt _ fn dir <- options]
let searchPath = [dir | SearchPathOpt dir <- options] let searchPath = [dir | SearchPathOpt dir <- options]
let verifyDirectoryExists dir = do let verifyDirectoryExists dir = do
...@@ -114,15 +116,49 @@ main = do ...@@ -114,15 +116,49 @@ main = do
exitFailure) exitFailure)
mapM_ verifyDirectoryExists [dir | (_, dir) <- outputs] mapM_ verifyDirectoryExists [dir | (_, dir) <- outputs]
CompilerState failed _ <- (failed, requestedFiles, allFiles) <-
execStateT (mapM_ (handleFile outputs isVerbose searchPath) files) evalStateT (handleFiles isVerbose searchPath files)
(CompilerState False Map.empty) (CompilerState False Map.empty)
mapM_ (doOutput requestedFiles allFiles) outputs
when failed exitFailure when failed exitFailure
handleFiles isVerbose searchPath files = do
requestedFiles <- liftM catMaybes $ mapM (handleFile isVerbose searchPath) files
CompilerState failed importMap <- State.get
return (failed, requestedFiles, [ file | (_, ImportSucceeded file) <- Map.toList importMap ])
parseOutputArg :: String -> Opt parseOutputArg :: String -> Opt
parseOutputArg str = case List.elemIndex ':' str of parseOutputArg str = let
Just i -> let (lang, _:dir) = splitAt i str in OutputOpt lang (Map.lookup lang generatorFns) dir generatorFn lang wd = fromMaybe (callPlugin lang wd) $ Map.lookup lang generatorFns
Nothing -> OutputOpt str (Map.lookup str generatorFns) "." in case List.elemIndex ':' str of
Just i -> let
(lang, _:dir) = splitAt i str
in OutputOpt lang (generatorFn lang (Just dir)) dir
Nothing -> OutputOpt str (generatorFn str Nothing) "."
pluginName lang = if '/' `elem` lang then lang else "capnpc-" ++ lang
callPlugin lang wd descs transitiveImports = do
let schema = encodeSchema descs transitiveImports
(Just hin, _, _, p) <- createProcess (proc (pluginName lang) [])
{ std_in = CreatePipe, cwd = wd }
hSetBinaryMode hin True
hPut hin (pack schema)
hClose hin
exitCode <- waitForProcess p
case exitCode of
ExitFailure 126 -> do
_ <- printf "Plugin for language '%s' is not executable.\n" lang
exitFailure
ExitFailure 127 -> do
_ <- printf "No plugin found for language '%s'.\n" lang
exitFailure
ExitFailure i -> do
_ <- printf "Plugin for language '%s' failed with exit code: %d\n" lang i
exitFailure
ExitSuccess -> return []
-- As always, here I am, writing my own path manipulation routines, because the ones in the -- As always, here I am, writing my own path manipulation routines, because the ones in the
-- standard lib don't do what I want. -- standard lib don't do what I want.
...@@ -227,21 +263,23 @@ parseFile isVerbose searchPath filename text = do ...@@ -227,21 +263,23 @@ parseFile isVerbose searchPath filename text = do
liftIO $ mapM_ printError (List.sortBy compareErrors e) liftIO $ mapM_ printError (List.sortBy compareErrors e)
return $ Right "File contained errors." return $ Right "File contained errors."
handleFile :: [(GeneratorFn, FilePath)] -> Bool -> [FilePath] -> FilePath -> CompilerMonad () handleFile :: Bool -> [FilePath] -> FilePath -> CompilerMonad (Maybe FileDesc)
handleFile outputs isVerbose searchPath filename = do handleFile isVerbose searchPath filename = do
result <- importFile isVerbose searchPath filename result <- importFile isVerbose searchPath filename
case result of case result of
Right _ -> return () Right _ -> return Nothing
Left desc -> do Left desc -> return $ Just desc
doOutput requestedFiles allFiles output = do
let write dir (name, content) = do let write dir (name, content) = do
let outFilename = dir ++ "/" ++ name let outFilename = dir ++ "/" ++ name
createDirectoryIfMissing True $ takeDirectory outFilename createDirectoryIfMissing True $ takeDirectory outFilename
LZ.writeFile outFilename content LZ.writeFile outFilename content
generate (generatorFn, dir) = do generate (generatorFn, dir) = do
files <- generatorFn desc files <- generatorFn requestedFiles allFiles
mapM_ (write dir) files mapM_ (write dir) files
liftIO $ mapM_ generate outputs liftIO $ generate output
compareErrors a b = compare (errorPos a) (errorPos b) compareErrors a b = compare (errorPos a) (errorPos b)
......
...@@ -89,6 +89,7 @@ descId (DescFile d) = fileId d ...@@ -89,6 +89,7 @@ descId (DescFile d) = fileId d
descId (DescEnum d) = enumId d descId (DescEnum d) = enumId d
descId (DescStruct d) = structId d descId (DescStruct d) = structId d
descId (DescInterface d) = interfaceId d descId (DescInterface d) = interfaceId d
descId (DescConstant d) = constantId d
descId (DescAnnotation d) = annotationId d descId (DescAnnotation d) = annotationId d
descId _ = error "This construct does not have an ID." descId _ = error "This construct does not have an ID."
...@@ -363,6 +364,14 @@ typeName _ (InlineDataType s) = printf "InlineData(%d)" s ...@@ -363,6 +364,14 @@ typeName _ (InlineDataType s) = printf "InlineData(%d)" s
-- symbol, and use them if so. A particularly important case of this is imports -- typically -- symbol, and use them if so. A particularly important case of this is imports -- typically
-- the import will have a `using` in the file scope. -- the import will have a `using` in the file scope.
descQualifiedName :: Desc -> Desc -> String descQualifiedName :: Desc -> Desc -> String
-- Builtin descs can be aliased with "using", so we need to support them.
descQualifiedName _ (DescBuiltinType t) = builtinTypeName t
descQualifiedName _ DescBuiltinList = "List"
descQualifiedName _ DescBuiltinInline = "Inline"
descQualifiedName _ DescBuiltinInlineList = "InlineList"
descQualifiedName _ DescBuiltinInlineData = "InlineData"
descQualifiedName (DescFile scope) (DescFile desc) = descQualifiedName (DescFile scope) (DescFile desc) =
if fileName scope == fileName desc if fileName scope == fileName desc
then "" then ""
...@@ -394,6 +403,7 @@ usingRuntimeImports _ = [] ...@@ -394,6 +403,7 @@ usingRuntimeImports _ = []
data ConstantDesc = ConstantDesc data ConstantDesc = ConstantDesc
{ constantName :: String { constantName :: String
, constantId :: Word64
, constantParent :: Desc , constantParent :: Desc
, constantType :: TypeDesc , constantType :: TypeDesc
, constantAnnotations :: AnnotationMap , constantAnnotations :: AnnotationMap
...@@ -544,7 +554,8 @@ descToCode indent self@(DescEnum desc) = printf "%senum %s @0x%016x%s {\n%s%s}\n ...@@ -544,7 +554,8 @@ descToCode indent self@(DescEnum desc) = printf "%senum %s @0x%016x%s {\n%s%s}\n
descToCode indent self@(DescEnumerant desc) = printf "%s%s @%d%s;\n" indent descToCode indent self@(DescEnumerant desc) = printf "%s%s @%d%s;\n" indent
(enumerantName desc) (enumerantNumber desc) (enumerantName desc) (enumerantNumber desc)
(annotationsCode self) (annotationsCode self)
descToCode indent self@(DescStruct desc) = printf "%sstruct %s @0x%016x%s%s {\n%s%s}\n" indent descToCode indent self@(DescStruct desc) =
printf "%sstruct %s @0x%016x%s%s { # %d bytes, %d pointers\n%s%s}\n" indent
(structName desc) (structName desc)
(structId desc) (structId desc)
(if structIsFixedWidth desc (if structIsFixedWidth desc
...@@ -553,11 +564,12 @@ descToCode indent self@(DescStruct desc) = printf "%sstruct %s @0x%016x%s%s {\n% ...@@ -553,11 +564,12 @@ descToCode indent self@(DescStruct desc) = printf "%sstruct %s @0x%016x%s%s {\n%
(structPointerCount desc) (structPointerCount desc)
else "") else "")
(annotationsCode self) (annotationsCode self)
(div (dataSectionBits $ structDataSize desc) 8)
(structPointerCount desc)
(blockCode indent (structMembers desc)) (blockCode indent (structMembers desc))
indent indent
descToCode indent self@(DescField desc) = printf "%s%s@%d%s: %s%s%s; # %s\n" indent descToCode indent self@(DescField desc) = printf "%s%s@%d: %s%s%s; # %s%s\n" indent
(fieldName desc) (fieldNumber desc) (fieldName desc) (fieldNumber desc)
(case fieldUnion desc of { Nothing -> ""; Just (u, _) -> " in " ++ unionName u})
(typeName (descParent self) (fieldType desc)) (typeName (descParent self) (fieldType desc))
(case fieldDefaultValue desc of { Nothing -> ""; Just v -> " = " ++ valueString v; }) (case fieldDefaultValue desc of { Nothing -> ""; Just v -> " = " ++ valueString v; })
(annotationsCode self) (annotationsCode self)
...@@ -572,6 +584,8 @@ descToCode indent self@(DescField desc) = printf "%s%s@%d%s: %s%s%s; # %s\n" in ...@@ -572,6 +584,8 @@ descToCode indent self@(DescField desc) = printf "%s%s@%d%s: %s%s%s; # %s\n" in
DataOffset dataSize offset -> let DataOffset dataSize offset -> let
bits = dataSizeInBits dataSize bits = dataSizeInBits dataSize
in printf "bits[%d, %d)" (offset * bits) ((offset + 1) * bits)) in printf "bits[%d, %d)" (offset * bits) ((offset + 1) * bits))
(case fieldUnion desc of { Nothing -> ""; Just (_, i) -> printf ", union tag = %d" i})
descToCode indent self@(DescUnion desc) = printf "%sunion %s@%d%s { # [%d, %d)\n%s%s}\n" indent descToCode indent self@(DescUnion desc) = printf "%sunion %s@%d%s { # [%d, %d)\n%s%s}\n" indent
(unionName desc) (unionNumber desc) (unionName desc) (unionNumber desc)
(annotationsCode self) (annotationsCode self)
...@@ -596,11 +610,11 @@ descToCode _ self@(DescParam desc) = printf "%s: %s%s%s" ...@@ -596,11 +610,11 @@ descToCode _ self@(DescParam desc) = printf "%s: %s%s%s"
Just v -> printf " = %s" $ valueString v Just v -> printf " = %s" $ valueString v
Nothing -> "") Nothing -> "")
(annotationsCode self) (annotationsCode self)
descToCode indent self@(DescAnnotation desc) = printf "%sannotation %s @0x%016x: %s on(%s)%s;\n" indent descToCode indent self@(DescAnnotation desc) = printf "%sannotation %s @0x%016x(%s): %s%s;\n" indent
(annotationName desc) (annotationName desc)
(annotationId desc) (annotationId desc)
(typeName (descParent self) (annotationType desc))
(delimit ", " $ map show $ Set.toList $ annotationTargets desc) (delimit ", " $ map show $ Set.toList $ annotationTargets desc)
(typeName (descParent self) (annotationType desc))
(annotationsCode self) (annotationsCode self)
descToCode _ (DescBuiltinType _) = error "Can't print code for builtin type." descToCode _ (DescBuiltinType _) = error "Can't print code for builtin type."
descToCode _ DescBuiltinList = error "Can't print code for builtin type." descToCode _ DescBuiltinList = error "Can't print code for builtin type."
......
...@@ -21,17 +21,22 @@ ...@@ -21,17 +21,22 @@
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module WireFormat(encodeMessage) where module WireFormat(encodeMessage, encodeSchema) where
import Data.List(sortBy, genericLength, genericReplicate) import Data.List(sortBy, genericLength, genericReplicate)
import Data.Word import Data.Word
import Data.Bits(shiftL, Bits, setBit, xor) import Data.Bits(shiftL, Bits, setBit, xor)
import Data.Function(on) import Data.Function(on)
import Data.Maybe(mapMaybe, listToMaybe, isNothing)
import Data.List(findIndices)
import qualified Data.Map as Map
import qualified Data.Set as Set
import Semantics import Semantics
import Data.Binary.IEEE754(floatToWord, doubleToWord) import Data.Binary.IEEE754(floatToWord, doubleToWord)
import Text.Printf(printf) import Text.Printf(printf)
import qualified Codec.Binary.UTF8.String as UTF8 import qualified Codec.Binary.UTF8.String as UTF8
import Util(intToBytes) import Util(intToBytes)
import Grammar(AnnotationTarget(..))
padToWord b = let padToWord b = let
trailing = mod (length b) 8 trailing = mod (length b) 8
...@@ -77,7 +82,8 @@ encodePointerValue _ (DataDesc d) = ...@@ -77,7 +82,8 @@ encodePointerValue _ (DataDesc d) =
(encodeListReference (SizeData Size8) (genericLength d), padToWord d) (encodeListReference (SizeData Size8) (genericLength d), padToWord d)
encodePointerValue (StructType desc) (StructValueDesc assignments) = let encodePointerValue (StructType desc) (StructValueDesc assignments) = let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0 (dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in (encodeStructReference desc, concat [dataBytes, refBytes, childBytes]) in (encodeStructReference (structDataSize desc, structPointerCount desc),
concat [dataBytes, refBytes, childBytes])
encodePointerValue (InlineStructType _) _ = encodePointerValue (InlineStructType _) _ =
error "Tried to encode inline struct as a pointer." error "Tried to encode inline struct as a pointer."
encodePointerValue (ListType elementType) (ListDesc items) = encodeList elementType items encodePointerValue (ListType elementType) (ListDesc items) = encodeList elementType items
...@@ -126,17 +132,10 @@ packPointers size items o = loop 0 items (o + size - 1) where ...@@ -126,17 +132,10 @@ packPointers size items o = loop 0 items (o + size - 1) where
in (genericReplicate (padCount * 8) 0 ++ restPtrs, restChildren) in (genericReplicate (padCount * 8) 0 ++ restPtrs, restChildren)
loop idx [] _ = (genericReplicate ((size - idx) * 8) 0, []) loop idx [] _ = (genericReplicate ((size - idx) * 8) 0, [])
encodeStructReference desc offset = encodeStructReference (dataSize, pointerCount) offset =
intToBytes (offset * 4 + structTag) 4 ++ intToBytes (offset * 4 + structTag) 4 ++
intToBytes (dataSectionWordSize $ structDataSize desc) 2 ++ intToBytes (dataSectionWordSize dataSize) 2 ++
intToBytes (structPointerCount desc) 2 intToBytes pointerCount 2
encodeInlineStructListReference elementDataSize elementPointerCount elementCount offset = let
dataBits = dataSectionBits elementDataSize * elementCount
dataWords = div (dataBits + 63) 64
in intToBytes (offset * 4 + structTag) 4 ++
intToBytes dataWords 2 ++
intToBytes (elementPointerCount * elementCount) 2
encodeListReference elemSize@(SizeInlineComposite ds rc) elementCount offset = encodeListReference elemSize@(SizeInlineComposite ds rc) elementCount offset =
intToBytes (offset * 4 + listTag) 4 ++ intToBytes (offset * 4 + listTag) 4 ++
...@@ -242,7 +241,7 @@ encodeList (StructType desc@StructDesc { ...@@ -242,7 +241,7 @@ encodeList (StructType desc@StructDesc {
-- Encode a list of any other sort of struct. -- Encode a list of any other sort of struct.
encodeList (StructType desc) elements = let encodeList (StructType desc) elements = let
count = genericLength elements count = genericLength elements
tag = encodeStructReference desc count tag = encodeStructReference (structDataSize desc, structPointerCount desc) count
eSize = dataSectionWordSize (structDataSize desc) + structPointerCount desc eSize = dataSectionWordSize (structDataSize desc) + structPointerCount desc
structElems = [v | StructValueDesc v <- elements] structElems = [v | StructValueDesc v <- elements]
(elemBytes, childBytes) = loop (eSize * genericLength structElems) structElems (elemBytes, childBytes) = loop (eSize * genericLength structElems) structElems
...@@ -340,8 +339,373 @@ inlineStructListPointerSectionValues elementDesc elements = do ...@@ -340,8 +339,373 @@ inlineStructListPointerSectionValues elementDesc elements = do
encodeMessage (StructType desc) (StructValueDesc assignments) = let encodeMessage (StructType desc) (StructValueDesc assignments) = let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0 (dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in concat [encodeStructReference desc (0::Integer), dataBytes, refBytes, childBytes] in concat [encodeStructReference (structDataSize desc, structPointerCount desc) (0::Integer),
dataBytes, refBytes, childBytes]
encodeMessage (ListType elementType) (ListDesc elements) = let encodeMessage (ListType elementType) (ListDesc elements) = let
(ptr, listBytes) = encodeList elementType elements (ptr, listBytes) = encodeList elementType elements
in ptr (0::Integer) ++ listBytes in ptr (0::Integer) ++ listBytes
encodeMessage _ _ = error "Not a message." encodeMessage _ _ = error "Not a message."
------------------------------------------------------------------------------------------
type EncodedPtr = (Integer -> [Word8], [Word8])
encodeSchema :: [FileDesc] -> [FileDesc] -> [Word8]
encodeSchema requestedFiles allFiles = encRoot where
encUInt64 = EncodedBytes . flip intToBytes 8
encUInt32 = EncodedBytes . flip intToBytes 4
encUInt16 :: (Integral a, Bits a) => a -> EncodedData
encUInt16 = EncodedBytes . flip intToBytes 2
encText :: String -> EncodedPtr
encText v = encodePointerValue (BuiltinType BuiltinText) (TextDesc v)
encDataList :: DataSize -> [EncodedData] -> EncodedPtr
encDataList elementSize elements = let
elemBits = dataSizeInBits elementSize
bytes = packBytes (elemBits * genericLength elements)
$ zip [0,elemBits..] elements
in (encodeListReference (SizeData elementSize) (genericLength elements), bytes)
encPtrList :: [EncodedPtr] -> EncodedPtr
encPtrList elements = let
(ptrBytes, childBytes) = packPointers (genericLength elements) (zip [0..] elements) 0
in (encodeListReference SizeReference (genericLength elements), ptrBytes ++ childBytes)
encStructList :: (DataSectionSize, Integer)
-> [([(Integer, EncodedData)], [(Integer, EncodedPtr)])]
-> EncodedPtr
encStructList elementSize@(dataSize, pointerCount) elements = let
count = genericLength elements
tag = encodeStructReference elementSize count
eSize = dataSectionWordSize dataSize + pointerCount
(elemBytes, childBytes) = loop (eSize * genericLength elements) elements
loop _ [] = ([], [])
loop offset ((dataValues, ptrValues):rest) = let
offsetFromElementEnd = offset - eSize
(dataBytes, ptrBytes, childBytes2) =
encStructBody elementSize dataValues ptrValues offsetFromElementEnd
childLen = genericLength childBytes2
childWordLen = if mod childLen 8 == 0
then div childLen 8
else error "Child not word-aligned."
(restBytes, restChildren) = loop (offsetFromElementEnd + childWordLen) rest
in (concat [dataBytes, ptrBytes, restBytes], childBytes2 ++ restChildren)
in (encodeListReference (SizeInlineComposite dataSize pointerCount) (genericLength elements),
concat [tag, elemBytes, childBytes])
encStructBody :: (DataSectionSize, Integer)
-> [(Integer, EncodedData)]
-> [(Integer, EncodedPtr)]
-> Integer
-> ([Word8], [Word8], [Word8])
encStructBody (dataSize, pointerCount) dataValues ptrValues offsetFromElementEnd = let
dataBytes = packBytes (dataSectionBits dataSize) dataValues
(ptrBytes, childBytes) = packPointers pointerCount ptrValues offsetFromElementEnd
in (dataBytes, ptrBytes, childBytes)
encStruct :: (DataSectionSize, Integer)
-> ([(Integer, EncodedData)], [(Integer, EncodedPtr)])
-> EncodedPtr
encStruct size (dataValues, ptrValues) = let
(dataBytes, ptrBytes, childBytes) = encStructBody size dataValues ptrValues 0
in (encodeStructReference size, concat [dataBytes, ptrBytes, childBytes])
---------------------------------------------
isNodeDesc (DescFile _) = True
isNodeDesc (DescStruct _) = True
isNodeDesc (DescEnum _) = True
isNodeDesc (DescInterface _) = True
isNodeDesc (DescConstant _) = True
isNodeDesc (DescAnnotation _) = True
isNodeDesc _ = False
descNestedNodes (DescFile d) = filter isNodeDesc $ fileMembers d
descNestedNodes (DescStruct d) = filter isNodeDesc $ structMembers d
descNestedNodes (DescInterface d) = filter isNodeDesc $ interfaceMembers d
descNestedNodes _ = []
flattenDescs desc = desc : concatMap flattenDescs (descNestedNodes desc)
allDescs = concatMap flattenDescs $ map DescFile allFiles
---------------------------------------------
encRoot = let
ptrVal = encStruct codeGeneratorRequestSize encCodeGeneratorRequest
(ptrBytes, childBytes) = packPointers 1 [(0, ptrVal)] 0
segment = ptrBytes ++ childBytes
in concat [[0,0,0,0], intToBytes (div (length segment) 8) 4, segment]
codeGeneratorRequestSize = (DataSectionWords 0, 2)
encCodeGeneratorRequest = (dataValues, ptrValues) where
dataValues = []
ptrValues = [ (0, encStructList nodeSize $ map encNode allDescs)
, (1, encDataList Size64 $ map (encUInt64 . fileId) requestedFiles)
]
typeSize = (DataSectionWords 2, 1)
encType t = (dataValues, ptrValues) where
dataValues = [ (0, encUInt16 discrim)
, (64, encUInt64 typeId)
]
ptrValues = case listElementType of
Nothing -> []
Just et -> [ (0, encStruct typeSize $ encType et) ]
(discrim, typeId, listElementType) = case t of
BuiltinType BuiltinVoid -> (0::Word16, 0, Nothing)
BuiltinType BuiltinBool -> (1, 0, Nothing)
BuiltinType BuiltinInt8 -> (2, 0, Nothing)
BuiltinType BuiltinInt16 -> (3, 0, Nothing)
BuiltinType BuiltinInt32 -> (4, 0, Nothing)
BuiltinType BuiltinInt64 -> (5, 0, Nothing)
BuiltinType BuiltinUInt8 -> (6, 0, Nothing)
BuiltinType BuiltinUInt16 -> (7, 0, Nothing)
BuiltinType BuiltinUInt32 -> (8, 0, Nothing)
BuiltinType BuiltinUInt64 -> (9, 0, Nothing)
BuiltinType BuiltinFloat32 -> (10, 0, Nothing)
BuiltinType BuiltinFloat64 -> (11, 0, Nothing)
BuiltinType BuiltinText -> (12, 0, Nothing)
BuiltinType BuiltinData -> (13, 0, Nothing)
BuiltinType BuiltinObject -> (18, 0, Nothing)
ListType et -> (14, 0, Just et)
EnumType d -> (15, enumId d, Nothing)
StructType d -> (16, structId d, Nothing)
InterfaceType d -> (17, interfaceId d, Nothing)
InlineStructType _ -> error "Inline types not currently supported by codegen plugins."
InlineListType _ _ -> error "Inline types not currently supported by codegen plugins."
InlineDataType _ -> error "Inline types not currently supported by codegen plugins."
valueSize = (DataSectionWords 2, 1)
encValue t maybeValue = (dataValues, ptrValues) where
dataValues = (0, encUInt16 discrim) : (case (maybeValue, fieldSize t) of
(Nothing, _) -> []
(_, SizeVoid) -> []
(Just value, SizeData _) -> [ (64, encodeDataValue t value) ]
(_, SizeReference) -> []
(_, SizeInlineComposite _ _) ->
error "Inline types not currently supported by codegen plugins.")
ptrValues = case (maybeValue, fieldSize t) of
(Nothing, _) -> []
(_, SizeVoid) -> []
(_, SizeData _) -> []
(Just value, SizeReference) -> [ (0, encodePointerValue t value) ]
(_, SizeInlineComposite _ _) ->
error "Inline types not currently supported by codegen plugins."
discrim = case t of
BuiltinType BuiltinVoid -> 9::Word16
BuiltinType BuiltinBool -> 1
BuiltinType BuiltinInt8 -> 2
BuiltinType BuiltinInt16 -> 3
BuiltinType BuiltinInt32 -> 4
BuiltinType BuiltinInt64 -> 5
BuiltinType BuiltinUInt8 -> 6
BuiltinType BuiltinUInt16 -> 7
BuiltinType BuiltinUInt32 -> 8
BuiltinType BuiltinUInt64 -> 0
BuiltinType BuiltinFloat32 -> 10
BuiltinType BuiltinFloat64 -> 11
BuiltinType BuiltinText -> 12
BuiltinType BuiltinData -> 13
BuiltinType BuiltinObject -> 19
ListType _ -> 14
EnumType _ -> 15
StructType _ -> 16
InterfaceType _ -> 17
InlineStructType _ -> error "Inline types not currently supported by codegen plugins."
InlineListType _ _ -> error "Inline types not currently supported by codegen plugins."
InlineDataType _ -> error "Inline types not currently supported by codegen plugins."
annotationSize = (DataSectionWords 1, 1)
encAnnotation (annId, (desc, value)) = (dataValues, ptrValues) where
dataValues = [ (0, encUInt64 annId) ]
ptrValues = [ (0, encStruct valueSize $ encValue (annotationType desc) (Just value)) ]
encAnnotationList annotations =
encStructList annotationSize $ map encAnnotation $ Map.toList annotations
nodeSize = (DataSectionWords 3, 4)
encNode :: Desc -> ([(Integer, EncodedData)], [(Integer, EncodedPtr)])
encNode desc = (dataValues, ptrValues) where
dataValues = [ (0, encUInt64 $ descId desc)
, (64, encUInt64 $ scopedId desc)
, (128, encUInt16 discrim)
]
ptrValues = [ (0, encText $ displayName desc)
, (1, encStructList nestedNodeSize $ map encNestedNode $ descNestedNodes desc)
, (2, encAnnotationList $ descAnnotations desc)
, (3, encStruct bodySize body)
]
(discrim, bodySize, body) = case desc of
DescFile d -> (0::Word16, fileNodeSize, encFileNode d)
DescStruct d -> (1, structNodeSize, encStructNode d)
DescEnum d -> (2, enumNodeSize, encEnumNode d)
DescInterface d -> (3, interfaceNodeSize, encInterfaceNode d)
DescConstant d -> (4, constNodeSize, encConstNode d)
DescAnnotation d -> (5, annotationNodeSize, encAnnotationNode d)
_ -> error "Not a node type."
displayName (DescFile f) = fileName f
displayName desc = concat [fileName (descFile desc), ":", descName desc]
nestedNodeSize = (DataSectionWords 1, 1)
encNestedNode desc = (dataValues, ptrValues) where
dataValues = [ (0, encUInt64 $ descId desc) ]
ptrValues = [ (0, encText $ descName desc) ]
scopedId (DescFile _) = 0
scopedId desc = descId $ descParent desc
fileNodeSize = (DataSectionWords 0, 1)
encFileNode desc = (dataValues, ptrValues) where
dataValues = []
ptrValues = [ (0, encStructList importSize $ map encImport $ Map.toList $ fileImportMap desc) ]
importSize = (DataSectionWords 1, 1)
encImport (impName, impFile) = (dataValues2, ptrValues2) where
dataValues2 = [ (0, encUInt64 $ fileId impFile) ]
ptrValues2 = [ (0, encText impName) ]
structNodeSize = (DataSectionWords 1, 1)
encStructNode desc = (dataValues, ptrValues) where
dataValues = [ (0, encUInt16 $ dataSectionWordSize $ structDataSize desc)
, (16, encUInt16 $ structPointerCount desc)
, (32, encUInt16 (fieldSizeEnum preferredListEncoding::Word16))
]
ptrValues = [ (0, encStructList memberSize $ map encMember $
sortMembers $ structMembers desc) ]
preferredListEncoding = case (structDataSize desc, structPointerCount desc) of
(DataSectionWords 0, 0) -> SizeVoid
(DataSectionWords 0, 1) -> SizeReference
(DataSection1, 0) -> SizeData Size1
(DataSection8, 0) -> SizeData Size8
(DataSection16, 0) -> SizeData Size16
(DataSection32, 0) -> SizeData Size32
(DataSectionWords 1, 0) -> SizeData Size64
(ds, pc) -> SizeInlineComposite ds pc
-- Extract just the field and union members, annotate them with ordinals and code order,
-- and then sort by ordinal.
sortMembers members = sortBy (compare `on` (fst . snd)) $ zip [0::Word16 ..]
$ mapMaybe selectFieldOrUnion members
selectFieldOrUnion d@(DescField f) = Just (fieldNumber f, d)
selectFieldOrUnion d@(DescUnion u) = Just (unionNumber u, d)
selectFieldOrUnion _ = Nothing
memberSize = (DataSectionWords 1, 3)
encMember (codeOrder, (_, DescField field)) = (dataValues2, ptrValues2) where
dataValues2 = [ (0, encUInt16 $ fieldNumber field)
, (16, encUInt16 codeOrder)
, (32, encUInt16 (0::Word16)) -- discriminant
]
ptrValues2 = [ (0, encText $ fieldName field)
, (1, encAnnotationList $ fieldAnnotations field)
, (2, encStruct (DataSection32, 2) (dataValues3, ptrValues3))
]
-- StructNode.Field
dataValues3 = [ (0, encUInt32 $ offsetToInt $ fieldOffset field) ]
ptrValues3 = [ (0, encStruct typeSize $ encType $ fieldType field)
, (1, encStruct valueSize $ encValue (fieldType field) $
fieldDefaultValue field)
]
offsetToInt VoidOffset = 0
offsetToInt (DataOffset _ i) = i
offsetToInt (PointerOffset i) = i
offsetToInt (InlineCompositeOffset {}) =
error "Inline types not currently supported by codegen plugins."
encMember (codeOrder, (_, DescUnion union)) = (dataValues2, ptrValues2) where
dataValues2 = [ (0, encUInt16 $ unionNumber union)
, (16, encUInt16 codeOrder)
, (32, encUInt16 (1::Word16)) -- discriminant
]
ptrValues2 = [ (0, encText $ unionName union)
, (1, encAnnotationList $ unionAnnotations union)
, (2, encStruct (DataSection32, 1) (dataValues3, ptrValues3))
]
-- StructNode.Union
dataValues3 = [ (0, encUInt32 $ unionTagOffset union) ]
ptrValues3 = [ (0, encStructList memberSize $ map encMember $ sortMembers $
unionMembers union) ]
encMember _ = error "Not a field or union?"
enumNodeSize = (DataSectionWords 0, 1)
encEnumNode desc = (dataValues, ptrValues) where
dataValues = []
ptrValues = [ (0, encStructList enumerantSize $ map encEnumerant sortedEnumerants) ]
sortedEnumerants = sortBy (compare `on` (enumerantNumber . snd))
$ zip [0::Word16 ..] $ enumerants desc
enumerantSize = (DataSection16, 2)
encEnumerant (codeOrder, enumerant) = (dataValues2, ptrValues2) where
dataValues2 = [ (0, encUInt16 codeOrder) ]
ptrValues2 = [ (0, encText $ enumerantName enumerant)
, (1, encAnnotationList $ enumerantAnnotations enumerant)
]
interfaceNodeSize = (DataSectionWords 0, 1)
encInterfaceNode desc = (dataValues, ptrValues) where
dataValues = []
ptrValues = [ (0, encStructList methodSize $ map encMethod sortedMethods) ]
sortedMethods = sortBy (compare `on` (methodNumber . snd))
$ zip [0::Word16 ..] $ interfaceMethods desc
methodSize = (DataSection32, 4)
encMethod (codeOrder, method) = (dataValues2, ptrValues2) where
dataValues2 = [ (0, encUInt16 codeOrder)
, (16, encUInt16 requiredParamCount) ]
ptrValues2 = [ (0, encText $ methodName method)
, (1, encStructList paramSize $ map encParam $ methodParams method)
, (2, encStruct typeSize $ encType $ methodReturnType method)
, (3, encAnnotationList $ methodAnnotations method)
]
paramIndicesWithoutDefaults =
findIndices (isNothing . paramDefaultValue) $ methodParams method
requiredParamCount = maybe 0 (+1) $ listToMaybe
$ reverse paramIndicesWithoutDefaults
paramSize = (DataSectionWords 0, 4)
encParam param = (dataValues2, ptrValues2) where
dataValues2 = []
ptrValues2 = [ (0, encText $ paramName param)
, (1, encStruct typeSize $ encType $ paramType param)
, (2, encStruct valueSize $ encValue (paramType param) $
paramDefaultValue param)
, (3, encAnnotationList $ paramAnnotations param)
]
constNodeSize = (DataSectionWords 0, 2)
encConstNode desc = (dataValues, ptrValues) where
dataValues = []
ptrValues = [ (0, encStruct typeSize $ encType $ constantType desc)
, (1, encStruct valueSize $ encValue (constantType desc) $ Just $
constantValue desc)
]
annotationNodeSize = (DataSection16, 1)
encAnnotationNode desc = (dataValues, ptrValues) where
dataValues = [ (0, encTarget FileAnnotation)
, (1, encTarget ConstantAnnotation)
, (2, encTarget EnumAnnotation)
, (3, encTarget EnumerantAnnotation)
, (4, encTarget StructAnnotation)
, (5, encTarget FieldAnnotation)
, (6, encTarget UnionAnnotation)
, (7, encTarget InterfaceAnnotation)
, (8, encTarget MethodAnnotation)
, (9, encTarget ParamAnnotation)
, (10, encTarget AnnotationAnnotation)
]
ptrValues = [ (0, encStruct typeSize $ encType $ annotationType desc) ]
encTarget t = EncodedBit $ Set.member t $ annotationTargets desc
...@@ -392,7 +392,7 @@ annotation qux @0xf8a1bedf44c89f00 (field) :Text; ...@@ -392,7 +392,7 @@ annotation qux @0xf8a1bedf44c89f00 (field) :Text;
If you omit the ID for a type or annotation, one will be assigned automatically. This default If you omit the ID for a type or annotation, one will be assigned automatically. This default
ID is derived by taking the first 8 bytes of the MD5 hash of the parent scope's ID concatenated ID is derived by taking the first 8 bytes of the MD5 hash of the parent scope's ID concatenated
with the declaration's name (where the parent scope means the file for top-level delarations, or with the declaration's name (where the "parent scope" is the file for top-level delarations, or
the outer type for nested declarations). You can see the automatically-generated IDs by running the outer type for nested declarations). You can see the automatically-generated IDs by running
`capnpc -v` on a file. In general, you would only specify an explicit ID for a declaration if that `capnpc -v` on a file. In general, you would only specify an explicit ID for a declaration if that
declaration has been renamed or moved and you want the ID to stay the same for declaration has been renamed or moved and you want the ID to stay the same for
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment