Commit 372612cd authored by Kenton Varda's avatar Kenton Varda

Mostly done with anonymous unions.

parent 248b149a
...@@ -73,3 +73,5 @@ for file in $INPUTS; do ...@@ -73,3 +73,5 @@ for file in $INPUTS; do
diff -u $srcfile tmp/capnp/bootstrap-test-tmp/$file.$ext >&2 diff -u $srcfile tmp/capnp/bootstrap-test-tmp/$file.$ext >&2
done done
done done
echo passed
...@@ -119,7 +119,7 @@ void enumerateDeps(schema::Node::Reader node, std::set<uint64_t>& deps) { ...@@ -119,7 +119,7 @@ void enumerateDeps(schema::Node::Reader node, std::set<uint64_t>& deps) {
struct OrderByName { struct OrderByName {
template <typename T> template <typename T>
inline bool operator()(const T& a, const T& b) const { inline bool operator()(const T& a, const T& b) const {
return a.getProto().getName() < b.getProto().getName(); return a.member.getProto().getName() < b.member.getProto().getName();
} }
}; };
...@@ -135,11 +135,12 @@ void makeSubMemberInfoTable(const StructSchema::Member& member, ...@@ -135,11 +135,12 @@ void makeSubMemberInfoTable(const StructSchema::Member& member,
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
// Only create a sub-table if the union is named. // Only create a sub-table if the union is named.
if (member.getProto().getName().size() > 0) { if (member.getProto().getName().size() > 0) {
makeMemberInfoTable(1 + member.getIndex(), member.asUnion().getMembers(), info); makeMemberInfoTable(1 + member.getProto().getOrdinal(),
member.asUnion().getMembers(), info);
} }
break; break;
case schema::StructNode::Member::Body::GROUP_MEMBER: case schema::StructNode::Member::Body::GROUP_MEMBER:
makeMemberInfoTable(1 + member.getIndex(), member.asGroup().getMembers(), info); makeMemberInfoTable(1 + member.getProto().getOrdinal(), member.asGroup().getMembers(), info);
break; break;
} }
} }
...@@ -148,29 +149,40 @@ void makeSubMemberInfoTable(const EnumSchema::Enumerant& member, ...@@ -148,29 +149,40 @@ void makeSubMemberInfoTable(const EnumSchema::Enumerant& member,
void makeSubMemberInfoTable(const InterfaceSchema::Method& member, void makeSubMemberInfoTable(const InterfaceSchema::Method& member,
kj::Vector<capnp::_::RawSchema::MemberInfo>& info) {} kj::Vector<capnp::_::RawSchema::MemberInfo>& info) {}
template <typename Member>
struct MemberAndIndex {
Member member;
uint index;
MemberAndIndex(Member member): member(member), index(member.getIndex()) {}
MemberAndIndex(Member member, uint index): member(member), index(index) {}
};
void enumerateScope(const StructSchema::MemberList& members, void enumerateScope(const StructSchema::MemberList& members,
kj::Vector<StructSchema::Member>& vec) { kj::Vector<MemberAndIndex<StructSchema::Member>>& vec,
uint offset = 0) {
// Given a member list, flatten all members of the scope into one vector. This basically means // Given a member list, flatten all members of the scope into one vector. This basically means
// copying all the members to the vector, except that unnamed unions are flattened. // copying all the members to the vector, except that unnamed unions are flattened, with their
// members' indexes being offset by the size of the parent scope.
for (auto member: members) { for (auto member: members) {
vec.add(member); vec.add(member, member.getIndex() + offset);
if (member.getProto().getName().size() == 0) { if (member.getProto().getName().size() == 0) {
// Flatten unnamed union. // Flatten unnamed union.
enumerateScope(member.asUnion().getMembers(), vec); enumerateScope(member.asUnion().getMembers(), vec, offset + members.size());
} }
} }
} }
void enumerateScope(const EnumSchema::EnumerantList& members, void enumerateScope(const EnumSchema::EnumerantList& members,
kj::Vector<EnumSchema::Enumerant>& vec) { kj::Vector<MemberAndIndex<EnumSchema::Enumerant>>& vec) {
for (auto member: members) { for (auto member: members) {
vec.add(member); vec.add(member);
} }
} }
void enumerateScope(const InterfaceSchema::MethodList& members, void enumerateScope(const InterfaceSchema::MethodList& members,
kj::Vector<InterfaceSchema::Method>& vec) { kj::Vector<MemberAndIndex<InterfaceSchema::Method>>& vec) {
for (auto member: members) { for (auto member: members) {
vec.add(member); vec.add(member);
} }
...@@ -179,16 +191,15 @@ void enumerateScope(const InterfaceSchema::MethodList& members, ...@@ -179,16 +191,15 @@ void enumerateScope(const InterfaceSchema::MethodList& members,
template <typename MemberList> template <typename MemberList>
void makeMemberInfoTable(uint parent, MemberList&& members, void makeMemberInfoTable(uint parent, MemberList&& members,
kj::Vector<capnp::_::RawSchema::MemberInfo>& info) { kj::Vector<capnp::_::RawSchema::MemberInfo>& info) {
kj::Vector<kj::Decay<decltype(members[0])>> sortedMembers(members.size()); kj::Vector<MemberAndIndex<decltype(members[0])>> sorted(members.size());
enumerateScope(members, sortedMembers); enumerateScope(members, sorted);
auto sorted = KJ_MAP(members, m) { return m; };
std::sort(sorted.begin(), sorted.end(), OrderByName()); std::sort(sorted.begin(), sorted.end(), OrderByName());
for (auto& member: sorted) { for (auto& member: sorted) {
info.add(capnp::_::RawSchema::MemberInfo { info.add(capnp::_::RawSchema::MemberInfo {
kj::implicitCast<uint16_t>(parent), kj::implicitCast<uint16_t>(parent),
kj::implicitCast<uint16_t>(member.getIndex()) kj::implicitCast<uint16_t>(member.index)
}); });
} }
for (auto member: members) { for (auto member: members) {
...@@ -1063,7 +1074,7 @@ private: ...@@ -1063,7 +1074,7 @@ private:
"};\n" "};\n"
"static const ::capnp::_::RawSchema::MemberInfo m_", hexId, "[] = {\n", "static const ::capnp::_::RawSchema::MemberInfo m_", hexId, "[] = {\n",
KJ_MAP(memberInfos, info) { KJ_MAP(memberInfos, info) {
return kj::strTree(" { ", info.unionIndex, ", ", info.index, " },\n"); return kj::strTree(" { ", info.scopeOrdinal, ", ", info.index, " },\n");
}, },
"};\n" "};\n"
"const ::capnp::_::RawSchema s_", hexId, " = {\n" "const ::capnp::_::RawSchema s_", hexId, " = {\n"
......
...@@ -1435,36 +1435,36 @@ static const ::capnp::_::RawSchema::MemberInfo m_96efe787c17e83bb[] = { ...@@ -1435,36 +1435,36 @@ static const ::capnp::_::RawSchema::MemberInfo m_96efe787c17e83bb[] = {
{ 2, 2 }, { 2, 2 },
{ 2, 1 }, { 2, 1 },
{ 2, 0 }, { 2, 0 },
{ 4, 9 }, { 7, 9 },
{ 4, 15 }, { 7, 15 },
{ 4, 27 }, { 7, 27 },
{ 4, 24 }, { 7, 24 },
{ 4, 25 }, { 7, 25 },
{ 4, 17 }, { 7, 17 },
{ 4, 18 }, { 7, 18 },
{ 4, 19 }, { 7, 19 },
{ 4, 16 }, { 7, 16 },
{ 4, 28 }, { 7, 28 },
{ 4, 29 }, { 7, 29 },
{ 4, 26 }, { 7, 26 },
{ 4, 21 }, { 7, 21 },
{ 4, 22 }, { 7, 22 },
{ 4, 23 }, { 7, 23 },
{ 4, 20 }, { 7, 20 },
{ 4, 14 }, { 7, 14 },
{ 4, 1 }, { 7, 1 },
{ 4, 2 }, { 7, 2 },
{ 4, 3 }, { 7, 3 },
{ 4, 5 }, { 7, 5 },
{ 4, 13 }, { 7, 13 },
{ 4, 12 }, { 7, 12 },
{ 4, 7 }, { 7, 7 },
{ 4, 8 }, { 7, 8 },
{ 4, 11 }, { 7, 11 },
{ 4, 10 }, { 7, 10 },
{ 4, 4 }, { 7, 4 },
{ 4, 6 }, { 7, 6 },
{ 4, 0 }, { 7, 0 },
}; };
const ::capnp::_::RawSchema s_96efe787c17e83bb = { const ::capnp::_::RawSchema s_96efe787c17e83bb = {
0x96efe787c17e83bb, b_96efe787c17e83bb.words, 664, d_96efe787c17e83bb, m_96efe787c17e83bb, 0x96efe787c17e83bb, b_96efe787c17e83bb.words, 664, d_96efe787c17e83bb, m_96efe787c17e83bb,
......
...@@ -293,6 +293,14 @@ TEST(DynamicApi, UnionsRead) { ...@@ -293,6 +293,14 @@ TEST(DynamicApi, UnionsRead) {
} }
} }
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#define EXPECT_NONFATAL_FAILURE(code) code
#else
#define EXPECT_NONFATAL_FAILURE EXPECT_ANY_THROW
#endif
TEST(DynamicApi, UnionsWrite) { TEST(DynamicApi, UnionsWrite) {
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto root = builder.initRoot<DynamicStruct>(Schema::from<TestUnion>()); auto root = builder.initRoot<DynamicStruct>(Schema::from<TestUnion>());
...@@ -314,29 +322,81 @@ TEST(DynamicApi, UnionsWrite) { ...@@ -314,29 +322,81 @@ TEST(DynamicApi, UnionsWrite) {
ASSERT_EQ(TestUnion::Union3::U3F0S64, reader.getUnion3().which()); ASSERT_EQ(TestUnion::Union3::U3F0S64, reader.getUnion3().which());
EXPECT_EQ(1234567890123456789ll, reader.getUnion3().getU3f0s64()); EXPECT_EQ(1234567890123456789ll, reader.getUnion3().getU3f0s64());
// Can't access union members by name from the root.
EXPECT_ANY_THROW(root.get("u0f1s32"));
EXPECT_ANY_THROW(root.set("u0f1s32", 1234567));
// But can access them by member pointer.
auto member = root.get("union0").as<DynamicUnion>().getSchema().getMemberByName("u0f1s32");
EXPECT_EQ(1234567, root.get(member).as<int>());
auto member2 = root.get("union0").as<DynamicUnion>().getSchema().getMemberByName("u0f1sp");
root.set(member2, "foo");
EXPECT_EQ("foo", reader.getUnion0().getU0f1sp());
} }
#if KJ_NO_EXCEPTIONS TEST(DynamicApi, UnnamedUnion) {
#undef EXPECT_ANY_THROW MallocMessageBuilder builder;
// All exceptions should be non-fatal, so when exceptions are disabled the code should return. StructSchema schema = Schema::from<test::TestUnnamedUnion>();
#define EXPECT_ANY_THROW(code) code auto root = builder.initRoot<DynamicStruct>(schema);
#endif
DynamicUnion::Builder unionBuilder =
root.get(KJ_ASSERT_NONNULL(schema.getUnnamedUnion())).as<DynamicUnion>();
EXPECT_EQ(schema.getMemberByName("foo"), KJ_ASSERT_NONNULL(unionBuilder.which()));
root.set("bar", 321);
EXPECT_EQ(schema.getMemberByName("bar"), KJ_ASSERT_NONNULL(unionBuilder.which()));
EXPECT_EQ(321u, root.get("bar").as<uint>());
EXPECT_EQ(321u, root.asReader().get("bar").as<uint>());
EXPECT_EQ(321u, unionBuilder.get().as<uint>());
EXPECT_EQ(321u, unionBuilder.asReader().get().as<uint>());
EXPECT_ANY_THROW(root.get("foo"));
EXPECT_ANY_THROW(root.asReader().get("foo"));
root.set("foo", 123);
EXPECT_EQ(schema.getMemberByName("foo"), KJ_ASSERT_NONNULL(unionBuilder.which()));
EXPECT_EQ(123u, root.get("foo").as<uint>());
EXPECT_EQ(123u, root.asReader().get("foo").as<uint>());
EXPECT_EQ(123u, unionBuilder.get().as<uint>());
EXPECT_EQ(123u, unionBuilder.asReader().get().as<uint>());
EXPECT_ANY_THROW(root.get("bar"));
EXPECT_ANY_THROW(root.asReader().get("bar"));
unionBuilder.set("bar", 321);
EXPECT_EQ(schema.getMemberByName("bar"), KJ_ASSERT_NONNULL(unionBuilder.which()));
EXPECT_EQ(321u, root.get("bar").as<uint>());
EXPECT_EQ(321u, root.asReader().get("bar").as<uint>());
EXPECT_EQ(321u, unionBuilder.get().as<uint>());
EXPECT_EQ(321u, unionBuilder.asReader().get().as<uint>());
EXPECT_ANY_THROW(root.get("foo"));
EXPECT_ANY_THROW(root.asReader().get("foo"));
unionBuilder.set("foo", 123);
EXPECT_EQ(schema.getMemberByName("foo"), KJ_ASSERT_NONNULL(unionBuilder.which()));
EXPECT_EQ(123u, root.get("foo").as<uint>());
EXPECT_EQ(123u, root.asReader().get("foo").as<uint>());
EXPECT_EQ(123u, unionBuilder.get().as<uint>());
EXPECT_EQ(123u, unionBuilder.asReader().get().as<uint>());
EXPECT_ANY_THROW(root.get("bar"));
EXPECT_ANY_THROW(root.asReader().get("bar"));
}
TEST(DynamicApi, ConversionFailures) { TEST(DynamicApi, ConversionFailures) {
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>());
root.set("int8Field", 123); root.set("int8Field", 123);
EXPECT_ANY_THROW(root.set("int8Field", 1234)); EXPECT_NONFATAL_FAILURE(root.set("int8Field", 1234));
root.set("uInt32Field", 1); root.set("uInt32Field", 1);
EXPECT_ANY_THROW(root.set("uInt32Field", -1)); EXPECT_NONFATAL_FAILURE(root.set("uInt32Field", -1));
root.set("int16Field", 5); root.set("int16Field", 5);
EXPECT_ANY_THROW(root.set("int16Field", 0.5)); EXPECT_NONFATAL_FAILURE(root.set("int16Field", 0.5));
root.set("boolField", true); root.set("boolField", true);
EXPECT_ANY_THROW(root.set("boolField", 1)); EXPECT_NONFATAL_FAILURE(root.set("boolField", 1));
} }
TEST(DynamicApi, LateUnion) { TEST(DynamicApi, LateUnion) {
......
...@@ -172,7 +172,7 @@ DynamicValue::Reader DynamicUnion::Reader::get() const { ...@@ -172,7 +172,7 @@ DynamicValue::Reader DynamicUnion::Reader::get() const {
KJ_IF_MAYBE(w, which()) { KJ_IF_MAYBE(w, which()) {
return DynamicValue::Reader(DynamicStruct::Reader::getImpl(reader, *w)); return DynamicValue::Reader(DynamicStruct::Reader::getImpl(reader, *w));
} else { } else {
return nullptr; KJ_FAIL_REQUIRE("DynamicUnion get() cannot be called if which() returns nullptr.");
} }
} }
...@@ -180,7 +180,7 @@ DynamicValue::Builder DynamicUnion::Builder::get() { ...@@ -180,7 +180,7 @@ DynamicValue::Builder DynamicUnion::Builder::get() {
KJ_IF_MAYBE(w, which()) { KJ_IF_MAYBE(w, which()) {
return DynamicValue::Builder(DynamicStruct::Builder::getImpl(builder, *w)); return DynamicValue::Builder(DynamicStruct::Builder::getImpl(builder, *w));
} else { } else {
return nullptr; KJ_FAIL_REQUIRE("DynamicUnion get() cannot be called if which() returns nullptr.");
} }
} }
...@@ -284,18 +284,61 @@ void DynamicUnion::Builder::setObjectDiscriminant(StructSchema::Member member) { ...@@ -284,18 +284,61 @@ void DynamicUnion::Builder::setObjectDiscriminant(StructSchema::Member member) {
// ======================================================================================= // =======================================================================================
void DynamicStruct::Builder::verifySetInUnion(StructSchema::Member member) {
// If a union member, verify that it is set.
KJ_IF_MAYBE(containingUnion, member.getContainingUnion()) {
uint16_t discrim = builder.getDataField<uint16_t>(
containingUnion->getProto().getBody().getUnionMember().getDiscriminantOffset() * ELEMENTS);
KJ_REQUIRE(member.getIndex() == discrim,
"Tried to get() a union member which is not currently initialized.",
member.getProto().getName(), containingUnion->getProto().getName(),
member.getContainingStruct().getProto().getDisplayName());
}
}
void DynamicStruct::Builder::setInUnion(StructSchema::Member member) {
// If a union member, set the discriminant to match.
KJ_IF_MAYBE(containingUnion, member.getContainingUnion()) {
builder.setDataField<uint16_t>(
containingUnion->getProto().getBody().getUnionMember().getDiscriminantOffset() * ELEMENTS,
member.getIndex());
}
}
DynamicValue::Reader DynamicStruct::Reader::get(StructSchema::Member member) const { DynamicValue::Reader DynamicStruct::Reader::get(StructSchema::Member member) const {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
// If a union member, verify that it is set.
KJ_IF_MAYBE(containingUnion, member.getContainingUnion()) {
uint16_t discrim = reader.getDataField<uint16_t>(
containingUnion->getProto().getBody().getUnionMember().getDiscriminantOffset() * ELEMENTS);
KJ_REQUIRE(member.getIndex() == discrim,
"Tried to get() a union member which is not currently initialized.",
member.getProto().getName(), containingUnion->getProto().getName(),
member.getContainingStruct().getProto().getDisplayName());
}
return getImpl(reader, member); return getImpl(reader, member);
} }
DynamicValue::Builder DynamicStruct::Builder::get(StructSchema::Member member) { DynamicValue::Builder DynamicStruct::Builder::get(StructSchema::Member member) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
verifySetInUnion(member);
return getImpl(builder, member); return getImpl(builder, member);
} }
bool DynamicStruct::Reader::has(StructSchema::Member member) const { bool DynamicStruct::Reader::has(StructSchema::Member member) const {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
// Check union member.
KJ_IF_MAYBE(containingUnion, member.getContainingUnion()) {
uint16_t discrim = reader.getDataField<uint16_t>(
containingUnion->getProto().getBody().getUnionMember().getDiscriminantOffset() * ELEMENTS);
if (discrim != member.getIndex()) {
return false;
}
}
auto body = member.getProto().getBody(); auto body = member.getProto().getBody();
switch (body.which()) { switch (body.which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: { case schema::StructNode::Member::Body::UNION_MEMBER: {
...@@ -362,6 +405,15 @@ bool DynamicStruct::Reader::has(StructSchema::Member member) const { ...@@ -362,6 +405,15 @@ bool DynamicStruct::Reader::has(StructSchema::Member member) const {
bool DynamicStruct::Builder::has(StructSchema::Member member) { bool DynamicStruct::Builder::has(StructSchema::Member member) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
// Check union member.
KJ_IF_MAYBE(containingUnion, member.getContainingUnion()) {
uint16_t discrim = builder.getDataField<uint16_t>(
containingUnion->getProto().getBody().getUnionMember().getDiscriminantOffset() * ELEMENTS);
if (discrim != member.getIndex()) {
return false;
}
}
auto body = member.getProto().getBody(); auto body = member.getProto().getBody();
switch (body.which()) { switch (body.which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: { case schema::StructNode::Member::Body::UNION_MEMBER: {
...@@ -428,21 +480,25 @@ bool DynamicStruct::Builder::has(StructSchema::Member member) { ...@@ -428,21 +480,25 @@ bool DynamicStruct::Builder::has(StructSchema::Member member) {
void DynamicStruct::Builder::set(StructSchema::Member member, const DynamicValue::Reader& value) { void DynamicStruct::Builder::set(StructSchema::Member member, const DynamicValue::Reader& value) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
setInUnion(member);
return setImpl(builder, member, value); return setImpl(builder, member, value);
} }
DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Member member) { DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Member member) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
setInUnion(member);
return initImpl(builder, member); return initImpl(builder, member);
} }
DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Member member, uint size) { DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Member member, uint size) {
KJ_REQUIRE(member.getContainingStruct() == schema, KJ_REQUIRE(member.getContainingStruct() == schema,
"`member` is not a member of this struct."); "`member` is not a member of this struct.");
setInUnion(member);
return initImpl(builder, member, size); return initImpl(builder, member, size);
} }
DynamicStruct::Builder DynamicStruct::Builder::getObject( DynamicStruct::Builder DynamicStruct::Builder::getObject(
StructSchema::Member member, StructSchema type) { StructSchema::Member member, StructSchema type) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
verifySetInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -462,6 +518,7 @@ DynamicStruct::Builder DynamicStruct::Builder::getObject( ...@@ -462,6 +518,7 @@ DynamicStruct::Builder DynamicStruct::Builder::getObject(
DynamicList::Builder DynamicStruct::Builder::getObject( DynamicList::Builder DynamicStruct::Builder::getObject(
StructSchema::Member member, ListSchema type) { StructSchema::Member member, ListSchema type) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
verifySetInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -480,6 +537,7 @@ DynamicList::Builder DynamicStruct::Builder::getObject( ...@@ -480,6 +537,7 @@ DynamicList::Builder DynamicStruct::Builder::getObject(
} }
Text::Builder DynamicStruct::Builder::getObjectAsText(StructSchema::Member member) { Text::Builder DynamicStruct::Builder::getObjectAsText(StructSchema::Member member) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
verifySetInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -498,6 +556,7 @@ Text::Builder DynamicStruct::Builder::getObjectAsText(StructSchema::Member membe ...@@ -498,6 +556,7 @@ Text::Builder DynamicStruct::Builder::getObjectAsText(StructSchema::Member membe
} }
Data::Builder DynamicStruct::Builder::getObjectAsData(StructSchema::Member member) { Data::Builder DynamicStruct::Builder::getObjectAsData(StructSchema::Member member) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
verifySetInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -518,6 +577,7 @@ Data::Builder DynamicStruct::Builder::getObjectAsData(StructSchema::Member membe ...@@ -518,6 +577,7 @@ Data::Builder DynamicStruct::Builder::getObjectAsData(StructSchema::Member membe
DynamicStruct::Builder DynamicStruct::Builder::initObject( DynamicStruct::Builder DynamicStruct::Builder::initObject(
StructSchema::Member member, StructSchema type) { StructSchema::Member member, StructSchema type) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
setInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -537,6 +597,7 @@ DynamicStruct::Builder DynamicStruct::Builder::initObject( ...@@ -537,6 +597,7 @@ DynamicStruct::Builder DynamicStruct::Builder::initObject(
DynamicList::Builder DynamicStruct::Builder::initObject( DynamicList::Builder DynamicStruct::Builder::initObject(
StructSchema::Member member, ListSchema type, uint size) { StructSchema::Member member, ListSchema type, uint size) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
setInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -555,6 +616,7 @@ DynamicList::Builder DynamicStruct::Builder::initObject( ...@@ -555,6 +616,7 @@ DynamicList::Builder DynamicStruct::Builder::initObject(
} }
Text::Builder DynamicStruct::Builder::initObjectAsText(StructSchema::Member member, uint size) { Text::Builder DynamicStruct::Builder::initObjectAsText(StructSchema::Member member, uint size) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
setInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -573,6 +635,7 @@ Text::Builder DynamicStruct::Builder::initObjectAsText(StructSchema::Member memb ...@@ -573,6 +635,7 @@ Text::Builder DynamicStruct::Builder::initObjectAsText(StructSchema::Member memb
} }
Data::Builder DynamicStruct::Builder::initObjectAsData(StructSchema::Member member, uint size) { Data::Builder DynamicStruct::Builder::initObjectAsData(StructSchema::Member member, uint size) {
KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct."); KJ_REQUIRE(member.getContainingStruct() == schema, "`member` is not a member of this struct.");
setInUnion(member);
switch (member.getProto().getBody().which()) { switch (member.getProto().getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
KJ_FAIL_REQUIRE("Expected an Object."); KJ_FAIL_REQUIRE("Expected an Object.");
...@@ -591,10 +654,10 @@ Data::Builder DynamicStruct::Builder::initObjectAsData(StructSchema::Member memb ...@@ -591,10 +654,10 @@ Data::Builder DynamicStruct::Builder::initObjectAsData(StructSchema::Member memb
} }
DynamicValue::Reader DynamicStruct::Reader::get(kj::StringPtr name) const { DynamicValue::Reader DynamicStruct::Reader::get(kj::StringPtr name) const {
return getImpl(reader, schema.getMemberByName(name)); return get(schema.getMemberByName(name));
} }
DynamicValue::Builder DynamicStruct::Builder::get(kj::StringPtr name) { DynamicValue::Builder DynamicStruct::Builder::get(kj::StringPtr name) {
return getImpl(builder, schema.getMemberByName(name)); return get(schema.getMemberByName(name));
} }
bool DynamicStruct::Reader::has(kj::StringPtr name) const { bool DynamicStruct::Reader::has(kj::StringPtr name) const {
return has(schema.getMemberByName(name)); return has(schema.getMemberByName(name));
...@@ -603,17 +666,17 @@ bool DynamicStruct::Builder::has(kj::StringPtr name) { ...@@ -603,17 +666,17 @@ bool DynamicStruct::Builder::has(kj::StringPtr name) {
return has(schema.getMemberByName(name)); return has(schema.getMemberByName(name));
} }
void DynamicStruct::Builder::set(kj::StringPtr name, const DynamicValue::Reader& value) { void DynamicStruct::Builder::set(kj::StringPtr name, const DynamicValue::Reader& value) {
setImpl(builder, schema.getMemberByName(name), value); set(schema.getMemberByName(name), value);
} }
void DynamicStruct::Builder::set(kj::StringPtr name, void DynamicStruct::Builder::set(kj::StringPtr name,
std::initializer_list<DynamicValue::Reader> value) { std::initializer_list<DynamicValue::Reader> value) {
init(name, value.size()).as<DynamicList>().copyFrom(value); init(name, value.size()).as<DynamicList>().copyFrom(value);
} }
DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name) { DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name) {
return initImpl(builder, schema.getMemberByName(name)); return init(schema.getMemberByName(name));
} }
DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name, uint size) { DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name, uint size) {
return initImpl(builder, schema.getMemberByName(name), size); return init(schema.getMemberByName(name), size);
} }
DynamicStruct::Builder DynamicStruct::Builder::getObject( DynamicStruct::Builder DynamicStruct::Builder::getObject(
kj::StringPtr name, StructSchema type) { kj::StringPtr name, StructSchema type) {
...@@ -645,12 +708,13 @@ Data::Builder DynamicStruct::Builder::initObjectAsData(kj::StringPtr name, uint ...@@ -645,12 +708,13 @@ Data::Builder DynamicStruct::Builder::initObjectAsData(kj::StringPtr name, uint
DynamicValue::Reader DynamicStruct::Reader::getImpl( DynamicValue::Reader DynamicStruct::Reader::getImpl(
_::StructReader reader, StructSchema::Member member) { _::StructReader reader, StructSchema::Member member) {
switch (member.getProto().getBody().which()) { auto proto = member.getProto();
switch (proto.getBody().which()) {
case schema::StructNode::Member::Body::UNION_MEMBER: case schema::StructNode::Member::Body::UNION_MEMBER:
return DynamicUnion::Reader(member.asUnion(), reader); return DynamicUnion::Reader(member.asUnion(), reader);
case schema::StructNode::Member::Body::FIELD_MEMBER: { case schema::StructNode::Member::Body::FIELD_MEMBER: {
auto field = member.getProto().getBody().getFieldMember(); auto field = proto.getBody().getFieldMember();
auto type = field.getType().getBody(); auto type = field.getType().getBody();
auto dval = field.getDefaultValue().getBody(); auto dval = field.getDefaultValue().getBody();
...@@ -731,7 +795,7 @@ DynamicValue::Reader DynamicStruct::Reader::getImpl( ...@@ -731,7 +795,7 @@ DynamicValue::Reader DynamicStruct::Reader::getImpl(
} }
} }
KJ_FAIL_ASSERT("switch() missing case.", (uint)member.getProto().getBody().which()); KJ_FAIL_ASSERT("switch() missing case.", (uint)proto.getBody().which());
return nullptr; return nullptr;
} }
......
...@@ -380,6 +380,9 @@ private: ...@@ -380,6 +380,9 @@ private:
inline Builder(StructSchema schema, _::StructBuilder builder) inline Builder(StructSchema schema, _::StructBuilder builder)
: schema(schema), builder(builder) {} : schema(schema), builder(builder) {}
void verifySetInUnion(StructSchema::Member member);
void setInUnion(StructSchema::Member member);
static DynamicValue::Builder getImpl( static DynamicValue::Builder getImpl(
_::StructBuilder builder, StructSchema::Member member); _::StructBuilder builder, StructSchema::Member member);
static DynamicStruct::Builder getObjectImpl( static DynamicStruct::Builder getObjectImpl(
......
...@@ -398,7 +398,7 @@ TEST(Encoding, UnionDefault) { ...@@ -398,7 +398,7 @@ TEST(Encoding, UnionDefault) {
TEST(Encoding, UnnamedUnion) { TEST(Encoding, UnnamedUnion) {
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto root = builder.getRoot<test::TestUnnamedUnion>(); auto root = builder.initRoot<test::TestUnnamedUnion>();
EXPECT_EQ(test::TestUnnamedUnion::FOO, root.which()); EXPECT_EQ(test::TestUnnamedUnion::FOO, root.which());
root.setBar(321); root.setBar(321);
...@@ -417,7 +417,8 @@ TEST(Encoding, UnnamedUnion) { ...@@ -417,7 +417,8 @@ TEST(Encoding, UnnamedUnion) {
EXPECT_DEBUG_ANY_THROW(root.getBar()); EXPECT_DEBUG_ANY_THROW(root.getBar());
EXPECT_DEBUG_ANY_THROW(root.asReader().getBar()); EXPECT_DEBUG_ANY_THROW(root.asReader().getBar());
KJ_IF_MAYBE(u, Schema::from<test::TestUnnamedUnion>().getUnnamedUnion()) { StructSchema schema = Schema::from<test::TestUnnamedUnion>();
KJ_IF_MAYBE(u, schema.getUnnamedUnion()) {
// The discriminant is allocated between allocating the first and second members. // The discriminant is allocated between allocating the first and second members.
EXPECT_EQ(1, u->getProto().getBody().getUnionMember().getDiscriminantOffset()); EXPECT_EQ(1, u->getProto().getBody().getUnionMember().getDiscriminantOffset());
EXPECT_EQ(0, u->getMemberByName("foo").getProto().getBody().getFieldMember().getOffset()); EXPECT_EQ(0, u->getMemberByName("foo").getProto().getBody().getFieldMember().getOffset());
......
...@@ -164,13 +164,9 @@ struct RawSchema { ...@@ -164,13 +164,9 @@ struct RawSchema {
// TODO(someday): Make this a hashtable. // TODO(someday): Make this a hashtable.
struct MemberInfo { struct MemberInfo {
uint16_t unionIndex; uint16_t scopeOrdinal;
// 0 = not in a union, >0 = one plus the index of the union within the scope indicated by // One plus the ordinal number of the parent scope of this member when looking up by name.
// scopeOrdinal. // Zero represents the top-level scope.
// uint16_t scopeOrdinal;
// // One plus the ordinal number of the parent scope of this member when looking up by name.
// // Zero represents the top-level scope.
uint16_t index; uint16_t index;
// Index of the member within its scope. If the index is greater than the number of elements // Index of the member within its scope. If the index is greater than the number of elements
......
...@@ -56,6 +56,40 @@ TEST(SchemaLoader, Load) { ...@@ -56,6 +56,40 @@ TEST(SchemaLoader, Load) {
EXPECT_EQ(0u, struct16Schema.getProto().getBody().getStructNode().getMembers().size()); EXPECT_EQ(0u, struct16Schema.getProto().getBody().getStructNode().getMembers().size());
} }
TEST(SchemaLoader, LoadLateUnion) {
SchemaLoader loader;
StructSchema schema =
loader.load(Schema::from<test::TestLateUnion>().getProto()).asStruct();
EXPECT_EQ(6, schema.getMemberByName("theUnion").asUnion()
.getMemberByName("grault").getProto().getOrdinal());
EXPECT_EQ(9, schema.getMemberByName("anotherUnion").asUnion()
.getMemberByName("corge").getProto().getOrdinal());
EXPECT_TRUE(schema.findMemberByName("corge") == nullptr);
EXPECT_TRUE(schema.findMemberByName("grault") == nullptr);
}
TEST(SchemaLoader, LoadUnnamedUnion) {
SchemaLoader loader;
StructSchema schema =
loader.load(Schema::from<test::TestUnnamedUnion>().getProto()).asStruct();
EXPECT_TRUE(schema.findMemberByName("") == nullptr);
KJ_IF_MAYBE(u, schema.getUnnamedUnion()) {
EXPECT_TRUE(schema.getMemberByName("foo") == u->getMemberByName("foo"));
EXPECT_TRUE(schema.getMemberByName("bar") == u->getMemberByName("bar"));
EXPECT_TRUE(u->findMemberByName("before") == nullptr);
EXPECT_TRUE(u->findMemberByName("after") == nullptr);
EXPECT_TRUE(schema.findMemberByName("before") != nullptr);
EXPECT_TRUE(schema.findMemberByName("after") != nullptr);
} else {
ADD_FAILURE() << "getUnnamedUnion() should have returned non-null.";
}
}
#if KJ_NO_EXCEPTIONS #if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW #undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".") #define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
......
...@@ -148,7 +148,7 @@ private: ...@@ -148,7 +148,7 @@ private:
bool isValid; bool isValid;
std::map<uint64_t, _::RawSchema*> dependencies; std::map<uint64_t, _::RawSchema*> dependencies;
// Maps (unionIndex, name) -> index for each member. // Maps (scopeOrdinal, name) -> index for each member.
std::map<std::pair<uint, Text::Reader>, uint> members; std::map<std::pair<uint, Text::Reader>, uint> members;
#define VALIDATE_SCHEMA(condition, ...) \ #define VALIDATE_SCHEMA(condition, ...) \
...@@ -226,21 +226,22 @@ private: ...@@ -226,21 +226,22 @@ private:
uint index = 0; uint index = 0;
for (auto member: members) { for (auto member: members) {
KJ_CONTEXT("validating struct member", member.getName()); KJ_CONTEXT("validating struct member", member.getName());
validate(member, sawCodeOrder, sawOrdinal, dataSizeInBits, pointerCount, 0, index++); validate(member, sawCodeOrder, sawOrdinal, dataSizeInBits, pointerCount, 0, members.size(),
index++);
} }
} }
void validateMemberName(kj::StringPtr name, uint unionIndex, uint index) { void validateMemberName(kj::StringPtr name, uint scopeOrdinal, uint adjustedIndex) {
bool isNewName = members.insert(std::make_pair( bool isNewName = members.insert(std::make_pair(
std::pair<uint, Text::Reader>(unionIndex, name), index)).second; std::pair<uint, Text::Reader>(scopeOrdinal, name), adjustedIndex)).second;
VALIDATE_SCHEMA(isNewName, "duplicate name", name); VALIDATE_SCHEMA(isNewName, "duplicate name", name);
} }
void validate(const schema::StructNode::Member::Reader& member, void validate(const schema::StructNode::Member::Reader& member,
kj::ArrayPtr<bool> sawCodeOrder, kj::ArrayPtr<bool> sawOrdinal, kj::ArrayPtr<bool> sawCodeOrder, kj::ArrayPtr<bool> sawOrdinal,
uint dataSizeInBits, uint pointerCount, uint dataSizeInBits, uint pointerCount,
uint unionIndex, uint index) { uint scopeOrdinal, uint scopeMemberCount, uint adjustedIndex) {
validateMemberName(member.getName(), unionIndex, index); validateMemberName(member.getName(), scopeOrdinal, adjustedIndex);
VALIDATE_SCHEMA(member.getCodeOrder() < sawCodeOrder.size() && VALIDATE_SCHEMA(member.getCodeOrder() < sawCodeOrder.size() &&
!sawCodeOrder[member.getCodeOrder()], !sawCodeOrder[member.getCodeOrder()],
"Invalid codeOrder."); "Invalid codeOrder.");
...@@ -284,14 +285,17 @@ private: ...@@ -284,14 +285,17 @@ private:
uMember.getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER, uMember.getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER,
"Union members must be fields."); "Union members must be fields.");
uint subUnionIndex; uint subScopeOrdinal;
uint indexAdjustment;
if (member.getName().size() == 0) { if (member.getName().size() == 0) {
subUnionIndex = unionIndex; subScopeOrdinal = scopeOrdinal;
indexAdjustment = scopeMemberCount;
} else { } else {
subUnionIndex = index + 1; subScopeOrdinal = member.getOrdinal() + 1;
indexAdjustment = 0;
} }
validate(uMember, uSawCodeOrder, sawOrdinal, dataSizeInBits, pointerCount, validate(uMember, uSawCodeOrder, sawOrdinal, dataSizeInBits, pointerCount,
subUnionIndex, subIndex++); subScopeOrdinal, uMembers.size(), subIndex++ + indexAdjustment);
} }
// Union ordinal may match the ordinal of its first member, meaning it was unspecified in // Union ordinal may match the ordinal of its first member, meaning it was unspecified in
......
...@@ -274,6 +274,17 @@ TEST(Schema, UnnamedUnion) { ...@@ -274,6 +274,17 @@ TEST(Schema, UnnamedUnion) {
StructSchema schema = Schema::from<test::TestUnnamedUnion>(); StructSchema schema = Schema::from<test::TestUnnamedUnion>();
EXPECT_TRUE(schema.findMemberByName("") == nullptr); EXPECT_TRUE(schema.findMemberByName("") == nullptr);
KJ_IF_MAYBE(u, schema.getUnnamedUnion()) {
EXPECT_TRUE(schema.getMemberByName("foo") == u->getMemberByName("foo"));
EXPECT_TRUE(schema.getMemberByName("bar") == u->getMemberByName("bar"));
EXPECT_TRUE(u->findMemberByName("before") == nullptr);
EXPECT_TRUE(u->findMemberByName("after") == nullptr);
EXPECT_TRUE(schema.findMemberByName("before") != nullptr);
EXPECT_TRUE(schema.findMemberByName("after") != nullptr);
} else {
ADD_FAILURE() << "getUnnamedUnion() should have returned non-null.";
}
} }
} // namespace } // namespace
......
...@@ -90,20 +90,74 @@ void Schema::requireUsableAs(const _::RawSchema* expected) const { ...@@ -90,20 +90,74 @@ void Schema::requireUsableAs(const _::RawSchema* expected) const {
namespace { namespace {
inline StructSchema::MemberList getUnionMembers(StructSchema::Member m) {
return m.asUnion().getMembers();
}
inline EnumSchema::EnumerantList getUnionMembers(EnumSchema::Enumerant) {
KJ_FAIL_ASSERT("MemberInfo for enum nonsensically expects unnamed unions.");
}
inline InterfaceSchema::MethodList getUnionMembers(InterfaceSchema::Method) {
KJ_FAIL_ASSERT("MemberInfo for interface nonsensically expects unnamed unions.");
}
template <typename List>
auto findUnnamedMemberOfScope(const _::RawSchema* raw, uint scopeOrdinal, List&& list)
-> decltype(list[0]) {
uint lower = 0;
uint upper = raw->memberCount;
// Since the empty string sorts before all other strings, we're looking for the first member
// that has the expected scopeOrdinal.
while (lower < upper) {
uint mid = (lower + upper) / 2;
const _::RawSchema::MemberInfo& member = raw->membersByName[mid];
if (member.scopeOrdinal < scopeOrdinal) {
lower = mid + 1;
} else {
upper = mid;
}
}
const _::RawSchema::MemberInfo& result = raw->membersByName[lower];
KJ_DASSERT(lower < raw->memberCount && list[result.index].getProto().getName().size() == 0,
"Expected to find an unnamed union, but didn't. MemberInfo table must be broken.");
return list[result.index];
}
template <typename List> template <typename List>
auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name, auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name,
uint unionIndex, List&& list) uint scopeOrdinal, List&& list)
-> kj::Maybe<kj::Decay<decltype(list[0])>> { -> kj::Maybe<decltype(list[0])> {
uint lower = 0; uint lower = 0;
uint upper = raw->memberCount; uint upper = raw->memberCount;
List unnamedUnionMembers;
while (lower < upper) { while (lower < upper) {
uint mid = (lower + upper) / 2; uint mid = (lower + upper) / 2;
const _::RawSchema::MemberInfo& member = raw->membersByName[mid]; const _::RawSchema::MemberInfo& member = raw->membersByName[mid];
if (member.unionIndex == unionIndex) { if (member.scopeOrdinal == scopeOrdinal) {
auto candidate = list[member.index]; decltype(list[member.index]) candidate;
if (member.index < list.size()) {
candidate = list[member.index];
} else {
// This looks like it's a member of an unnamed union.
if (unnamedUnionMembers.size() == 0) {
// We haven't expanded the unnamed union yet. We have to find it. It should be the very
// first item with the expected ordinal.
unnamedUnionMembers = getUnionMembers(findUnnamedMemberOfScope(raw, scopeOrdinal, list));
}
uint uIndex = member.index - list.size();
KJ_DASSERT(uIndex < unnamedUnionMembers.size());
candidate = unnamedUnionMembers[uIndex];
}
kj::StringPtr candidateName = candidate.getProto().getName(); kj::StringPtr candidateName = candidate.getProto().getName();
if (candidateName == name) { if (candidateName == name) {
return candidate; return candidate;
...@@ -112,7 +166,7 @@ auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name, ...@@ -112,7 +166,7 @@ auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name,
} else { } else {
upper = mid; upper = mid;
} }
} else if (member.unionIndex < unionIndex) { } else if (member.scopeOrdinal < scopeOrdinal) {
lower = mid + 1; lower = mid + 1;
} else { } else {
upper = mid; upper = mid;
...@@ -208,7 +262,24 @@ StructSchema::MemberList StructSchema::Union::getMembers() const { ...@@ -208,7 +262,24 @@ StructSchema::MemberList StructSchema::Union::getMembers() const {
} }
kj::Maybe<StructSchema::Member> StructSchema::Union::findMemberByName(kj::StringPtr name) const { kj::Maybe<StructSchema::Member> StructSchema::Union::findMemberByName(kj::StringPtr name) const {
return findSchemaMemberByName(parent.raw, name, index + 1, getMembers()); auto proto = getProto();
if (proto.getName().size() == 0) {
// Unnamed union, so we have to search the parent scope.
KJ_IF_MAYBE(result, getContainingStruct().findMemberByName(name)) {
KJ_IF_MAYBE(containingUnion, result->getContainingUnion()) {
KJ_ASSERT(*containingUnion == *this,
"Found a member of some other union? But there can only be one unnamed union!");
return *result;
} else {
// Oops, we found a member of the parent scope that is *not* also a member of the union.
return nullptr;
}
} else {
return nullptr;
}
} else {
return findSchemaMemberByName(parent.raw, name, getProto().getOrdinal() + 1, getMembers());
}
} }
StructSchema::Member StructSchema::Union::getMemberByName(kj::StringPtr name) const { StructSchema::Member StructSchema::Union::getMemberByName(kj::StringPtr name) const {
...@@ -226,7 +297,7 @@ StructSchema::MemberList StructSchema::Group::getMembers() const { ...@@ -226,7 +297,7 @@ StructSchema::MemberList StructSchema::Group::getMembers() const {
#if 0 #if 0
// TODO(soon): Implement correctly. Requires some changes to lookup table format. // TODO(soon): Implement correctly. Requires some changes to lookup table format.
kj::Maybe<StructSchema::Member> StructSchema::Group::findMemberByName(kj::StringPtr name) const { kj::Maybe<StructSchema::Member> StructSchema::Group::findMemberByName(kj::StringPtr name) const {
return findSchemaMemberByName(parent.raw, name, index + 1, getMembers()); return findSchemaMemberByName(parent.raw, name, getProto().getOrdinal() + 1, getMembers());
} }
StructSchema::Member StructSchema::Group::getMemberByName(kj::StringPtr name) const { StructSchema::Member StructSchema::Group::getMemberByName(kj::StringPtr name) const {
......
...@@ -254,6 +254,8 @@ private: ...@@ -254,6 +254,8 @@ private:
class StructSchema::MemberList { class StructSchema::MemberList {
public: public:
MemberList() = default; // empty list
inline uint size() const { return list.size(); } inline uint size() const { return list.size(); }
inline Member operator[](uint index) const { return Member(parent, unionIndex, index, list[index]); } inline Member operator[](uint index) const { return Member(parent, unionIndex, index, list[index]); }
...@@ -323,6 +325,8 @@ private: ...@@ -323,6 +325,8 @@ private:
class EnumSchema::EnumerantList { class EnumSchema::EnumerantList {
public: public:
EnumerantList() = default; // empty list
inline uint size() const { return list.size(); } inline uint size() const { return list.size(); }
inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); } inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); }
...@@ -391,6 +395,8 @@ private: ...@@ -391,6 +395,8 @@ private:
class InterfaceSchema::MethodList { class InterfaceSchema::MethodList {
public: public:
MethodList() = default; // empty list
inline uint size() const { return list.size(); } inline uint size() const { return list.size(); }
inline Method operator[](uint index) const { return Method(parent, index, list[index]); } inline Method operator[](uint index) const { return Method(parent, index, list[index]); }
......
...@@ -359,6 +359,34 @@ TEST(Stringify, Unions) { ...@@ -359,6 +359,34 @@ TEST(Stringify, Unions) {
EXPECT_EQ("u3f0s64(123456789012345678)", kj::str(root.getUnion3())); EXPECT_EQ("u3f0s64(123456789012345678)", kj::str(root.getUnion3()));
} }
TEST(Stringify, UnnamedUnions) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestUnnamedUnion>();
root.setBar(123);
EXPECT_EQ("(bar(123))", kj::str(root));
EXPECT_EQ("(bar(123))", prettyPrint(root).flatten());
root.setAfter("foooooooooooooooooooooooooooooooo");
EXPECT_EQ("(bar(123), after = \"foooooooooooooooooooooooooooooooo\")", kj::str(root));
EXPECT_EQ(
"( bar(123),\n"
" after = \"foooooooooooooooooooooooooooooooo\" )",
prettyPrint(root).flatten());
root.setBefore("before");
EXPECT_EQ("(before = \"before\", bar(123), "
"after = \"foooooooooooooooooooooooooooooooo\")", kj::str(root));
EXPECT_EQ(
"( before = \"before\",\n"
" bar(123),\n"
" after = \"foooooooooooooooooooooooooooooooo\" )",
prettyPrint(root).flatten());
}
TEST(Stringify, StructUnions) { TEST(Stringify, StructUnions) {
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestStructUnion>(); auto root = builder.initRoot<test::TestStructUnion>();
......
...@@ -197,9 +197,16 @@ static kj::StringTree print(const DynamicValue::Reader& value, ...@@ -197,9 +197,16 @@ static kj::StringTree print(const DynamicValue::Reader& value,
kj::Vector<kj::StringTree> printedMembers(memberSchemas.size()); kj::Vector<kj::StringTree> printedMembers(memberSchemas.size());
for (auto member: memberSchemas) { for (auto member: memberSchemas) {
if (structValue.has(member)) { if (structValue.has(member)) {
printedMembers.add(kj::strTree( auto name = member.getProto().getName();
member.getProto().getName(), " = ", if (name.size() == 0) {
print(structValue.get(member), whichMemberType(member), indent.next(), PREFIXED))); // Unnamed union. Just print the content.
printedMembers.add(kj::strTree(
print(structValue.get(member), whichMemberType(member), indent.next(), BARE)));
} else {
printedMembers.add(kj::strTree(
name, " = ",
print(structValue.get(member), whichMemberType(member), indent.next(), PREFIXED)));
}
} }
} }
......
...@@ -341,8 +341,8 @@ struct TestListDefaults { ...@@ -341,8 +341,8 @@ struct TestListDefaults {
} }
struct TestLateUnion { struct TestLateUnion {
# Test what happens if the unions are the first ordinals in the struct. At one point this was # Test what happens if the unions are not the first ordinals in the struct. At one point this
# broken for the dynamic API. # was broken for the dynamic API.
foo @0 :Int32; foo @0 :Int32;
bar @1 :Text; bar @1 :Text;
...@@ -385,12 +385,12 @@ struct TestStructUnion { ...@@ -385,12 +385,12 @@ struct TestStructUnion {
} }
struct TestUnnamedUnion { struct TestUnnamedUnion {
before @0 :Void; before @0 :Text;
union { union {
foo @1 :UInt16; foo @1 :UInt16;
bar @2 :UInt32; bar @2 :UInt32;
} }
after @3 :Void; after @3 :Text;
} }
...@@ -600,6 +600,10 @@ inline T* readMaybe(Maybe<T&>&& maybe) { return maybe.ptr; } ...@@ -600,6 +600,10 @@ inline T* readMaybe(Maybe<T&>&& maybe) { return maybe.ptr; }
template <typename T> template <typename T>
inline T* readMaybe(const Maybe<T&>& maybe) { return maybe.ptr; } inline T* readMaybe(const Maybe<T&>& maybe) { return maybe.ptr; }
template <typename T>
inline T* readMaybe(T* ptr) { return ptr; }
// Allow KJ_IF_MAYBE to work on regular pointers.
} // namespace _ (private) } // namespace _ (private)
#define KJ_IF_MAYBE(name, exp) if (auto name = ::kj::_::readMaybe(exp)) #define KJ_IF_MAYBE(name, exp) if (auto name = ::kj::_::readMaybe(exp))
......
...@@ -147,6 +147,18 @@ namespace kj { ...@@ -147,6 +147,18 @@ namespace kj {
::kj::_::Debug::ContextImpl<decltype(KJ_UNIQUE_NAME(_kjContextFunc))> \ ::kj::_::Debug::ContextImpl<decltype(KJ_UNIQUE_NAME(_kjContextFunc))> \
KJ_UNIQUE_NAME(_kjContext)(KJ_UNIQUE_NAME(_kjContextFunc)) KJ_UNIQUE_NAME(_kjContext)(KJ_UNIQUE_NAME(_kjContextFunc))
#define _kJ_NONNULL(nature, value, ...) \
({ \
auto result = ::kj::_::readMaybe(value); \
if (KJ_UNLIKELY(!result)) { \
::kj::_::Debug::Fault(__FILE__, __LINE__, ::kj::Exception::Nature::nature, 0, \
#value " != nullptr", #__VA_ARGS__, ##__VA_ARGS__).fatal(); \
} \
*result; \
})
#define KJ_ASSERT_NONNULL(value, ...) _kJ_NONNULL(LOCAL_BUG, value, ##__VA_ARGS__)
#define KJ_REQUIRE_NONNULL(value, ...) _kJ_NONNULL(PRECONDITION, value, ##__VA_ARGS__)
#ifdef NDEBUG #ifdef NDEBUG
#define KJ_DLOG(...) do {} while (false) #define KJ_DLOG(...) do {} while (false)
#define KJ_DASSERT(...) do {} while (false) #define KJ_DASSERT(...) do {} while (false)
......
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