1857 lines
69 KiB
C++
1857 lines
69 KiB
C++
// 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 `capnp compile` which generates java code.
|
|
// It is a modified version of the C++ code generator plugin, capnpc-c++.
|
|
|
|
|
|
#include <capnp/schema.capnp.h>
|
|
#include <capnp/serialize.h>
|
|
#include <kj/debug.h>
|
|
#include <kj/io.h>
|
|
#include <kj/string-tree.h>
|
|
#include <kj/vector.h>
|
|
#include <capnp/schema-loader.h>
|
|
#include <capnp/dynamic.h>
|
|
#include <unistd.h>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <set>
|
|
#include <kj/main.h>
|
|
#include <algorithm>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifndef VERSION
|
|
#define VERSION "(unknown)"
|
|
#endif
|
|
|
|
namespace capnp {
|
|
namespace {
|
|
|
|
static constexpr uint64_t OUTER_CLASSNAME_ANNOTATION_ID = 0x9b066bb4881f7cd3;
|
|
static constexpr uint64_t PACKAGE_ANNOTATION_ID = 0x9ee4c8f803b3b596ull;
|
|
|
|
static constexpr const char* FIELD_SIZE_NAMES[] = {
|
|
"VOID", "BIT", "BYTE", "TWO_BYTES", "FOUR_BYTES", "EIGHT_BYTES", "POINTER", "INLINE_COMPOSITE"
|
|
};
|
|
|
|
bool hasDiscriminantValue(const schema::Field::Reader& reader) {
|
|
return reader.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT;
|
|
}
|
|
|
|
void enumerateDeps(schema::Type::Reader type, std::set<uint64_t>& deps) {
|
|
switch (type.which()) {
|
|
case schema::Type::STRUCT:
|
|
deps.insert(type.getStruct().getTypeId());
|
|
break;
|
|
case schema::Type::ENUM:
|
|
deps.insert(type.getEnum().getTypeId());
|
|
break;
|
|
case schema::Type::INTERFACE:
|
|
deps.insert(type.getInterface().getTypeId());
|
|
break;
|
|
case schema::Type::LIST:
|
|
enumerateDeps(type.getList().getElementType(), deps);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void enumerateDeps(schema::Node::Reader node, std::set<uint64_t>& deps) {
|
|
switch (node.which()) {
|
|
case schema::Node::STRUCT: {
|
|
auto structNode = node.getStruct();
|
|
for (auto field: structNode.getFields()) {
|
|
switch (field.which()) {
|
|
case schema::Field::SLOT:
|
|
enumerateDeps(field.getSlot().getType(), deps);
|
|
break;
|
|
case schema::Field::GROUP:
|
|
deps.insert(field.getGroup().getTypeId());
|
|
break;
|
|
}
|
|
}
|
|
if (structNode.getIsGroup()) {
|
|
deps.insert(node.getScopeId());
|
|
}
|
|
break;
|
|
}
|
|
case schema::Node::INTERFACE: {
|
|
auto interfaceNode = node.getInterface();
|
|
for (auto extend: interfaceNode.getExtends()) {
|
|
deps.insert(extend);
|
|
}
|
|
for (auto method: interfaceNode.getMethods()) {
|
|
deps.insert(method.getParamStructType());
|
|
deps.insert(method.getResultStructType());
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct OrderByName {
|
|
template <typename T>
|
|
inline bool operator()(const T& a, const T& b) const {
|
|
return a.getProto().getName() < b.getProto().getName();
|
|
}
|
|
};
|
|
|
|
template <typename MemberList>
|
|
kj::Array<uint> makeMembersByName(MemberList&& members) {
|
|
auto sorted = KJ_MAP(member, members) { return member; };
|
|
std::sort(sorted.begin(), sorted.end(), OrderByName());
|
|
return KJ_MAP(member, sorted) { return member.getIndex(); };
|
|
}
|
|
|
|
kj::StringPtr baseName(kj::StringPtr path) {
|
|
KJ_IF_MAYBE(slashPos, path.findLast('/')) {
|
|
return path.slice(*slashPos + 1);
|
|
} else {
|
|
return path;
|
|
}
|
|
}
|
|
|
|
kj::String safeIdentifier(kj::StringPtr identifier) {
|
|
// Given a desired identifier name, munge it to make it safe for use in generated code.
|
|
//
|
|
// If the identifier is a keyword, this adds an underscore to the end.
|
|
|
|
static const std::set<kj::StringPtr> keywords({
|
|
"alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break",
|
|
"case", "catch", "char", "char16_t", "char32_t", "class", "compl", "const", "constexpr",
|
|
"const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast",
|
|
"else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto",
|
|
"if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not", "not_eq",
|
|
"nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register",
|
|
"reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_assert",
|
|
"static_cast", "struct", "switch", "template", "this", "thread_local", "throw", "true",
|
|
"try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void",
|
|
"volatile", "wchar_t", "while", "xor", "xor_eq"
|
|
});
|
|
|
|
if (keywords.count(identifier) > 0) {
|
|
return kj::str(identifier, '_');
|
|
} else {
|
|
return kj::heapString(identifier);
|
|
}
|
|
}
|
|
|
|
kj::String spaces(int n) {
|
|
return kj::str(kj::repeat(' ', n * 2));
|
|
}
|
|
|
|
// =======================================================================================
|
|
|
|
class CapnpcJavaMain {
|
|
public:
|
|
CapnpcJavaMain(kj::ProcessContext& context): context(context) {}
|
|
|
|
kj::MainFunc getMain() {
|
|
return kj::MainBuilder(context, "Cap'n Proto Java plugin version " VERSION,
|
|
"This is a Cap'n Proto compiler plugin which generates Java code."
|
|
" This is meant to be run using the Cap'n Proto compiler, e.g.:\n"
|
|
" capnp compile -ojava foo.capnp")
|
|
.callAfterParsing(KJ_BIND_METHOD(*this, run))
|
|
.build();
|
|
}
|
|
|
|
private:
|
|
kj::ProcessContext& context;
|
|
SchemaLoader schemaLoader;
|
|
std::unordered_set<uint64_t> usedImports;
|
|
bool hasInterfaces = false;
|
|
|
|
kj::StringTree javaFullName(Schema schema) {
|
|
auto node = schema.getProto();
|
|
if (node.getScopeId() == 0) {
|
|
usedImports.insert(node.getId());
|
|
for (auto annotation: node.getAnnotations()) {
|
|
if (annotation.getId() == OUTER_CLASSNAME_ANNOTATION_ID) {
|
|
return kj::strTree("", toTitleCase(annotation.getValue().getText()));
|
|
}
|
|
}
|
|
return kj::strTree(" ");//kj::strTree(outerClassName);
|
|
} else {
|
|
Schema parent = schemaLoader.get(node.getScopeId());
|
|
for (auto nested: parent.getProto().getNestedNodes()) {
|
|
if (nested.getId() == node.getId()) {
|
|
return kj::strTree(javaFullName(parent), ".", nested.getName());
|
|
}
|
|
}
|
|
KJ_FAIL_REQUIRE("A schema Node's supposed scope did not contain the node as a NestedNode.");
|
|
}
|
|
}
|
|
|
|
|
|
kj::String toUpperCase(kj::StringPtr name) {
|
|
kj::Vector<char> result(name.size() + 4);
|
|
|
|
for (char c: name) {
|
|
if ('a' <= c && c <= 'z') {
|
|
result.add(c - 'a' + 'A');
|
|
} else if (result.size() > 0 && 'A' <= c && c <= 'Z') {
|
|
result.add('_');
|
|
result.add(c);
|
|
} else {
|
|
result.add(c);
|
|
}
|
|
}
|
|
|
|
result.add('\0');
|
|
|
|
return kj::String(result.releaseAsArray());
|
|
}
|
|
|
|
kj::String toTitleCase(kj::StringPtr name) {
|
|
kj::String result = kj::heapString(name);
|
|
if ('a' <= result[0] && result[0] <= 'z') {
|
|
result[0] = result[0] - 'a' + 'A';
|
|
}
|
|
return kj::mv(result);
|
|
}
|
|
|
|
kj::StringTree typeName(schema::Type::Reader type) {
|
|
switch (type.which()) {
|
|
case schema::Type::VOID: return kj::strTree("org.capnproto.Void");
|
|
|
|
case schema::Type::BOOL: return kj::strTree("boolean");
|
|
case schema::Type::INT8: return kj::strTree("byte");
|
|
case schema::Type::INT16: return kj::strTree("short");
|
|
case schema::Type::INT32: return kj::strTree("int");
|
|
case schema::Type::INT64: return kj::strTree("long");
|
|
case schema::Type::UINT8: return kj::strTree("byte");
|
|
case schema::Type::UINT16: return kj::strTree("short");
|
|
case schema::Type::UINT32: return kj::strTree("int");
|
|
case schema::Type::UINT64: return kj::strTree("long");
|
|
case schema::Type::FLOAT32: return kj::strTree("float");
|
|
case schema::Type::FLOAT64: return kj::strTree("double");
|
|
|
|
case schema::Type::TEXT: return kj::strTree(" org.capnproto.Text");
|
|
case schema::Type::DATA: return kj::strTree(" org.capnproto.Data");
|
|
|
|
case schema::Type::ENUM:
|
|
return javaFullName(schemaLoader.get(type.getEnum().getTypeId()));
|
|
case schema::Type::STRUCT:
|
|
return javaFullName(schemaLoader.get(type.getStruct().getTypeId()));
|
|
case schema::Type::INTERFACE:
|
|
return javaFullName(schemaLoader.get(type.getInterface().getTypeId()));
|
|
|
|
case schema::Type::LIST:
|
|
{
|
|
auto elementType = type.getList().getElementType();
|
|
switch (elementType.which()) {
|
|
case schema::Type::VOID:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Void");
|
|
case schema::Type::BOOL:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Boolean");
|
|
case schema::Type::INT8:
|
|
case schema::Type::UINT8:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Byte");
|
|
case schema::Type::INT16:
|
|
case schema::Type::UINT16:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Short");
|
|
case schema::Type::INT32:
|
|
case schema::Type::UINT32:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Int");
|
|
case schema::Type::INT64:
|
|
case schema::Type::UINT64:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Long");
|
|
case schema::Type::FLOAT32:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Float");
|
|
case schema::Type::FLOAT64:
|
|
return kj::strTree(" org.capnproto.PrimitiveList.Double");
|
|
case schema::Type::STRUCT:
|
|
return kj::strTree(" org.capnproto.StructList");
|
|
case schema::Type::TEXT:
|
|
return kj::strTree( "org.capnproto.TextList");
|
|
case schema::Type::DATA:
|
|
return kj::strTree( "org.capnproto.DataList");
|
|
case schema::Type::ENUM:
|
|
case schema::Type::INTERFACE:
|
|
case schema::Type::ANY_POINTER:
|
|
case schema::Type::LIST:
|
|
KJ_FAIL_REQUIRE("unimplemented");
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
case schema::Type::ANY_POINTER:
|
|
// Not used.
|
|
return kj::strTree();
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
kj::StringTree literalValue(schema::Type::Reader type, schema::Value::Reader value) {
|
|
switch (value.which()) {
|
|
case schema::Value::VOID: return kj::strTree("org.capnproto.Void.VOID");
|
|
case schema::Value::BOOL: return kj::strTree(value.getBool() ? "true" : "false");
|
|
case schema::Value::INT8: return kj::strTree(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(), "L");
|
|
case schema::Value::UINT8: return kj::strTree(kj::implicitCast<int8_t>(value.getUint8()));
|
|
case schema::Value::UINT16: return kj::strTree(kj::implicitCast<int16_t>(value.getUint16()));
|
|
case schema::Value::UINT32: return kj::strTree(kj::implicitCast<int32_t>(value.getUint32()));
|
|
case schema::Value::UINT64: return kj::strTree(kj::implicitCast<int64_t>(value.getUint64()), "L");
|
|
case schema::Value::FLOAT32: return kj::strTree(value.getFloat32(), "f");
|
|
case schema::Value::FLOAT64: return kj::strTree(value.getFloat64());
|
|
case schema::Value::ENUM: {
|
|
EnumSchema schema = schemaLoader.get(type.getEnum().getTypeId()).asEnum();
|
|
if (value.getEnum() < schema.getEnumerants().size()) {
|
|
return kj::strTree(
|
|
javaFullName(schema), ".",
|
|
toUpperCase(schema.getEnumerants()[value.getEnum()].getProto().getName()));
|
|
} else {
|
|
return kj::strTree("static_cast<", javaFullName(schema), ">(", value.getEnum(), ")");
|
|
}
|
|
}
|
|
|
|
case schema::Value::TEXT:
|
|
case schema::Value::DATA:
|
|
case schema::Value::STRUCT:
|
|
case schema::Value::INTERFACE:
|
|
case schema::Value::LIST:
|
|
case schema::Value::ANY_POINTER:
|
|
KJ_FAIL_REQUIRE("literalValue() can only be used on primitive types.");
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
// Code to deal with "slots" -- determines what to zero out when we clear a group.
|
|
|
|
static uint typeSizeBits(schema::Type::Which whichType) {
|
|
switch (whichType) {
|
|
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::ENUM: return 16;
|
|
|
|
case schema::Type::VOID:
|
|
case schema::Type::TEXT:
|
|
case schema::Type::DATA:
|
|
case schema::Type::LIST:
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::INTERFACE:
|
|
case schema::Type::ANY_POINTER:
|
|
KJ_FAIL_REQUIRE("Should only be called for data types.");
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
enum class Section {
|
|
NONE,
|
|
DATA,
|
|
POINTERS
|
|
};
|
|
|
|
static Section sectionFor(schema::Type::Which whichType) {
|
|
switch (whichType) {
|
|
case schema::Type::VOID:
|
|
return Section::NONE;
|
|
case schema::Type::BOOL:
|
|
case schema::Type::INT8:
|
|
case schema::Type::INT16:
|
|
case schema::Type::INT32:
|
|
case schema::Type::INT64:
|
|
case schema::Type::UINT8:
|
|
case schema::Type::UINT16:
|
|
case schema::Type::UINT32:
|
|
case schema::Type::UINT64:
|
|
case schema::Type::FLOAT32:
|
|
case schema::Type::FLOAT64:
|
|
case schema::Type::ENUM:
|
|
return Section::DATA;
|
|
case schema::Type::TEXT:
|
|
case schema::Type::DATA:
|
|
case schema::Type::LIST:
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::INTERFACE:
|
|
case schema::Type::ANY_POINTER:
|
|
return Section::POINTERS;
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
static kj::StringPtr maskType(schema::Type::Which whichType) {
|
|
switch (whichType) {
|
|
case schema::Type::BOOL: return "boolean";
|
|
case schema::Type::INT8: return "byte";
|
|
case schema::Type::INT16: return "short";
|
|
case schema::Type::INT32: return "int";
|
|
case schema::Type::INT64: return "long";
|
|
case schema::Type::UINT8: return "byte";
|
|
case schema::Type::UINT16: return "short";
|
|
case schema::Type::UINT32: return "int";
|
|
case schema::Type::UINT64: return "long";
|
|
case schema::Type::FLOAT32: return "int";
|
|
case schema::Type::FLOAT64: return "long";
|
|
case schema::Type::ENUM: return "short";
|
|
|
|
case schema::Type::VOID:
|
|
case schema::Type::TEXT:
|
|
case schema::Type::DATA:
|
|
case schema::Type::LIST:
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::INTERFACE:
|
|
case schema::Type::ANY_POINTER:
|
|
KJ_FAIL_REQUIRE("Should only be called for data types.");
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
static kj::StringPtr maskZeroLiteral(schema::Type::Which whichType) {
|
|
switch (whichType) {
|
|
case schema::Type::BOOL: return "false";
|
|
case schema::Type::INT8: return "(byte)0";
|
|
case schema::Type::INT16: return "(short)0";
|
|
case schema::Type::INT32: return "0";
|
|
case schema::Type::INT64: return "0L";
|
|
case schema::Type::UINT8: return "(byte)0";
|
|
case schema::Type::UINT16: return "(short)0";
|
|
case schema::Type::UINT32: return "0";
|
|
case schema::Type::UINT64: return "0L";
|
|
case schema::Type::FLOAT32: return "0";
|
|
case schema::Type::FLOAT64: return "0L";
|
|
case schema::Type::ENUM: return "(short)0";
|
|
|
|
case schema::Type::VOID:
|
|
case schema::Type::TEXT:
|
|
case schema::Type::DATA:
|
|
case schema::Type::LIST:
|
|
case schema::Type::STRUCT:
|
|
case schema::Type::INTERFACE:
|
|
case schema::Type::ANY_POINTER:
|
|
KJ_FAIL_REQUIRE("Should only be called for data types.");
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
|
|
struct Slot {
|
|
schema::Type::Which whichType;
|
|
uint offset;
|
|
|
|
bool isSupersetOf(Slot other) const {
|
|
auto section = sectionFor(whichType);
|
|
if (section != sectionFor(other.whichType)) return false;
|
|
switch (section) {
|
|
case Section::NONE:
|
|
return true; // all voids overlap
|
|
case Section::DATA: {
|
|
auto bits = typeSizeBits(whichType);
|
|
auto start = offset * bits;
|
|
auto otherBits = typeSizeBits(other.whichType);
|
|
auto otherStart = other.offset * otherBits;
|
|
return start <= otherStart && otherStart + otherBits <= start + bits;
|
|
}
|
|
case Section::POINTERS:
|
|
return offset == other.offset;
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
bool operator<(Slot other) const {
|
|
// Sort by section, then start position, and finally size.
|
|
|
|
auto section = sectionFor(whichType);
|
|
auto otherSection = sectionFor(other.whichType);
|
|
if (section < otherSection) {
|
|
return true;
|
|
} else if (section > otherSection) {
|
|
return false;
|
|
}
|
|
|
|
switch (section) {
|
|
case Section::NONE:
|
|
return false;
|
|
case Section::DATA: {
|
|
auto bits = typeSizeBits(whichType);
|
|
auto start = offset * bits;
|
|
auto otherBits = typeSizeBits(other.whichType);
|
|
auto otherStart = other.offset * otherBits;
|
|
if (start < otherStart) {
|
|
return true;
|
|
} else if (start > otherStart) {
|
|
return false;
|
|
}
|
|
|
|
// Sort larger sizes before smaller.
|
|
return bits > otherBits;
|
|
}
|
|
case Section::POINTERS:
|
|
return offset < other.offset;
|
|
}
|
|
KJ_UNREACHABLE;
|
|
}
|
|
};
|
|
|
|
void getSlots(StructSchema schema, kj::Vector<Slot>& slots) {
|
|
auto structProto = schema.getProto().getStruct();
|
|
if (structProto.getDiscriminantCount() > 0) {
|
|
slots.add(Slot { schema::Type::UINT16, structProto.getDiscriminantOffset() });
|
|
}
|
|
|
|
for (auto field: schema.getFields()) {
|
|
auto proto = field.getProto();
|
|
switch (proto.which()) {
|
|
case schema::Field::SLOT: {
|
|
auto slot = proto.getSlot();
|
|
slots.add(Slot { slot.getType().which(), slot.getOffset() });
|
|
break;
|
|
}
|
|
case schema::Field::GROUP:
|
|
getSlots(schema.getDependency(proto.getGroup().getTypeId()).asStruct(), slots);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
kj::Array<Slot> getSortedSlots(StructSchema schema) {
|
|
// Get a representation of all of the field locations owned by this schema, e.g. so that they
|
|
// can be zero'd out.
|
|
|
|
kj::Vector<Slot> slots(schema.getFields().size());
|
|
getSlots(schema, slots);
|
|
std::sort(slots.begin(), slots.end());
|
|
|
|
kj::Vector<Slot> result(slots.size());
|
|
|
|
// All void slots are redundant, and they sort towards the front of the list. By starting out
|
|
// with `prevSlot` = void, we will end up skipping them all, which is what we want.
|
|
Slot prevSlot = { schema::Type::VOID, 0 };
|
|
for (auto slot: slots) {
|
|
if (prevSlot.isSupersetOf(slot)) {
|
|
// This slot is redundant as prevSlot is a superset of it.
|
|
continue;
|
|
}
|
|
|
|
// Since all sizes are power-of-two, if two slots overlap at all, one must be a superset of
|
|
// the other. Since we sort slots by starting position, we know that the only way `slot`
|
|
// could be a superset of `prevSlot` is if they have the same starting position. However,
|
|
// since we sort slots with the same starting position by descending size, this is not
|
|
// possible.
|
|
KJ_DASSERT(!slot.isSupersetOf(prevSlot));
|
|
|
|
result.add(slot);
|
|
|
|
prevSlot = slot;
|
|
}
|
|
|
|
return result.releaseAsArray();
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
struct DiscriminantChecks {
|
|
kj::String has;
|
|
kj::String check;
|
|
kj::String set;
|
|
kj::StringTree readerIsDecl;
|
|
kj::StringTree builderIsDecl;
|
|
kj::StringTree isDefs;
|
|
};
|
|
|
|
DiscriminantChecks makeDiscriminantChecks(kj::StringPtr scope,
|
|
kj::StringPtr memberName,
|
|
StructSchema containingStruct,
|
|
int indent) {
|
|
auto discrimOffset = containingStruct.getProto().getStruct().getDiscriminantOffset();
|
|
|
|
kj::String titleCase = toTitleCase(memberName);
|
|
kj::String upperCase = toUpperCase(memberName);
|
|
|
|
return DiscriminantChecks {
|
|
kj::str(spaces(indent),
|
|
" if (which() != ", scope, "Which.", upperCase, ") return false;\n"),
|
|
kj::str(), // XXX
|
|
//kj::str(
|
|
// " KJ_IREQUIRE(which() == ", scope, upperCase, ",\n"
|
|
// " \"Must check which() before get()ing a union member.\");\n"),
|
|
kj::str(
|
|
spaces(indent), " _builder.setShortField(", discrimOffset, ", (short)",
|
|
scope, "Which.", upperCase, ".ordinal());\n"),
|
|
kj::strTree(spaces(indent), "public final boolean is", titleCase, "() {\n",
|
|
spaces(indent), " return which() == ", scope, "Which.", upperCase,";\n",
|
|
spaces(indent), "}\n"),
|
|
kj::strTree(spaces(indent), "public final boolean is", titleCase, "() {\n",
|
|
spaces(indent), " return which() == ", scope, "Which.", upperCase, ";\n",
|
|
spaces(indent), "}\n"),
|
|
kj::strTree(
|
|
"inline boolean ", scope, "Reader::is", titleCase, "() const {\n"
|
|
" return which() == ", scope, upperCase, ";\n"
|
|
"}\n"
|
|
"inline boolean ", scope, "Builder::is", titleCase, "() {\n"
|
|
" return which() == ", scope, upperCase, ";\n"
|
|
"}\n")
|
|
};
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
struct FieldText {
|
|
kj::StringTree readerMethodDecls;
|
|
kj::StringTree builderMethodDecls;
|
|
kj::StringTree pipelineMethodDecls;
|
|
kj::StringTree inlineMethodDefs;
|
|
};
|
|
|
|
enum class FieldKind {
|
|
PRIMITIVE,
|
|
BLOB,
|
|
STRUCT,
|
|
LIST,
|
|
INTERFACE,
|
|
ANY_POINTER
|
|
};
|
|
|
|
kj::StringTree makeEnumGetter(EnumSchema schema, kj::String member, uint offset, int indent) {
|
|
auto enumerants = schema.getEnumerants();
|
|
return kj::strTree(
|
|
spaces(indent), "switch(", member, ".getShortField(", offset, ")) {\n",
|
|
KJ_MAP(e, enumerants) {
|
|
return kj::strTree(spaces(indent+1), "case ", e.getOrdinal(), " : return ",
|
|
javaFullName(schema), ".",
|
|
toUpperCase(e.getProto().getName()), ";\n");
|
|
},
|
|
spaces(indent+1), "default: return ", javaFullName(schema), "._UNKNOWN;\n",
|
|
spaces(indent), "}\n"
|
|
);
|
|
}
|
|
|
|
FieldText makeFieldText(kj::StringPtr scope, StructSchema::Field field, int indent) {
|
|
auto proto = field.getProto();
|
|
kj::String titleCase = toTitleCase(proto.getName());
|
|
|
|
DiscriminantChecks unionDiscrim;
|
|
if (hasDiscriminantValue(proto)) {
|
|
unionDiscrim = makeDiscriminantChecks(scope, proto.getName(), field.getContainingStruct(), indent + 1);
|
|
}
|
|
|
|
switch (proto.which()) {
|
|
case schema::Field::SLOT:
|
|
// Continue below.
|
|
break;
|
|
|
|
case schema::Field::GROUP: {
|
|
auto slots = getSortedSlots(schemaLoader.get(
|
|
field.getProto().getGroup().getTypeId()).asStruct());
|
|
return FieldText {
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.readerIsDecl),
|
|
spaces(indent), " public ", titleCase, ".Reader get", titleCase, "() {\n",
|
|
spaces(indent), " return new ", scope, titleCase, ".Reader(_reader);\n",
|
|
spaces(indent), " }\n",
|
|
"\n"),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.builderIsDecl),
|
|
spaces(indent), " public final ", titleCase, ".Builder get", titleCase, "() {\n",
|
|
spaces(indent), " return new ", scope, titleCase, ".Builder(_builder);\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final ", titleCase, ".Builder init", titleCase, "() {\n",
|
|
unionDiscrim.set,
|
|
KJ_MAP(slot, slots) {
|
|
switch (sectionFor(slot.whichType)) {
|
|
case Section::NONE:
|
|
return kj::strTree();
|
|
case Section::DATA:
|
|
return kj::strTree(
|
|
spaces(indent),
|
|
" _builder.set", toTitleCase(maskType(slot.whichType)),
|
|
"Field(", slot.offset, ",", maskZeroLiteral(slot.whichType),
|
|
");\n");
|
|
case Section::POINTERS:
|
|
return kj::strTree(
|
|
spaces(indent), " _builder.getPointerField(", slot.offset, ").clear();\n");
|
|
}
|
|
KJ_UNREACHABLE;
|
|
},
|
|
" return new ", scope, titleCase, ".Builder(_builder);\n",
|
|
spaces(indent), " }\n",
|
|
"\n"),
|
|
|
|
kj::strTree(),
|
|
kj::strTree()
|
|
};
|
|
}
|
|
}
|
|
|
|
auto slot = proto.getSlot();
|
|
|
|
FieldKind kind = FieldKind::PRIMITIVE;
|
|
kj::String ownedType;
|
|
kj::String type = typeName(slot.getType()).flatten();
|
|
kj::StringPtr setterDefault; // only for void
|
|
kj::String defaultMask; // primitives only
|
|
size_t defaultOffset = 0; // pointers only: offset of the default value within the schema.
|
|
size_t defaultSize = 0; // blobs only: byte size of the default value.
|
|
|
|
auto typeBody = slot.getType();
|
|
auto defaultBody = slot.getDefaultValue();
|
|
switch (typeBody.which()) {
|
|
case schema::Type::VOID:
|
|
kind = FieldKind::PRIMITIVE;
|
|
setterDefault = " = ::capnp::VOID";
|
|
break;
|
|
|
|
#define HANDLE_PRIMITIVE(discrim, typeName, javaTypeName, defaultName, suffix) \
|
|
case schema::Type::discrim: \
|
|
kind = FieldKind::PRIMITIVE; \
|
|
if (defaultBody.get##defaultName() != 0) { \
|
|
defaultMask = kj::str("(", #javaTypeName, ")", kj::implicitCast< typeName>(defaultBody.get##defaultName()), #suffix); \
|
|
} \
|
|
break;
|
|
|
|
HANDLE_PRIMITIVE(BOOL, bool, boolean, Bool, );
|
|
HANDLE_PRIMITIVE(INT8 , ::int8_t , byte, Int8 , );
|
|
HANDLE_PRIMITIVE(INT16, ::int16_t, short, Int16, );
|
|
HANDLE_PRIMITIVE(INT32, ::int32_t, int, Int32, );
|
|
HANDLE_PRIMITIVE(INT64, ::int64_t, long, Int64, L);
|
|
HANDLE_PRIMITIVE(UINT8 , ::int8_t , byte, Uint8 , );
|
|
HANDLE_PRIMITIVE(UINT16, ::int16_t, short, Uint16, );
|
|
HANDLE_PRIMITIVE(UINT32, ::int32_t, int, Uint32, );
|
|
HANDLE_PRIMITIVE(UINT64, ::int64_t, long, Uint64, L);
|
|
#undef HANDLE_PRIMITIVE
|
|
|
|
case schema::Type::FLOAT32:
|
|
kind = FieldKind::PRIMITIVE;
|
|
if (defaultBody.getFloat32() != 0) {
|
|
int32_t mask;
|
|
float value = defaultBody.getFloat32();
|
|
static_assert(sizeof(mask) == sizeof(value), "bug");
|
|
memcpy(&mask, &value, sizeof(mask));
|
|
defaultMask = kj::str(mask);
|
|
}
|
|
break;
|
|
|
|
case schema::Type::FLOAT64:
|
|
kind = FieldKind::PRIMITIVE;
|
|
if (defaultBody.getFloat64() != 0) {
|
|
int64_t mask;
|
|
double value = defaultBody.getFloat64();
|
|
static_assert(sizeof(mask) == sizeof(value), "bug");
|
|
memcpy(&mask, &value, sizeof(mask));
|
|
defaultMask = kj::str(mask, "L");
|
|
}
|
|
break;
|
|
|
|
case schema::Type::TEXT:
|
|
kind = FieldKind::BLOB;
|
|
if (defaultBody.hasText()) {
|
|
defaultOffset = field.getDefaultValueSchemaOffset();
|
|
defaultSize = defaultBody.getText().size();
|
|
}
|
|
break;
|
|
case schema::Type::DATA:
|
|
kind = FieldKind::BLOB;
|
|
if (defaultBody.hasData()) {
|
|
defaultOffset = field.getDefaultValueSchemaOffset();
|
|
defaultSize = defaultBody.getData().size();
|
|
}
|
|
break;
|
|
|
|
case schema::Type::ENUM:
|
|
kind = FieldKind::PRIMITIVE;
|
|
if (defaultBody.getEnum() != 0) {
|
|
defaultMask = kj::str(defaultBody.getEnum(), "u");
|
|
}
|
|
break;
|
|
|
|
case schema::Type::STRUCT:
|
|
kind = FieldKind::STRUCT;
|
|
if (defaultBody.hasStruct()) {
|
|
defaultOffset = field.getDefaultValueSchemaOffset();
|
|
}
|
|
break;
|
|
case schema::Type::LIST:
|
|
kind = FieldKind::LIST;
|
|
if (defaultBody.hasList()) {
|
|
defaultOffset = field.getDefaultValueSchemaOffset();
|
|
}
|
|
break;
|
|
case schema::Type::INTERFACE:
|
|
kind = FieldKind::INTERFACE;
|
|
break;
|
|
case schema::Type::ANY_POINTER:
|
|
kind = FieldKind::ANY_POINTER;
|
|
if (defaultBody.hasAnyPointer()) {
|
|
defaultOffset = field.getDefaultValueSchemaOffset();
|
|
}
|
|
break;
|
|
}
|
|
|
|
kj::String defaultMaskParam;
|
|
if (defaultMask.size() > 0) {
|
|
defaultMaskParam = kj::str(", ", defaultMask);
|
|
}
|
|
|
|
uint offset = slot.getOffset();
|
|
|
|
auto structSchema = field.getContainingStruct();
|
|
|
|
if (kind == FieldKind::PRIMITIVE) {
|
|
return FieldText {
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.readerIsDecl),
|
|
spaces(indent), " public final ", type, " get", titleCase, "() {\n",
|
|
(typeBody.which() == schema::Type::ENUM ?
|
|
makeEnumGetter(structSchema.getDependency(typeBody.getEnum().getTypeId()).asEnum(),
|
|
kj::str("_reader"), offset, indent + 2) :
|
|
(typeBody.which() == schema::Type::VOID ?
|
|
kj::strTree(spaces(indent), " return org.capnproto.Void.VOID;\n") :
|
|
kj::strTree(spaces(indent), " return _reader.get",toTitleCase(type),"Field(", offset, defaultMaskParam, ");\n"))),
|
|
spaces(indent), " }\n",
|
|
"\n"),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.builderIsDecl),
|
|
spaces(indent), " public final ", type, " get", titleCase, "() {\n",
|
|
(typeBody.which() == schema::Type::ENUM ?
|
|
makeEnumGetter(structSchema.getDependency(typeBody.getEnum().getTypeId()).asEnum(),
|
|
kj::str("_builder"), offset, indent + 2) :
|
|
(typeBody.which() == schema::Type::VOID ?
|
|
kj::strTree(spaces(indent), " return org.capnproto.Void.VOID;\n") :
|
|
kj::strTree(spaces(indent), " return _builder.get",toTitleCase(type),"Field(", offset, defaultMaskParam, ");\n"))),
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public final void set", titleCase, "(", type, " value) {\n",
|
|
unionDiscrim.set,
|
|
(typeBody.which() == schema::Type::ENUM ?
|
|
kj::strTree(spaces(indent), " _builder.setShortField(", offset, ", (short)value.ordinal());\n") :
|
|
(typeBody.which() == schema::Type::VOID ?
|
|
kj::strTree() :
|
|
kj::strTree(spaces(indent), " _builder.set",
|
|
toTitleCase(type), "Field(", offset, ", value", defaultMaskParam, ");\n"))),
|
|
spaces(indent), " }\n",
|
|
"\n"),
|
|
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
};
|
|
|
|
} else if (kind == FieldKind::INTERFACE) {
|
|
KJ_FAIL_REQUIRE("interfaces unimplemented");
|
|
} else if (kind == FieldKind::ANY_POINTER) {
|
|
return FieldText {
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.readerIsDecl),
|
|
spaces(indent), " public boolean has", titleCase, "() {\n",
|
|
unionDiscrim.has,
|
|
spaces(indent), " return !_reader.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public org.capnproto.AnyPointer.Reader get", titleCase, "() {\n",
|
|
unionDiscrim.check,
|
|
spaces(indent), " return new org.capnproto.AnyPointer.Reader(_reader.getPointerField(",
|
|
offset,"));\n",
|
|
spaces(indent), " }\n"),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.builderIsDecl),
|
|
spaces(indent), " public final boolean has", titleCase, "() {\n",
|
|
spaces(indent), " return !_builder.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public org.capnproto.AnyPointer.Builder get", titleCase, "() {\n",
|
|
unionDiscrim.check,
|
|
spaces(indent), " return new org.capnproto.AnyPointer.Builder(_builder.getPointerField(",
|
|
offset, "));\n",
|
|
spaces(indent), " }\n",
|
|
//" inline ::capnp::AnyPointer::Builder init", titleCase, "();\n"
|
|
"\n"),
|
|
|
|
kj::strTree(),
|
|
|
|
kj::strTree(
|
|
"inline ::capnp::AnyPointer::Builder ", scope, "Builder::init", titleCase, "() {\n",
|
|
unionDiscrim.set,
|
|
" auto result = ::capnp::AnyPointer::Builder(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS));\n"
|
|
" result.clear();\n"
|
|
" return result;\n"
|
|
"}\n"
|
|
"\n")
|
|
};
|
|
|
|
} else if (kind == FieldKind::STRUCT) {
|
|
|
|
return FieldText {
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.readerIsDecl),
|
|
spaces(indent), " public boolean has", titleCase, "() {\n",
|
|
spaces(indent), " return !_reader.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public ", type, ".Reader get", titleCase, "() {\n",
|
|
spaces(indent), " return ", type,
|
|
".factory.fromStructReader(_reader.getPointerField(", offset,").getStruct());\n",
|
|
spaces(indent), " }\n", "\n"),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.builderIsDecl),
|
|
spaces(indent), " public final ", type, ".Builder get", titleCase, "() {\n",
|
|
spaces(indent), " return ", type,
|
|
".factory.fromStructBuilder(_builder.getPointerField(", offset, ").getStruct(",
|
|
type, ".STRUCT_SIZE", "));\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final void set", titleCase, "(", type, ".Reader value) {\n",
|
|
unionDiscrim.set,
|
|
spaces(indent), " throw new Error();\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final ", type, ".Builder init", titleCase, "() {\n",
|
|
unionDiscrim.set,
|
|
spaces(indent), " return ",
|
|
type, ".factory.fromStructBuilder(_builder.getPointerField(", offset, ").initStruct(",
|
|
type, ".STRUCT_SIZE", "));\n",
|
|
spaces(indent), " }\n"),
|
|
|
|
kj::strTree(),
|
|
kj::strTree()
|
|
};
|
|
|
|
} else if (kind == FieldKind::BLOB && typeBody.which() == schema::Type::TEXT ) {
|
|
|
|
kj::String blobKind = kj::str("Text");
|
|
|
|
return FieldText {
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.readerIsDecl),
|
|
spaces(indent), " public boolean has", titleCase, "() {\n",
|
|
unionDiscrim.has,
|
|
spaces(indent), " return !_reader.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public ", type, ".Reader",
|
|
" get", titleCase, "() {\n",
|
|
spaces(indent), " return _reader.getPointerField(",
|
|
offset, ").get", blobKind, " ();\n", // XXX
|
|
spaces(indent), " }\n", "\n"),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.builderIsDecl),
|
|
spaces(indent), " public final boolean has", titleCase, "() {\n",
|
|
unionDiscrim.has,
|
|
spaces(indent), " return !_builder.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final ", type, ".Builder get", titleCase, "() {\n",
|
|
spaces(indent), " return _builder.getPointerField(",
|
|
offset, ").get", blobKind, " ();\n", // XXX
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final void set", titleCase, "(", type, ".Reader value) {\n",
|
|
unionDiscrim.set,
|
|
spaces(indent), " _builder.getPointerField(", offset, ").set", blobKind, "(value);\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final void set", titleCase, "(String value) {\n",
|
|
unionDiscrim.set,
|
|
spaces(indent), " _builder.getPointerField(", offset, ").set", blobKind, "( new",
|
|
type, ".Reader(value));\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public final ", type, ".Builder init", titleCase, "(int size) {\n",
|
|
spaces(indent), " throw new Error();\n",
|
|
spaces(indent), " }\n"),
|
|
|
|
kj::strTree(),
|
|
kj::strTree()
|
|
};
|
|
} else if (kind == FieldKind::BLOB && typeBody.which() == schema::Type::DATA ) {
|
|
|
|
kj::String blobKind = kj::str("Data");
|
|
|
|
return FieldText {
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.readerIsDecl),
|
|
spaces(indent), " public boolean has", titleCase, "() {\n",
|
|
unionDiscrim.has,
|
|
spaces(indent), " return !_reader.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public ", type, ".Reader",
|
|
" get", titleCase, "() {\n",
|
|
spaces(indent), " return _reader.getPointerField(",
|
|
offset, ").get", blobKind, " ();\n", // XXX
|
|
spaces(indent), " }\n", "\n"),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.builderIsDecl),
|
|
spaces(indent), " public final boolean has", titleCase, "() {\n",
|
|
unionDiscrim.has,
|
|
spaces(indent), " return !_builder.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final ", type, ".Builder get", titleCase, "() {\n",
|
|
spaces(indent), " return _builder.getPointerField(",
|
|
offset, ").get", blobKind, " ();\n", // XXX
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final void set", titleCase, "(", type, ".Reader value) {\n",
|
|
unionDiscrim.set,
|
|
spaces(indent), " _builder.getPointerField(", offset, ").set", blobKind, "(value);\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final ", type, ".Builder init", titleCase, "(int size) {\n",
|
|
spaces(indent), " throw new Error();\n",
|
|
spaces(indent), " }\n"),
|
|
|
|
kj::strTree(),
|
|
kj::strTree()
|
|
};
|
|
|
|
} else if (kind == FieldKind::LIST) {
|
|
|
|
uint64_t typeId = field.getContainingStruct().getProto().getId();
|
|
kj::String defaultParam = defaultOffset == 0 ? kj::str() : kj::str(
|
|
",\n ::capnp::schemas::s_", kj::hex(typeId), ".encodedNode + ", defaultOffset,
|
|
defaultSize == 0 ? kj::strTree() : kj::strTree(", ", defaultSize));
|
|
|
|
kj::String elementReaderType;
|
|
kj::String elementBuilderType;
|
|
kj::String builderFactoryArg = kj::str("");
|
|
kj::String readerFactoryArg = kj::str("");
|
|
kj::String fieldSize;
|
|
kj::String readerClass = kj::str("Reader");
|
|
kj::String builderClass = kj::str("Builder");
|
|
bool isStructOrCapList = false;
|
|
bool isStructList = false;
|
|
if (kind == FieldKind::LIST) {
|
|
bool primitiveElement = false;
|
|
bool interface = false;
|
|
switch (typeBody.getList().getElementType().which()) {
|
|
case schema::Type::VOID:
|
|
primitiveElement = true;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.VOID");
|
|
break;
|
|
case schema::Type::BOOL:
|
|
primitiveElement = true;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.BIT");
|
|
break;
|
|
|
|
case schema::Type::INT8:
|
|
case schema::Type::UINT8:
|
|
primitiveElement = true;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.BYTE");
|
|
break;
|
|
|
|
|
|
case schema::Type::INT16:
|
|
case schema::Type::UINT16:
|
|
primitiveElement = true;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.TWO_BYTES");
|
|
break;
|
|
|
|
case schema::Type::INT32:
|
|
case schema::Type::UINT32:
|
|
case schema::Type::FLOAT32:
|
|
primitiveElement = true;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.FOUR_BYTES");
|
|
break;
|
|
|
|
case schema::Type::INT64:
|
|
case schema::Type::UINT64:
|
|
case schema::Type::FLOAT64:
|
|
primitiveElement = true;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.EIGHT_BYTES");
|
|
break;
|
|
|
|
case schema::Type::ENUM:
|
|
primitiveElement = true;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.TWO_BYTES");
|
|
break;
|
|
|
|
case schema::Type::TEXT:
|
|
primitiveElement = false;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.POINTER");
|
|
break;
|
|
case schema::Type::DATA:
|
|
primitiveElement = false;
|
|
fieldSize = kj::str("org.capnproto.FieldSize.POINTER");
|
|
break;
|
|
case schema::Type::LIST:
|
|
case schema::Type::ANY_POINTER:
|
|
primitiveElement = false;
|
|
break;
|
|
|
|
case schema::Type::INTERFACE:
|
|
isStructOrCapList = true;
|
|
primitiveElement = false;
|
|
interface = true;
|
|
break;
|
|
|
|
case schema::Type::STRUCT:
|
|
isStructList = true;
|
|
isStructOrCapList = true;
|
|
primitiveElement = false;
|
|
elementReaderType = kj::str(typeName(typeBody.getList().getElementType()), ".Reader");
|
|
readerClass = kj::str("Reader<", elementReaderType, ">");
|
|
elementBuilderType = kj::str(typeName(typeBody.getList().getElementType()), ".Builder");
|
|
builderClass = kj::str("Builder<", elementBuilderType, ">");
|
|
readerFactoryArg = kj::str(typeName(typeBody.getList().getElementType()), ".factory, ");
|
|
builderFactoryArg = kj::str(typeName(typeBody.getList().getElementType()), ".factory, ");
|
|
fieldSize = kj::str(typeName(typeBody.getList().getElementType()),".STRUCT_SIZE.preferredListEncoding");
|
|
break;
|
|
}
|
|
if (primitiveElement) {
|
|
elementReaderType = kj::str(typeName(typeBody.getList().getElementType()));
|
|
elementBuilderType = kj::str(typeName(typeBody.getList().getElementType()));
|
|
}
|
|
}
|
|
|
|
|
|
return FieldText {
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.readerIsDecl),
|
|
spaces(indent), " public final boolean has", titleCase, "() {\n",
|
|
spaces(indent), " return !_reader.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public final ", type, ".", readerClass,
|
|
" get", titleCase, "() {\n",
|
|
spaces(indent), " return new ", type, ".", readerClass, "(\n",
|
|
spaces(indent), " ", readerFactoryArg, "_reader.getPointerField(", offset, ").getList(",
|
|
fieldSize, ")",
|
|
");\n",
|
|
spaces(indent), " }\n",
|
|
"\n"),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.builderIsDecl),
|
|
spaces(indent), " public final boolean has", titleCase, "() {\n",
|
|
spaces(indent), " return !_builder.getPointerField(", offset, ").isNull();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public final ", type, ".", builderClass,
|
|
" get", titleCase, "() {\n",
|
|
spaces(indent), " return new ", type, ".", builderClass, " (\n",
|
|
spaces(indent), " ", builderFactoryArg, "_builder.getPointerField(", offset, ").get",
|
|
(isStructList ?
|
|
kj::strTree("StructList(", typeName(typeBody.getList().getElementType()),".STRUCT_SIZE)") :
|
|
kj::strTree("List(", fieldSize, ")")),
|
|
");\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public final void set", titleCase, "(", type, ".Reader value) {\n",
|
|
spaces(indent), " throw new Error();\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " public final ", type, ".", builderClass,
|
|
" init", titleCase, "(int size) {\n",
|
|
spaces(indent), " return new ", type, ".", builderClass, "(\n",
|
|
spaces(indent), " ", builderFactoryArg, "_builder.getPointerField(", offset, ").init",
|
|
(isStructList ?
|
|
kj::strTree("StructList(size,", typeName(typeBody.getList().getElementType()),".STRUCT_SIZE)") :
|
|
kj::strTree("List(", fieldSize, ", size)")),
|
|
");\n",
|
|
spaces(indent), " }\n"),
|
|
|
|
kj::strTree(
|
|
kind == FieldKind::STRUCT && !hasDiscriminantValue(proto)
|
|
? kj::strTree(
|
|
" inline ", type, "::Pipeline get", titleCase, "();\n")
|
|
: kj::strTree()),
|
|
|
|
kj::strTree(
|
|
kj::mv(unionDiscrim.isDefs),
|
|
"inline ", type, "::Reader ", scope, "Reader::get", titleCase, "() const {\n",
|
|
unionDiscrim.check,
|
|
" return ::capnp::_::PointerHelpers<", type, ">::get(\n"
|
|
" _reader.getPointerField(", offset, " * ::capnp::POINTERS)", defaultParam, ");\n"
|
|
"}\n"
|
|
"inline ", type, "::Builder ", scope, "Builder::get", titleCase, "() {\n",
|
|
unionDiscrim.check,
|
|
" return ::capnp::_::PointerHelpers<", type, ">::get(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS)", defaultParam, ");\n"
|
|
"}\n",
|
|
kind == FieldKind::STRUCT && !hasDiscriminantValue(proto)
|
|
? kj::strTree(
|
|
"inline ", type, "::Pipeline ", scope, "Pipeline::get", titleCase, "() {\n",
|
|
" return ", type, "::Pipeline(_typeless.getPointerField(", offset, "));\n"
|
|
"}\n")
|
|
: kj::strTree(),
|
|
"inline void ", scope, "Builder::set", titleCase, "(", type, "::Reader value) {\n",
|
|
unionDiscrim.set,
|
|
" ::capnp::_::PointerHelpers<", type, ">::set(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS), value);\n"
|
|
"}\n",
|
|
kind == FieldKind::LIST && !isStructOrCapList
|
|
? kj::strTree(
|
|
"inline void ", scope, "Builder::set", titleCase, "(::kj::ArrayPtr<const ", elementReaderType, "> value) {\n",
|
|
unionDiscrim.set,
|
|
" ::capnp::_::PointerHelpers<", type, ">::set(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS), value);\n"
|
|
"}\n")
|
|
: kj::strTree(),
|
|
kind == FieldKind::STRUCT
|
|
? kj::strTree(
|
|
"inline ", type, "::Builder ", scope, "Builder::init", titleCase, "() {\n",
|
|
unionDiscrim.set,
|
|
" return ::capnp::_::PointerHelpers<", type, ">::init(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS));\n"
|
|
"}\n")
|
|
: kj::strTree(
|
|
"inline ", type, "::Builder ", scope, "Builder::init", titleCase, "(unsigned int size) {\n",
|
|
unionDiscrim.set,
|
|
" return ::capnp::_::PointerHelpers<", type, ">::init(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS), size);\n"
|
|
"}\n"),
|
|
"inline void ", scope, "Builder::adopt", titleCase, "(\n"
|
|
" ::capnp::Orphan<", type, ">&& value) {\n",
|
|
unionDiscrim.set,
|
|
" ::capnp::_::PointerHelpers<", type, ">::adopt(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS), kj::mv(value));\n"
|
|
"}\n"
|
|
"inline ::capnp::Orphan<", type, "> ", scope, "Builder::disown", titleCase, "() {\n",
|
|
unionDiscrim.check,
|
|
" return ::capnp::_::PointerHelpers<", type, ">::disown(\n"
|
|
" _builder.getPointerField(", offset, " * ::capnp::POINTERS));\n"
|
|
"}\n"
|
|
"\n")
|
|
};
|
|
} else {
|
|
KJ_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
struct StructText {
|
|
kj::StringTree outerTypeDef;
|
|
kj::StringTree readerBuilderDefs;
|
|
kj::StringTree inlineMethodDefs;
|
|
};
|
|
|
|
kj::StringTree makeWhich(StructSchema schema, kj::String member, int indent) {
|
|
if (schema.getProto().getStruct().getDiscriminantCount() == 0) {
|
|
return kj::strTree();
|
|
} else {
|
|
auto fields = schema.getUnionFields();
|
|
return kj::strTree(
|
|
spaces(indent), "public Which which() {\n",
|
|
spaces(indent+1), "switch(", member, ".getShortField(",
|
|
schema.getProto().getStruct().getDiscriminantOffset(), ")) {\n",
|
|
KJ_MAP(f, fields) {
|
|
return kj::strTree(spaces(indent+2), "case ", f.getProto().getDiscriminantValue(), " : return ",
|
|
"Which.",
|
|
toUpperCase(f.getProto().getName()), ";\n");
|
|
},
|
|
spaces(indent+2), "default: return Which._UNKNOWN;\n",
|
|
spaces(indent+1), "}\n",
|
|
spaces(indent), "}\n"
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
kj::StringTree makeReaderDef(kj::StringPtr fullName, kj::StringPtr unqualifiedParentType,
|
|
StructSchema schema, kj::Array<kj::StringTree>&& methodDecls,
|
|
int indent) {
|
|
return kj::strTree(
|
|
spaces(indent), "public static final class Reader {\n",
|
|
spaces(indent), " public Reader(org.capnproto.StructReader base){ this._reader = base; }\n",
|
|
"\n",
|
|
makeWhich(schema, kj::str("_reader"), indent+1),
|
|
kj::mv(methodDecls),
|
|
spaces(indent), " public org.capnproto.StructReader _reader;\n",
|
|
spaces(indent), "}\n"
|
|
"\n");
|
|
}
|
|
|
|
kj::StringTree makeBuilderDef(kj::StringPtr fullName, kj::StringPtr unqualifiedParentType,
|
|
StructSchema schema, kj::Array<kj::StringTree>&& methodDecls,
|
|
int indent) {
|
|
return kj::strTree(
|
|
spaces(indent), "public static final class Builder {\n",
|
|
spaces(indent), " public Builder(org.capnproto.StructBuilder base){ this._builder = base; }\n",
|
|
spaces(indent), " public org.capnproto.StructBuilder _builder;\n",
|
|
makeWhich(schema, kj::str("_builder"), indent+1),
|
|
spaces(indent), " public final Reader asReader() {\n",
|
|
spaces(indent), " return new Reader(this._builder.asReader());\n",
|
|
spaces(indent), " }\n",
|
|
kj::mv(methodDecls),
|
|
spaces(indent), "}\n",
|
|
"\n");
|
|
}
|
|
|
|
StructText makeStructText(kj::StringPtr scope, kj::StringPtr name, StructSchema schema,
|
|
kj::Array<kj::StringTree> nestedTypeDecls, int indent) {
|
|
auto proto = schema.getProto();
|
|
auto fullName = kj::str(scope, name);
|
|
auto subScope = kj::str(fullName, ".");
|
|
auto fieldTexts = KJ_MAP(f, schema.getFields()) { return makeFieldText(subScope, f, indent + 1); };
|
|
|
|
auto structNode = proto.getStruct();
|
|
uint discrimOffset = structNode.getDiscriminantOffset();
|
|
structNode.getPointerCount();
|
|
|
|
return StructText {
|
|
kj::strTree(
|
|
spaces(indent), "public static class ", name, " {\n",
|
|
kj::strTree(
|
|
spaces(indent), " public static final org.capnproto.StructSize STRUCT_SIZE =\n",
|
|
spaces(indent), " new org.capnproto.StructSize((short)", structNode.getDataWordCount(),
|
|
",(short)", structNode.getPointerCount(),
|
|
", org.capnproto.FieldSize.", FIELD_SIZE_NAMES[(int)structNode.getPreferredListEncoding()], ");\n"),
|
|
|
|
spaces(indent), " public static class Factory implements org.capnproto.StructFactory<Builder, Reader> {\n",
|
|
spaces(indent),
|
|
" public final Reader fromStructReader(org.capnproto.StructReader reader) {\n",
|
|
spaces(indent), " return new Reader(reader);\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final Builder fromStructBuilder(org.capnproto.StructBuilder builder) {\n",
|
|
spaces(indent), " return new Builder(builder);\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final org.capnproto.StructSize structSize() {\n",
|
|
spaces(indent), " return ", fullName, ".STRUCT_SIZE;\n",
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public final Reader asReader(Builder builder) {\n",
|
|
spaces(indent), " return new Reader(builder._builder.asReader());\n",
|
|
spaces(indent), " }\n",
|
|
|
|
spaces(indent), " }\n",
|
|
spaces(indent), " public static final Factory factory = new Factory();\n",
|
|
|
|
|
|
kj::strTree(makeReaderDef(fullName, name, schema,
|
|
KJ_MAP(f, fieldTexts) { return kj::mv(f.readerMethodDecls); },
|
|
indent + 1),
|
|
makeBuilderDef(fullName, name, schema,
|
|
KJ_MAP(f, fieldTexts) { return kj::mv(f.builderMethodDecls); },
|
|
indent + 1)),
|
|
|
|
structNode.getDiscriminantCount() == 0 ?
|
|
kj::strTree() :
|
|
kj::strTree(
|
|
spaces(indent), " public enum Which {\n",
|
|
KJ_MAP(f, structNode.getFields()) {
|
|
if (hasDiscriminantValue(f)) {
|
|
return kj::strTree(spaces(indent), " ", toUpperCase(f.getName()), ",\n");
|
|
} else {
|
|
return kj::strTree();
|
|
}
|
|
},
|
|
spaces(indent), " _UNKNOWN,\n",
|
|
spaces(indent), " }\n"),
|
|
KJ_MAP(n, nestedTypeDecls) { return kj::mv(n); },
|
|
spaces(indent), "}\n"
|
|
"\n",
|
|
"\n"),
|
|
|
|
kj::strTree(),
|
|
kj::strTree()
|
|
};
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
struct ConstText {
|
|
bool needsSchema;
|
|
kj::StringTree decl;
|
|
};
|
|
|
|
ConstText makeConstText(kj::StringPtr scope, kj::StringPtr name, ConstSchema schema) {
|
|
auto proto = schema.getProto();
|
|
auto constProto = proto.getConst();
|
|
auto type = constProto.getType();
|
|
auto typeName_ = typeName(type).flatten();
|
|
auto upperCase = toUpperCase(name);
|
|
|
|
// Linkage qualifier for non-primitive types.
|
|
const char* linkage = scope.size() == 0 ? "extern " : "static ";
|
|
|
|
switch (type.which()) {
|
|
case schema::Value::VOID:
|
|
case schema::Value::BOOL:
|
|
case schema::Value::INT8:
|
|
case schema::Value::INT16:
|
|
case schema::Value::INT32:
|
|
case schema::Value::INT64:
|
|
case schema::Value::UINT8:
|
|
case schema::Value::UINT16:
|
|
case schema::Value::UINT32:
|
|
case schema::Value::UINT64:
|
|
case schema::Value::FLOAT32:
|
|
case schema::Value::FLOAT64:
|
|
case schema::Value::ENUM:
|
|
return ConstText {
|
|
false,
|
|
kj::strTree("public static final ", typeName_, ' ', upperCase, " = ",
|
|
literalValue(constProto.getType(), constProto.getValue()), ";\n")
|
|
};
|
|
|
|
case schema::Value::TEXT: {
|
|
return ConstText {
|
|
true,
|
|
kj::strTree("public static final org.capnproto.Text.Reader ", upperCase,
|
|
" = new org.capnproto.Text.Reader(Schemas.b_",
|
|
kj::hex(proto.getId()), ", ", schema.getValueSchemaOffset(),
|
|
", ", constProto.getValue().getText().size(), ");\n")
|
|
};
|
|
}
|
|
|
|
case schema::Value::DATA: {
|
|
kj::String constType = kj::strTree(
|
|
"::capnp::_::ConstData<", schema.as<Data>().size(), ">").flatten();
|
|
return ConstText {
|
|
true,
|
|
kj::strTree("const ", constType, ' ', scope, upperCase, "(::capnp::schemas::b_",
|
|
kj::hex(proto.getId()), ".words + ", schema.getValueSchemaOffset(), ");\n")
|
|
};
|
|
}
|
|
|
|
case schema::Value::STRUCT: {
|
|
kj::String constType = kj::strTree(
|
|
"::capnp::_::ConstStruct<", typeName_, ">").flatten();
|
|
return ConstText {
|
|
true,
|
|
kj::strTree("const ", constType, ' ', scope, upperCase, "(::capnp::schemas::b_",
|
|
kj::hex(proto.getId()), ".words + ", schema.getValueSchemaOffset(), ");\n")
|
|
};
|
|
}
|
|
|
|
case schema::Value::LIST: {
|
|
kj::String constType = kj::strTree(
|
|
"::capnp::_::ConstList<", typeName(type.getList().getElementType()), ">").flatten();
|
|
return ConstText {
|
|
true,
|
|
kj::strTree("const ", constType, ' ', scope, upperCase, "(::capnp::schemas::b_",
|
|
kj::hex(proto.getId()), ".words + ", schema.getValueSchemaOffset(), ");\n")
|
|
};
|
|
}
|
|
|
|
case schema::Value::ANY_POINTER:
|
|
case schema::Value::INTERFACE:
|
|
return ConstText { false, kj::strTree() };
|
|
}
|
|
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
struct NodeText {
|
|
kj::StringTree outerTypeDef;
|
|
kj::StringTree readerBuilderDefs;
|
|
kj::StringTree inlineMethodDefs;
|
|
kj::StringTree capnpSchemaDefs;
|
|
kj::StringTree capnpPrivateDecls;
|
|
kj::StringTree capnpPrivateDefs;
|
|
kj::StringTree sourceFileDefs;
|
|
};
|
|
|
|
struct NodeTextNoSchema {
|
|
kj::StringTree outerTypeDef;
|
|
kj::StringTree readerBuilderDefs;
|
|
kj::StringTree inlineMethodDefs;
|
|
kj::StringTree capnpPrivateDecls;
|
|
kj::StringTree capnpPrivateDefs;
|
|
kj::StringTree sourceFileDefs;
|
|
};
|
|
|
|
NodeText makeNodeText(kj::StringPtr scope,
|
|
kj::StringPtr name, Schema schema,
|
|
int indent) {
|
|
auto proto = schema.getProto();
|
|
auto fullName = kj::str(scope, name);
|
|
auto subScope = kj::str(fullName, ".");
|
|
auto hexId = kj::hex(proto.getId());
|
|
|
|
// Compute nested nodes, including groups.
|
|
kj::Vector<NodeText> nestedTexts(proto.getNestedNodes().size());
|
|
for (auto nested: proto.getNestedNodes()) {
|
|
nestedTexts.add(makeNodeText(
|
|
subScope, nested.getName(), schemaLoader.get(nested.getId()), indent + 1));
|
|
};
|
|
|
|
if (proto.isStruct()) {
|
|
for (auto field: proto.getStruct().getFields()) {
|
|
if (field.isGroup()) {
|
|
nestedTexts.add(makeNodeText(subScope, toTitleCase(field.getName()),
|
|
schemaLoader.get(field.getGroup().getTypeId()), indent + 1));
|
|
}
|
|
}
|
|
} else if (proto.isInterface()) {
|
|
KJ_FAIL_REQUIRE("interfaces not implemented");
|
|
}
|
|
|
|
// Convert the encoded schema to a literal byte array.
|
|
kj::ArrayPtr<const word> rawSchema = schema.asUncheckedMessage();
|
|
auto schemaLiteral = kj::StringTree(KJ_MAP(w, rawSchema) {
|
|
const byte* bytes = reinterpret_cast<const byte*>(&w);
|
|
|
|
return kj::strTree(
|
|
"\"",
|
|
KJ_MAP(i, kj::range<uint>(0, sizeof(word))) {
|
|
switch(bytes[i]) {
|
|
case 0x0a:
|
|
return kj::strTree("\\n");
|
|
case 0x0d:
|
|
return kj::strTree("\\r");
|
|
case 0x22:
|
|
return kj::strTree("\\\"");
|
|
case 0x5c:
|
|
return kj::strTree("\\\\");
|
|
default:
|
|
auto text = kj::hex(bytes[i]);
|
|
return kj::strTree("\\u", kj::repeat('0', 4 - text.size()), text);
|
|
}
|
|
},
|
|
"\" +");
|
|
}, "\n ");
|
|
|
|
std::set<uint64_t> deps;
|
|
enumerateDeps(proto, deps);
|
|
|
|
kj::Array<uint> membersByName;
|
|
kj::Array<uint> membersByDiscrim;
|
|
switch (proto.which()) {
|
|
case schema::Node::STRUCT: {
|
|
auto structSchema = schema.asStruct();
|
|
membersByName = makeMembersByName(structSchema.getFields());
|
|
auto builder = kj::heapArrayBuilder<uint>(structSchema.getFields().size());
|
|
for (auto field: structSchema.getUnionFields()) {
|
|
builder.add(field.getIndex());
|
|
}
|
|
for (auto field: structSchema.getNonUnionFields()) {
|
|
builder.add(field.getIndex());
|
|
}
|
|
membersByDiscrim = builder.finish();
|
|
break;
|
|
}
|
|
case schema::Node::ENUM:
|
|
membersByName = makeMembersByName(schema.asEnum().getEnumerants());
|
|
break;
|
|
case schema::Node::INTERFACE:
|
|
membersByName = makeMembersByName(schema.asInterface().getMethods());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Java limits method code size to 64KB. Maybe we should use class.getResource()?
|
|
auto schemaDef = kj::strTree(
|
|
"public static final java.nio.ByteBuffer b_", hexId, " =\n",
|
|
" org.capnproto.GeneratedClassSupport.decodeRawBytes(\n",
|
|
" ", kj::mv(schemaLiteral), " \"\"",
|
|
");\n");
|
|
/*
|
|
deps.size() == 0 ? kj::strTree() : kj::strTree(
|
|
"static const ::capnp::_::RawSchema* const d_", hexId, "[] = {\n",
|
|
KJ_MAP(depId, deps) {
|
|
return kj::strTree(" &s_", kj::hex(depId), ",\n");
|
|
},
|
|
"};\n"),
|
|
membersByName.size() == 0 ? kj::strTree() : kj::strTree(
|
|
"static const uint16_t m_", hexId, "[] = {",
|
|
kj::StringTree(KJ_MAP(index, membersByName) { return kj::strTree(index); }, ", "),
|
|
"};\n"),
|
|
membersByDiscrim.size() == 0 ? kj::strTree() : kj::strTree(
|
|
"static const uint16_t i_", hexId, "[] = {",
|
|
kj::StringTree(KJ_MAP(index, membersByDiscrim) { return kj::strTree(index); }, ", "),
|
|
"};\n"),
|
|
"const ::capnp::_::RawSchema s_", hexId, " = {\n"
|
|
" 0x", hexId, ", b_", hexId, ".words, ", rawSchema.size(), ", ",
|
|
deps.size() == 0 ? kj::strTree("nullptr") : kj::strTree("d_", hexId), ", ",
|
|
membersByName.size() == 0 ? kj::strTree("nullptr") : kj::strTree("m_", hexId), ",\n",
|
|
" ", deps.size(), ", ", membersByName.size(), ", ",
|
|
membersByDiscrim.size() == 0 ? kj::strTree("nullptr") : kj::strTree("i_", hexId),
|
|
", nullptr, nullptr\n"
|
|
"};\n");*/
|
|
|
|
NodeTextNoSchema top = makeNodeTextWithoutNested(
|
|
scope, name, schema,
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.outerTypeDef); }, indent);
|
|
|
|
return NodeText {
|
|
kj::strTree(
|
|
kj::mv(top.outerTypeDef),
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.outerTypeDef); }),
|
|
|
|
kj::strTree(
|
|
kj::mv(top.readerBuilderDefs),
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.readerBuilderDefs); }),
|
|
|
|
kj::strTree(
|
|
kj::mv(top.inlineMethodDefs),
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.inlineMethodDefs); }),
|
|
|
|
kj::strTree(
|
|
kj::mv(schemaDef),
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.capnpSchemaDefs); }),
|
|
|
|
kj::strTree(
|
|
kj::mv(top.capnpPrivateDecls),
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.capnpPrivateDecls); }),
|
|
|
|
kj::strTree(
|
|
kj::mv(top.capnpPrivateDefs),
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.capnpPrivateDefs); }),
|
|
|
|
kj::strTree(
|
|
kj::mv(top.sourceFileDefs),
|
|
KJ_MAP(n, nestedTexts) { return kj::mv(n.sourceFileDefs); }),
|
|
};
|
|
}
|
|
|
|
NodeTextNoSchema makeNodeTextWithoutNested(kj::StringPtr scope,
|
|
kj::StringPtr name, Schema schema,
|
|
kj::Array<kj::StringTree> nestedTypeDecls,
|
|
int indent) {
|
|
auto proto = schema.getProto();
|
|
auto fullName = kj::str(scope, name);
|
|
auto hexId = kj::hex(proto.getId());
|
|
|
|
switch (proto.which()) {
|
|
case schema::Node::FILE:
|
|
KJ_FAIL_REQUIRE("This method shouldn't be called on file nodes.");
|
|
|
|
case schema::Node::STRUCT: {
|
|
StructText structText =
|
|
makeStructText(scope, name, schema.asStruct(), kj::mv(nestedTypeDecls), indent);
|
|
auto structNode = proto.getStruct();
|
|
|
|
return NodeTextNoSchema {
|
|
kj::mv(structText.outerTypeDef),
|
|
kj::mv(structText.readerBuilderDefs),
|
|
kj::mv(structText.inlineMethodDefs),
|
|
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
|
|
kj::strTree(),
|
|
};
|
|
}
|
|
|
|
case schema::Node::ENUM: {
|
|
auto enumerants = schema.asEnum().getEnumerants();
|
|
|
|
return NodeTextNoSchema {
|
|
kj::strTree(
|
|
spaces(indent), "public enum ", name, " {\n",
|
|
KJ_MAP(e, enumerants) {
|
|
return kj::strTree(spaces(indent), " ", toUpperCase(e.getProto().getName()), ",\n");
|
|
},
|
|
spaces(indent), " _UNKNOWN,\n",
|
|
spaces(indent), "}\n"
|
|
"\n"),
|
|
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
|
|
kj::strTree(),
|
|
};
|
|
}
|
|
|
|
case schema::Node::INTERFACE: {
|
|
hasInterfaces = true;
|
|
KJ_FAIL_REQUIRE("unimplemented");
|
|
}
|
|
|
|
case schema::Node::CONST: {
|
|
auto constText = makeConstText(scope, name, schema.asConst());
|
|
|
|
return NodeTextNoSchema {
|
|
kj::strTree(" ", kj::mv(constText.decl)),
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
};
|
|
}
|
|
|
|
case schema::Node::ANNOTATION: {
|
|
return NodeTextNoSchema {
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
kj::strTree(),
|
|
};
|
|
}
|
|
}
|
|
|
|
KJ_UNREACHABLE;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
struct FileText {
|
|
kj::String outerClassname;
|
|
kj::StringTree source;
|
|
};
|
|
|
|
FileText makeFileText(Schema schema,
|
|
schema::CodeGeneratorRequest::RequestedFile::Reader request) {
|
|
usedImports.clear();
|
|
|
|
auto node = schema.getProto();
|
|
auto displayName = node.getDisplayName();
|
|
|
|
kj::StringPtr packageName;
|
|
kj::StringPtr outerClassname;
|
|
|
|
for (auto annotation: node.getAnnotations()) {
|
|
if (annotation.getId() == PACKAGE_ANNOTATION_ID) {
|
|
packageName = annotation.getValue().getText();
|
|
} else if (annotation.getId() == OUTER_CLASSNAME_ANNOTATION_ID) {
|
|
outerClassname = annotation.getValue().getText();
|
|
}
|
|
}
|
|
|
|
if (packageName.size() == 0) {
|
|
context.exitError(kj::str(displayName, ": must provide a Java package name."));
|
|
}
|
|
if (outerClassname.size() == 0) {
|
|
context.exitError(kj::str(displayName, ": must provide a Java outer classname."));
|
|
}
|
|
|
|
auto nodeTexts = KJ_MAP(nested, node.getNestedNodes()) {
|
|
return makeNodeText("", nested.getName(), schemaLoader.get(nested.getId()), 1);
|
|
};
|
|
|
|
kj::String separator = kj::str("// ", kj::repeat('=', 87), "\n");
|
|
|
|
kj::Vector<kj::StringPtr> includes;
|
|
for (auto import: request.getImports()) {
|
|
if (usedImports.count(import.getId()) > 0) {
|
|
includes.add(import.getName());
|
|
}
|
|
}
|
|
|
|
kj::StringTree sourceDefs = kj::strTree(
|
|
KJ_MAP(n, nodeTexts) { return kj::mv(n.sourceFileDefs); });
|
|
|
|
return FileText {
|
|
kj::str(outerClassname),
|
|
kj::strTree(
|
|
"// Generated by Cap'n Proto compiler, DO NOT EDIT\n"
|
|
"// source: ", baseName(displayName), "\n\n",
|
|
"package ", packageName, ";\n\n",
|
|
//"import org.capnproto;\n",
|
|
"public final class ", outerClassname, " {\n",
|
|
KJ_MAP(n, nodeTexts) { return kj::mv(n.outerTypeDef); },
|
|
"\n",
|
|
"public static final class Schemas {\n",
|
|
KJ_MAP(n, nodeTexts) { return kj::mv(n.capnpSchemaDefs); },
|
|
"}\n",
|
|
"}\n",
|
|
"\n")
|
|
};
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
|
|
void makeDirectory(kj::StringPtr path) {
|
|
KJ_IF_MAYBE(slashpos, path.findLast('/')) {
|
|
// Make the parent dir.
|
|
makeDirectory(kj::str(path.slice(0, *slashpos)));
|
|
}
|
|
|
|
if (mkdir(path.cStr(), 0777) < 0) {
|
|
int error = errno;
|
|
if (error != EEXIST) {
|
|
KJ_FAIL_SYSCALL("mkdir(path)", error, path);
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeFile(kj::StringPtr filename, const kj::StringTree& text) {
|
|
if (!filename.startsWith("/")) {
|
|
KJ_IF_MAYBE(slashpos, filename.findLast('/')) {
|
|
// Make the parent dir.
|
|
makeDirectory(kj::str(filename.slice(0, *slashpos)));
|
|
}
|
|
}
|
|
|
|
int fd;
|
|
KJ_SYSCALL(fd = open(filename.cStr(), O_CREAT | O_WRONLY | O_TRUNC, 0666), filename);
|
|
kj::FdOutputStream out((kj::AutoCloseFd(fd)));
|
|
|
|
text.visit(
|
|
[&](kj::ArrayPtr<const char> text) {
|
|
out.write(text.begin(), text.size());
|
|
});
|
|
}
|
|
|
|
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()) {
|
|
auto schema = schemaLoader.get(requestedFile.getId());
|
|
|
|
auto filename = requestedFile.getFilename();
|
|
size_t stemstart = 0;
|
|
size_t stemend = filename.size();
|
|
KJ_IF_MAYBE(slashpos, filename.findLast('/')) {
|
|
stemstart = *slashpos + 1;
|
|
}
|
|
KJ_IF_MAYBE(dotpos, filename.findLast('.')) {
|
|
stemend = *dotpos;
|
|
}
|
|
|
|
auto fileText = makeFileText(schema, requestedFile);
|
|
|
|
writeFile(kj::str(filename.slice(0, stemstart), fileText.outerClassname, ".java"), fileText.source);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
} // namespace capnp
|
|
|
|
KJ_MAIN(capnp::CapnpcJavaMain);
|