Block default load of polymorphic types from a pointer in SLK

Summary:
Serialization of std::unique_ptr and std::shared_ptr now requires a
custom callback for handling the concrete element that is pointed to.
We use C++ type trait which checks whether the we are using a
polymorphic type --- class that has at least 1 virtual function. This
obviously doesn't work with pointers to base classes of hierarchies
without virtual member functions. But we don't use that kind of
inheritance, why would we, right? :)

(Luckily the breaking case isn't serialized, and hopefully never will
be. Perhaps we fix such inheritance in the future.)

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1753
This commit is contained in:
Teon Banek 2018-12-04 11:06:41 +01:00
parent b995dac623
commit 8b1aa3c2b6

View File

@ -63,6 +63,9 @@ template <typename T>
void Save(const std::unique_ptr<T> &obj, Builder *builder);
template <typename T>
void Load(std::unique_ptr<T> *obj, Reader *reader);
template <typename T>
void Load(std::unique_ptr<T> *obj, Reader *reader,
const std::function<void(std::unique_ptr<T> *, Reader *)> &load);
template <typename T>
void Save(const std::experimental::optional<T> &obj, Builder *builder);
@ -73,8 +76,17 @@ template <typename T>
void Save(const std::shared_ptr<T> &obj, Builder *builder,
std::vector<T *> *saved);
template <typename T>
void Save(const std::shared_ptr<T> &obj, Builder *builder,
std::vector<T *> *saved,
const std::function<void(const T &, Builder *builder)> &save);
template <typename T>
void Load(std::shared_ptr<T> *obj, Reader *reader,
std::vector<std::shared_ptr<T>> *loaded);
template <typename T>
void Load(
std::shared_ptr<T> *obj, Reader *reader,
std::vector<std::shared_ptr<T>> *loaded,
const std::function<void(std::unique_ptr<T> *, Reader *reader)> &load);
// Implementation of serialization for primitive types.
@ -235,6 +247,12 @@ inline void Save(const std::unique_ptr<T> &obj, Builder *builder) {
template <typename T>
inline void Load(std::unique_ptr<T> *obj, Reader *reader) {
// Prevent any loading which may potentially break class hierarchies.
// Unfortunately, C++14 doesn't have (or I'm not aware of it) a trait for
// checking whether some type has any derived or base classes.
static_assert(!std::is_polymorphic<T>::value,
"Only non polymorphic types can be loaded generically from a "
"pointer. Pass a custom load function as the 3rd argument.");
bool exists = false;
Load(&exists, reader);
if (exists) {
@ -246,6 +264,19 @@ inline void Load(std::unique_ptr<T> *obj, Reader *reader) {
}
}
template <typename T>
inline void Load(
std::unique_ptr<T> *obj, Reader *reader,
const std::function<void(std::unique_ptr<T> *, Reader *)> &load) {
bool exists = false;
Load(&exists, reader);
if (exists) {
load(obj, reader);
} else {
*obj = nullptr;
}
}
template <typename T>
inline void Save(const std::experimental::optional<T> &obj, Builder *builder) {
if (obj == std::experimental::nullopt) {
@ -291,6 +322,14 @@ inline void Load(std::pair<A, B> *obj, Reader *reader) {
template <typename T>
inline void Save(const std::shared_ptr<T> &obj, Builder *builder,
std::vector<T *> *saved) {
Save<T>(obj, builder, saved,
[](const auto &elem, auto *builder) { Save(elem, builder); });
}
template <typename T>
inline void Save(const std::shared_ptr<T> &obj, Builder *builder,
std::vector<T *> *saved,
const std::function<void(const T &, Builder *builder)> &save) {
if (obj.get() == nullptr) {
bool exists = false;
Save(exists, builder);
@ -306,7 +345,7 @@ inline void Save(const std::shared_ptr<T> &obj, Builder *builder,
} else {
bool in_place = true;
Save(in_place, builder);
Save(*obj.get(), builder);
save(*obj, builder);
saved->push_back(obj.get());
}
}
@ -315,6 +354,12 @@ inline void Save(const std::shared_ptr<T> &obj, Builder *builder,
template <typename T>
inline void Load(std::shared_ptr<T> *obj, Reader *reader,
std::vector<std::shared_ptr<T>> *loaded) {
// Prevent any loading which may potentially break class hierarchies.
// Unfortunately, C++14 doesn't have (or I'm not aware of it) a trait for
// checking whether some type has any derived or base classes.
static_assert(!std::is_polymorphic<T>::value,
"Only non polymorphic types can be loaded generically from a "
"pointer. Pass a custom load function as the 4th argument.");
bool exists = false;
Load(&exists, reader);
if (exists) {
@ -339,6 +384,35 @@ inline void Load(std::shared_ptr<T> *obj, Reader *reader,
}
}
template <typename T>
inline void Load(
std::shared_ptr<T> *obj, Reader *reader,
std::vector<std::shared_ptr<T>> *loaded,
const std::function<void(std::unique_ptr<T> *, Reader *reader)> &load) {
bool exists = false;
Load(&exists, reader);
if (exists) {
bool in_place = false;
Load(&in_place, reader);
if (in_place) {
std::unique_ptr<T> item;
load(&item, reader);
*obj = std::move(item);
loaded->push_back(*obj);
} else {
uint64_t index = 0;
Load(&index, reader);
if (index < loaded->size()) {
*obj = (*loaded)[index];
} else {
throw SlkDecodeException("Couldn't load shared pointer!");
}
}
} else {
*obj = nullptr;
}
}
template <typename T>
inline void Save(const std::vector<T> &obj, Builder *builder,
std::function<void(const T &, Builder *)> item_save_function) {