Commit ac1e532d authored by Kenton Varda's avatar Kenton Varda

Extend schema API to cover constants, test parsing of constants.

parent bc7b57a7
...@@ -1724,7 +1724,7 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant( ...@@ -1724,7 +1724,7 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
if (constValue.getType() == DynamicValue::OBJECT) { if (constValue.getType() == DynamicValue::OBJECT) {
// We need to assign an appropriate schema to this object. // We need to assign an appropriate schema to this object.
DynamicObject objValue = constValue.as<DynamicObject>(); DynamicObject::Reader objValue = constValue.as<DynamicObject>();
auto constType = constReader.getType(); auto constType = constReader.getType();
switch (constType.which()) { switch (constType.which()) {
case schema::Type::STRUCT: case schema::Type::STRUCT:
......
...@@ -193,7 +193,9 @@ TEST(DynamicApi, DynamicGenericObjects) { ...@@ -193,7 +193,9 @@ TEST(DynamicApi, DynamicGenericObjects) {
checkDynamicTestMessage( checkDynamicTestMessage(
root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<TestAllTypes>())); root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<TestAllTypes>()));
checkDynamicTestMessage( checkDynamicTestMessage(
root.get("objectField").as<DynamicObject>().as(Schema::from<TestAllTypes>())); root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<TestAllTypes>()));
checkDynamicTestMessage(
root.get("objectField").as<DynamicObject>().asReader().as(Schema::from<TestAllTypes>()));
checkDynamicTestMessage( checkDynamicTestMessage(
root.getObject("objectField", Schema::from<TestAllTypes>())); root.getObject("objectField", Schema::from<TestAllTypes>()));
...@@ -219,7 +221,10 @@ TEST(DynamicApi, DynamicGenericObjects) { ...@@ -219,7 +221,10 @@ TEST(DynamicApi, DynamicGenericObjects) {
root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<List<uint32_t>>()), root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<List<uint32_t>>()),
{123u, 456u, 789u, 123456789u}); {123u, 456u, 789u, 123456789u});
checkList<uint32_t>( checkList<uint32_t>(
root.get("objectField").as<DynamicObject>().as(Schema::from<List<uint32_t>>()), root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<List<uint32_t>>()),
{123u, 456u, 789u, 123456789u});
checkList<uint32_t>(
root.get("objectField").as<DynamicObject>().asReader().as(Schema::from<List<uint32_t>>()),
{123u, 456u, 789u, 123456789u}); {123u, 456u, 789u, 123456789u});
checkList<uint32_t>( checkList<uint32_t>(
root.getObject("objectField", Schema::from<List<uint32_t>>()), root.getObject("objectField", Schema::from<List<uint32_t>>()),
......
This diff is collapsed.
...@@ -71,7 +71,11 @@ struct DynamicValue { ...@@ -71,7 +71,11 @@ struct DynamicValue {
class Builder; class Builder;
}; };
class DynamicEnum; class DynamicEnum;
class DynamicObject; struct DynamicObject {
DynamicObject() = delete;
class Reader;
class Builder;
};
struct DynamicStruct { struct DynamicStruct {
DynamicStruct() = delete; DynamicStruct() = delete;
class Reader; class Reader;
...@@ -134,19 +138,18 @@ private: ...@@ -134,19 +138,18 @@ private:
friend struct DynamicStruct; friend struct DynamicStruct;
friend struct DynamicList; friend struct DynamicList;
friend struct DynamicValue;
template <typename T> template <typename T>
friend DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value); friend DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value);
}; };
// ------------------------------------------------------------------- // -------------------------------------------------------------------
class DynamicObject { class DynamicObject::Reader {
// Represents an "Object" field of unknown type. This class behaves as a Reader. There is no // Represents an "Object" field of unknown type.
// equivalent Builder; you must use getObject() or initObject() on the containing struct and
// specify a type if you want to build an Object field.
public: public:
DynamicObject() = default; Reader() = default;
template <typename T> template <typename T>
inline typename T::Reader as() const { return AsImpl<T>::apply(*this); } inline typename T::Reader as() const { return AsImpl<T>::apply(*this); }
...@@ -158,7 +161,7 @@ public: ...@@ -158,7 +161,7 @@ public:
private: private:
_::ObjectReader reader; _::ObjectReader reader;
inline DynamicObject(_::ObjectReader reader): reader(reader) {} inline Reader(_::ObjectReader reader): reader(reader) {}
template <typename T, Kind kind = kind<T>()> struct AsImpl; template <typename T, Kind kind = kind<T>()> struct AsImpl;
// Implementation backing the as() method. Needs to be a struct to allow partial // Implementation backing the as() method. Needs to be a struct to allow partial
...@@ -166,6 +169,34 @@ private: ...@@ -166,6 +169,34 @@ private:
friend struct DynamicStruct; friend struct DynamicStruct;
friend struct DynamicList; friend struct DynamicList;
template <typename T, Kind K>
friend struct _::PointerHelpers;
friend class DynamicObject::Builder;
};
class DynamicObject::Builder: kj::DisallowConstCopy {
// Represents an "Object" field of unknown type.
//
// You can't actually do anything with a DynamicObject::Builder except read it. It can't be
// converted to a Builder for any specific type because that could require initializing or
// updating the pointer that points *to* this object. Therefore, you must call
// DynamicStruct::Builder::{get,set,init}Object() and pass a type schema to build object fields.
public:
Builder() = default;
Builder(Builder&) = default;
Builder(Builder&&) = default;
Reader asReader() const { return Reader(builder.asReader()); }
private:
_::ObjectBuilder builder;
inline Builder(_::ObjectBuilder builder): builder(builder) {}
friend struct DynamicStruct;
friend struct DynamicList;
template <typename T, Kind K>
friend struct _::PointerHelpers;
}; };
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -219,7 +250,7 @@ private: ...@@ -219,7 +250,7 @@ private:
template <typename T, Kind K> template <typename T, Kind K>
friend struct _::PointerHelpers; friend struct _::PointerHelpers;
friend class DynamicObject; friend struct DynamicObject;
friend class DynamicStruct::Builder; friend class DynamicStruct::Builder;
friend struct DynamicList; friend struct DynamicList;
friend class MessageReader; friend class MessageReader;
...@@ -366,7 +397,7 @@ private: ...@@ -366,7 +397,7 @@ private:
template <typename T, Kind k> template <typename T, Kind k>
friend struct _::PointerHelpers; friend struct _::PointerHelpers;
friend struct DynamicStruct; friend struct DynamicStruct;
friend class DynamicObject; friend struct DynamicObject;
friend class DynamicList::Builder; friend class DynamicList::Builder;
template <typename T, ::capnp::Kind k> template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_; friend struct ::capnp::ToDynamic_;
...@@ -428,8 +459,8 @@ private: ...@@ -428,8 +459,8 @@ private:
template <> struct ReaderFor_ <DynamicEnum, Kind::UNKNOWN> { typedef DynamicEnum Type; }; template <> struct ReaderFor_ <DynamicEnum, Kind::UNKNOWN> { typedef DynamicEnum Type; };
template <> struct BuilderFor_<DynamicEnum, Kind::UNKNOWN> { typedef DynamicEnum Type; }; template <> struct BuilderFor_<DynamicEnum, Kind::UNKNOWN> { typedef DynamicEnum Type; };
template <> struct ReaderFor_ <DynamicObject, Kind::UNKNOWN> { typedef DynamicObject Type; }; template <> struct ReaderFor_ <DynamicObject, Kind::UNKNOWN> { typedef DynamicObject::Reader Type; };
template <> struct BuilderFor_<DynamicObject, Kind::UNKNOWN> { typedef DynamicObject Type; }; template <> struct BuilderFor_<DynamicObject, Kind::UNKNOWN> { typedef DynamicObject::Builder Type; };
template <> struct ReaderFor_ <DynamicStruct, Kind::UNKNOWN> { typedef DynamicStruct::Reader Type; }; template <> struct ReaderFor_ <DynamicStruct, Kind::UNKNOWN> { typedef DynamicStruct::Reader Type; };
template <> struct BuilderFor_<DynamicStruct, Kind::UNKNOWN> { typedef DynamicStruct::Builder Type; }; template <> struct BuilderFor_<DynamicStruct, Kind::UNKNOWN> { typedef DynamicStruct::Builder Type; };
template <> struct ReaderFor_ <DynamicList, Kind::UNKNOWN> { typedef DynamicList::Reader Type; }; template <> struct ReaderFor_ <DynamicList, Kind::UNKNOWN> { typedef DynamicList::Reader Type; };
...@@ -461,7 +492,8 @@ public: ...@@ -461,7 +492,8 @@ public:
inline Reader(const DynamicList::Reader& value); inline Reader(const DynamicList::Reader& value);
inline Reader(DynamicEnum value); inline Reader(DynamicEnum value);
inline Reader(const DynamicStruct::Reader& value); inline Reader(const DynamicStruct::Reader& value);
inline Reader(DynamicObject value); inline Reader(const DynamicObject::Reader& value);
Reader(ConstSchema constant);
template <typename T, typename = decltype(toDynamic(kj::instance<T>()))> template <typename T, typename = decltype(toDynamic(kj::instance<T>()))>
inline Reader(T value): Reader(toDynamic(value)) {} inline Reader(T value): Reader(toDynamic(value)) {}
...@@ -505,7 +537,7 @@ private: ...@@ -505,7 +537,7 @@ private:
DynamicList::Reader listValue; DynamicList::Reader listValue;
DynamicEnum enumValue; DynamicEnum enumValue;
DynamicStruct::Reader structValue; DynamicStruct::Reader structValue;
DynamicObject objectValue; DynamicObject::Reader objectValue;
}; };
template <typename T, Kind kind = kind<T>()> struct AsImpl; template <typename T, Kind kind = kind<T>()> struct AsImpl;
...@@ -538,7 +570,7 @@ public: ...@@ -538,7 +570,7 @@ public:
inline Builder(DynamicList::Builder value); inline Builder(DynamicList::Builder value);
inline Builder(DynamicEnum value); inline Builder(DynamicEnum value);
inline Builder(DynamicStruct::Builder value); inline Builder(DynamicStruct::Builder value);
inline Builder(DynamicObject value); inline Builder(DynamicObject::Builder value);
template <typename T, typename = decltype(toDynamic(kj::instance<T>()))> template <typename T, typename = decltype(toDynamic(kj::instance<T>()))>
inline Builder(T value): Builder(toDynamic(value)) {} inline Builder(T value): Builder(toDynamic(value)) {}
...@@ -552,6 +584,13 @@ public: ...@@ -552,6 +584,13 @@ public:
Reader asReader() const; Reader asReader() const;
inline Builder(Builder& other) { memcpy(this, &other, sizeof(*this)); }
inline Builder(Builder&& other) { memcpy(this, &other, sizeof(*this)); }
static_assert(__has_trivial_copy(StructSchema) && __has_trivial_copy(ListSchema),
"Assumptions made here do not hold.");
// Hack: We know this type is trivially constructable but the use of DisallowConstCopy causes
// the compiler to believe otherwise.
private: private:
Type type; Type type;
...@@ -566,7 +605,7 @@ private: ...@@ -566,7 +605,7 @@ private:
DynamicList::Builder listValue; DynamicList::Builder listValue;
DynamicEnum enumValue; DynamicEnum enumValue;
DynamicStruct::Builder structValue; DynamicStruct::Builder structValue;
DynamicObject objectValue; DynamicObject::Builder objectValue;
}; };
template <typename T, Kind kind = kind<T>()> struct AsImpl; template <typename T, Kind kind = kind<T>()> struct AsImpl;
...@@ -577,7 +616,8 @@ private: ...@@ -577,7 +616,8 @@ private:
kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value); kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value);
kj::StringTree KJ_STRINGIFY(const DynamicValue::Builder& value); kj::StringTree KJ_STRINGIFY(const DynamicValue::Builder& value);
kj::StringTree KJ_STRINGIFY(DynamicEnum value); kj::StringTree KJ_STRINGIFY(DynamicEnum value);
kj::StringTree KJ_STRINGIFY(const DynamicObject& value); kj::StringTree KJ_STRINGIFY(const DynamicObject::Reader& value);
kj::StringTree KJ_STRINGIFY(const DynamicObject::Builder& value);
kj::StringTree KJ_STRINGIFY(const DynamicStruct::Reader& value); kj::StringTree KJ_STRINGIFY(const DynamicStruct::Reader& value);
kj::StringTree KJ_STRINGIFY(const DynamicStruct::Builder& value); kj::StringTree KJ_STRINGIFY(const DynamicStruct::Builder& value);
kj::StringTree KJ_STRINGIFY(const DynamicList::Reader& value); kj::StringTree KJ_STRINGIFY(const DynamicList::Reader& value);
...@@ -726,6 +766,14 @@ struct PointerHelpers<DynamicList, Kind::UNKNOWN> { ...@@ -726,6 +766,14 @@ struct PointerHelpers<DynamicList, Kind::UNKNOWN> {
} }
}; };
template <>
struct PointerHelpers<DynamicObject, Kind::UNKNOWN> {
// DynamicObject can only be used with get().
static DynamicObject::Reader get(StructReader reader, WirePointerCount index);
static DynamicObject::Builder get(StructBuilder builder, WirePointerCount index);
};
} // namespace _ (private) } // namespace _ (private)
// ======================================================================================= // =======================================================================================
...@@ -789,7 +837,6 @@ CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(unsigned long long, UINT, uint); ...@@ -789,7 +837,6 @@ CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(unsigned long long, UINT, uint);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(float, FLOAT, float); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(float, FLOAT, float);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(double, FLOAT, float); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(double, FLOAT, float);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicEnum, ENUM, enum); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicEnum, ENUM, enum);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicObject, OBJECT, object);
#undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR #undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR
#define CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \ #define CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \
...@@ -802,6 +849,7 @@ CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Text, TEXT, text); ...@@ -802,6 +849,7 @@ CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Text, TEXT, text);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Data, DATA, data); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(Data, DATA, data);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicList, LIST, list); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicList, LIST, list);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicStruct, STRUCT, struct); CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicStruct, STRUCT, struct);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicObject, OBJECT, object);
#undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR #undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR
...@@ -891,15 +939,15 @@ struct DynamicValue::Builder::AsImpl<T, Kind::LIST> { ...@@ -891,15 +939,15 @@ struct DynamicValue::Builder::AsImpl<T, Kind::LIST> {
// ------------------------------------------------------------------- // -------------------------------------------------------------------
template <typename T> template <typename T>
struct DynamicObject::AsImpl<T, Kind::STRUCT> { struct DynamicObject::Reader::AsImpl<T, Kind::STRUCT> {
static T apply(DynamicObject value) { static T apply(DynamicObject::Reader value) {
return value.as(Schema::from<T>()).template as<T>(); return value.as(Schema::from<T>()).template as<T>();
} }
}; };
template <typename T> template <typename T>
struct DynamicObject::AsImpl<T, Kind::LIST> { struct DynamicObject::Reader::AsImpl<T, Kind::LIST> {
static T apply(DynamicObject value) { static T apply(DynamicObject::Reader value) {
return value.as(Schema::from<T>()).template as<T>(); return value.as(Schema::from<T>()).template as<T>();
} }
}; };
...@@ -960,6 +1008,13 @@ inline DynamicList::Builder DynamicList::Builder::as<DynamicList>() { ...@@ -960,6 +1008,13 @@ inline DynamicList::Builder DynamicList::Builder::as<DynamicList>() {
return *this; return *this;
} }
// -------------------------------------------------------------------
template <typename T>
ReaderFor<T> ConstSchema::as() const {
return DynamicValue::Reader(*this).as<T>();
}
} // namespace capnp } // namespace capnp
#endif // CAPNP_DYNAMIC_H_ #endif // CAPNP_DYNAMIC_H_
...@@ -2537,6 +2537,18 @@ ObjectReader ListReader::getObjectElement(ElementCount index) const { ...@@ -2537,6 +2537,18 @@ ObjectReader ListReader::getObjectElement(ElementCount index) const {
segment, checkAlignment(ptr + index * step / BITS_PER_BYTE), nullptr, nestingLimit); segment, checkAlignment(ptr + index * step / BITS_PER_BYTE), nullptr, nestingLimit);
} }
ObjectReader ObjectBuilder::asReader() const {
switch (kind) {
case ObjectKind::NULL_POINTER:
return ObjectReader();
case ObjectKind::STRUCT:
return ObjectReader(structBuilder.asReader());
case ObjectKind::LIST:
return ObjectReader(listBuilder.asReader());
}
KJ_UNREACHABLE;
}
// ======================================================================================= // =======================================================================================
// OrphanBuilder // OrphanBuilder
......
...@@ -710,6 +710,14 @@ struct ObjectBuilder { ...@@ -710,6 +710,14 @@ struct ObjectBuilder {
: kind(ObjectKind::STRUCT), structBuilder(structBuilder) {} : kind(ObjectKind::STRUCT), structBuilder(structBuilder) {}
ObjectBuilder(ListBuilder listBuilder) ObjectBuilder(ListBuilder listBuilder)
: kind(ObjectKind::LIST), listBuilder(listBuilder) {} : kind(ObjectKind::LIST), listBuilder(listBuilder) {}
ObjectReader asReader() const;
inline ObjectBuilder(ObjectBuilder& other) { memcpy(this, &other, sizeof(*this)); }
inline ObjectBuilder(ObjectBuilder&& other) { memcpy(this, &other, sizeof(*this)); }
// Hack: Compiler thinks StructBuilder and ListBuilder are non-trivially-copyable due to the
// inheritance from DisallowConstCopy, but that means the union causes ObjectBuilder's copy
// constructor to be deleted. We happen to know that trivial copying works here...
}; };
struct ObjectReader { struct ObjectReader {
......
...@@ -91,7 +91,7 @@ TEST(SchemaParser, Basic) { ...@@ -91,7 +91,7 @@ TEST(SchemaParser, Basic) {
}; };
ParsedSchema barSchema = parser.parseFile(SchemaFile::newDiskFile( ParsedSchema barSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("foo2/bar2.capnp"), kj::str("src/foo/bar.capnp"), importPath, reader)); "foo2/bar2.capnp", "src/foo/bar.capnp", importPath, reader));
auto barProto = barSchema.getProto(); auto barProto = barSchema.getProto();
EXPECT_EQ(0x8123456789abcdefull, barProto.getId()); EXPECT_EQ(0x8123456789abcdefull, barProto.getId());
...@@ -110,24 +110,24 @@ TEST(SchemaParser, Basic) { ...@@ -110,24 +110,24 @@ TEST(SchemaParser, Basic) {
EXPECT_EQ(0x856789abcdef1234ull, getFieldTypeFileId(barFields[3])); EXPECT_EQ(0x856789abcdef1234ull, getFieldTypeFileId(barFields[3]));
auto bazSchema = parser.parseFile(SchemaFile::newDiskFile( auto bazSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("not/used/because/already/loaded"), "not/used/because/already/loaded",
kj::str("src/foo/baz.capnp"), importPath, reader)); "src/foo/baz.capnp", importPath, reader));
EXPECT_EQ(0x823456789abcdef1ull, bazSchema.getProto().getId()); EXPECT_EQ(0x823456789abcdef1ull, bazSchema.getProto().getId());
EXPECT_EQ("foo2/baz.capnp", bazSchema.getProto().getDisplayName()); EXPECT_EQ("foo2/baz.capnp", bazSchema.getProto().getDisplayName());
auto bazStruct = bazSchema.getNested("Baz").asStruct(); auto bazStruct = bazSchema.getNested("Baz").asStruct();
EXPECT_EQ(bazStruct, barStruct.getDependency(bazStruct.getProto().getId())); EXPECT_EQ(bazStruct, barStruct.getDependency(bazStruct.getProto().getId()));
auto corgeSchema = parser.parseFile(SchemaFile::newDiskFile( auto corgeSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("not/used/because/already/loaded"), "not/used/because/already/loaded",
kj::str("src/qux/corge.capnp"), importPath, reader)); "src/qux/corge.capnp", importPath, reader));
EXPECT_EQ(0x83456789abcdef12ull, corgeSchema.getProto().getId()); EXPECT_EQ(0x83456789abcdef12ull, corgeSchema.getProto().getId());
EXPECT_EQ("qux/corge.capnp", corgeSchema.getProto().getDisplayName()); EXPECT_EQ("qux/corge.capnp", corgeSchema.getProto().getDisplayName());
auto corgeStruct = corgeSchema.getNested("Corge").asStruct(); auto corgeStruct = corgeSchema.getNested("Corge").asStruct();
EXPECT_EQ(corgeStruct, barStruct.getDependency(corgeStruct.getProto().getId())); EXPECT_EQ(corgeStruct, barStruct.getDependency(corgeStruct.getProto().getId()));
auto graultSchema = parser.parseFile(SchemaFile::newDiskFile( auto graultSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("not/used/because/already/loaded"), "not/used/because/already/loaded",
kj::str("/usr/include/grault.capnp"), importPath, reader)); "/usr/include/grault.capnp", importPath, reader));
EXPECT_EQ(0x8456789abcdef123ull, graultSchema.getProto().getId()); EXPECT_EQ(0x8456789abcdef123ull, graultSchema.getProto().getId());
EXPECT_EQ("grault.capnp", graultSchema.getProto().getDisplayName()); EXPECT_EQ("grault.capnp", graultSchema.getProto().getDisplayName());
auto graultStruct = graultSchema.getNested("Grault").asStruct(); auto graultStruct = graultSchema.getNested("Grault").asStruct();
...@@ -136,11 +136,45 @@ TEST(SchemaParser, Basic) { ...@@ -136,11 +136,45 @@ TEST(SchemaParser, Basic) {
// Try importing the other grault.capnp directly. It'll get the display name we specify since // Try importing the other grault.capnp directly. It'll get the display name we specify since
// it wasn't imported before. // it wasn't imported before.
auto wrongGraultSchema = parser.parseFile(SchemaFile::newDiskFile( auto wrongGraultSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("weird/display/name.capnp"), "weird/display/name.capnp",
kj::str("/opt/include/grault.capnp"), importPath, reader)); "/opt/include/grault.capnp", importPath, reader));
EXPECT_EQ(0x8000000000000001ull, wrongGraultSchema.getProto().getId()); EXPECT_EQ(0x8000000000000001ull, wrongGraultSchema.getProto().getId());
EXPECT_EQ("weird/display/name.capnp", wrongGraultSchema.getProto().getDisplayName()); EXPECT_EQ("weird/display/name.capnp", wrongGraultSchema.getProto().getDisplayName());
} }
TEST(SchemaParser, Constants) {
// This is actually a test of the full dynamic API stack for constants, because the schemas for
// constants are not actually accessible from the generated code API, so the only way to ever
// get a ConstSchema is by parsing it.
SchemaParser parser;
FakeFileReader reader;
reader.add("const.capnp",
"@0x8123456789abcdef;\n"
"const uint32Const :UInt32 = 1234;\n"
"const listConst :List(Float32) = [1.25, 2.5, 3e4];\n"
"const structConst :Foo = (bar = 123, baz = \"qux\");\n"
"struct Foo {\n"
" bar @0 :Int16;\n"
" baz @1 :Text;\n"
"}\n");
ParsedSchema barSchema = parser.parseFile(SchemaFile::newDiskFile(
"const.capnp", "const.capnp", nullptr, reader));
EXPECT_EQ(1234, barSchema.getNested("uint32Const").asConst().as<uint32_t>());
auto list = barSchema.getNested("listConst").asConst().as<DynamicList>();
ASSERT_EQ(3u, list.size());
EXPECT_EQ(1.25, list[0].as<float>());
EXPECT_EQ(2.5, list[1].as<float>());
EXPECT_EQ(3e4f, list[2].as<float>());
auto structConst = barSchema.getNested("structConst").asConst().as<DynamicStruct>();
EXPECT_EQ(123, structConst.get("bar").as<int16_t>());
EXPECT_EQ("qux", structConst.get("baz").as<Text>());
}
} // namespace } // namespace
} // namespace capnp } // namespace capnp
...@@ -77,12 +77,45 @@ InterfaceSchema Schema::asInterface() const { ...@@ -77,12 +77,45 @@ InterfaceSchema Schema::asInterface() const {
return InterfaceSchema(raw); return InterfaceSchema(raw);
} }
ConstSchema Schema::asConst() const {
KJ_REQUIRE(getProto().isConst(), "Tried to use non-constant schema as a constant.",
getProto().getDisplayName());
return ConstSchema(raw);
}
void Schema::requireUsableAs(const _::RawSchema* expected) const { void Schema::requireUsableAs(const _::RawSchema* expected) const {
KJ_REQUIRE(raw == expected || KJ_REQUIRE(raw == expected ||
(raw != nullptr && expected != nullptr && raw->canCastTo == expected), (raw != nullptr && expected != nullptr && raw->canCastTo == expected),
"This schema is not compatible with the requested native type."); "This schema is not compatible with the requested native type.");
} }
uint32_t Schema::getSchemaOffset(const schema::Value::Reader& value) const {
const word* ptr;
switch (value.which()) {
case schema::Value::TEXT:
ptr = reinterpret_cast<const word*>(value.getText().begin());
break;
case schema::Value::DATA:
ptr = reinterpret_cast<const word*>(value.getData().begin());
break;
case schema::Value::STRUCT:
ptr = value.getStruct<_::UncheckedMessage>();
break;
case schema::Value::LIST:
ptr = value.getList<_::UncheckedMessage>();
break;
case schema::Value::OBJECT:
ptr = value.getObject<_::UncheckedMessage>();
break;
default:
KJ_FAIL_ASSERT("getDefaultValueSchemaOffset() can only be called on struct, list, "
"and object fields.");
}
return ptr - raw->encodedNode;
}
// ======================================================================================= // =======================================================================================
namespace { namespace {
...@@ -156,31 +189,7 @@ kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t dis ...@@ -156,31 +189,7 @@ kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t dis
} }
uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const { uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const {
auto defaultValue = proto.getNonGroup().getDefaultValue(); return parent.getSchemaOffset(proto.getNonGroup().getDefaultValue());
const word* ptr;
switch (defaultValue.which()) {
case schema::Value::TEXT:
ptr = reinterpret_cast<const word*>(defaultValue.getText().begin());
break;
case schema::Value::DATA:
ptr = reinterpret_cast<const word*>(defaultValue.getData().begin());
break;
case schema::Value::STRUCT:
ptr = defaultValue.getStruct<_::UncheckedMessage>();
break;
case schema::Value::LIST:
ptr = defaultValue.getList<_::UncheckedMessage>();
break;
case schema::Value::OBJECT:
ptr = defaultValue.getObject<_::UncheckedMessage>();
break;
default:
KJ_FAIL_ASSERT("getDefaultValueSchemaOffset() can only be called on struct, list, "
"and object fields.");
}
return ptr - parent.raw->encodedNode;
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -219,6 +228,12 @@ InterfaceSchema::Method InterfaceSchema::getMethodByName(kj::StringPtr name) con ...@@ -219,6 +228,12 @@ InterfaceSchema::Method InterfaceSchema::getMethodByName(kj::StringPtr name) con
} }
} }
// -------------------------------------------------------------------
uint32_t ConstSchema::getValueSchemaOffset() const {
return getSchemaOffset(getProto().getConst().getValue());
}
// ======================================================================================= // =======================================================================================
ListSchema ListSchema::of(schema::Type::Which primitiveType) { ListSchema ListSchema::of(schema::Type::Which primitiveType) {
......
...@@ -32,6 +32,7 @@ class Schema; ...@@ -32,6 +32,7 @@ class Schema;
class StructSchema; class StructSchema;
class EnumSchema; class EnumSchema;
class InterfaceSchema; class InterfaceSchema;
class ConstSchema;
class ListSchema; class ListSchema;
template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; }; template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; };
...@@ -57,6 +58,8 @@ public: ...@@ -57,6 +58,8 @@ public:
// Get the Schema for a particular compiled-in type. // Get the Schema for a particular compiled-in type.
schema::Node::Reader getProto() const; schema::Node::Reader getProto() const;
// Get the underlying Cap'n Proto representation of the schema node. (Note that this accessor
// has performance comparable to accessors of struct-typed fields on Reader classes.)
kj::ArrayPtr<const word> asUncheckedMessage() const; kj::ArrayPtr<const word> asUncheckedMessage() const;
// Get the encoded schema node content as a single message segment. It is safe to read as an // Get the encoded schema node content as a single message segment. It is safe to read as an
...@@ -84,7 +87,9 @@ public: ...@@ -84,7 +87,9 @@ public:
StructSchema asStruct() const; StructSchema asStruct() const;
EnumSchema asEnum() const; EnumSchema asEnum() const;
InterfaceSchema asInterface() const; InterfaceSchema asInterface() const;
// Cast the Schema to a specific type. Throws an exception if the type doesn't match. ConstSchema asConst() const;
// Cast the Schema to a specific type. Throws an exception if the type doesn't match. Use
// getProto() to determine type, e.g. getProto().isStruct().
inline bool operator==(const Schema& other) const { return raw == other.raw; } inline bool operator==(const Schema& other) const { return raw == other.raw; }
inline bool operator!=(const Schema& other) const { return raw != other.raw; } inline bool operator!=(const Schema& other) const { return raw != other.raw; }
...@@ -114,9 +119,12 @@ private: ...@@ -114,9 +119,12 @@ private:
void requireUsableAs(const _::RawSchema* expected) const; void requireUsableAs(const _::RawSchema* expected) const;
uint32_t getSchemaOffset(const schema::Value::Reader& value) const;
friend class StructSchema; friend class StructSchema;
friend class EnumSchema; friend class EnumSchema;
friend class InterfaceSchema; friend class InterfaceSchema;
friend class ConstSchema;
friend class ListSchema; friend class ListSchema;
friend class SchemaLoader; friend class SchemaLoader;
}; };
...@@ -401,6 +409,32 @@ private: ...@@ -401,6 +409,32 @@ private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
class ConstSchema: public Schema {
// Represents a constant declaration.
//
// `ConstSchema` can be implicitly cast to DynamicValue to read its value.
public:
ConstSchema() = default;
template <typename T>
ReaderFor<T> as() const;
// Read the constant's value. This is a convenience method equivalent to casting the ConstSchema
// to a DynamicValue and then calling its `as<T>()` method. For dependency reasons, this method
// is defined in <capnp/dynamic.h>, which you must #include explicitly.
uint32_t getValueSchemaOffset() const;
// Much like StructSchema::Field::getDefaultValueSchemaOffset(), if the constant has pointer
// type, this gets the offset from the beginning of the constant's schema node to a pointer
// representing the constant value.
private:
ConstSchema(const _::RawSchema* raw): Schema(raw) {}
friend class Schema;
};
// -------------------------------------------------------------------
class ListSchema { class ListSchema {
// ListSchema is a little different because list types are not described by schema nodes. So, // ListSchema is a little different because list types are not described by schema nodes. So,
// ListSchema doesn't subclass Schema. // ListSchema doesn't subclass Schema.
......
...@@ -267,7 +267,8 @@ kj::StringTree prettyPrint(DynamicList::Builder value) { return prettyPrint(valu ...@@ -267,7 +267,8 @@ kj::StringTree prettyPrint(DynamicList::Builder value) { return prettyPrint(valu
kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value) { return stringify(value); } kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value) { return stringify(value); }
kj::StringTree KJ_STRINGIFY(const DynamicValue::Builder& value) { return stringify(value.asReader()); } kj::StringTree KJ_STRINGIFY(const DynamicValue::Builder& value) { return stringify(value.asReader()); }
kj::StringTree KJ_STRINGIFY(DynamicEnum value) { return stringify(value); } kj::StringTree KJ_STRINGIFY(DynamicEnum value) { return stringify(value); }
kj::StringTree KJ_STRINGIFY(const DynamicObject& value) { return stringify(value); } kj::StringTree KJ_STRINGIFY(const DynamicObject::Reader& value) { return stringify(value); }
kj::StringTree KJ_STRINGIFY(const DynamicObject::Builder& value) { return stringify(value.asReader()); }
kj::StringTree KJ_STRINGIFY(const DynamicStruct::Reader& value) { return stringify(value); } kj::StringTree KJ_STRINGIFY(const DynamicStruct::Reader& value) { return stringify(value); }
kj::StringTree KJ_STRINGIFY(const DynamicStruct::Builder& value) { return stringify(value.asReader()); } kj::StringTree KJ_STRINGIFY(const DynamicStruct::Builder& value) { return stringify(value.asReader()); }
kj::StringTree KJ_STRINGIFY(const DynamicList::Reader& value) { return stringify(value); } kj::StringTree KJ_STRINGIFY(const DynamicList::Reader& value) { return stringify(value); }
......
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