diff --git a/.github/workflows/yocto_build.yaml b/.github/workflows/yocto_build.yaml index 22b06c9f1ea..101ed584d0a 100644 --- a/.github/workflows/yocto_build.yaml +++ b/.github/workflows/yocto_build.yaml @@ -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 diff --git a/api/cpp/cbindgen.rs b/api/cpp/cbindgen.rs index c07b0445509..ac6b170f43c 100644 --- a/api/cpp/cbindgen.rs +++ b/api/cpp/cbindgen.rs @@ -768,13 +768,13 @@ fn gen_interpreter( dependencies: &mut Vec, ) -> 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 using Box = T*; }", ) .generate() diff --git a/api/cpp/include/slint-interpreter.h b/api/cpp/include/slint-interpreter.h index 8f13dffd034..4c036124359 100644 --- a/api/cpp/include/slint-interpreter.h +++ b/api/cpp/include/slint-interpreter.h @@ -151,30 +151,17 @@ struct Struct 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(next.v); - k = std::string_view(reinterpret_cast(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 @@ class Value { 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 @@ class Value /// Type::Double, otherwise an empty optional is returned. std::optional 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 @@ class Value /// Type::String, otherwise an empty optional is returned. std::optional 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 @@ class Value /// Type::Bool, otherwise an empty optional is returned. std::optional 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 @@ class Value /// Type::Brush, otherwise an empty optional is returned. std::optional 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 @@ class Value /// Type::Struct, otherwise an empty optional is returned. std::optional 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 @@ class Value /// Type::Image, otherwise an empty optional is returned. std::optional 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(img); } else { return {}; @@ -368,66 +355,68 @@ class Value // template std::optional 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(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 &v); /// Constructs a new Value that holds the value model \a m. Value(const std::shared_ptr> &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 &array) + : inner(cbindgen_private::slint_interpreter_value_new_array_model( + reinterpret_cast *>( + &array))) { - cbindgen_private::slint_interpreter_value_new_array_model( - reinterpret_cast *>(&array), &inner); } inline std::optional> Value::to_array() const { slint::SharedVector array; if (cbindgen_private::slint_interpreter_value_to_array( - &inner, reinterpret_cast *>(&array))) { + &inner, + reinterpret_cast *>( + &array))) { return array; } else { return {}; @@ -470,18 +459,21 @@ inline Value::Value(const std::shared_ptr> &model) auto row_count = [](VRef self) -> uintptr_t { return reinterpret_cast(self.instance)->model->row_count(); }; - auto row_data = [](VRef self, uintptr_t row, ValueOpaque *out) { + auto row_data = [](VRef self, + uintptr_t row) -> slint::cbindgen_private::Value * { std::optional v = reinterpret_cast(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 self, uintptr_t row, const ValueOpaque *value) { - Value v = *reinterpret_cast(value); + auto set_row_data = [](VRef self, uintptr_t row, + slint::cbindgen_private::Value *value) { + Value v(std::move(value)); reinterpret_cast(self.instance)->model->set_row_data(int(row), v); }; auto get_notify = @@ -493,8 +485,8 @@ inline Value::Value(const std::shared_ptr> &model) }; static const ModelAdaptorVTable vt { row_count, row_data, set_row_data, get_notify, drop }; - cbindgen_private::slint_interpreter_value_new_model(reinterpret_cast(wrapper.get()), - &vt, &inner); + inner = cbindgen_private::slint_interpreter_value_new_model( + reinterpret_cast(wrapper.get()), &vt); } inline Struct::Struct(std::initializer_list> args) @@ -504,12 +496,14 @@ inline Struct::Struct(std::initializer_list> inline std::optional Struct::get_field(std::string_view name) const { + using namespace cbindgen_private; cbindgen_private::Slice name_view { const_cast(reinterpret_cast(name.data())), name.size() }; - if (auto *value = cbindgen_private::slint_interpreter_struct_get_field(&inner, name_view)) { - return *reinterpret_cast(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(reinterpret_cast(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 name_slice; + + if (cbindgen_private::Value *nextval_inner = + cbindgen_private::slint_interpreter_struct_iterator_next(&inner, &name_slice)) { + k = std::string_view(reinterpret_cast(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 @@ class ComponentInstance : vtable::Dyn { 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 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 @@ class ComponentInstance : vtable::Dyn std::optional invoke(std::string_view name, std::span args) const { using namespace cbindgen_private; - Slice args_view { const_cast( - reinterpret_cast(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> args_view { + const_cast *>( + reinterpret_cast *>(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 @@ class ComponentInstance : vtable::Dyn 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 arg, - ValueOpaque *ret) { + using namespace cbindgen_private; + auto actual_cb = [](void *data, + cbindgen_private::Slice> + arg) { std::span args_view { reinterpret_cast(arg.ptr), arg.len }; Value r = (*reinterpret_cast(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 @@ class ComponentInstance : vtable::Dyn 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 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 @@ class ComponentInstance : vtable::Dyn template> 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 arg, - ValueOpaque *ret) { + using namespace cbindgen_private; + auto actual_cb = [](void *data, + cbindgen_private::Slice> + arg) { std::span args_view { reinterpret_cast(arg.ptr), arg.len }; Value r = (*reinterpret_cast(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 @@ class ComponentInstance : vtable::Dyn std::span args) const { using namespace cbindgen_private; - Slice args_view { const_cast( - reinterpret_cast(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> args_view { + const_cast *>( + reinterpret_cast *>( + 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 @@ class ComponentCompiler } } }; - } namespace slint::testing { diff --git a/internal/interpreter/ffi.rs b/internal/interpreter/ffi.rs index 9c65c644f06..0b67c46c834 100644 --- a/internal/interpreter/ffi.rs +++ b/internal/interpreter/ffi.rs @@ -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::()] = [(); std::mem::size_of::()]; -const _: [(); std::mem::align_of::()] = [(); std::mem::align_of::()]; - -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(val: *mut ValueOpaque) { - std::ptr::write(val as *mut Value, Value::default()) +pub unsafe extern "C" fn slint_interpreter_value_new() -> Box { + Box::new(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 { + 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) { + 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 { + 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 { + 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 { + 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, - val: *mut ValueOpaque, -) { - // Safety: We assert that Value and ValueOpaque have the same size and alignment - let vec = std::mem::transmute::, SharedVector>(a.clone()); - std::ptr::write( - val as *mut Value, - Value::Model(ModelRc::new(SharedVectorModel::::from(vec))), - ) + a: &SharedVector>, +) -> Box { + let vec = a.iter().map(|vb| vb.as_ref().clone()).collect::>(); + 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 { + 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 { + 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 { + 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, 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 { + 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, + val: &Box, + out: *mut SharedVector>, ) -> bool { - match val.as_value() { + match val.as_ref() { Value::Model(m) => { - let vec = if let Some(model) = m.as_any().downcast_ref::>() { - 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::>(); unsafe { - std::ptr::write( - out, - std::mem::transmute::, SharedVector>(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, -) -> 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, - 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, - 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, - 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, - args: Slice, - out: *mut ValueOpaque, -) -> bool { - let args = std::mem::transmute::, Slice>(args); + args: Slice>, +) -> *mut Value { + let args = args.iter().map(|vb| vb.as_ref().clone()).collect::>(); 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, - callback: extern "C" fn(user_data: *mut c_void, arg: Slice, ret: *mut ValueOpaque), + callback: extern "C" fn(user_data: *mut c_void, arg: Slice>) -> Box, } 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::::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::>(); + (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, - callback: extern "C" fn(user_data: *mut c_void, arg: Slice, ret: *mut ValueOpaque), + callback: extern "C" fn(user_data: *mut c_void, arg: Slice>) -> Box, user_data: *mut c_void, drop_user_data: Option, ) -> 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, property_name: Slice, - 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, property_name: Slice, - 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, name: Slice, - callback: extern "C" fn(user_data: *mut c_void, arg: Slice, ret: *mut ValueOpaque), + callback: extern "C" fn(user_data: *mut c_void, arg: Slice>) -> Box, user_data: *mut c_void, drop_user_data: Option, ) -> 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, callable_name: Slice, - args: Slice, - out: *mut ValueOpaque, -) -> bool { - let args = std::mem::transmute::, Slice>(args); + args: Slice>, +) -> *mut Value { + let args = args.iter().map(|vb| vb.as_ref().clone()).collect::>(); 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) -> usize, - pub row_data: - unsafe extern "C" fn(VRef, row: usize, out: *mut ValueOpaque) -> bool, - pub set_row_data: extern "C" fn(VRef, row: usize, value: &ValueOpaque), + pub row_data: unsafe extern "C" fn(VRef, row: usize) -> *mut Value, + pub set_row_data: extern "C" fn(VRef, row: usize, value: Box), pub get_notify: extern "C" fn(VRef) -> &ModelNotifyOpaque, pub drop: extern "C" fn(VRefMut), } @@ -618,13 +539,11 @@ impl Model for ModelAdaptorWrapper { } fn row_data(&self, row: usize) -> Option { - unsafe { - let mut v = std::mem::MaybeUninit::::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); } }