mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00

When converting a Weak<VT, T> into Weak<VT, Dyn>, we were ignoring that while T inside VRcInner may be at a certain offset to satisfy alignment, the empty Dyn field may not and thus be at a different offset. On wasm32 for example the compiler generated component struct has a 16-byte alignment, while the Dyn has none. So converting the Weak<VT, Dyn> to a VRc<VT, Dyn> and then calling borrow() would use the wrong instance pointer. To fix this, this patch introduces an extra offset field that's preserved during the casting and that's used for borrow() as well as drop().
158 lines
3.8 KiB
C++
158 lines
3.8 KiB
C++
/* LICENSE BEGIN
|
|
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
|
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
|
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
|
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
This file is also available under commercial licensing terms.
|
|
Please contact info@sixtyfps.io for more information.
|
|
LICENSE END */
|
|
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <new>
|
|
#include <algorithm>
|
|
#include <optional>
|
|
|
|
namespace vtable {
|
|
|
|
template<typename T>
|
|
struct VRefMut
|
|
{
|
|
const T *vtable;
|
|
void *instance;
|
|
};
|
|
|
|
struct Layout {
|
|
std::size_t size;
|
|
std::size_t align;
|
|
};
|
|
|
|
// For the C++'s purpose, they are all the same
|
|
template<typename T>
|
|
using VRef = VRefMut<T>;
|
|
template<typename T>
|
|
using VBox = VRefMut<T>;
|
|
|
|
template<typename T>
|
|
using Pin = T;
|
|
|
|
/*
|
|
template<typename T>
|
|
struct VBox {
|
|
const T *vtable;
|
|
void *instance;
|
|
};
|
|
|
|
template<typename T>
|
|
struct VRef {
|
|
const T *vtable;
|
|
const void *instance;
|
|
};
|
|
*/
|
|
|
|
struct AllowPin;
|
|
|
|
template<typename Base, typename T, typename Flag = void>
|
|
struct VOffset
|
|
{
|
|
const T *vtable;
|
|
std::uintptr_t offset;
|
|
};
|
|
|
|
template<typename VTable, typename X>
|
|
struct VRcInner {
|
|
template<typename VTable_, typename X_> friend class VRc;
|
|
template<typename VTable_, typename X_> friend class VWeak;
|
|
private:
|
|
VRcInner() : layout {} {}
|
|
const VTable *vtable = &X::component_type;
|
|
int strong_ref = 1;
|
|
int weak_ref = 1;
|
|
std::uint16_t data_offset = offsetof(VRcInner, data);
|
|
union {
|
|
X data;
|
|
Layout layout;
|
|
};
|
|
};
|
|
|
|
struct Dyn {};
|
|
|
|
template<typename VTable, typename X = Dyn>
|
|
class VRc {
|
|
VRcInner<VTable, X> *inner;
|
|
VRc(VRcInner<VTable, X> *inner) : inner(inner) {}
|
|
template<typename VTable_, typename X_> friend class VWeak;
|
|
public:
|
|
~VRc() {
|
|
if (!--inner->strong_ref) {
|
|
Layout layout = inner->vtable->drop_in_place({inner->vtable, &inner->data});
|
|
layout.size += inner->data_offset;
|
|
layout.align = std::max<size_t>(layout.align, alignof(VRcInner<VTable, Dyn>));
|
|
inner->layout = layout;
|
|
if (!--inner->weak_ref) {
|
|
inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t*>(inner), layout);
|
|
}
|
|
}
|
|
}
|
|
VRc(const VRc &other): inner(other.inner) {
|
|
inner->strong_ref++;
|
|
}
|
|
VRc &operator=(const VRc &other) {
|
|
if (inner == other.inner)
|
|
return *this;
|
|
this->~VRc();
|
|
new(this) VRc(other);
|
|
return *this;
|
|
}
|
|
template<typename ...Args> static VRc make(Args... args) {
|
|
auto mem = ::operator new(sizeof(VRcInner<VTable, X>), static_cast<std::align_val_t>(alignof(VRcInner<VTable, X>)));
|
|
auto inner = new (mem) VRcInner<VTable, X>;
|
|
new (&inner->data) X(args...);
|
|
return VRc(inner);
|
|
}
|
|
|
|
const X* operator->() const {
|
|
return &inner->data;
|
|
}
|
|
const X& operator*() const {
|
|
return inner->data;
|
|
}
|
|
};
|
|
|
|
template<typename VTable, typename X = Dyn>
|
|
class VWeak {
|
|
VRcInner<VTable, X> *inner = nullptr;
|
|
public:
|
|
VWeak() = default;
|
|
~VWeak() {
|
|
if (inner && !--inner->weak_ref) {
|
|
inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t*>(inner), inner->layout);
|
|
}
|
|
}
|
|
VWeak(const VWeak &other): inner(other.inner) {
|
|
inner && inner->weak_ref++;
|
|
}
|
|
VWeak(const VRc<VTable, X> &other): inner(other.inner) {
|
|
inner && inner->weak_ref++;
|
|
}
|
|
VWeak &operator=(const VWeak &other) {
|
|
if (inner == other.inner)
|
|
return *this;
|
|
this->~VWeak();
|
|
new(this) VWeak(other);
|
|
return *this;
|
|
}
|
|
|
|
std::optional<VRc<VTable, X>> lock() const {
|
|
if (!inner || inner->strong_ref == 0)
|
|
return {};
|
|
inner->strong_ref++;
|
|
return { VRc<VTable, X>(inner) };
|
|
}
|
|
};
|
|
|
|
|
|
|
|
} //namespace vtable
|
|
|