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
diff -u $srcfile tmp/capnp/bootstrap-test-tmp/$file.$ext >&2
done
done
echo passed
......@@ -119,7 +119,7 @@ void enumerateDeps(schema::Node::Reader node, std::set<uint64_t>& deps) {
struct OrderByName {
template <typename T>
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,
case schema::StructNode::Member::Body::UNION_MEMBER:
// Only create a sub-table if the union is named.
if (member.getProto().getName().size() > 0) {
makeMemberInfoTable(1 + member.getIndex(), member.asUnion().getMembers(), info);
makeMemberInfoTable(1 + member.getProto().getOrdinal(),
member.asUnion().getMembers(), info);
}
break;
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;
}
}
......@@ -148,29 +149,40 @@ void makeSubMemberInfoTable(const EnumSchema::Enumerant& member,
void makeSubMemberInfoTable(const InterfaceSchema::Method& member,
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,
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
// 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) {
vec.add(member);
vec.add(member, member.getIndex() + offset);
if (member.getProto().getName().size() == 0) {
// Flatten unnamed union.
enumerateScope(member.asUnion().getMembers(), vec);
enumerateScope(member.asUnion().getMembers(), vec, offset + members.size());
}
}
}
void enumerateScope(const EnumSchema::EnumerantList& members,
kj::Vector<EnumSchema::Enumerant>& vec) {
kj::Vector<MemberAndIndex<EnumSchema::Enumerant>>& vec) {
for (auto member: members) {
vec.add(member);
}
}
void enumerateScope(const InterfaceSchema::MethodList& members,
kj::Vector<InterfaceSchema::Method>& vec) {
kj::Vector<MemberAndIndex<InterfaceSchema::Method>>& vec) {
for (auto member: members) {
vec.add(member);
}
......@@ -179,16 +191,15 @@ void enumerateScope(const InterfaceSchema::MethodList& members,
template <typename MemberList>
void makeMemberInfoTable(uint parent, MemberList&& members,
kj::Vector<capnp::_::RawSchema::MemberInfo>& info) {
kj::Vector<kj::Decay<decltype(members[0])>> sortedMembers(members.size());
enumerateScope(members, sortedMembers);
kj::Vector<MemberAndIndex<decltype(members[0])>> sorted(members.size());
enumerateScope(members, sorted);
auto sorted = KJ_MAP(members, m) { return m; };
std::sort(sorted.begin(), sorted.end(), OrderByName());
for (auto& member: sorted) {
info.add(capnp::_::RawSchema::MemberInfo {
kj::implicitCast<uint16_t>(parent),
kj::implicitCast<uint16_t>(member.getIndex())
kj::implicitCast<uint16_t>(member.index)
});
}
for (auto member: members) {
......@@ -1063,7 +1074,7 @@ private:
"};\n"
"static const ::capnp::_::RawSchema::MemberInfo m_", hexId, "[] = {\n",
KJ_MAP(memberInfos, info) {
return kj::strTree(" { ", info.unionIndex, ", ", info.index, " },\n");
return kj::strTree(" { ", info.scopeOrdinal, ", ", info.index, " },\n");
},
"};\n"
"const ::capnp::_::RawSchema s_", hexId, " = {\n"
......
......@@ -1435,36 +1435,36 @@ static const ::capnp::_::RawSchema::MemberInfo m_96efe787c17e83bb[] = {
{ 2, 2 },
{ 2, 1 },
{ 2, 0 },
{ 4, 9 },
{ 4, 15 },
{ 4, 27 },
{ 4, 24 },
{ 4, 25 },
{ 4, 17 },
{ 4, 18 },
{ 4, 19 },
{ 4, 16 },
{ 4, 28 },
{ 4, 29 },
{ 4, 26 },
{ 4, 21 },
{ 4, 22 },
{ 4, 23 },
{ 4, 20 },
{ 4, 14 },
{ 4, 1 },
{ 4, 2 },
{ 4, 3 },
{ 4, 5 },
{ 4, 13 },
{ 4, 12 },
{ 4, 7 },
{ 4, 8 },
{ 4, 11 },
{ 4, 10 },
{ 4, 4 },
{ 4, 6 },
{ 4, 0 },
{ 7, 9 },
{ 7, 15 },
{ 7, 27 },
{ 7, 24 },
{ 7, 25 },
{ 7, 17 },
{ 7, 18 },
{ 7, 19 },
{ 7, 16 },
{ 7, 28 },
{ 7, 29 },
{ 7, 26 },
{ 7, 21 },
{ 7, 22 },
{ 7, 23 },
{ 7, 20 },
{ 7, 14 },
{ 7, 1 },
{ 7, 2 },
{ 7, 3 },
{ 7, 5 },
{ 7, 13 },
{ 7, 12 },
{ 7, 7 },
{ 7, 8 },
{ 7, 11 },
{ 7, 10 },
{ 7, 4 },
{ 7, 6 },
{ 7, 0 },
};
const ::capnp::_::RawSchema s_96efe787c17e83bb = {
0x96efe787c17e83bb, b_96efe787c17e83bb.words, 664, d_96efe787c17e83bb, m_96efe787c17e83bb,
......
......@@ -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) {
MallocMessageBuilder builder;
auto root = builder.initRoot<DynamicStruct>(Schema::from<TestUnion>());
......@@ -314,29 +322,81 @@ TEST(DynamicApi, UnionsWrite) {
ASSERT_EQ(TestUnion::Union3::U3F0S64, reader.getUnion3().which());
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
#undef EXPECT_ANY_THROW
// All exceptions should be non-fatal, so when exceptions are disabled the code should return.
#define EXPECT_ANY_THROW(code) code
#endif
TEST(DynamicApi, UnnamedUnion) {
MallocMessageBuilder builder;
StructSchema schema = Schema::from<test::TestUnnamedUnion>();
auto root = builder.initRoot<DynamicStruct>(schema);
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) {
MallocMessageBuilder builder;
auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>());
root.set("int8Field", 123);
EXPECT_ANY_THROW(root.set("int8Field", 1234));
EXPECT_NONFATAL_FAILURE(root.set("int8Field", 1234));
root.set("uInt32Field", 1);
EXPECT_ANY_THROW(root.set("uInt32Field", -1));
EXPECT_NONFATAL_FAILURE(root.set("uInt32Field", -1));
root.set("int16Field", 5);
EXPECT_ANY_THROW(root.set("int16Field", 0.5));
EXPECT_NONFATAL_FAILURE(root.set("int16Field", 0.5));
root.set("boolField", true);
EXPECT_ANY_THROW(root.set("boolField", 1));
EXPECT_NONFATAL_FAILURE(root.set("boolField", 1));
}
TEST(DynamicApi, LateUnion) {
......
This diff is collapsed.
......@@ -380,6 +380,9 @@ private:
inline Builder(StructSchema schema, _::StructBuilder builder)
: schema(schema), builder(builder) {}
void verifySetInUnion(StructSchema::Member member);
void setInUnion(StructSchema::Member member);
static DynamicValue::Builder getImpl(
_::StructBuilder builder, StructSchema::Member member);
static DynamicStruct::Builder getObjectImpl(
......
......@@ -398,7 +398,7 @@ TEST(Encoding, UnionDefault) {
TEST(Encoding, UnnamedUnion) {
MallocMessageBuilder builder;
auto root = builder.getRoot<test::TestUnnamedUnion>();
auto root = builder.initRoot<test::TestUnnamedUnion>();
EXPECT_EQ(test::TestUnnamedUnion::FOO, root.which());
root.setBar(321);
......@@ -417,7 +417,8 @@ TEST(Encoding, UnnamedUnion) {
EXPECT_DEBUG_ANY_THROW(root.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.
EXPECT_EQ(1, u->getProto().getBody().getUnionMember().getDiscriminantOffset());
EXPECT_EQ(0, u->getMemberByName("foo").getProto().getBody().getFieldMember().getOffset());
......
......@@ -164,13 +164,9 @@ struct RawSchema {
// TODO(someday): Make this a hashtable.
struct MemberInfo {
uint16_t unionIndex;
// 0 = not in a union, >0 = one plus the index of the union within the scope indicated by
// scopeOrdinal.
// 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 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;
// Index of the member within its scope. If the index is greater than the number of elements
......
......@@ -56,6 +56,40 @@ TEST(SchemaLoader, Load) {
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
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
......
......@@ -148,7 +148,7 @@ private:
bool isValid;
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;
#define VALIDATE_SCHEMA(condition, ...) \
......@@ -226,21 +226,22 @@ private:
uint index = 0;
for (auto member: members) {
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(
std::pair<uint, Text::Reader>(unionIndex, name), index)).second;
std::pair<uint, Text::Reader>(scopeOrdinal, name), adjustedIndex)).second;
VALIDATE_SCHEMA(isNewName, "duplicate name", name);
}
void validate(const schema::StructNode::Member::Reader& member,
kj::ArrayPtr<bool> sawCodeOrder, kj::ArrayPtr<bool> sawOrdinal,
uint dataSizeInBits, uint pointerCount,
uint unionIndex, uint index) {
validateMemberName(member.getName(), unionIndex, index);
uint scopeOrdinal, uint scopeMemberCount, uint adjustedIndex) {
validateMemberName(member.getName(), scopeOrdinal, adjustedIndex);
VALIDATE_SCHEMA(member.getCodeOrder() < sawCodeOrder.size() &&
!sawCodeOrder[member.getCodeOrder()],
"Invalid codeOrder.");
......@@ -284,14 +285,17 @@ private:
uMember.getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER,
"Union members must be fields.");
uint subUnionIndex;
uint subScopeOrdinal;
uint indexAdjustment;
if (member.getName().size() == 0) {
subUnionIndex = unionIndex;
subScopeOrdinal = scopeOrdinal;
indexAdjustment = scopeMemberCount;
} else {
subUnionIndex = index + 1;
subScopeOrdinal = member.getOrdinal() + 1;
indexAdjustment = 0;
}
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
......
......@@ -274,6 +274,17 @@ TEST(Schema, UnnamedUnion) {
StructSchema schema = Schema::from<test::TestUnnamedUnion>();
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
......
......@@ -90,20 +90,74 @@ void Schema::requireUsableAs(const _::RawSchema* expected) const {
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>
auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name,
uint unionIndex, List&& list)
-> kj::Maybe<kj::Decay<decltype(list[0])>> {
uint scopeOrdinal, List&& list)
-> kj::Maybe<decltype(list[0])> {
uint lower = 0;
uint upper = raw->memberCount;
List unnamedUnionMembers;
while (lower < upper) {
uint mid = (lower + upper) / 2;
const _::RawSchema::MemberInfo& member = raw->membersByName[mid];
if (member.unionIndex == unionIndex) {
auto candidate = list[member.index];
if (member.scopeOrdinal == scopeOrdinal) {
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();
if (candidateName == name) {
return candidate;
......@@ -112,7 +166,7 @@ auto findSchemaMemberByName(const _::RawSchema* raw, kj::StringPtr name,
} else {
upper = mid;
}
} else if (member.unionIndex < unionIndex) {
} else if (member.scopeOrdinal < scopeOrdinal) {
lower = mid + 1;
} else {
upper = mid;
......@@ -208,7 +262,24 @@ StructSchema::MemberList StructSchema::Union::getMembers() 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 {
......@@ -226,7 +297,7 @@ StructSchema::MemberList StructSchema::Group::getMembers() const {
#if 0
// TODO(soon): Implement correctly. Requires some changes to lookup table format.
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 {
......
......@@ -254,6 +254,8 @@ private:
class StructSchema::MemberList {
public:
MemberList() = default; // empty list
inline uint size() const { return list.size(); }
inline Member operator[](uint index) const { return Member(parent, unionIndex, index, list[index]); }
......@@ -323,6 +325,8 @@ private:
class EnumSchema::EnumerantList {
public:
EnumerantList() = default; // empty list
inline uint size() const { return list.size(); }
inline Enumerant operator[](uint index) const { return Enumerant(parent, index, list[index]); }
......@@ -391,6 +395,8 @@ private:
class InterfaceSchema::MethodList {
public:
MethodList() = default; // empty list
inline uint size() const { return list.size(); }
inline Method operator[](uint index) const { return Method(parent, index, list[index]); }
......
......@@ -359,6 +359,34 @@ TEST(Stringify, Unions) {
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) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestStructUnion>();
......
......@@ -197,9 +197,16 @@ static kj::StringTree print(const DynamicValue::Reader& value,
kj::Vector<kj::StringTree> printedMembers(memberSchemas.size());
for (auto member: memberSchemas) {
if (structValue.has(member)) {
printedMembers.add(kj::strTree(
member.getProto().getName(), " = ",
print(structValue.get(member), whichMemberType(member), indent.next(), PREFIXED)));
auto name = member.getProto().getName();
if (name.size() == 0) {
// 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 {
}
struct TestLateUnion {
# Test what happens if the unions are the first ordinals in the struct. At one point this was
# broken for the dynamic API.
# Test what happens if the unions are not the first ordinals in the struct. At one point this
# was broken for the dynamic API.
foo @0 :Int32;
bar @1 :Text;
......@@ -385,12 +385,12 @@ struct TestStructUnion {
}
struct TestUnnamedUnion {
before @0 :Void;
before @0 :Text;
union {
foo @1 :UInt16;
bar @2 :UInt32;
}
after @3 :Void;
after @3 :Text;
}
......@@ -600,6 +600,10 @@ inline T* readMaybe(Maybe<T&>&& maybe) { return maybe.ptr; }
template <typename T>
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)
#define KJ_IF_MAYBE(name, exp) if (auto name = ::kj::_::readMaybe(exp))
......
......@@ -147,6 +147,18 @@ namespace kj {
::kj::_::Debug::ContextImpl<decltype(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
#define KJ_DLOG(...) 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