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(
if (constValue.getType() == DynamicValue::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();
switch (constType.which()) {
case schema::Type::STRUCT:
......
......@@ -193,7 +193,9 @@ TEST(DynamicApi, DynamicGenericObjects) {
checkDynamicTestMessage(
root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<TestAllTypes>()));
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(
root.getObject("objectField", Schema::from<TestAllTypes>()));
......@@ -219,7 +221,10 @@ TEST(DynamicApi, DynamicGenericObjects) {
root.asReader().get("objectField").as<DynamicObject>().as(Schema::from<List<uint32_t>>()),
{123u, 456u, 789u, 123456789u});
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});
checkList<uint32_t>(
root.getObject("objectField", Schema::from<List<uint32_t>>()),
......
......@@ -125,7 +125,7 @@ uint16_t DynamicEnum::asImpl(uint64_t requestedTypeId) const {
// =======================================================================================
DynamicStruct::Reader DynamicObject::as(StructSchema schema) const {
DynamicStruct::Reader DynamicObject::Reader::as(StructSchema schema) const {
if (reader.kind == _::ObjectKind::NULL_POINTER) {
return DynamicStruct::Reader(schema, _::StructReader());
}
......@@ -136,7 +136,7 @@ DynamicStruct::Reader DynamicObject::as(StructSchema schema) const {
return DynamicStruct::Reader(schema, reader.structReader);
}
DynamicList::Reader DynamicObject::as(ListSchema schema) const {
DynamicList::Reader DynamicObject::Reader::as(ListSchema schema) const {
if (reader.kind == _::ObjectKind::NULL_POINTER) {
return DynamicList::Reader(schema, _::ListReader());
}
......@@ -206,14 +206,13 @@ DynamicValue::Reader DynamicStruct::Reader::get(StructSchema::Field field) const
switch (type.which()) {
case schema::Type::VOID:
return DynamicValue::Reader(
reader.getDataField<Void>(nonGroup.getOffset() * ELEMENTS));
return reader.getDataField<Void>(nonGroup.getOffset() * ELEMENTS);
#define HANDLE_TYPE(discrim, titleCase, type) \
case schema::Type::discrim: \
return DynamicValue::Reader(reader.getDataField<type>( \
return reader.getDataField<type>( \
nonGroup.getOffset() * ELEMENTS, \
bitCast<_::Mask<type>>(dval.get##titleCase())));
bitCast<_::Mask<type>>(dval.get##titleCase()));
HANDLE_TYPE(BOOL, Bool, bool)
HANDLE_TYPE(INT8, Int8, int8_t)
......@@ -232,45 +231,43 @@ DynamicValue::Reader DynamicStruct::Reader::get(StructSchema::Field field) const
case schema::Type::ENUM: {
uint16_t typedDval;
typedDval = dval.getEnum();
return DynamicValue::Reader(DynamicEnum(
return DynamicEnum(
field.getContainingStruct().getDependency(type.getEnum()).asEnum(),
reader.getDataField<uint16_t>(nonGroup.getOffset() * ELEMENTS, typedDval)));
reader.getDataField<uint16_t>(nonGroup.getOffset() * ELEMENTS, typedDval));
}
case schema::Type::TEXT: {
Text::Reader typedDval = dval.getText();
return DynamicValue::Reader(
reader.getBlobField<Text>(nonGroup.getOffset() * POINTERS,
typedDval.begin(), typedDval.size() * BYTES));
return reader.getBlobField<Text>(nonGroup.getOffset() * POINTERS,
typedDval.begin(), typedDval.size() * BYTES);
}
case schema::Type::DATA: {
Data::Reader typedDval = dval.getData();
return DynamicValue::Reader(
reader.getBlobField<Data>(nonGroup.getOffset() * POINTERS,
typedDval.begin(), typedDval.size() * BYTES));
return reader.getBlobField<Data>(nonGroup.getOffset() * POINTERS,
typedDval.begin(), typedDval.size() * BYTES);
}
case schema::Type::LIST: {
auto elementType = type.getList();
return DynamicValue::Reader(DynamicList::Reader(
return DynamicList::Reader(
ListSchema::of(elementType, field.getContainingStruct()),
reader.getListField(nonGroup.getOffset() * POINTERS,
elementSizeFor(elementType.which()),
dval.getList<_::UncheckedMessage>())));
dval.getList<_::UncheckedMessage>()));
}
case schema::Type::STRUCT: {
return DynamicValue::Reader(DynamicStruct::Reader(
return DynamicStruct::Reader(
field.getContainingStruct().getDependency(type.getStruct()).asStruct(),
reader.getStructField(nonGroup.getOffset() * POINTERS,
dval.getStruct<_::UncheckedMessage>())));
dval.getStruct<_::UncheckedMessage>()));
}
case schema::Type::OBJECT: {
return DynamicValue::Reader(DynamicObject(
return DynamicObject::Reader(
reader.getObjectField(nonGroup.getOffset() * POINTERS,
dval.getObject<_::UncheckedMessage>())));
dval.getObject<_::UncheckedMessage>()));
}
case schema::Type::INTERFACE:
......@@ -369,8 +366,8 @@ DynamicValue::Builder DynamicStruct::Builder::get(StructSchema::Field field) {
}
case schema::Type::OBJECT: {
return DynamicObject(
builder.asReader().getObjectField(
return DynamicObject::Builder(
builder.getObjectField(
nonGroup.getOffset() * POINTERS,
dval.getObject<_::UncheckedMessage>()));
}
......@@ -890,7 +887,7 @@ DynamicValue::Reader DynamicList::Reader::operator[](uint index) const {
switch (schema.whichElementType()) {
#define HANDLE_TYPE(name, discrim, typeName) \
case schema::Type::discrim: \
return DynamicValue::Reader(reader.getDataElement<typeName>(index * ELEMENTS));
return reader.getDataElement<typeName>(index * ELEMENTS);
HANDLE_TYPE(void, VOID, Void)
HANDLE_TYPE(bool, BOOL, bool)
......@@ -907,28 +904,26 @@ DynamicValue::Reader DynamicList::Reader::operator[](uint index) const {
#undef HANDLE_TYPE
case schema::Type::TEXT:
return DynamicValue::Reader(reader.getBlobElement<Text>(index * ELEMENTS));
return reader.getBlobElement<Text>(index * ELEMENTS);
case schema::Type::DATA:
return DynamicValue::Reader(reader.getBlobElement<Data>(index * ELEMENTS));
return reader.getBlobElement<Data>(index * ELEMENTS);
case schema::Type::LIST: {
auto elementType = schema.getListElementType();
return DynamicValue::Reader(DynamicList::Reader(
elementType, reader.getListElement(
index * ELEMENTS, elementSizeFor(elementType.whichElementType()))));
return DynamicList::Reader(elementType, reader.getListElement(
index * ELEMENTS, elementSizeFor(elementType.whichElementType())));
}
case schema::Type::STRUCT:
return DynamicValue::Reader(DynamicStruct::Reader(
schema.getStructElementType(), reader.getStructElement(index * ELEMENTS)));
return DynamicStruct::Reader(schema.getStructElementType(),
reader.getStructElement(index * ELEMENTS));
case schema::Type::ENUM:
return DynamicValue::Reader(DynamicEnum(
schema.getEnumElementType(), reader.getDataElement<uint16_t>(index * ELEMENTS)));
return DynamicEnum(schema.getEnumElementType(),
reader.getDataElement<uint16_t>(index * ELEMENTS));
case schema::Type::OBJECT:
return DynamicValue::Reader(DynamicObject(
reader.getObjectElement(index * ELEMENTS)));
return DynamicObject::Reader(reader.getObjectElement(index * ELEMENTS));
case schema::Type::INTERFACE:
KJ_FAIL_ASSERT("Interfaces not implemented.") {
......@@ -945,7 +940,7 @@ DynamicValue::Builder DynamicList::Builder::operator[](uint index) {
switch (schema.whichElementType()) {
#define HANDLE_TYPE(name, discrim, typeName) \
case schema::Type::discrim: \
return DynamicValue::Builder(builder.getDataElement<typeName>(index * ELEMENTS));
return builder.getDataElement<typeName>(index * ELEMENTS);
HANDLE_TYPE(void, VOID, Void)
HANDLE_TYPE(bool, BOOL, bool)
......@@ -962,32 +957,32 @@ DynamicValue::Builder DynamicList::Builder::operator[](uint index) {
#undef HANDLE_TYPE
case schema::Type::TEXT:
return DynamicValue::Builder(builder.getBlobElement<Text>(index * ELEMENTS));
return builder.getBlobElement<Text>(index * ELEMENTS);
case schema::Type::DATA:
return DynamicValue::Builder(builder.getBlobElement<Data>(index * ELEMENTS));
return builder.getBlobElement<Data>(index * ELEMENTS);
case schema::Type::LIST: {
ListSchema elementType = schema.getListElementType();
if (elementType.whichElementType() == schema::Type::STRUCT) {
return DynamicValue::Builder(DynamicList::Builder(elementType,
return DynamicList::Builder(elementType,
builder.getStructListElement(
index * ELEMENTS,
structSizeFromSchema(elementType.getStructElementType()))));
structSizeFromSchema(elementType.getStructElementType())));
} else {
return DynamicValue::Builder(DynamicList::Builder(elementType,
return DynamicList::Builder(elementType,
builder.getListElement(
index * ELEMENTS,
elementSizeFor(elementType.whichElementType()))));
elementSizeFor(elementType.whichElementType())));
}
}
case schema::Type::STRUCT:
return DynamicValue::Builder(DynamicStruct::Builder(
schema.getStructElementType(), builder.getStructElement(index * ELEMENTS)));
return DynamicStruct::Builder(schema.getStructElementType(),
builder.getStructElement(index * ELEMENTS));
case schema::Type::ENUM:
return DynamicValue::Builder(DynamicEnum(
schema.getEnumElementType(), builder.getDataElement<uint16_t>(index * ELEMENTS)));
return DynamicEnum(schema.getEnumElementType(),
builder.getDataElement<uint16_t>(index * ELEMENTS));
case schema::Type::OBJECT:
KJ_FAIL_ASSERT("List(Object) not supported.");
......@@ -1103,24 +1098,24 @@ DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
return nullptr;
case schema::Type::TEXT:
return DynamicValue::Builder(builder.initBlobElement<Text>(index * ELEMENTS, size * BYTES));
return builder.initBlobElement<Text>(index * ELEMENTS, size * BYTES);
case schema::Type::DATA:
return DynamicValue::Builder(builder.initBlobElement<Data>(index * ELEMENTS, size * BYTES));
return builder.initBlobElement<Data>(index * ELEMENTS, size * BYTES);
case schema::Type::LIST: {
auto elementType = schema.getListElementType();
if (elementType.whichElementType() == schema::Type::STRUCT) {
return DynamicValue::Builder(DynamicList::Builder(
return DynamicList::Builder(
elementType, builder.initStructListElement(
index * ELEMENTS, size * ELEMENTS,
structSizeFromSchema(elementType.getStructElementType()))));
structSizeFromSchema(elementType.getStructElementType())));
} else {
return DynamicValue::Builder(DynamicList::Builder(
return DynamicList::Builder(
elementType, builder.initListElement(
index * ELEMENTS, elementSizeFor(elementType.whichElementType()),
size * ELEMENTS)));
size * ELEMENTS));
}
}
......@@ -1147,6 +1142,47 @@ DynamicList::Reader DynamicList::Builder::asReader() const {
// =======================================================================================
DynamicValue::Reader::Reader(ConstSchema constant) {
auto typeSchema = constant.getProto().getConst().getType();
auto value = constant.getProto().getConst().getValue();
switch (typeSchema.which()) {
case schema::Type::VOID: *this = capnp::VOID; break;
case schema::Type::BOOL: *this = value.getBool(); break;
case schema::Type::INT8: *this = value.getInt8(); break;
case schema::Type::INT16: *this = value.getInt16(); break;
case schema::Type::INT32: *this = value.getInt32(); break;
case schema::Type::INT64: *this = value.getInt64(); break;
case schema::Type::UINT8: *this = value.getUint8(); break;
case schema::Type::UINT16: *this = value.getUint16(); break;
case schema::Type::UINT32: *this = value.getUint32(); break;
case schema::Type::UINT64: *this = value.getUint64(); break;
case schema::Type::FLOAT32: *this = value.getFloat32(); break;
case schema::Type::FLOAT64: *this = value.getFloat64(); break;
case schema::Type::TEXT: *this = value.getText(); break;
case schema::Type::DATA: *this = value.getData(); break;
case schema::Type::ENUM:
*this = DynamicEnum(constant.getDependency(typeSchema.getEnum()).asEnum(), value.getEnum());
break;
case schema::Type::STRUCT:
*this = value.getStruct<DynamicStruct>(
constant.getDependency(typeSchema.getStruct()).asStruct());
break;
case schema::Type::LIST:
*this = value.getList<DynamicList>(ListSchema::of(typeSchema.getList(), constant));
break;
case schema::Type::OBJECT:
*this = value.getObject<DynamicObject>();
break;
case schema::Type::INTERFACE:
KJ_FAIL_ASSERT("Constants can't have interface type.");
}
}
DynamicValue::Reader DynamicValue::Builder::asReader() const {
switch (type) {
case UNKNOWN: return Reader();
......@@ -1161,7 +1197,7 @@ DynamicValue::Reader DynamicValue::Builder::asReader() const {
case ENUM: return Reader(enumValue);
case STRUCT: return Reader(structValue.asReader());
case INTERFACE: KJ_FAIL_ASSERT("Interfaces not implemented."); return Reader();
case OBJECT: return Reader(objectValue);
case OBJECT: return Reader(objectValue.asReader());
}
KJ_FAIL_ASSERT("Missing switch case.");
return Reader();
......@@ -1407,6 +1443,16 @@ DynamicList::Builder PointerHelpers<DynamicList, Kind::UNKNOWN>::init(
}
}
DynamicObject::Reader PointerHelpers<DynamicObject, Kind::UNKNOWN>::get(
StructReader reader, WirePointerCount index) {
return DynamicObject::Reader(reader.getObjectField(index, nullptr));
}
DynamicObject::Builder PointerHelpers<DynamicObject, Kind::UNKNOWN>::get(
StructBuilder builder, WirePointerCount index) {
return DynamicObject::Builder(builder.getObjectField(index, nullptr));
}
} // namespace _ (private)
// -------------------------------------------------------------------
......
......@@ -71,7 +71,11 @@ struct DynamicValue {
class Builder;
};
class DynamicEnum;
class DynamicObject;
struct DynamicObject {
DynamicObject() = delete;
class Reader;
class Builder;
};
struct DynamicStruct {
DynamicStruct() = delete;
class Reader;
......@@ -134,19 +138,18 @@ private:
friend struct DynamicStruct;
friend struct DynamicList;
friend struct DynamicValue;
template <typename T>
friend DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value);
};
// -------------------------------------------------------------------
class DynamicObject {
// Represents an "Object" field of unknown type. This class behaves as a Reader. There is no
// equivalent Builder; you must use getObject() or initObject() on the containing struct and
// specify a type if you want to build an Object field.
class DynamicObject::Reader {
// Represents an "Object" field of unknown type.
public:
DynamicObject() = default;
Reader() = default;
template <typename T>
inline typename T::Reader as() const { return AsImpl<T>::apply(*this); }
......@@ -158,7 +161,7 @@ public:
private:
_::ObjectReader reader;
inline DynamicObject(_::ObjectReader reader): reader(reader) {}
inline Reader(_::ObjectReader reader): reader(reader) {}
template <typename T, Kind kind = kind<T>()> struct AsImpl;
// Implementation backing the as() method. Needs to be a struct to allow partial
......@@ -166,6 +169,34 @@ private:
friend struct DynamicStruct;
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:
template <typename T, Kind K>
friend struct _::PointerHelpers;
friend class DynamicObject;
friend struct DynamicObject;
friend class DynamicStruct::Builder;
friend struct DynamicList;
friend class MessageReader;
......@@ -366,7 +397,7 @@ private:
template <typename T, Kind k>
friend struct _::PointerHelpers;
friend struct DynamicStruct;
friend class DynamicObject;
friend struct DynamicObject;
friend class DynamicList::Builder;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
......@@ -428,8 +459,8 @@ private:
template <> struct ReaderFor_ <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 BuilderFor_<DynamicObject, Kind::UNKNOWN> { typedef DynamicObject Type; };
template <> struct ReaderFor_ <DynamicObject, Kind::UNKNOWN> { typedef DynamicObject::Reader Type; };
template <> struct BuilderFor_<DynamicObject, Kind::UNKNOWN> { typedef DynamicObject::Builder Type; };
template <> struct ReaderFor_ <DynamicStruct, Kind::UNKNOWN> { typedef DynamicStruct::Reader Type; };
template <> struct BuilderFor_<DynamicStruct, Kind::UNKNOWN> { typedef DynamicStruct::Builder Type; };
template <> struct ReaderFor_ <DynamicList, Kind::UNKNOWN> { typedef DynamicList::Reader Type; };
......@@ -461,7 +492,8 @@ public:
inline Reader(const DynamicList::Reader& value);
inline Reader(DynamicEnum 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>()))>
inline Reader(T value): Reader(toDynamic(value)) {}
......@@ -505,7 +537,7 @@ private:
DynamicList::Reader listValue;
DynamicEnum enumValue;
DynamicStruct::Reader structValue;
DynamicObject objectValue;
DynamicObject::Reader objectValue;
};
template <typename T, Kind kind = kind<T>()> struct AsImpl;
......@@ -538,7 +570,7 @@ public:
inline Builder(DynamicList::Builder value);
inline Builder(DynamicEnum value);
inline Builder(DynamicStruct::Builder value);
inline Builder(DynamicObject value);
inline Builder(DynamicObject::Builder value);
template <typename T, typename = decltype(toDynamic(kj::instance<T>()))>
inline Builder(T value): Builder(toDynamic(value)) {}
......@@ -552,6 +584,13 @@ public:
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:
Type type;
......@@ -566,7 +605,7 @@ private:
DynamicList::Builder listValue;
DynamicEnum enumValue;
DynamicStruct::Builder structValue;
DynamicObject objectValue;
DynamicObject::Builder objectValue;
};
template <typename T, Kind kind = kind<T>()> struct AsImpl;
......@@ -577,7 +616,8 @@ private:
kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value);
kj::StringTree KJ_STRINGIFY(const DynamicValue::Builder& 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::Builder& value);
kj::StringTree KJ_STRINGIFY(const DynamicList::Reader& value);
......@@ -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)
// =======================================================================================
......@@ -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(double, FLOAT, float);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicEnum, ENUM, enum);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicObject, OBJECT, object);
#undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR
#define CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \
......@@ -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(DynamicList, LIST, list);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicStruct, STRUCT, struct);
CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(DynamicObject, OBJECT, object);
#undef CAPNP_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR
......@@ -891,15 +939,15 @@ struct DynamicValue::Builder::AsImpl<T, Kind::LIST> {
// -------------------------------------------------------------------
template <typename T>
struct DynamicObject::AsImpl<T, Kind::STRUCT> {
static T apply(DynamicObject value) {
struct DynamicObject::Reader::AsImpl<T, Kind::STRUCT> {
static T apply(DynamicObject::Reader value) {
return value.as(Schema::from<T>()).template as<T>();
}
};
template <typename T>
struct DynamicObject::AsImpl<T, Kind::LIST> {
static T apply(DynamicObject value) {
struct DynamicObject::Reader::AsImpl<T, Kind::LIST> {
static T apply(DynamicObject::Reader value) {
return value.as(Schema::from<T>()).template as<T>();
}
};
......@@ -960,6 +1008,13 @@ inline DynamicList::Builder DynamicList::Builder::as<DynamicList>() {
return *this;
}
// -------------------------------------------------------------------
template <typename T>
ReaderFor<T> ConstSchema::as() const {
return DynamicValue::Reader(*this).as<T>();
}
} // namespace capnp
#endif // CAPNP_DYNAMIC_H_
......@@ -2537,6 +2537,18 @@ ObjectReader ListReader::getObjectElement(ElementCount index) const {
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
......
......@@ -710,6 +710,14 @@ struct ObjectBuilder {
: kind(ObjectKind::STRUCT), structBuilder(structBuilder) {}
ObjectBuilder(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 {
......
......@@ -91,7 +91,7 @@ TEST(SchemaParser, Basic) {
};
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();
EXPECT_EQ(0x8123456789abcdefull, barProto.getId());
......@@ -110,24 +110,24 @@ TEST(SchemaParser, Basic) {
EXPECT_EQ(0x856789abcdef1234ull, getFieldTypeFileId(barFields[3]));
auto bazSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("not/used/because/already/loaded"),
kj::str("src/foo/baz.capnp"), importPath, reader));
"not/used/because/already/loaded",
"src/foo/baz.capnp", importPath, reader));
EXPECT_EQ(0x823456789abcdef1ull, bazSchema.getProto().getId());
EXPECT_EQ("foo2/baz.capnp", bazSchema.getProto().getDisplayName());
auto bazStruct = bazSchema.getNested("Baz").asStruct();
EXPECT_EQ(bazStruct, barStruct.getDependency(bazStruct.getProto().getId()));
auto corgeSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("not/used/because/already/loaded"),
kj::str("src/qux/corge.capnp"), importPath, reader));
"not/used/because/already/loaded",
"src/qux/corge.capnp", importPath, reader));
EXPECT_EQ(0x83456789abcdef12ull, corgeSchema.getProto().getId());
EXPECT_EQ("qux/corge.capnp", corgeSchema.getProto().getDisplayName());
auto corgeStruct = corgeSchema.getNested("Corge").asStruct();
EXPECT_EQ(corgeStruct, barStruct.getDependency(corgeStruct.getProto().getId()));
auto graultSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("not/used/because/already/loaded"),
kj::str("/usr/include/grault.capnp"), importPath, reader));
"not/used/because/already/loaded",
"/usr/include/grault.capnp", importPath, reader));
EXPECT_EQ(0x8456789abcdef123ull, graultSchema.getProto().getId());
EXPECT_EQ("grault.capnp", graultSchema.getProto().getDisplayName());
auto graultStruct = graultSchema.getNested("Grault").asStruct();
......@@ -136,11 +136,45 @@ TEST(SchemaParser, Basic) {
// Try importing the other grault.capnp directly. It'll get the display name we specify since
// it wasn't imported before.
auto wrongGraultSchema = parser.parseFile(SchemaFile::newDiskFile(
kj::str("weird/display/name.capnp"),
kj::str("/opt/include/grault.capnp"), importPath, reader));
"weird/display/name.capnp",
"/opt/include/grault.capnp", importPath, reader));
EXPECT_EQ(0x8000000000000001ull, wrongGraultSchema.getProto().getId());
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 capnp
......@@ -77,12 +77,45 @@ InterfaceSchema Schema::asInterface() const {
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 {
KJ_REQUIRE(raw == expected ||
(raw != nullptr && expected != nullptr && raw->canCastTo == expected),
"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 {
......@@ -156,31 +189,7 @@ kj::Maybe<StructSchema::Field> StructSchema::getFieldByDiscriminant(uint16_t dis
}
uint32_t StructSchema::Field::getDefaultValueSchemaOffset() const {
auto defaultValue = 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;
return parent.getSchemaOffset(proto.getNonGroup().getDefaultValue());
}
// -------------------------------------------------------------------
......@@ -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) {
......
......@@ -32,6 +32,7 @@ class Schema;
class StructSchema;
class EnumSchema;
class InterfaceSchema;
class ConstSchema;
class ListSchema;
template <typename T, Kind k = kind<T>()> struct SchemaType_ { typedef Schema Type; };
......@@ -57,6 +58,8 @@ public:
// Get the Schema for a particular compiled-in type.
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;
// Get the encoded schema node content as a single message segment. It is safe to read as an
......@@ -84,7 +87,9 @@ public:
StructSchema asStruct() const;
EnumSchema asEnum() 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; }
......@@ -114,9 +119,12 @@ private:
void requireUsableAs(const _::RawSchema* expected) const;
uint32_t getSchemaOffset(const schema::Value::Reader& value) const;
friend class StructSchema;
friend class EnumSchema;
friend class InterfaceSchema;
friend class ConstSchema;
friend class ListSchema;
friend class SchemaLoader;
};
......@@ -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 {
// ListSchema is a little different because list types are not described by schema nodes. So,
// ListSchema doesn't subclass Schema.
......
......@@ -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::Builder& value) { return stringify(value.asReader()); }
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::Builder& value) { return stringify(value.asReader()); }
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