Commit c1f3e3ff authored by Kenton Varda's avatar Kenton Varda

Flesh out dynamic API for orphans.

parent 55e48fc4
......@@ -669,6 +669,165 @@ DynamicValue::Builder DynamicStruct::Builder::init(StructSchema::Field field, ui
KJ_UNREACHABLE;
}
void DynamicStruct::Builder::adopt(StructSchema::Field field, Orphan<DynamicValue>&& orphan) {
KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
setInUnion(field);
auto proto = field.getProto();
switch (proto.which()) {
case schema::Field::NON_GROUP: {
auto nonGroup = proto.getNonGroup();
auto type = nonGroup.getType();
switch (type.which()) {
case schema::Type::VOID:
case schema::Type::BOOL:
case schema::Type::INT8:
case schema::Type::INT16:
case schema::Type::INT32:
case schema::Type::INT64:
case schema::Type::UINT8:
case schema::Type::UINT16:
case schema::Type::UINT32:
case schema::Type::UINT64:
case schema::Type::FLOAT32:
case schema::Type::FLOAT64:
case schema::Type::ENUM:
set(field, orphan.getReader());
return;
case schema::Type::TEXT:
orphan.getReader().as<Text>(); // type check
break;
case schema::Type::DATA:
orphan.getReader().as<Data>(); // type check
break;
case schema::Type::LIST: {
ListSchema listType = ListSchema::of(type.getList(), field.getContainingStruct());
KJ_REQUIRE(orphan.getType() == DynamicValue::LIST && orphan.listSchema == listType,
"Value type mismatch.");
break;
}
case schema::Type::STRUCT: {
auto structType =
field.getContainingStruct().getDependency(type.getStruct()).asStruct();
KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == structType,
"Value type mismatch.");
break;
}
case schema::Type::OBJECT:
KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT ||
orphan.getType() == DynamicValue::LIST ||
orphan.getType() == DynamicValue::OBJECT,
"Value type mismatch.");
break;
case schema::Type::INTERFACE:
KJ_FAIL_ASSERT("Interfaces not yet implemented.");
break;
}
builder.adopt(nonGroup.getOffset() * POINTERS, kj::mv(orphan.builder));
return;
}
case schema::Field::GROUP:
// Have to transfer fields.
auto src = orphan.get().as<DynamicStruct>();
auto dst = init(field).as<DynamicStruct>();
KJ_REQUIRE(orphan.getType() == DynamicValue::STRUCT && orphan.structSchema == dst.getSchema(),
"Value type mismatch.");
KJ_IF_MAYBE(unionField, src.which()) {
dst.adopt(*unionField, src.disown(*unionField));
}
for (auto field: src.schema.getNonUnionFields()) {
if (src.has(field)) {
dst.adopt(field, src.disown(field));
}
}
return;
}
KJ_UNREACHABLE;
}
Orphan<DynamicValue> DynamicStruct::Builder::disown(StructSchema::Field field) {
// We end up calling get(field) below, so we don't need to validate `field` here.
auto proto = field.getProto();
switch (proto.which()) {
case schema::Field::NON_GROUP: {
auto nonGroup = proto.getNonGroup();
switch (nonGroup.getType().which()) {
case schema::Type::VOID:
case schema::Type::BOOL:
case schema::Type::INT8:
case schema::Type::INT16:
case schema::Type::INT32:
case schema::Type::INT64:
case schema::Type::UINT8:
case schema::Type::UINT16:
case schema::Type::UINT32:
case schema::Type::UINT64:
case schema::Type::FLOAT32:
case schema::Type::FLOAT64:
case schema::Type::ENUM: {
auto result = Orphan<DynamicValue>(get(field), _::OrphanBuilder());
clear(field);
return kj::mv(result);
}
case schema::Type::TEXT:
case schema::Type::DATA:
case schema::Type::LIST:
case schema::Type::STRUCT:
case schema::Type::OBJECT:
case schema::Type::INTERFACE: {
auto value = get(field);
return Orphan<DynamicValue>(value, builder.disown(nonGroup.getOffset() * POINTERS));
}
}
KJ_UNREACHABLE;
}
case schema::Field::GROUP: {
// We have to allocate new space for the group, unfortunately.
auto src = get(field).as<DynamicStruct>();
Orphan<DynamicStruct> result =
Orphanage::getForMessageContaining(*this).newOrphan(src.getSchema());
auto dst = result.get();
KJ_IF_MAYBE(unionField, src.which()) {
dst.adopt(*unionField, src.disown(*unionField));
}
// We need to explicitly reset the union to its default field.
KJ_IF_MAYBE(unionField, src.schema.getFieldByDiscriminant(0)) {
src.clear(*unionField);
}
for (auto field: src.schema.getNonUnionFields()) {
if (src.has(field)) {
dst.adopt(field, src.disown(field));
}
}
return kj::mv(result);
}
}
KJ_UNREACHABLE;
}
void DynamicStruct::Builder::clear(StructSchema::Field field) {
KJ_REQUIRE(field.getContainingStruct() == schema, "`field` is not a field of this struct.");
......@@ -723,9 +882,13 @@ void DynamicStruct::Builder::clear(StructSchema::Field field) {
case schema::Field::GROUP: {
DynamicStruct::Builder group(schema.getDependency(proto.getGroup()).asStruct(), builder);
// We clear the union field with discriminant 0 rather than the one that is set because
// we want the union to end up with its default field active.
KJ_IF_MAYBE(unionField, group.schema.getFieldByDiscriminant(0)) {
group.clear(*unionField);
}
for (auto subField: group.schema.getNonUnionFields()) {
group.clear(subField);
}
......@@ -848,6 +1011,12 @@ DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name) {
DynamicValue::Builder DynamicStruct::Builder::init(kj::StringPtr name, uint size) {
return init(schema.getFieldByName(name), size);
}
void DynamicStruct::Builder::adopt(kj::StringPtr name, Orphan<DynamicValue>&& orphan) {
adopt(schema.getFieldByName(name), kj::mv(orphan));
}
Orphan<DynamicValue> DynamicStruct::Builder::disown(kj::StringPtr name) {
return disown(schema.getFieldByName(name));
}
void DynamicStruct::Builder::clear(kj::StringPtr name) {
clear(schema.getFieldByName(name));
}
......@@ -1447,11 +1616,14 @@ 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));
}
void PointerHelpers<DynamicObject, Kind::UNKNOWN>::set(
StructBuilder builder, WirePointerCount index, const DynamicObject::Reader& value) {
builder.setObjectField(index, value.reader);
}
} // namespace _ (private)
......@@ -1495,4 +1667,138 @@ DynamicList::Reader Orphan<DynamicList>::getReader() const {
schema, builder.asListReader(elementSizeFor(schema.whichElementType())));
}
DynamicObject::Builder Orphan<DynamicObject>::get() {
return DynamicObject::Builder(builder.asObject());
}
DynamicObject::Reader Orphan<DynamicObject>::getReader() const {
return DynamicObject::Reader(builder.asObjectReader());
}
DynamicStruct::Builder Orphan<DynamicObject>::getAs(StructSchema schema) {
return DynamicStruct::Builder(schema, builder.asStruct(structSizeFromSchema(schema)));
}
DynamicList::Builder Orphan<DynamicObject>::getAs(ListSchema schema) {
if (schema.whichElementType() == schema::Type::STRUCT) {
return DynamicList::Builder(schema,
builder.asStructList(structSizeFromSchema(schema.getStructElementType())));
} else {
return DynamicList::Builder(schema,
builder.asList(elementSizeFor(schema.whichElementType())));
}
}
template <>
Text::Builder Orphan<DynamicObject>::getAs<Text>() {
return builder.asText();
}
template <>
Data::Builder Orphan<DynamicObject>::getAs<Data>() {
return builder.asData();
}
Orphan<DynamicStruct> Orphan<DynamicObject>::releaseAs(StructSchema schema) {
getAs(schema); // type check
return Orphan<DynamicStruct>(schema, kj::mv(builder));
}
Orphan<DynamicList> Orphan<DynamicObject>::releaseAs(ListSchema schema) {
getAs(schema); // type check
return Orphan<DynamicList>(schema, kj::mv(builder));
}
template <>
Orphan<Text> Orphan<DynamicObject>::releaseAs<Text>() {
getAs<Text>(); // type check
return Orphan<Text>(kj::mv(builder));
}
template <>
Orphan<Data> Orphan<DynamicObject>::releaseAs<Data>() {
getAs<Data>(); // type check
return Orphan<Data>(kj::mv(builder));
}
Orphan<DynamicValue>::Orphan(DynamicValue::Builder value, _::OrphanBuilder&& builder)
: type(value.getType()), builder(kj::mv(builder)) {
switch (type) {
case DynamicValue::UNKNOWN: break;
case DynamicValue::VOID: voidValue = value.voidValue; break;
case DynamicValue::BOOL: boolValue = value.boolValue; break;
case DynamicValue::INT: intValue = value.intValue; break;
case DynamicValue::UINT: uintValue = value.uintValue; break;
case DynamicValue::FLOAT: floatValue = value.floatValue; break;
case DynamicValue::ENUM: enumValue = value.enumValue; break;
case DynamicValue::TEXT: break;
case DynamicValue::DATA: break;
case DynamicValue::LIST: listSchema = value.listValue.getSchema(); break;
case DynamicValue::STRUCT: structSchema = value.structValue.getSchema(); break;
case DynamicValue::INTERFACE: KJ_FAIL_ASSERT("Interfaces not implemented.");
case DynamicValue::OBJECT: break;
}
}
DynamicValue::Builder Orphan<DynamicValue>::get() {
switch (type) {
case DynamicValue::UNKNOWN: return nullptr;
case DynamicValue::VOID: return voidValue;
case DynamicValue::BOOL: return boolValue;
case DynamicValue::INT: return intValue;
case DynamicValue::UINT: return uintValue;
case DynamicValue::FLOAT: return floatValue;
case DynamicValue::ENUM: return enumValue;
case DynamicValue::TEXT: return builder.asText();
case DynamicValue::DATA: return builder.asData();
case DynamicValue::LIST:
if (listSchema.whichElementType() == schema::Type::STRUCT) {
return DynamicList::Builder(listSchema,
builder.asStructList(structSizeFromSchema(listSchema.getStructElementType())));
} else {
return DynamicList::Builder(listSchema,
builder.asList(elementSizeFor(listSchema.whichElementType())));
}
case DynamicValue::STRUCT:
return DynamicStruct::Builder(structSchema,
builder.asStruct(structSizeFromSchema(structSchema)));
case DynamicValue::INTERFACE:
KJ_FAIL_ASSERT("Interfaces not implemented.");
case DynamicValue::OBJECT:
return DynamicObject::Builder(builder.asObject());
}
KJ_UNREACHABLE;
}
DynamicValue::Reader Orphan<DynamicValue>::getReader() const {
switch (type) {
case DynamicValue::UNKNOWN: return nullptr;
case DynamicValue::VOID: return voidValue;
case DynamicValue::BOOL: return boolValue;
case DynamicValue::INT: return intValue;
case DynamicValue::UINT: return uintValue;
case DynamicValue::FLOAT: return floatValue;
case DynamicValue::ENUM: return enumValue;
case DynamicValue::TEXT: return builder.asTextReader();
case DynamicValue::DATA: return builder.asDataReader();
case DynamicValue::LIST:
return DynamicList::Reader(listSchema,
builder.asListReader(elementSizeFor(listSchema.whichElementType())));
case DynamicValue::STRUCT:
return DynamicStruct::Reader(structSchema,
builder.asStructReader(structSizeFromSchema(structSchema)));
case DynamicValue::INTERFACE:
KJ_FAIL_ASSERT("Interfaces not implemented.");
case DynamicValue::OBJECT:
return DynamicObject::Reader(builder.asObjectReader());
}
KJ_UNREACHABLE;
}
template <>
Orphan<DynamicStruct> Orphan<DynamicValue>::releaseAs<DynamicStruct>() {
KJ_REQUIRE(type == DynamicValue::STRUCT, "Value type mismatch.");
return Orphan<DynamicStruct>(structSchema, kj::mv(builder));
}
template <>
Orphan<DynamicList> Orphan<DynamicValue>::releaseAs<DynamicList>() {
KJ_REQUIRE(type == DynamicValue::LIST, "Value type mismatch.");
return Orphan<DynamicList>(listSchema, kj::mv(builder));
}
} // namespace capnp
......@@ -86,6 +86,7 @@ struct DynamicList {
class Reader;
class Builder;
};
template <> class Orphan<DynamicValue>;
template <Kind k> struct DynamicTypeFor_;
template <> struct DynamicTypeFor_<Kind::ENUM> { typedef DynamicEnum Type; };
......@@ -152,7 +153,7 @@ public:
Reader() = default;
template <typename T>
inline typename T::Reader as() const { return AsImpl<T>::apply(*this); }
typename T::Reader as() const;
// Convert the object to the given struct, list, or blob type.
DynamicStruct::Reader as(StructSchema schema) const;
......@@ -163,18 +164,16 @@ private:
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
// specialization. Has a method apply() which does the work.
friend struct DynamicStruct;
friend struct DynamicList;
template <typename T, Kind K>
friend struct _::PointerHelpers;
friend class DynamicObject::Builder;
friend class Orphan<DynamicObject>;
friend class Orphan<DynamicValue>;
};
class DynamicObject::Builder: kj::DisallowConstCopy {
class DynamicObject::Builder: public 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
......@@ -197,6 +196,8 @@ private:
friend struct DynamicList;
template <typename T, Kind K>
friend struct _::PointerHelpers;
friend class Orphan<DynamicObject>;
friend class Orphan<DynamicValue>;
};
// -------------------------------------------------------------------
......@@ -261,6 +262,7 @@ private:
_::StructReader reader, const _::RawSchema& schema);
friend class Orphanage;
friend class Orphan<DynamicStruct>;
friend class Orphan<DynamicValue>;
};
class DynamicStruct::Builder {
......@@ -302,6 +304,11 @@ public:
DynamicValue::Builder init(StructSchema::Field field, uint size);
// Init a struct, list, or blob field.
void adopt(StructSchema::Field field, Orphan<DynamicValue>&& orphan);
Orphan<DynamicValue> disown(StructSchema::Field field);
// Adopt/disown. This works even for non-pointer fields: adopt() becomes equivalent to set()
// and disown() becomes like get() followed by clear().
void clear(StructSchema::Field field);
// Clear a field, setting it to its default value. For pointer fields, this actually makes the
// field null.
......@@ -326,6 +333,8 @@ public:
void set(kj::StringPtr name, std::initializer_list<DynamicValue::Reader> value);
DynamicValue::Builder init(kj::StringPtr name);
DynamicValue::Builder init(kj::StringPtr name, uint size);
void adopt(kj::StringPtr name, Orphan<DynamicValue>&& orphan);
Orphan<DynamicValue> disown(kj::StringPtr name);
void clear(kj::StringPtr name);
DynamicStruct::Builder getObject(kj::StringPtr name, StructSchema type);
DynamicList::Builder getObject(kj::StringPtr name, ListSchema type);
......@@ -361,6 +370,8 @@ private:
friend struct ::capnp::ToDynamic_;
friend class Orphanage;
friend class Orphan<DynamicStruct>;
friend class Orphan<DynamicObject>;
friend class Orphan<DynamicValue>;
};
// -------------------------------------------------------------------
......@@ -403,6 +414,7 @@ private:
friend struct ::capnp::ToDynamic_;
friend class Orphanage;
friend class Orphan<DynamicList>;
friend class Orphan<DynamicValue>;
};
class DynamicList::Builder {
......@@ -450,6 +462,8 @@ private:
template <typename T, Kind k>
friend struct _::OrphanGetImpl;
friend class Orphan<DynamicList>;
friend class Orphan<DynamicObject>;
friend class Orphan<DynamicValue>;
};
// -------------------------------------------------------------------
......@@ -611,6 +625,8 @@ private:
template <typename T, Kind kind = kind<T>()> struct AsImpl;
// Implementation backing the as() method. Needs to be a struct to allow partial
// specialization. Has a method apply() which does the work.
friend class Orphan<DynamicValue>;
};
kj::StringTree KJ_STRINGIFY(const DynamicValue::Reader& value);
......@@ -637,8 +653,14 @@ public:
DynamicStruct::Builder get();
DynamicStruct::Reader getReader() const;
inline bool operator==(decltype(nullptr)) { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) { return builder == nullptr; }
template <typename T>
Orphan<T> releaseAs();
// Like DynamicStruct::Builder::as(), but coerces the Orphan type. Since Orphans are move-only,
// the original Orphan<DynamicStruct> is no longer valid after this call; ownership is
// transferred to the returned Orphan<T>.
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
private:
StructSchema schema;
......@@ -651,6 +673,8 @@ private:
friend struct _::PointerHelpers;
friend struct DynamicList;
friend class Orphanage;
friend class Orphan<DynamicObject>;
friend class Orphan<DynamicValue>;
};
template <>
......@@ -664,8 +688,14 @@ public:
DynamicList::Builder get();
DynamicList::Reader getReader() const;
inline bool operator==(decltype(nullptr)) { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) { return builder == nullptr; }
template <typename T>
Orphan<T> releaseAs();
// Like DynamicList::Builder::as(), but coerces the Orphan type. Since Orphans are move-only,
// the original Orphan<DynamicStruct> is no longer valid after this call; ownership is
// transferred to the returned Orphan<T>.
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
private:
ListSchema schema;
......@@ -678,8 +708,157 @@ private:
friend struct _::PointerHelpers;
friend struct DynamicList;
friend class Orphanage;
friend class Orphan<DynamicObject>;
friend class Orphan<DynamicValue>;
};
template <>
class Orphan<DynamicObject> {
public:
Orphan() = default;
KJ_DISALLOW_COPY(Orphan);
Orphan(Orphan&&) = default;
Orphan& operator=(Orphan&&) = default;
DynamicObject::Builder get();
DynamicObject::Reader getReader() const;
template <typename T>
BuilderFor<T> getAs();
// Coerce the object to the given type and return a builder for that type. This may relocate
// the object if it was originally created with a previous version of the schema and the sizes
// don't match.
//
// Notice that DynamicObject::Builder does not have an "as<T>()" method, which is why this is
// needed.
template <typename T>
Orphan<T> releaseAs();
// Like DynamicValue::Builder::as(), but coerces the Orphan type. Since Orphans are move-only,
// the original Orphan<DynamicStruct> is no longer valid after this call; ownership is
// transferred to the returned Orphan<T>.
DynamicStruct::Builder getAs(StructSchema schema);
DynamicList::Builder getAs(ListSchema schema);
// Dynamic versions of 'getAs()'.
Orphan<DynamicStruct> releaseAs(StructSchema schema);
Orphan<DynamicList> releaseAs(ListSchema schema);
// Dynamic versions of 'releaseAs()'.
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
private:
_::OrphanBuilder builder;
explicit Orphan(_::OrphanBuilder&& builder): builder(kj::mv(builder)) {}
template <typename, Kind>
friend struct _::PointerHelpers;
friend class Orphan<DynamicValue>;
};
template <>
class Orphan<DynamicValue> {
public:
Orphan() = default;
KJ_DISALLOW_COPY(Orphan);
Orphan(Orphan&&) = default;
template <typename T>
Orphan(Orphan<T>&&);
Orphan& operator=(Orphan&&) = default;
inline DynamicValue::Type getType() { return type; }
DynamicValue::Builder get();
DynamicValue::Reader getReader() const;
template <typename T>
Orphan<T> releaseAs();
// Like DynamicValue::Builder::as(), but coerces the Orphan type. Since Orphans are move-only,
// the original Orphan<DynamicStruct> is no longer valid after this call; ownership is
// transferred to the returned Orphan<T>.
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
private:
DynamicValue::Type type;
union {
Void voidValue;
bool boolValue;
int64_t intValue;
uint64_t uintValue;
double floatValue;
DynamicEnum enumValue;
StructSchema structSchema;
ListSchema listSchema;
};
_::OrphanBuilder builder;
// Only used if `type` is a pointer type.
Orphan(DynamicValue::Builder value, _::OrphanBuilder&& builder);
Orphan(DynamicValue::Type type, _::OrphanBuilder&& builder)
: type(type), builder(kj::mv(builder)) {}
Orphan(StructSchema structSchema, _::OrphanBuilder&& builder)
: type(DynamicValue::STRUCT), structSchema(structSchema), builder(kj::mv(builder)) {}
Orphan(ListSchema listSchema, _::OrphanBuilder&& builder)
: type(DynamicValue::LIST), listSchema(listSchema), builder(kj::mv(builder)) {}
template <typename, Kind>
friend struct _::PointerHelpers;
friend struct DynamicStruct;
friend struct DynamicList;
friend class Orphanage;
};
template <typename T>
BuilderFor<T> Orphan<DynamicObject>::getAs() {
return getAs(Schema::from<T>()).template as<T>();
}
template <>
Text::Builder Orphan<DynamicObject>::getAs<Text>();
template <>
Data::Builder Orphan<DynamicObject>::getAs<Data>();
template <typename T>
Orphan<T> Orphan<DynamicObject>::releaseAs() {
return releaseAs(Schema::from<T>()).template releaseAs<T>();
}
template <>
Orphan<Text> Orphan<DynamicObject>::releaseAs<Text>();
template <>
Orphan<Data> Orphan<DynamicObject>::releaseAs<Data>();
template <typename T>
Orphan<DynamicValue>::Orphan(Orphan<T>&& other)
: Orphan(other.get(), kj::mv(other.builder)) {}
template <typename T>
Orphan<T> Orphan<DynamicStruct>::releaseAs() {
get().as<T>(); // type check
return Orphan<T>(kj::mv(builder));
}
template <typename T>
Orphan<T> Orphan<DynamicList>::releaseAs() {
get().as<T>(); // type check
return Orphan<T>(kj::mv(builder));
}
template <typename T>
Orphan<T> Orphan<DynamicValue>::releaseAs() {
get().as<T>(); // type check
return Orphan<T>(kj::mv(builder));
}
template <>
Orphan<DynamicStruct> Orphan<DynamicValue>::releaseAs<DynamicStruct>();
template <>
Orphan<DynamicList> Orphan<DynamicValue>::releaseAs<DynamicList>();
template <>
struct Orphanage::GetInnerBuilder<DynamicStruct, Kind::UNKNOWN> {
static inline _::StructBuilder apply(DynamicStruct::Builder& t) {
......@@ -768,10 +947,17 @@ 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);
static void set(
StructBuilder builder, WirePointerCount index, const DynamicObject::Reader& value);
static inline void adopt(StructBuilder builder, WirePointerCount index,
Orphan<DynamicObject>&& value) {
builder.adopt(index, kj::mv(value.builder));
}
static inline Orphan<DynamicObject> disown(StructBuilder builder, WirePointerCount index) {
return Orphan<DynamicObject>(builder.disown(index));
}
};
} // namespace _ (private)
......@@ -939,18 +1125,9 @@ struct DynamicValue::Builder::AsImpl<T, Kind::LIST> {
// -------------------------------------------------------------------
template <typename T>
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::Reader::AsImpl<T, Kind::LIST> {
static T apply(DynamicObject::Reader value) {
return value.as(Schema::from<T>()).template as<T>();
}
};
inline typename T::Reader DynamicObject::Reader::as() const {
return as(Schema::from<T>()).template as<T>();
}
// -------------------------------------------------------------------
......
......@@ -1619,10 +1619,14 @@ struct WireHelpers {
}
static OrphanBuilder disown(SegmentBuilder* segment, WirePointer* ref) {
OrphanBuilder result(ref, segment,
ref->kind() == WirePointer::FAR ? nullptr : ref->target());
memset(ref, 0, sizeof(*ref));
return result;
if (ref->isNull()) {
return OrphanBuilder();
} else {
OrphanBuilder result(ref, segment,
ref->kind() == WirePointer::FAR ? nullptr : ref->target());
memset(ref, 0, sizeof(*ref));
return result;
}
}
// -----------------------------------------------------------------
......@@ -2744,8 +2748,14 @@ ObjectReader OrphanBuilder::asObjectReader() const {
}
void OrphanBuilder::euthanize() {
WireHelpers::zeroObject(segment, reinterpret_cast<WirePointer*>(&tag), location);
memset(&tag, 0, sizeof(tag)); // Use memset to comply with aliasing rules.
auto ref = reinterpret_cast<WirePointer*>(&tag);
if (ref->kind() == WirePointer::FAR) {
WireHelpers::zeroObject(segment, ref);
} else {
WireHelpers::zeroObject(segment, reinterpret_cast<WirePointer*>(&tag), location);
}
memset(ref, 0, sizeof(*ref));
segment = nullptr;
location = nullptr;
}
......
......@@ -751,7 +751,7 @@ public:
inline OrphanBuilder(): segment(nullptr), location(nullptr) { memset(&tag, 0, sizeof(tag)); }
OrphanBuilder(const OrphanBuilder& other) = delete;
inline OrphanBuilder(OrphanBuilder&& other);
inline ~OrphanBuilder();
inline ~OrphanBuilder() noexcept(false);
static OrphanBuilder initStruct(BuilderArena* arena, StructSize size);
static OrphanBuilder initList(BuilderArena* arena, ElementCount elementCount,
......@@ -769,8 +769,8 @@ public:
OrphanBuilder& operator=(const OrphanBuilder& other) = delete;
inline OrphanBuilder& operator=(OrphanBuilder&& other);
inline bool operator==(decltype(nullptr)) { return location == nullptr; }
inline bool operator!=(decltype(nullptr)) { return location != nullptr; }
inline bool operator==(decltype(nullptr)) const { return segment == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return segment != nullptr; }
StructBuilder asStruct(StructSize size);
// Interpret as a struct, or throw an exception if not a struct.
......@@ -1041,7 +1041,7 @@ inline OrphanBuilder::OrphanBuilder(OrphanBuilder&& other)
other.location = nullptr;
}
inline OrphanBuilder::~OrphanBuilder() {
inline OrphanBuilder::~OrphanBuilder() noexcept(false) {
if (segment != nullptr) euthanize();
}
......
......@@ -437,6 +437,157 @@ TEST(Orphans, OrphanageDynamicListCopy) {
checkList(root2.getObjectField<List<uint32_t>>(), {12u, 34u, 56u});
}
TEST(Orphans, DynamicStructAs) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestObject>();
initTestMessage(root.initObjectField<TestAllTypes>());
EXPECT_TRUE(root.hasObjectField());
Orphan<DynamicValue> orphan =
root.disownObjectField<DynamicStruct>(Schema::from<TestAllTypes>());
EXPECT_FALSE(orphan == nullptr);
checkTestMessage(orphan.getReader().as<TestAllTypes>());
checkTestMessage(orphan.get().as<TestAllTypes>());
{
Orphan<DynamicStruct> structOrphan = orphan.releaseAs<DynamicStruct>();
EXPECT_TRUE(orphan == nullptr);
EXPECT_FALSE(structOrphan == nullptr);
checkDynamicTestMessage(structOrphan.getReader());
checkDynamicTestMessage(structOrphan.get());
checkTestMessage(structOrphan.getReader().as<TestAllTypes>());
checkTestMessage(structOrphan.get().as<TestAllTypes>());
{
Orphan<TestAllTypes> typedOrphan = structOrphan.releaseAs<TestAllTypes>();
EXPECT_TRUE(structOrphan == nullptr);
EXPECT_FALSE(typedOrphan == nullptr);
checkTestMessage(typedOrphan.getReader());
checkTestMessage(typedOrphan.get());
orphan = kj::mv(typedOrphan);
EXPECT_FALSE(orphan == nullptr);
EXPECT_TRUE(typedOrphan == nullptr);
}
}
{
Orphan<TestAllTypes> typedOrphan = orphan.releaseAs<TestAllTypes>();
checkTestMessage(typedOrphan.getReader());
checkTestMessage(typedOrphan.get());
}
}
TEST(Orphans, DynamicListAs) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestObject>();
root.setObjectField<List<uint32_t>>({12, 34, 56});
EXPECT_TRUE(root.hasObjectField());
Orphan<DynamicValue> orphan = root.disownObjectField<DynamicList>(Schema::from<List<uint32_t>>());
EXPECT_FALSE(orphan == nullptr);
checkList(orphan.getReader().as<List<uint32_t>>(), {12, 34, 56});
checkList(orphan.get().as<List<uint32_t>>(), {12, 34, 56});
{
Orphan<DynamicList> listOrphan = orphan.releaseAs<DynamicList>();
EXPECT_TRUE(orphan == nullptr);
EXPECT_FALSE(listOrphan == nullptr);
checkList<uint32_t>(listOrphan.getReader(), {12, 34, 56});
checkList<uint32_t>(listOrphan.get(), {12, 34, 56});
checkList(listOrphan.getReader().as<List<uint32_t>>(), {12, 34, 56});
checkList(listOrphan.get().as<List<uint32_t>>(), {12, 34, 56});
{
Orphan<List<uint32_t>> typedOrphan = listOrphan.releaseAs<List<uint32_t>>();
EXPECT_TRUE(listOrphan == nullptr);
EXPECT_FALSE(typedOrphan == nullptr);
checkList(typedOrphan.getReader(), {12, 34, 56});
checkList(typedOrphan.get(), {12, 34, 56});
orphan = kj::mv(typedOrphan);
EXPECT_FALSE(orphan == nullptr);
EXPECT_TRUE(typedOrphan == nullptr);
}
}
{
Orphan<List<uint32_t>> typedOrphan = orphan.releaseAs<List<uint32_t>>();
checkList(typedOrphan.getReader(), {12, 34, 56});
checkList(typedOrphan.get(), {12, 34, 56});
}
}
TEST(Orphans, DynamicObject) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestObject>();
initTestMessage(root.initObjectField<TestAllTypes>());
EXPECT_TRUE(root.hasObjectField());
Orphan<DynamicValue> orphan = root.disownObjectField<DynamicObject>();
EXPECT_FALSE(orphan == nullptr);
checkTestMessage(orphan.getReader().as<DynamicObject>().as<TestAllTypes>());
Orphan<DynamicObject> objectOrphan = orphan.releaseAs<DynamicObject>();
checkTestMessage(objectOrphan.getAs<TestAllTypes>());
checkDynamicTestMessage(objectOrphan.getAs(Schema::from<TestAllTypes>()));
}
TEST(Orphans, DynamicDisown) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
initTestMessage(root);
Orphan<TestAllTypes> dstOrphan =
Orphanage::getForMessageContaining(root).newOrphan<TestAllTypes>();
auto dst = dstOrphan.get();
DynamicStruct::Builder dynamic = root;
DynamicStruct::Builder dynamicDst = dst;
for (auto field: dynamic.getSchema().getFields()) {
dynamicDst.adopt(field, dynamic.disown(field));
}
checkTestMessageAllZero(root.asReader());
checkTestMessage(dst.asReader());
for (auto field: dynamic.getSchema().getFields()) {
dynamicDst.adopt(field, dynamic.disown(field));
}
checkTestMessageAllZero(root.asReader());
checkTestMessageAllZero(dst.asReader());
}
TEST(Orphans, DynamicDisownGroup) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestGroups>();
auto bar = root.initGroups().initBar();
bar.setCorge(123);
bar.setGrault("foo");
bar.setGarply(9876543210987ll);
Orphan<test::TestGroups> dstOrphan =
Orphanage::getForMessageContaining(root).newOrphan<test::TestGroups>();
auto dst = dstOrphan.get();
toDynamic(dst).adopt("groups", toDynamic(root).disown("groups"));
EXPECT_EQ(test::TestGroups::Groups::FOO, root.getGroups().which());
EXPECT_EQ(test::TestGroups::Groups::BAR, dst.getGroups().which());
auto newBar = dst.getGroups().getBar();
EXPECT_EQ(123, newBar.getCorge());
EXPECT_EQ("foo", newBar.getGrault());
EXPECT_EQ(9876543210987ll, newBar.getGarply());
}
TEST(Orphans, OrphanageFromBuilder) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
......@@ -619,6 +770,23 @@ TEST(Orphans, DataZerodAfterUse) {
EXPECT_EQ("foo", root.getTextField());
}
TEST(Orphans, FarPointer) {
MallocMessageBuilder builder(0, AllocationStrategy::FIXED_SIZE);
auto root = builder.initRoot<TestAllTypes>();
auto child = root.initStructField();
initTestMessage(child);
auto orphan = root.disownStructField();
EXPECT_FALSE(root.hasStructField());
EXPECT_TRUE(orphan != nullptr);
EXPECT_FALSE(orphan == nullptr);
KJ_DBG(orphan != nullptr, orphan == nullptr);
checkTestMessage(orphan.getReader());
checkTestMessage(orphan.get());
}
} // namespace
} // namespace _ (private)
} // namespace capnp
......@@ -55,8 +55,8 @@ public:
inline typename T::Builder get();
inline typename T::Reader getReader() const;
inline bool operator==(decltype(nullptr)) { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) { return builder == nullptr; }
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
private:
_::OrphanBuilder builder;
......@@ -67,6 +67,8 @@ private:
friend struct _::PointerHelpers;
template <typename, Kind>
friend struct List;
template <typename U>
friend class Orphan;
friend class Orphanage;
};
......
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