Commit 2331da3e authored by Kenton Varda's avatar Kenton Varda

Gracefully handle the case where the schema contains types from a newer version…

Gracefully handle the case where the schema contains types from a newer version of Cap'n Proto that we don't know about yet.
parent a1e067fb
...@@ -85,7 +85,8 @@ internal::FieldSize elementSizeFor(schema::Type::Body::Which elementType) { ...@@ -85,7 +85,8 @@ internal::FieldSize elementSizeFor(schema::Type::Body::Which elementType) {
case schema::Type::Body::INTERFACE_TYPE: return internal::FieldSize::POINTER; case schema::Type::Body::INTERFACE_TYPE: return internal::FieldSize::POINTER;
case schema::Type::Body::OBJECT_TYPE: FAIL_CHECK("List(Object) not supported."); break; case schema::Type::Body::OBJECT_TYPE: FAIL_CHECK("List(Object) not supported."); break;
} }
FAIL_CHECK("Can't get here.");
// Unknown type. Treat it as zero-size.
return internal::FieldSize::VOID; return internal::FieldSize::VOID;
} }
...@@ -168,11 +169,21 @@ Maybe<StructSchema::Member> DynamicUnion::Builder::which() { ...@@ -168,11 +169,21 @@ Maybe<StructSchema::Member> DynamicUnion::Builder::which() {
} }
DynamicValue::Reader DynamicUnion::Reader::get() { DynamicValue::Reader DynamicUnion::Reader::get() {
return DynamicValue::Reader(DynamicStruct::Reader::getImpl(reader, checkIsKnown())); auto w = which();
if (w == nullptr) {
return nullptr;
} else {
return DynamicValue::Reader(DynamicStruct::Reader::getImpl(reader, *w));
}
} }
DynamicValue::Builder DynamicUnion::Builder::get() { DynamicValue::Builder DynamicUnion::Builder::get() {
return DynamicValue::Builder(DynamicStruct::Builder::getImpl(builder, checkIsKnown())); auto w = which();
if (w == nullptr) {
return nullptr;
} else {
return DynamicValue::Builder(DynamicStruct::Builder::getImpl(builder, *w));
}
} }
void DynamicUnion::Builder::set(StructSchema::Member member, DynamicValue::Reader value) { void DynamicUnion::Builder::set(StructSchema::Member member, DynamicValue::Reader value) {
...@@ -244,26 +255,14 @@ Data::Builder DynamicUnion::Builder::initObjectAsData(Text::Reader name, uint si ...@@ -244,26 +255,14 @@ Data::Builder DynamicUnion::Builder::initObjectAsData(Text::Reader name, uint si
return initObjectAsData(schema.getMemberByName(name), size); return initObjectAsData(schema.getMemberByName(name), size);
} }
StructSchema::Member DynamicUnion::Reader::checkIsKnown() { StructSchema::Member DynamicUnion::Builder::checkIsObject() {
auto w = which();
PRECOND(w != nullptr, "Can't get() unknown union value.");
CHECK(w->getProto().getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER,
"Unsupported union member type.");
return *w;
}
StructSchema::Member DynamicUnion::Builder::checkIsKnown() {
auto w = which(); auto w = which();
PRECOND(w != nullptr, "Can't get() unknown union value."); PRECOND(w != nullptr, "Can't get() unknown union value.");
CHECK(w->getProto().getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER, CHECK(w->getProto().getBody().which() == schema::StructNode::Member::Body::FIELD_MEMBER,
"Unsupported union member type."); "Unsupported union member type.");
return *w; PRECOND(w->getProto().getBody().getFieldMember().getType().getBody().which() ==
}
StructSchema::Member DynamicUnion::Builder::checkIsObject() {
auto result = checkIsKnown();
PRECOND(result.getProto().getBody().getFieldMember().getType().getBody().which() ==
schema::Type::Body::OBJECT_TYPE, "Expected Object."); schema::Type::Body::OBJECT_TYPE, "Expected Object.");
return result; return *w;
} }
void DynamicUnion::Builder::setDiscriminant(StructSchema::Member member) { void DynamicUnion::Builder::setDiscriminant(StructSchema::Member member) {
...@@ -362,7 +361,7 @@ bool DynamicStruct::Reader::has(StructSchema::Member member) { ...@@ -362,7 +361,7 @@ bool DynamicStruct::Reader::has(StructSchema::Member member) {
return !reader.isPointerFieldNull(field.getOffset() * POINTERS); return !reader.isPointerFieldNull(field.getOffset() * POINTERS);
} }
FAIL_CHECK("switch() missing case.", type.which()); // Unknown type. As far as we know, it isn't set.
return false; return false;
} }
bool DynamicStruct::Builder::has(StructSchema::Member member) { bool DynamicStruct::Builder::has(StructSchema::Member member) {
...@@ -428,7 +427,7 @@ bool DynamicStruct::Builder::has(StructSchema::Member member) { ...@@ -428,7 +427,7 @@ bool DynamicStruct::Builder::has(StructSchema::Member member) {
return !builder.isPointerFieldNull(field.getOffset() * POINTERS); return !builder.isPointerFieldNull(field.getOffset() * POINTERS);
} }
FAIL_CHECK("switch() missing case.", type.which()); // Unknown type. As far as we know, it isn't set.
return false; return false;
} }
...@@ -733,13 +732,12 @@ DynamicValue::Reader DynamicStruct::Reader::getImpl( ...@@ -733,13 +732,12 @@ DynamicValue::Reader DynamicStruct::Reader::getImpl(
break; break;
} }
FAIL_CHECK("switch() missing case.", type.which()); return nullptr;
return DynamicValue::Reader();
} }
} }
FAIL_CHECK("switch() missing case.", member.getProto().getBody().which()); FAIL_CHECK("switch() missing case.", member.getProto().getBody().which());
return DynamicValue::Reader(); return nullptr;
} }
DynamicValue::Builder DynamicStruct::Builder::getImpl( DynamicValue::Builder DynamicStruct::Builder::getImpl(
...@@ -837,13 +835,12 @@ DynamicValue::Builder DynamicStruct::Builder::getImpl( ...@@ -837,13 +835,12 @@ DynamicValue::Builder DynamicStruct::Builder::getImpl(
break; break;
} }
FAIL_CHECK("switch() missing case.", type.which()); return nullptr;
return DynamicValue::Builder();
} }
} }
FAIL_CHECK("switch() missing case.", member.getProto().getBody().which()); FAIL_CHECK("switch() missing case.", member.getProto().getBody().which());
return DynamicValue::Builder(); return nullptr;
} }
DynamicStruct::Builder DynamicStruct::Builder::getObjectImpl( DynamicStruct::Builder DynamicStruct::Builder::getObjectImpl(
internal::StructBuilder builder, StructSchema::Member field, StructSchema type) { internal::StructBuilder builder, StructSchema::Member field, StructSchema type) {
...@@ -974,7 +971,7 @@ void DynamicStruct::Builder::setImpl( ...@@ -974,7 +971,7 @@ void DynamicStruct::Builder::setImpl(
return; return;
} }
FAIL_CHECK("switch() missing case.", type.which()); FAIL_RECOVERABLE_PRECOND("can't set field of unknown type", type.which());
return; return;
} }
} }
...@@ -1117,11 +1114,10 @@ DynamicValue::Reader DynamicList::Reader::operator[](uint index) const { ...@@ -1117,11 +1114,10 @@ DynamicValue::Reader DynamicList::Reader::operator[](uint index) const {
case schema::Type::Body::INTERFACE_TYPE: case schema::Type::Body::INTERFACE_TYPE:
FAIL_RECOVERABLE_CHECK("Interfaces not implemented.") {} FAIL_RECOVERABLE_CHECK("Interfaces not implemented.") {}
return DynamicValue::Reader(); return nullptr;
} }
FAIL_CHECK("switch() missing case.", schema.whichElementType()); return nullptr;
return DynamicValue::Reader();
} }
DynamicValue::Builder DynamicList::Builder::operator[](uint index) const { DynamicValue::Builder DynamicList::Builder::operator[](uint index) const {
...@@ -1176,15 +1172,14 @@ DynamicValue::Builder DynamicList::Builder::operator[](uint index) const { ...@@ -1176,15 +1172,14 @@ DynamicValue::Builder DynamicList::Builder::operator[](uint index) const {
case schema::Type::Body::OBJECT_TYPE: case schema::Type::Body::OBJECT_TYPE:
FAIL_CHECK("List(Object) not supported."); FAIL_CHECK("List(Object) not supported.");
break; return nullptr;
case schema::Type::Body::INTERFACE_TYPE: case schema::Type::Body::INTERFACE_TYPE:
FAIL_RECOVERABLE_CHECK("Interfaces not implemented.") {} FAIL_RECOVERABLE_CHECK("Interfaces not implemented.") {}
return DynamicValue::Builder(); return nullptr;
} }
FAIL_CHECK("switch() missing case.", schema.whichElementType()); return nullptr;
return DynamicValue::Builder();
} }
void DynamicList::Builder::set(uint index, DynamicValue::Reader value) { void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
...@@ -1196,7 +1191,7 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) { ...@@ -1196,7 +1191,7 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
#define HANDLE_TYPE(name, discrim, typeName) \ #define HANDLE_TYPE(name, discrim, typeName) \
case schema::Type::Body::discrim##_TYPE: \ case schema::Type::Body::discrim##_TYPE: \
builder.setDataElement<typeName>(index * ELEMENTS, value.as<typeName>()); \ builder.setDataElement<typeName>(index * ELEMENTS, value.as<typeName>()); \
break; return;
HANDLE_TYPE(void, VOID, Void) HANDLE_TYPE(void, VOID, Void)
HANDLE_TYPE(bool, BOOL, bool) HANDLE_TYPE(bool, BOOL, bool)
...@@ -1214,14 +1209,14 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) { ...@@ -1214,14 +1209,14 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
case schema::Type::Body::TEXT_TYPE: case schema::Type::Body::TEXT_TYPE:
builder.setBlobElement<Text>(index * ELEMENTS, value.as<Text>()); builder.setBlobElement<Text>(index * ELEMENTS, value.as<Text>());
break; return;
case schema::Type::Body::DATA_TYPE: case schema::Type::Body::DATA_TYPE:
builder.setBlobElement<Data>(index * ELEMENTS, value.as<Data>()); builder.setBlobElement<Data>(index * ELEMENTS, value.as<Data>());
break; return;
case schema::Type::Body::LIST_TYPE: { case schema::Type::Body::LIST_TYPE: {
builder.setListElement(index * ELEMENTS, value.as<DynamicList>().reader); builder.setListElement(index * ELEMENTS, value.as<DynamicList>().reader);
break; return;
} }
case schema::Type::Body::STRUCT_TYPE: case schema::Type::Body::STRUCT_TYPE:
...@@ -1229,7 +1224,7 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) { ...@@ -1229,7 +1224,7 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
// element is already allocated, and if it's smaller than the input value the copy would // element is already allocated, and if it's smaller than the input value the copy would
// have to be lossy. // have to be lossy.
FAIL_RECOVERABLE_CHECK("DynamicList of structs does not support set()."); FAIL_RECOVERABLE_CHECK("DynamicList of structs does not support set().");
break; return;
case schema::Type::Body::ENUM_TYPE: { case schema::Type::Body::ENUM_TYPE: {
uint16_t rawValue; uint16_t rawValue;
...@@ -1245,17 +1240,19 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) { ...@@ -1245,17 +1240,19 @@ void DynamicList::Builder::set(uint index, DynamicValue::Reader value) {
rawValue = enumValue.getRaw(); rawValue = enumValue.getRaw();
} }
builder.setDataElement<uint16_t>(index * ELEMENTS, rawValue); builder.setDataElement<uint16_t>(index * ELEMENTS, rawValue);
break; return;
} }
case schema::Type::Body::OBJECT_TYPE: case schema::Type::Body::OBJECT_TYPE:
FAIL_RECOVERABLE_CHECK("List(Object) not supported."); FAIL_RECOVERABLE_CHECK("List(Object) not supported.");
break; return;
case schema::Type::Body::INTERFACE_TYPE: case schema::Type::Body::INTERFACE_TYPE:
FAIL_RECOVERABLE_CHECK("Interfaces not implemented.") {} FAIL_RECOVERABLE_CHECK("Interfaces not implemented.") {}
break; return;
} }
FAIL_RECOVERABLE_PRECOND("can't set element of unknown type", schema.whichElementType());
} }
DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) { DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
...@@ -1278,7 +1275,7 @@ DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) { ...@@ -1278,7 +1275,7 @@ DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
case schema::Type::Body::STRUCT_TYPE: case schema::Type::Body::STRUCT_TYPE:
case schema::Type::Body::INTERFACE_TYPE: case schema::Type::Body::INTERFACE_TYPE:
FAIL_PRECOND("Expected a list or blob."); FAIL_PRECOND("Expected a list or blob.");
break; return nullptr;
case schema::Type::Body::TEXT_TYPE: case schema::Type::Body::TEXT_TYPE:
return DynamicValue::Builder(builder.initBlobElement<Text>(index * ELEMENTS, size * BYTES)); return DynamicValue::Builder(builder.initBlobElement<Text>(index * ELEMENTS, size * BYTES));
...@@ -1304,12 +1301,11 @@ DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) { ...@@ -1304,12 +1301,11 @@ DynamicValue::Builder DynamicList::Builder::init(uint index, uint size) {
case schema::Type::Body::OBJECT_TYPE: { case schema::Type::Body::OBJECT_TYPE: {
FAIL_CHECK("List(Object) not supported."); FAIL_CHECK("List(Object) not supported.");
return DynamicValue::Builder(); return nullptr;
} }
} }
FAIL_CHECK("switch() missing case.", schema.whichElementType()); return nullptr;
return DynamicValue::Builder();
} }
void DynamicList::Builder::copyFrom(std::initializer_list<DynamicValue::Reader> value) { void DynamicList::Builder::copyFrom(std::initializer_list<DynamicValue::Reader> value) {
......
...@@ -46,6 +46,11 @@ class MessageBuilder; ...@@ -46,6 +46,11 @@ class MessageBuilder;
struct DynamicValue { struct DynamicValue {
enum Type { enum Type {
UNKNOWN,
// Means that the value has unknown type and content because it comes from a newer version of
// the schema, or from a newer version of Cap'n Proto that has new features that this version
// doesn't understand.
VOID, VOID,
BOOL, BOOL,
INT, INT,
...@@ -185,8 +190,6 @@ private: ...@@ -185,8 +190,6 @@ private:
inline Reader(StructSchema::Union schema, internal::StructReader reader) inline Reader(StructSchema::Union schema, internal::StructReader reader)
: schema(schema), reader(reader) {} : schema(schema), reader(reader) {}
StructSchema::Member checkIsKnown();
friend struct DynamicStruct; friend struct DynamicStruct;
}; };
...@@ -231,7 +234,6 @@ private: ...@@ -231,7 +234,6 @@ private:
inline Builder(StructSchema::Union schema, internal::StructBuilder builder) inline Builder(StructSchema::Union schema, internal::StructBuilder builder)
: schema(schema), builder(builder) {} : schema(schema), builder(builder) {}
StructSchema::Member checkIsKnown();
StructSchema::Member checkIsObject(); StructSchema::Member checkIsObject();
void setDiscriminant(StructSchema::Member member); void setDiscriminant(StructSchema::Member member);
void setObjectDiscriminant(StructSchema::Member member); void setObjectDiscriminant(StructSchema::Member member);
...@@ -511,7 +513,8 @@ struct MaybeReaderBuilder<DynamicList, Kind::UNKNOWN> { ...@@ -511,7 +513,8 @@ struct MaybeReaderBuilder<DynamicList, Kind::UNKNOWN> {
class DynamicValue::Reader { class DynamicValue::Reader {
public: public:
inline Reader(Void value = Void::VOID); inline Reader(std::nullptr_t n = nullptr); // UNKNOWN
inline Reader(Void value);
inline Reader(bool value); inline Reader(bool value);
inline Reader(char value); inline Reader(char value);
inline Reader(signed char value); inline Reader(signed char value);
...@@ -589,7 +592,8 @@ private: ...@@ -589,7 +592,8 @@ private:
class DynamicValue::Builder { class DynamicValue::Builder {
public: public:
inline Builder(Void value = Void::VOID); inline Builder(std::nullptr_t n = nullptr); // UNKNOWN
inline Builder(Void value);
inline Builder(bool value); inline Builder(bool value);
inline Builder(char value); inline Builder(char value);
inline Builder(signed char value); inline Builder(signed char value);
...@@ -728,7 +732,10 @@ DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value) { ...@@ -728,7 +732,10 @@ DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value) {
return DynamicEnum(Schema::from<RemoveReference<T>>(), static_cast<uint16_t>(value)); return DynamicEnum(Schema::from<RemoveReference<T>>(), static_cast<uint16_t>(value));
} }
#define CAPNPROTO_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \ inline DynamicValue::Reader::Reader(std::nullptr_t n): type(UNKNOWN) {}
inline DynamicValue::Builder::Builder(std::nullptr_t n): type(UNKNOWN) {}
#define CAPNPROTO_DECLARE_DYNAMIC_VALUE_CONSTRUCTOR(cppType, typeTag, fieldName) \
inline DynamicValue::Reader::Reader(cppType value) \ inline DynamicValue::Reader::Reader(cppType value) \
: type(typeTag), fieldName##Value(value) {} \ : type(typeTag), fieldName##Value(value) {} \
inline DynamicValue::Builder::Builder(cppType value) \ inline DynamicValue::Builder::Builder(cppType value) \
......
...@@ -257,8 +257,8 @@ ListSchema ListSchema::of(schema::Type::Reader elementType, Schema context) { ...@@ -257,8 +257,8 @@ ListSchema ListSchema::of(schema::Type::Reader elementType, Schema context) {
return ListSchema(); return ListSchema();
} }
FAIL_CHECK("missing switch case"); // Unknown type is acceptable.
return ListSchema(); return ListSchema(body.which());
} }
StructSchema ListSchema::getStructElementType() const { StructSchema ListSchema::getStructElementType() const {
......
...@@ -202,10 +202,9 @@ struct StructNode { ...@@ -202,10 +202,9 @@ struct StructNode {
# Indicates where this member appeared in the code, relative to other members. # Indicates where this member appeared in the code, relative to other members.
# Code ordering may have semantic relevance -- programmers tend to place related fields # Code ordering may have semantic relevance -- programmers tend to place related fields
# together. So, using code ordering makes sense in human-readable formats where ordering is # together. So, using code ordering makes sense in human-readable formats where ordering is
# otherwise irrelevant, like JSON. The values of codeOrder are tightly-packed, so for unions # otherwise irrelevant, like JSON. The values of codeOrder are tightly-packed, so the maximum
# and non-union fields the maximum value of codeOrder is count(fields) + count(unions). # value is count(members) - 1. Fields that are members of a union are only ordered relative to
# Fields that are members of a union are only ordered relative to the other members of that # the other members of that union, so the maximum value there is count(union.members).
# union, so the maximum value there is count(union.fields).
annotations @3 :List(Annotation); annotations @3 :List(Annotation);
......
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