Fix cross-compilation of C++ interpreter API to 32-bit architecture (#3810)

cbindgen doesn't reliably tranfser the 64-bit/32-bit #ifdefs around ValueOpaque.
Instead, move Value onto the heap by using a Box.

Fixes #3653
This commit is contained in:
Simon Hausmann 2023-10-31 17:33:49 +01:00 committed by GitHub
parent 4b37d9a1f1
commit 1441719f61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 226 additions and 280 deletions

View file

@ -20,7 +20,6 @@ jobs:
- sdk_url: https://nextcloud.slint.dev/s/BTL5NtLACjgS7Pf/download/poky-glibc-x86_64-core-image-weston-cortexa15t2hf-neon-qemuarm-toolchain-4.0.9.sh
env_setup: environment-setup-cortexa15t2hf-neon-poky-linux-gnueabi
target: armv7-unknown-linux-gnueabihf
extra_args: -DSLINT_FEATURE_INTERPRETER=OFF
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
@ -43,5 +42,5 @@ jobs:
# f5c3908b7ec5131b7b19ff642b5975660c7484f8
export BINDGEN_EXTRA_CLANG_ARGS=$OECORE_TUNE_CCARGS
mkdir ${{ runner.workspace }}/cppbuild
cmake -GNinja -B ${{ runner.workspace }}/cppbuild -S . -DRust_CARGO_TARGET=${{ matrix.target }} -DSLINT_BUILD_TESTING=ON -DSLINT_BUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=Debug -DSLINT_FEATURE_RENDERER_SKIA=ON -DSLINT_FEATURE_BACKEND_QT=OFF -DSLINT_FEATURE_BACKEND_LINUXKMS=ON ${{ matrix.extra_args }}
cmake -GNinja -B ${{ runner.workspace }}/cppbuild -S . -DRust_CARGO_TARGET=${{ matrix.target }} -DSLINT_BUILD_TESTING=ON -DSLINT_BUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=Debug -DSLINT_FEATURE_RENDERER_SKIA=ON -DSLINT_FEATURE_BACKEND_QT=OFF -DSLINT_FEATURE_BACKEND_LINUXKMS=ON -DSLINT_FEATURE_INTERPRETER=ON
cmake --build ${{ runner.workspace }}/cppbuild

View file

@ -768,13 +768,13 @@ fn gen_interpreter(
dependencies: &mut Vec<PathBuf>,
) -> anyhow::Result<()> {
let mut config = default_config();
// Avoid Value, just export ValueOpaque.
config.export.exclude = IntoIterator::into_iter([
"Value",
"ValueType",
"PropertyDescriptor",
"Diagnostic",
"PropertyDescriptor",
"Box",
])
.map(String::from)
.collect();
@ -795,7 +795,7 @@ fn gen_interpreter(
"StructIteratorOpaque",
"ComponentInstance",
"StructIteratorResult",
"ValueOpaque",
"Value",
"StructOpaque",
"ModelNotifyOpaque",
])
@ -821,6 +821,7 @@ fn gen_interpreter(
using slint::interpreter::ValueType;
using slint::interpreter::PropertyDescriptor;
using slint::interpreter::Diagnostic;
template <typename T> using Box = T*;
}",
)
.generate()

View file

@ -151,30 +151,17 @@ public:
private:
cbindgen_private::StructIteratorOpaque inner;
const Value *v = nullptr;
Value *v = nullptr;
std::string_view k;
friend Struct;
explicit iterator(cbindgen_private::StructIteratorOpaque inner) : inner(inner) { next(); }
// construct a end iterator
iterator() = default;
void next()
{
auto next = cbindgen_private::slint_interpreter_struct_iterator_next(&inner);
v = reinterpret_cast<const Value *>(next.v);
k = std::string_view(reinterpret_cast<char *>(next.k.ptr), next.k.len);
if (!v) {
cbindgen_private::slint_interpreter_struct_iterator_destructor(&inner);
}
}
inline void next();
public:
/// Destroys this field iterator.
~iterator()
{
if (v) {
cbindgen_private::slint_interpreter_struct_iterator_destructor(&inner);
}
}
inline ~iterator();
// FIXME I believe iterators are supposed to be copy constructible
iterator(const iterator &) = delete;
iterator &operator=(const iterator &) = delete;
@ -256,23 +243,23 @@ class Value
{
public:
/// Constructs a new value of type Value::Type::Void.
Value() { cbindgen_private::slint_interpreter_value_new(&inner); }
Value() : inner(cbindgen_private::slint_interpreter_value_new()) { }
/// Constructs a new value by copying \a other.
Value(const Value &other) { slint_interpreter_value_clone(&other.inner, &inner); }
Value(const Value &other) : inner(slint_interpreter_value_clone(other.inner)) { }
/// Constructs a new value by moving \a other to this.
Value(Value &&other)
{
inner = other.inner;
cbindgen_private::slint_interpreter_value_new(&other.inner);
other.inner = cbindgen_private::slint_interpreter_value_new();
}
/// Assigns the value \a other to this.
Value &operator=(const Value &other)
{
if (this == &other)
return *this;
cbindgen_private::slint_interpreter_value_destructor(&inner);
slint_interpreter_value_clone(&other.inner, &inner);
cbindgen_private::slint_interpreter_value_destructor(inner);
inner = slint_interpreter_value_clone(other.inner);
return *this;
}
/// Moves the value \a other to this.
@ -280,13 +267,13 @@ public:
{
if (this == &other)
return *this;
cbindgen_private::slint_interpreter_value_destructor(&inner);
cbindgen_private::slint_interpreter_value_destructor(inner);
inner = other.inner;
cbindgen_private::slint_interpreter_value_new(&other.inner);
other.inner = cbindgen_private::slint_interpreter_value_new();
return *this;
}
/// Destroys the value.
~Value() { cbindgen_private::slint_interpreter_value_destructor(&inner); }
~Value() { cbindgen_private::slint_interpreter_value_destructor(inner); }
/// A convenience alias for the value type enum.
using Type = ValueType;
@ -297,7 +284,7 @@ public:
/// Type::Double, otherwise an empty optional is returned.
std::optional<double> to_number() const
{
if (auto *number = cbindgen_private::slint_interpreter_value_to_number(&inner)) {
if (auto *number = cbindgen_private::slint_interpreter_value_to_number(inner)) {
return *number;
} else {
return {};
@ -308,7 +295,7 @@ public:
/// Type::String, otherwise an empty optional is returned.
std::optional<slint::SharedString> to_string() const
{
if (auto *str = cbindgen_private::slint_interpreter_value_to_string(&inner)) {
if (auto *str = cbindgen_private::slint_interpreter_value_to_string(inner)) {
return *str;
} else {
return {};
@ -319,7 +306,7 @@ public:
/// Type::Bool, otherwise an empty optional is returned.
std::optional<bool> to_bool() const
{
if (auto *b = cbindgen_private::slint_interpreter_value_to_bool(&inner)) {
if (auto *b = cbindgen_private::slint_interpreter_value_to_bool(inner)) {
return *b;
} else {
return {};
@ -336,7 +323,7 @@ public:
/// Type::Brush, otherwise an empty optional is returned.
std::optional<slint::Brush> to_brush() const
{
if (auto *brush = cbindgen_private::slint_interpreter_value_to_brush(&inner)) {
if (auto *brush = cbindgen_private::slint_interpreter_value_to_brush(inner)) {
return *brush;
} else {
return {};
@ -347,7 +334,7 @@ public:
/// Type::Struct, otherwise an empty optional is returned.
std::optional<Struct> to_struct() const
{
if (auto *opaque_struct = cbindgen_private::slint_interpreter_value_to_struct(&inner)) {
if (auto *opaque_struct = cbindgen_private::slint_interpreter_value_to_struct(inner)) {
return Struct(*opaque_struct);
} else {
return {};
@ -358,7 +345,7 @@ public:
/// Type::Image, otherwise an empty optional is returned.
std::optional<Image> to_image() const
{
if (auto *img = cbindgen_private::slint_interpreter_value_to_image(&inner)) {
if (auto *img = cbindgen_private::slint_interpreter_value_to_image(inner)) {
return *reinterpret_cast<const Image *>(img);
} else {
return {};
@ -368,66 +355,68 @@ public:
// template<typename T> std::optional<T> get() const;
/// Constructs a new Value that holds the double \a value.
Value(double value) { cbindgen_private::slint_interpreter_value_new_double(value, &inner); }
Value(double value) : inner(cbindgen_private::slint_interpreter_value_new_double(value)) { }
/// Constructs a new Value that holds the int \a value.
/// Internally this is stored as a double and Value::type() will return Value::Type::Number.
Value(int value) : Value(static_cast<double>(value)) { }
/// Constructs a new Value that holds the string \a str.
Value(const SharedString &str)
: inner(cbindgen_private::slint_interpreter_value_new_string(&str))
{
cbindgen_private::slint_interpreter_value_new_string(&str, &inner);
}
/// Constructs a new Value that holds the boolean \a b.
Value(bool b) { cbindgen_private::slint_interpreter_value_new_bool(b, &inner); }
Value(bool b) : inner(cbindgen_private::slint_interpreter_value_new_bool(b)) { }
/// Constructs a new Value that holds the value vector \a v as a model.
inline Value(const SharedVector<Value> &v);
/// Constructs a new Value that holds the value model \a m.
Value(const std::shared_ptr<slint::Model<Value>> &m);
/// Constructs a new Value that holds the brush \a b.
Value(const slint::Brush &brush)
: inner(cbindgen_private::slint_interpreter_value_new_brush(&brush))
{
cbindgen_private::slint_interpreter_value_new_brush(&brush, &inner);
}
/// Constructs a new Value that holds the Struct \a struc.
Value(const Struct &struc)
: inner(cbindgen_private::slint_interpreter_value_new_struct(&struc.inner))
{
cbindgen_private::slint_interpreter_value_new_struct(&struc.inner, &inner);
}
/// Constructs a new Value that holds the Image \a img.
Value(const Image &img) { cbindgen_private::slint_interpreter_value_new_image(&img, &inner); }
Value(const Image &img) : inner(cbindgen_private::slint_interpreter_value_new_image(&img)) { }
/// Returns the type the variant holds.
Type type() const { return cbindgen_private::slint_interpreter_value_type(&inner); }
Type type() const { return cbindgen_private::slint_interpreter_value_type(inner); }
/// Returns true if \a a and \a b hold values of the same type and the underlying vales are
/// equal.
friend bool operator==(const Value &a, const Value &b)
{
return cbindgen_private::slint_interpreter_value_eq(&a.inner, &b.inner);
return cbindgen_private::slint_interpreter_value_eq(a.inner, b.inner);
}
private:
inline Value(const void *) = delete; // Avoid that for example Value("foo") turns to Value(bool)
using ValueOpaque = slint::cbindgen_private::ValueOpaque;
ValueOpaque inner;
slint::cbindgen_private::Value *inner;
friend struct Struct;
friend class ComponentInstance;
// Internal constructor that takes ownership of the value
explicit Value(ValueOpaque &inner) : inner(inner) { }
explicit Value(slint::cbindgen_private::Value *&&inner) : inner(inner) { }
};
inline Value::Value(const slint::SharedVector<Value> &array)
: inner(cbindgen_private::slint_interpreter_value_new_array_model(
reinterpret_cast<const slint::SharedVector<slint::cbindgen_private::Value *> *>(
&array)))
{
cbindgen_private::slint_interpreter_value_new_array_model(
reinterpret_cast<const slint::SharedVector<ValueOpaque> *>(&array), &inner);
}
inline std::optional<slint::SharedVector<Value>> Value::to_array() const
{
slint::SharedVector<Value> array;
if (cbindgen_private::slint_interpreter_value_to_array(
&inner, reinterpret_cast<slint::SharedVector<ValueOpaque> *>(&array))) {
&inner,
reinterpret_cast<slint::SharedVector<slint::cbindgen_private::Value *> *>(
&array))) {
return array;
} else {
return {};
@ -470,18 +459,21 @@ inline Value::Value(const std::shared_ptr<slint::Model<Value>> &model)
auto row_count = [](VRef<ModelAdaptorVTable> self) -> uintptr_t {
return reinterpret_cast<ModelWrapper *>(self.instance)->model->row_count();
};
auto row_data = [](VRef<ModelAdaptorVTable> self, uintptr_t row, ValueOpaque *out) {
auto row_data = [](VRef<ModelAdaptorVTable> self,
uintptr_t row) -> slint::cbindgen_private::Value * {
std::optional<Value> v =
reinterpret_cast<ModelWrapper *>(self.instance)->model->row_data(int(row));
if (v.has_value()) {
*out = v->inner;
cbindgen_private::slint_interpreter_value_new(&v->inner);
return true;
slint::cbindgen_private::Value *rval = v->inner;
v->inner = cbindgen_private::slint_interpreter_value_new();
return rval;
} else {
return nullptr;
}
return false;
};
auto set_row_data = [](VRef<ModelAdaptorVTable> self, uintptr_t row, const ValueOpaque *value) {
Value v = *reinterpret_cast<const Value *>(value);
auto set_row_data = [](VRef<ModelAdaptorVTable> self, uintptr_t row,
slint::cbindgen_private::Value *value) {
Value v(std::move(value));
reinterpret_cast<ModelWrapper *>(self.instance)->model->set_row_data(int(row), v);
};
auto get_notify =
@ -493,8 +485,8 @@ inline Value::Value(const std::shared_ptr<slint::Model<Value>> &model)
};
static const ModelAdaptorVTable vt { row_count, row_data, set_row_data, get_notify, drop };
cbindgen_private::slint_interpreter_value_new_model(reinterpret_cast<uint8_t *>(wrapper.get()),
&vt, &inner);
inner = cbindgen_private::slint_interpreter_value_new_model(
reinterpret_cast<uint8_t *>(wrapper.get()), &vt);
}
inline Struct::Struct(std::initializer_list<std::pair<std::string_view, Value>> args)
@ -504,12 +496,14 @@ inline Struct::Struct(std::initializer_list<std::pair<std::string_view, Value>>
inline std::optional<Value> Struct::get_field(std::string_view name) const
{
using namespace cbindgen_private;
cbindgen_private::Slice<uint8_t> name_view {
const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(name.data())),
name.size()
};
if (auto *value = cbindgen_private::slint_interpreter_struct_get_field(&inner, name_view)) {
return *reinterpret_cast<const Value *>(value);
if (cbindgen_private::Value *field_val =
cbindgen_private::slint_interpreter_struct_get_field(&inner, name_view)) {
return Value(std::move(field_val));
} else {
return {};
}
@ -520,7 +514,32 @@ inline void Struct::set_field(std::string_view name, const Value &value)
const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(name.data())),
name.size()
};
cbindgen_private::slint_interpreter_struct_set_field(&inner, name_view, &value.inner);
cbindgen_private::slint_interpreter_struct_set_field(&inner, name_view, value.inner);
}
inline void Struct::iterator::next()
{
cbindgen_private::Slice<uint8_t> name_slice;
if (cbindgen_private::Value *nextval_inner =
cbindgen_private::slint_interpreter_struct_iterator_next(&inner, &name_slice)) {
k = std::string_view(reinterpret_cast<char *>(name_slice.ptr), name_slice.len);
if (!v)
v = new Value();
*v = Value(std::move(nextval_inner));
} else {
cbindgen_private::slint_interpreter_struct_iterator_destructor(&inner);
delete v;
v = nullptr;
}
}
inline Struct::iterator::~iterator()
{
if (v) {
cbindgen_private::slint_interpreter_struct_iterator_destructor(&inner);
delete v;
}
}
/// The ComponentInstance represents a running instance of a component.
@ -606,16 +625,15 @@ public:
{
using namespace cbindgen_private;
return slint_interpreter_component_instance_set_property(
inner(), slint::private_api::string_to_slice(name), &value.inner);
inner(), slint::private_api::string_to_slice(name), value.inner);
}
/// Returns the value behind a property declared in .slint.
std::optional<Value> get_property(std::string_view name) const
{
using namespace cbindgen_private;
ValueOpaque out;
if (slint_interpreter_component_instance_get_property(
inner(), slint::private_api::string_to_slice(name), &out)) {
return Value(out);
if (cbindgen_private::Value *prop_inner = slint_interpreter_component_instance_get_property(
inner(), slint::private_api::string_to_slice(name))) {
return Value(std::move(prop_inner));
} else {
return {};
}
@ -638,13 +656,14 @@ public:
std::optional<Value> invoke(std::string_view name, std::span<const Value> args) const
{
using namespace cbindgen_private;
Slice<ValueOpaque> args_view { const_cast<ValueOpaque *>(
reinterpret_cast<const ValueOpaque *>(args.data())),
args.size() };
ValueOpaque out;
if (slint_interpreter_component_instance_invoke(
inner(), slint::private_api::string_to_slice(name), args_view, &out)) {
return Value(out);
Slice<Box<cbindgen_private::Value>> args_view {
const_cast<Box<cbindgen_private::Value> *>(
reinterpret_cast<const Box<cbindgen_private::Value> *>(args.data())),
args.size()
};
if (cbindgen_private::Value *rval_inner = slint_interpreter_component_instance_invoke(
inner(), slint::private_api::string_to_slice(name), args_view)) {
return Value(std::move(rval_inner));
} else {
return {};
}
@ -677,12 +696,15 @@ public:
auto set_callback(std::string_view name, F callback) const -> bool
// clang-format on
{
using cbindgen_private::ValueOpaque;
auto actual_cb = [](void *data, cbindgen_private::Slice<ValueOpaque> arg,
ValueOpaque *ret) {
using namespace cbindgen_private;
auto actual_cb = [](void *data,
cbindgen_private::Slice<cbindgen_private::Box<cbindgen_private::Value>>
arg) {
std::span<const Value> args_view { reinterpret_cast<const Value *>(arg.ptr), arg.len };
Value r = (*reinterpret_cast<F *>(data))(args_view);
new (ret) Value(std::move(r));
auto inner = r.inner;
r.inner = cbindgen_private::slint_interpreter_value_new();
return inner;
};
return cbindgen_private::slint_interpreter_component_instance_set_callback(
inner(), slint::private_api::string_to_slice(name), actual_cb,
@ -709,18 +731,18 @@ public:
using namespace cbindgen_private;
return slint_interpreter_component_instance_set_global_property(
inner(), slint::private_api::string_to_slice(global),
slint::private_api::string_to_slice(prop_name), &value.inner);
slint::private_api::string_to_slice(prop_name), value.inner);
}
/// Returns the value behind a property in an exported global singleton.
std::optional<Value> get_global_property(std::string_view global,
std::string_view prop_name) const
{
using namespace cbindgen_private;
ValueOpaque out;
if (slint_interpreter_component_instance_get_global_property(
inner(), slint::private_api::string_to_slice(global),
slint::private_api::string_to_slice(prop_name), &out)) {
return Value(out);
if (cbindgen_private::Value *rval_inner =
slint_interpreter_component_instance_get_global_property(
inner(), slint::private_api::string_to_slice(global),
slint::private_api::string_to_slice(prop_name))) {
return Value(std::move(rval_inner));
} else {
return {};
}
@ -748,12 +770,15 @@ public:
template<std::invocable<std::span<const Value>> F>
bool set_global_callback(std::string_view global, std::string_view name, F callback) const
{
using cbindgen_private::ValueOpaque;
auto actual_cb = [](void *data, cbindgen_private::Slice<ValueOpaque> arg,
ValueOpaque *ret) {
using namespace cbindgen_private;
auto actual_cb = [](void *data,
cbindgen_private::Slice<cbindgen_private::Box<cbindgen_private::Value>>
arg) {
std::span<const Value> args_view { reinterpret_cast<const Value *>(arg.ptr), arg.len };
Value r = (*reinterpret_cast<F *>(data))(args_view);
new (ret) Value(std::move(r));
auto inner = r.inner;
r.inner = cbindgen_private::slint_interpreter_value_new();
return inner;
};
return cbindgen_private::slint_interpreter_component_instance_set_global_callback(
inner(), slint::private_api::string_to_slice(global),
@ -766,14 +791,17 @@ public:
std::span<const Value> args) const
{
using namespace cbindgen_private;
Slice<ValueOpaque> args_view { const_cast<ValueOpaque *>(
reinterpret_cast<const ValueOpaque *>(args.data())),
args.size() };
ValueOpaque out;
if (slint_interpreter_component_instance_invoke_global(
inner(), slint::private_api::string_to_slice(global),
slint::private_api::string_to_slice(callable_name), args_view, &out)) {
return Value(out);
Slice<cbindgen_private::Box<cbindgen_private::Value>> args_view {
const_cast<cbindgen_private::Box<cbindgen_private::Value> *>(
reinterpret_cast<const cbindgen_private::Box<cbindgen_private::Value> *>(
args.data())),
args.size()
};
if (cbindgen_private::Value *rval_inner =
slint_interpreter_component_instance_invoke_global(
inner(), slint::private_api::string_to_slice(global),
slint::private_api::string_to_slice(callable_name), args_view)) {
return Value(std::move(rval_inner));
} else {
return {};
}
@ -1001,7 +1029,6 @@ public:
}
}
};
}
namespace slint::testing {

View file

@ -11,101 +11,72 @@ use i_slint_core::window::WindowAdapter;
use std::ffi::c_void;
use vtable::VRef;
#[repr(C)]
#[cfg(target_pointer_width = "64")]
pub struct ValueOpaque([usize; 7]);
#[repr(C)]
#[cfg(target_pointer_width = "32")]
#[repr(align(8))]
pub struct ValueOpaque([usize; 9]);
/// Asserts that ValueOpaque is as large as Value and has the same alignment, to make transmute safe.
const _: [(); std::mem::size_of::<ValueOpaque>()] = [(); std::mem::size_of::<Value>()];
const _: [(); std::mem::align_of::<ValueOpaque>()] = [(); std::mem::align_of::<Value>()];
impl ValueOpaque {
fn as_value(&self) -> &Value {
// Safety: there should be no way to construct a ValueOpaque without it holding an actual Value
unsafe { std::mem::transmute::<&ValueOpaque, &Value>(self) }
}
/// Construct a new Value in the given memory location
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new() -> Box<Value> {
Box::new(Value::default())
}
/// Construct a new Value in the given memory location
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new(val: *mut ValueOpaque) {
std::ptr::write(val as *mut Value, Value::default())
}
/// Construct a new Value in the given memory location
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_clone(other: &ValueOpaque, val: *mut ValueOpaque) {
std::ptr::write(val as *mut Value, other.as_value().clone())
pub unsafe extern "C" fn slint_interpreter_value_clone(other: &Value) -> Box<Value> {
Box::new(other.clone())
}
/// Destruct the value in that memory location
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_destructor(val: *mut ValueOpaque) {
drop(std::ptr::read(val as *mut Value))
pub unsafe extern "C" fn slint_interpreter_value_destructor(val: Box<Value>) {
drop(val);
}
#[no_mangle]
pub extern "C" fn slint_interpreter_value_eq(a: &ValueOpaque, b: &ValueOpaque) -> bool {
a.as_value() == b.as_value()
pub extern "C" fn slint_interpreter_value_eq(a: &Value, b: &Value) -> bool {
a == b
}
/// Construct a new Value in the given memory location as string
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new_string(
str: &SharedString,
val: *mut ValueOpaque,
) {
std::ptr::write(val as *mut Value, Value::String(str.clone()))
pub unsafe extern "C" fn slint_interpreter_value_new_string(str: &SharedString) -> Box<Value> {
Box::new(Value::String(str.clone()))
}
/// Construct a new Value in the given memory location as double
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new_double(double: f64, val: *mut ValueOpaque) {
std::ptr::write(val as *mut Value, Value::Number(double))
pub unsafe extern "C" fn slint_interpreter_value_new_double(double: f64) -> Box<Value> {
Box::new(Value::Number(double))
}
/// Construct a new Value in the given memory location as bool
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new_bool(b: bool, val: *mut ValueOpaque) {
std::ptr::write(val as *mut Value, Value::Bool(b))
pub unsafe extern "C" fn slint_interpreter_value_new_bool(b: bool) -> Box<Value> {
Box::new(Value::Bool(b))
}
/// Construct a new Value in the given memory location as array model
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new_array_model(
a: &SharedVector<ValueOpaque>,
val: *mut ValueOpaque,
) {
// Safety: We assert that Value and ValueOpaque have the same size and alignment
let vec = std::mem::transmute::<SharedVector<ValueOpaque>, SharedVector<Value>>(a.clone());
std::ptr::write(
val as *mut Value,
Value::Model(ModelRc::new(SharedVectorModel::<Value>::from(vec))),
)
a: &SharedVector<Box<Value>>,
) -> Box<Value> {
let vec = a.iter().map(|vb| vb.as_ref().clone()).collect::<SharedVector<_>>();
Box::new(Value::Model(ModelRc::new(SharedVectorModel::from(vec))))
}
/// Construct a new Value in the given memory location as Brush
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new_brush(brush: &Brush, val: *mut ValueOpaque) {
std::ptr::write(val as *mut Value, Value::Brush(brush.clone()))
pub unsafe extern "C" fn slint_interpreter_value_new_brush(brush: &Brush) -> Box<Value> {
Box::new(Value::Brush(brush.clone()))
}
/// Construct a new Value in the given memory location as Struct
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new_struct(
struc: &StructOpaque,
val: *mut ValueOpaque,
) {
std::ptr::write(val as *mut Value, Value::Struct(struc.as_struct().clone()))
pub unsafe extern "C" fn slint_interpreter_value_new_struct(struc: &StructOpaque) -> Box<Value> {
Box::new(Value::Struct(struc.as_struct().clone()))
}
/// Construct a new Value in the given memory location as image
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_new_image(img: &Image, val: *mut ValueOpaque) {
std::ptr::write(val as *mut Value, Value::Image(img.clone()))
pub unsafe extern "C" fn slint_interpreter_value_new_image(img: &Image) -> Box<Value> {
Box::new(Value::Image(img.clone()))
}
/// Construct a new Value containing a model in the given memory location
@ -113,41 +84,37 @@ pub unsafe extern "C" fn slint_interpreter_value_new_image(img: &Image, val: *mu
pub unsafe extern "C" fn slint_interpreter_value_new_model(
model: NonNull<u8>,
vtable: &ModelAdaptorVTable,
val: *mut ValueOpaque,
) {
std::ptr::write(
val as *mut Value,
Value::Model(ModelRc::new(ModelAdaptorWrapper(vtable::VBox::from_raw(
NonNull::from(vtable),
model,
)))),
)
) -> Box<Value> {
Box::new(Value::Model(ModelRc::new(ModelAdaptorWrapper(vtable::VBox::from_raw(
NonNull::from(vtable),
model,
)))))
}
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_value_type(val: *const ValueOpaque) -> ValueType {
(&*val).as_value().value_type()
pub unsafe extern "C" fn slint_interpreter_value_type(val: &Value) -> ValueType {
val.value_type()
}
#[no_mangle]
pub extern "C" fn slint_interpreter_value_to_string(val: &ValueOpaque) -> Option<&SharedString> {
match val.as_value() {
pub extern "C" fn slint_interpreter_value_to_string(val: &Value) -> Option<&SharedString> {
match val {
Value::String(v) => Some(v),
_ => None,
}
}
#[no_mangle]
pub extern "C" fn slint_interpreter_value_to_number(val: &ValueOpaque) -> Option<&f64> {
match val.as_value() {
pub extern "C" fn slint_interpreter_value_to_number(val: &Value) -> Option<&f64> {
match val {
Value::Number(v) => Some(v),
_ => None,
}
}
#[no_mangle]
pub extern "C" fn slint_interpreter_value_to_bool(val: &ValueOpaque) -> Option<&bool> {
match val.as_value() {
pub extern "C" fn slint_interpreter_value_to_bool(val: &Value) -> Option<&bool> {
match val {
Value::Bool(v) => Some(v),
_ => None,
}
@ -158,23 +125,14 @@ pub extern "C" fn slint_interpreter_value_to_bool(val: &ValueOpaque) -> Option<&
/// array.
#[no_mangle]
pub extern "C" fn slint_interpreter_value_to_array(
val: &ValueOpaque,
out: *mut SharedVector<ValueOpaque>,
val: &Box<Value>,
out: *mut SharedVector<Box<Value>>,
) -> bool {
match val.as_value() {
match val.as_ref() {
Value::Model(m) => {
let vec = if let Some(model) = m.as_any().downcast_ref::<SharedVectorModel<Value>>() {
model.shared_vector()
} else {
m.iter().collect()
};
// Safety: We assert that Value and ValueOpaque have the same size and alignment
let vec = m.iter().map(|vb| Box::new(vb)).collect::<SharedVector<_>>();
unsafe {
std::ptr::write(
out,
std::mem::transmute::<SharedVector<Value>, SharedVector<ValueOpaque>>(vec),
);
std::ptr::write(out, vec);
}
true
@ -184,24 +142,24 @@ pub extern "C" fn slint_interpreter_value_to_array(
}
#[no_mangle]
pub extern "C" fn slint_interpreter_value_to_brush(val: &ValueOpaque) -> Option<&Brush> {
match val.as_value() {
pub extern "C" fn slint_interpreter_value_to_brush(val: &Value) -> Option<&Brush> {
match val {
Value::Brush(b) => Some(b),
_ => None,
}
}
#[no_mangle]
pub extern "C" fn slint_interpreter_value_to_struct(val: &ValueOpaque) -> *const StructOpaque {
match val.as_value() {
pub extern "C" fn slint_interpreter_value_to_struct(val: &Value) -> *const StructOpaque {
match val {
Value::Struct(s) => s as *const Struct as *const StructOpaque,
_ => std::ptr::null(),
}
}
#[no_mangle]
pub extern "C" fn slint_interpreter_value_to_image(val: &ValueOpaque) -> Option<&Image> {
match val.as_value() {
pub extern "C" fn slint_interpreter_value_to_image(val: &Value) -> Option<&Image> {
match val {
Value::Image(img) => Some(img),
_ => None,
}
@ -249,23 +207,24 @@ pub unsafe extern "C" fn slint_interpreter_struct_destructor(val: *mut StructOpa
}
#[no_mangle]
pub extern "C" fn slint_interpreter_struct_get_field<'a>(
stru: &'a StructOpaque,
pub extern "C" fn slint_interpreter_struct_get_field(
stru: &StructOpaque,
name: Slice<u8>,
) -> Option<&'a ValueOpaque> {
stru.as_struct()
.get_field(std::str::from_utf8(&name).unwrap())
.and_then(|v| unsafe { (v as *const Value as *const ValueOpaque).as_ref() })
) -> *mut Value {
if let Some(value) = stru.as_struct().get_field(std::str::from_utf8(&name).unwrap()) {
Box::into_raw(Box::new(value.clone()))
} else {
std::ptr::null_mut()
}
}
#[no_mangle]
pub extern "C" fn slint_interpreter_struct_set_field<'a>(
stru: &'a mut StructOpaque,
name: Slice<u8>,
value: &ValueOpaque,
value: &Value,
) {
stru.as_struct_mut()
.set_field(std::str::from_utf8(&name).unwrap().into(), value.as_value().clone())
stru.as_struct_mut().set_field(std::str::from_utf8(&name).unwrap().into(), value.clone())
}
type StructIterator<'a> = std::collections::hash_map::Iter<'a, String, Value>;
@ -283,23 +242,18 @@ pub unsafe extern "C" fn slint_interpreter_struct_iterator_destructor(
drop(std::ptr::read(val as *mut StructIterator))
}
/// The result of the slint_interpreter_struct_iterator_next function
/// If the iterator was at the end, the key will be empty, and the value will be None
#[repr(C)]
pub struct StructIteratorResult<'a> {
k: Slice<'a, u8>,
v: Option<&'a Value>,
}
/// Advance the iterator and return the next value, or an
/// Advance the iterator and return the next value, or a null pointer
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_struct_iterator_next<'a>(
iter: &'a mut StructIteratorOpaque,
) -> StructIteratorResult<'a> {
k: &mut Slice<'a, u8>,
) -> *mut Value {
if let Some((str, val)) = (*(iter as *mut StructIteratorOpaque as *mut StructIterator)).next() {
StructIteratorResult { k: Slice::from_slice(str.as_bytes()), v: Some(val) }
*k = Slice::from_slice(str.as_bytes());
Box::into_raw(Box::new(val.clone()))
} else {
StructIteratorResult { k: Slice::default(), v: None }
*k = Slice::default();
std::ptr::null_mut()
}
}
@ -313,26 +267,20 @@ pub extern "C" fn slint_interpreter_struct_make_iter(stru: &StructOpaque) -> Str
}
}
/// Get a property.
/// The `out` parameter must be uninitialized. If this function returns true, the out will be initialized
/// to the resulting value. If this function returns false, out is unchanged
/// Get a property. Returns a null pointer if the property does not exist.
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_component_instance_get_property(
inst: &ErasedItemTreeBox,
name: Slice<u8>,
out: *mut ValueOpaque,
) -> bool {
) -> *mut Value {
generativity::make_guard!(guard);
let comp = inst.unerase(guard);
match comp
.description()
.get_property(comp.borrow(), &normalize_identifier(std::str::from_utf8(&name).unwrap()))
{
Ok(val) => {
std::ptr::write(out as *mut Value, val);
true
}
Err(_) => false,
Ok(val) => Box::into_raw(Box::new(val)),
Err(_) => std::ptr::null_mut(),
}
}
@ -340,7 +288,7 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_get_property(
pub extern "C" fn slint_interpreter_component_instance_set_property(
inst: &ErasedItemTreeBox,
name: Slice<u8>,
val: &ValueOpaque,
val: &Value,
) -> bool {
generativity::make_guard!(guard);
let comp = inst.unerase(guard);
@ -348,22 +296,19 @@ pub extern "C" fn slint_interpreter_component_instance_set_property(
.set_property(
comp.borrow(),
&normalize_identifier(std::str::from_utf8(&name).unwrap()),
val.as_value().clone(),
val.clone(),
)
.is_ok()
}
/// Invoke a callback or function
/// The `out` parameter must be uninitialized. If this function returns true, the out will be initialized
/// to the resulting value. If this function returns false, out is unchanged
/// Invoke a callback or function. Returns raw boxed value on success and null ptr on failure.
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_component_instance_invoke(
inst: &ErasedItemTreeBox,
name: Slice<u8>,
args: Slice<ValueOpaque>,
out: *mut ValueOpaque,
) -> bool {
let args = std::mem::transmute::<Slice<ValueOpaque>, Slice<Value>>(args);
args: Slice<Box<Value>>,
) -> *mut Value {
let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
generativity::make_guard!(guard);
let comp = inst.unerase(guard);
match comp.description().invoke(
@ -371,11 +316,8 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_invoke(
&normalize_identifier(std::str::from_utf8(&name).unwrap()),
args.as_slice(),
) {
Ok(val) => {
std::ptr::write(out as *mut Value, val);
true
}
Err(_) => false,
Ok(val) => Box::into_raw(Box::new(val)),
Err(_) => std::ptr::null_mut(),
}
}
@ -386,7 +328,7 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_invoke(
struct CallbackUserData {
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<ValueOpaque>, ret: *mut ValueOpaque),
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
}
impl Drop for CallbackUserData {
@ -399,16 +341,8 @@ impl Drop for CallbackUserData {
impl CallbackUserData {
fn call(&self, args: &[Value]) -> Value {
unsafe {
let args = std::mem::transmute::<&[Value], &[ValueOpaque]>(args);
let mut ret = std::mem::MaybeUninit::<Value>::uninit();
(self.callback)(
self.user_data,
Slice::from_slice(args),
ret.as_mut_ptr() as *mut ValueOpaque,
);
ret.assume_init()
}
let args = args.iter().map(|v| v.clone().into()).collect::<Vec<_>>();
(self.callback)(self.user_data, Slice::from_slice(args.as_ref())).as_ref().clone()
}
}
@ -418,7 +352,7 @@ impl CallbackUserData {
pub unsafe extern "C" fn slint_interpreter_component_instance_set_callback(
inst: &ErasedItemTreeBox,
name: Slice<u8>,
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<ValueOpaque>, ret: *mut ValueOpaque),
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
) -> bool {
@ -435,16 +369,13 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_set_callback(
.is_ok()
}
/// Get a global property.
/// The `out` parameter must be uninitialized. If this function returns true, the out will be initialized
/// to the resulting value. If this function returns false, out is unchanged
/// Get a global property. Returns a raw boxed value on success; nullptr otherwise.
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_component_instance_get_global_property(
inst: &ErasedItemTreeBox,
global: Slice<u8>,
property_name: Slice<u8>,
out: *mut ValueOpaque,
) -> bool {
) -> *mut Value {
generativity::make_guard!(guard);
let comp = inst.unerase(guard);
match comp
@ -454,11 +385,8 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_get_global_propert
g.as_ref()
.get_property(&normalize_identifier(std::str::from_utf8(&property_name).unwrap()))
}) {
Ok(val) => {
std::ptr::write(out as *mut Value, val);
true
}
Err(_) => false,
Ok(val) => Box::into_raw(Box::new(val)),
Err(_) => std::ptr::null_mut(),
}
}
@ -467,7 +395,7 @@ pub extern "C" fn slint_interpreter_component_instance_set_global_property(
inst: &ErasedItemTreeBox,
global: Slice<u8>,
property_name: Slice<u8>,
val: &ValueOpaque,
val: &Value,
) -> bool {
generativity::make_guard!(guard);
let comp = inst.unerase(guard);
@ -477,7 +405,7 @@ pub extern "C" fn slint_interpreter_component_instance_set_global_property(
g.as_ref()
.set_property(
&normalize_identifier(std::str::from_utf8(&property_name).unwrap()),
val.as_value().clone(),
val.clone(),
)
.map_err(|_| ())
})
@ -490,7 +418,7 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_set_global_callbac
inst: &ErasedItemTreeBox,
global: Slice<u8>,
name: Slice<u8>,
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<ValueOpaque>, ret: *mut ValueOpaque),
callback: extern "C" fn(user_data: *mut c_void, arg: Slice<Box<Value>>) -> Box<Value>,
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
) -> bool {
@ -509,18 +437,15 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_set_global_callbac
.is_ok()
}
/// Invoke a global callback or function.
/// The `out` parameter must be uninitialized. If this function returns true, the out will be initialized
/// to the resulting value. If this function returns false, out is unchanged
/// Invoke a global callback or function. Returns raw boxed value on success; nullptr otherwise.
#[no_mangle]
pub unsafe extern "C" fn slint_interpreter_component_instance_invoke_global(
inst: &ErasedItemTreeBox,
global: Slice<u8>,
callable_name: Slice<u8>,
args: Slice<ValueOpaque>,
out: *mut ValueOpaque,
) -> bool {
let args = std::mem::transmute::<Slice<ValueOpaque>, Slice<Value>>(args);
args: Slice<Box<Value>>,
) -> *mut Value {
let args = args.iter().map(|vb| vb.as_ref().clone()).collect::<Vec<_>>();
generativity::make_guard!(guard);
let comp = inst.unerase(guard);
let callable_name = std::str::from_utf8(&callable_name).unwrap();
@ -545,11 +470,8 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_invoke_global(
g.as_ref().invoke_callback(&normalize_identifier(callable_name), args.as_slice())
}
}) {
Ok(val) => {
std::ptr::write(out as *mut Value, val);
true
}
Err(_) => false,
Ok(val) => Box::into_raw(Box::new(val)),
Err(_) => std::ptr::null_mut(),
}
}
@ -602,9 +524,8 @@ pub unsafe extern "C" fn slint_interpreter_component_instance_create(
#[repr(C)]
pub struct ModelAdaptorVTable {
pub row_count: extern "C" fn(VRef<ModelAdaptorVTable>) -> usize,
pub row_data:
unsafe extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, out: *mut ValueOpaque) -> bool,
pub set_row_data: extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, value: &ValueOpaque),
pub row_data: unsafe extern "C" fn(VRef<ModelAdaptorVTable>, row: usize) -> *mut Value,
pub set_row_data: extern "C" fn(VRef<ModelAdaptorVTable>, row: usize, value: Box<Value>),
pub get_notify: extern "C" fn(VRef<ModelAdaptorVTable>) -> &ModelNotifyOpaque,
pub drop: extern "C" fn(VRefMut<ModelAdaptorVTable>),
}
@ -618,13 +539,11 @@ impl Model for ModelAdaptorWrapper {
}
fn row_data(&self, row: usize) -> Option<Value> {
unsafe {
let mut v = std::mem::MaybeUninit::<Value>::uninit();
if self.0.row_data(row, v.as_mut_ptr() as *mut ValueOpaque) {
Some(v.assume_init())
} else {
None
}
let val_ptr = unsafe { self.0.row_data(row) };
if val_ptr.is_null() {
None
} else {
Some(*unsafe { Box::from_raw(val_ptr) })
}
}
@ -633,7 +552,7 @@ impl Model for ModelAdaptorWrapper {
}
fn set_row_data(&self, row: usize, data: Value) {
let val: &ValueOpaque = unsafe { std::mem::transmute::<&Value, &ValueOpaque>(&data) };
let val = Box::new(data);
self.0.set_row_data(row, val);
}
}