diff --git a/api/sixtyfps-cpp/include/sixtyfps.h b/api/sixtyfps-cpp/include/sixtyfps.h index 3661190a5..ce6a3147a 100644 --- a/api/sixtyfps-cpp/include/sixtyfps.h +++ b/api/sixtyfps-cpp/include/sixtyfps.h @@ -51,11 +51,6 @@ using internal::Rectangle; using internal::Text; using internal::TouchArea; -// the component has static lifetime so it does not need to be destroyed -// FIXME: we probably need some kind of way to dinstinguish static component and -// these on the heap -inline void dummy_destory(ComponentRef) { } - constexpr inline ItemTreeNode make_item_node(std::uintptr_t offset, const internal::ItemVTable *vtable, uint32_t child_count, uint32_t child_index) diff --git a/api/sixtyfps-cpp/include/vtable.h b/api/sixtyfps-cpp/include/vtable.h index 984658e0f..38b68de12 100644 --- a/api/sixtyfps-cpp/include/vtable.h +++ b/api/sixtyfps-cpp/include/vtable.h @@ -13,6 +13,9 @@ using VRef = VRefMut; template using VBox = VRefMut; +template +using Pin = T; + /* template struct VBox { diff --git a/api/sixtyfps-node/native/lib.rs b/api/sixtyfps-node/native/lib.rs index 758b19369..487270817 100644 --- a/api/sixtyfps-node/native/lib.rs +++ b/api/sixtyfps-node/native/lib.rs @@ -1,18 +1,15 @@ use core::cell::RefCell; use neon::prelude::*; use sixtyfps_compilerlib::typeregister::Type; -use sixtyfps_corelib::{ - abi::datastructures::{ComponentBox, ComponentRef, Resource}, - EvaluationContext, -}; +use sixtyfps_corelib::abi::datastructures::Resource; +use sixtyfps_corelib::{ComponentRefPin, EvaluationContext}; + use std::rc::Rc; mod persistent_context; struct WrappedComponentType(Option>); -struct WrappedComponentBox( - Option<(Rc, Rc)>, -); +struct WrappedComponentBox(Option>); /// We need to do some gymnastic with closures to pass the ExecuteContext with the right lifetime type GlobalContextCallback = @@ -98,7 +95,7 @@ fn create<'cx>( let mut obj = SixtyFpsComponent::new::<_, JsValue, _>(cx, std::iter::empty())?; persistent_context.save_to_object(cx, obj.downcast().unwrap()); - cx.borrow_mut(&mut obj, |mut obj| obj.0 = Some((Rc::new(component), component_type))); + cx.borrow_mut(&mut obj, |mut obj| obj.0 = Some(Rc::new(component))); Ok(obj.as_value(cx)) } @@ -158,7 +155,7 @@ fn to_js_value<'cx>( fn show<'cx>( cx: &mut CallContext<'cx, impl neon::object::This>, - component: ComponentRef, + component: ComponentRefPin, presistent_context: persistent_context::PersistentContext<'cx>, ) -> JsResult<'cx, JsUndefined> { cx.execute_scoped(|cx| { @@ -229,7 +226,7 @@ declare_types! { method show(mut cx) { let mut this = cx.this(); let component = cx.borrow(&mut this, |x| x.0.clone()); - let component = component.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?.0; + let component = component.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; let persistent_context = persistent_context::PersistentContext::from_object(&mut cx, this.downcast().unwrap())?; show(&mut cx, component.borrow(), persistent_context)?; Ok(JsUndefined::new().as_value(&mut cx)) @@ -239,8 +236,8 @@ declare_types! { let this = cx.this(); let lock = cx.lock(); let x = this.borrow(&lock).0.clone(); - let (component, component_ty) = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; - let value = component_ty + let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; + let value = component.description() .get_property(&EvaluationContext::for_root_component(component.borrow()), prop_name.as_str()) .or_else(|_| cx.throw_error(format!("Cannot read property")))?; to_js_value(value, &mut cx) @@ -250,8 +247,8 @@ declare_types! { let this = cx.this(); let lock = cx.lock(); let x = this.borrow(&lock).0.clone(); - let (component, component_ty) = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; - let ty = component_ty.properties() + let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; + let ty = component.description().properties() .get(&prop_name) .ok_or(()) .or_else(|()| { @@ -260,7 +257,7 @@ declare_types! { .clone(); let value = to_eval_value(cx.argument::(1)?, ty, &mut cx)?; - component_ty + component.description() .set_property(component.borrow(), prop_name.as_str(), value) .or_else(|_| cx.throw_error(format!("Cannot assign property")))?; @@ -271,8 +268,8 @@ declare_types! { let this = cx.this(); let lock = cx.lock(); let x = this.borrow(&lock).0.clone(); - let (component, component_ty) = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; - component_ty + let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?; + component.description() .emit_signal(&EvaluationContext::for_root_component(component.borrow()), signal_name.as_str()) .or_else(|_| cx.throw_error(format!("Cannot emit signal")))?; diff --git a/api/sixtyfps-rs/Cargo.toml b/api/sixtyfps-rs/Cargo.toml index e5bad233e..aac5a2b43 100644 --- a/api/sixtyfps-rs/Cargo.toml +++ b/api/sixtyfps-rs/Cargo.toml @@ -12,6 +12,7 @@ once_cell = "1.4" sixtyfps-rs-macro= { path = "sixtyfps-rs-macro" } const-field-offset = { path = "../../helper_crates/const-field-offset" } vtable = { path = "../../helper_crates/vtable" } +pin-utils = "0.1" sixtyfps_corelib = { path="../../sixtyfps_runtime/corelib" } # FIXME: should be under a feature or somehow done differently sixtyfps_rendering_backend_gl = { path="../../sixtyfps_runtime/rendering_backends/gl" } diff --git a/api/sixtyfps-rs/lib.rs b/api/sixtyfps-rs/lib.rs index 8bc6aa573..e76c081cf 100644 --- a/api/sixtyfps-rs/lib.rs +++ b/api/sixtyfps-rs/lib.rs @@ -74,6 +74,7 @@ pub mod re_exports { pub use crate::repeater::*; pub use const_field_offset::{self, FieldOffsets}; pub use once_cell::sync::Lazy; + pub use pin_utils::pin_mut; pub use sixtyfps_corelib::abi::datastructures::*; pub use sixtyfps_corelib::abi::primitives::*; pub use sixtyfps_corelib::abi::properties::Property; diff --git a/api/sixtyfps-rs/repeater.rs b/api/sixtyfps-rs/repeater.rs index ce96426da..9c79ea6bc 100644 --- a/api/sixtyfps-rs/repeater.rs +++ b/api/sixtyfps-rs/repeater.rs @@ -1,5 +1,5 @@ /// Component that can be instantiated by a repeater. -pub trait RepeatedComponent: sixtyfps_corelib::abi::datastructures::Component { +pub trait RepeatedComponent: sixtyfps_corelib::abi::datastructures::Component + Default { /// The data corresponding to the model type Data; @@ -11,7 +11,7 @@ pub trait RepeatedComponent: sixtyfps_corelib::abi::datastructures::Component { /// It helps instantiating the components `C` #[derive(Default)] pub struct Repeater { - components: Vec>, + components: Vec>>, } impl Repeater @@ -25,16 +25,16 @@ where { self.components.clear(); for (i, d) in data.enumerate() { - let c = C::create(); + let c = C::default(); c.update(i, d); - self.components.push(Box::new(c)); + self.components.push(Box::pin(c)); } } /// Call the visitor for each component pub fn visit(&self, mut visitor: sixtyfps_corelib::abi::datastructures::ItemVisitorRefMut) { for c in &self.components { - c.visit_children_item(-1, visitor.borrow_mut()); + c.as_ref().visit_children_item(-1, visitor.borrow_mut()); } } } diff --git a/examples/rusttest/src/main.rs b/examples/rusttest/src/main.rs index cf052cdbd..17fc78b86 100644 --- a/examples/rusttest/src/main.rs +++ b/examples/rusttest/src/main.rs @@ -110,11 +110,11 @@ fn main() { let mut app = Hello::default(); app.plus_clicked.set_handler(|context, ()| { - let app = context.component.downcast::().unwrap(); + let app = context.get_component::().unwrap(); app.counter.set(app.counter.get(context) + 1); }); app.minus_clicked.set_handler(|context, ()| { - let app = context.component.downcast::().unwrap(); + let app = context.get_component::().unwrap(); app.counter.set(app.counter.get(context) - 1); }); diff --git a/examples/rusttest2/src/main.rs b/examples/rusttest2/src/main.rs index bfc95ddee..291bb34fb 100644 --- a/examples/rusttest2/src/main.rs +++ b/examples/rusttest2/src/main.rs @@ -6,11 +6,11 @@ fn main() { let mut app = Hello::default(); app.plus_clicked.set_handler(|context, ()| { - let app = context.component.downcast::().unwrap(); + let app = context.get_component::().unwrap(); app.counter.set(app.counter.get(context) + 1); }); app.minus_clicked.set_handler(|context, ()| { - let app = context.component.downcast::().unwrap(); + let app = context.get_component::().unwrap(); app.counter.set(app.counter.get(context) - 1); }); app.run(); diff --git a/helper_crates/vtable/macro/macro.rs b/helper_crates/vtable/macro/macro.rs index a3806c600..3c9e38479 100644 --- a/helper_crates/vtable/macro/macro.rs +++ b/helper_crates/vtable/macro/macro.rs @@ -26,6 +26,23 @@ fn match_generic_type(ty: &Type, container: &str, containee: &Ident) -> bool { false } +/// Returns Some(type) if the type is `Pin` +fn is_pin<'a>(ty: &'a Type) -> Option<&'a Type> { + if let Type::Path(pat) = ty { + if let Some(seg) = pat.path.segments.last() { + if seg.ident != "Pin" { + return None; + } + if let PathArguments::AngleBracketed(args) = &seg.arguments { + if let Some(GenericArgument::Type(t)) = args.args.last() { + return Some(t); + } + } + } + } + None +} + /** This macro need to be applied to a VTable structure @@ -56,6 +73,8 @@ For fields whose type is a function: - If a field is called `drop` it is understood that this is the destructor for a VBox - If the first argument of the function is `VRef` or `VRefMut` this is understood as a `&self` or `&mut self` argument in the trait. + - Similarily, if it is a `Pin>` or `Pin>`, self is mapped + to `Pin` or `Pin<&Self>` For the other fields - They are considered assotiated const of the MyTraitConsts @@ -239,10 +258,15 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { } } + let (is_pin, self_ty) = match is_pin(¶m.ty) { + Some(t) => (true, t), + None => (false, ¶m.ty), + }; + // check for self - if let (true, mutability) = if match_generic_type(¶m.ty, "VRef", &vtable_name) { + if let (true, mutability) = if match_generic_type(self_ty, "VRef", &vtable_name) { (true, None) - } else if match_generic_type(¶m.ty, "VRefMut", &vtable_name) { + } else if match_generic_type(self_ty, "VRefMut", &vtable_name) { (true, Some(Default::default())) } else { (false, None) @@ -252,19 +276,36 @@ pub fn vtable(_attr: TokenStream, item: TokenStream) -> TokenStream { .to_compile_error() .into(); } - sig.inputs.push(FnArg::Receiver(Receiver { - attrs: param.attrs.clone(), - reference: Some(Default::default()), - mutability, - self_token: Default::default(), - })); - let self_ty = ¶m.ty; + let const_or_mut = mutability.map_or_else(|| quote!(const), |x| quote!(#x)); - call_code = - Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),)); - self_call = - Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),)); has_self = true; + if !is_pin { + sig.inputs.push(FnArg::Receiver(Receiver { + attrs: param.attrs.clone(), + reference: Some(Default::default()), + mutability, + self_token: Default::default(), + })); + call_code = + Some(quote!(#call_code <#self_ty>::from_raw(self.vtable, self.ptr),)); + self_call = + Some(quote!(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T)),)); + } else { + // Pinned + sig.inputs.push(FnArg::Typed(PatType { + attrs: param.attrs.clone(), + pat: Box::new(parse2(quote!(self)).unwrap()), + colon_token: Default::default(), + ty: parse2(quote!(core::pin::Pin<& #mutability Self>)).unwrap(), + })); + + call_code = Some( + quote!(#call_code core::pin::Pin::new_unchecked(<#self_ty>::from_raw(self.vtable, self.ptr)),), + ); + self_call = Some( + quote!(core::pin::Pin::new_unchecked(&#mutability (*(#arg_name.as_ptr() as *#const_or_mut T))),), + ); + } continue; } sig.inputs.push(typed_arg); diff --git a/helper_crates/vtable/src/lib.rs b/helper_crates/vtable/src/lib.rs index b07a27e9d..e7294dc1e 100644 --- a/helper_crates/vtable/src/lib.rs +++ b/helper_crates/vtable/src/lib.rs @@ -57,7 +57,7 @@ that `cbindgen` can see the actual vtable. pub use const_field_offset::FieldOffset; use core::marker::PhantomData; use core::ops::{Deref, DerefMut, Drop}; -use core::ptr::NonNull; +use core::{pin::Pin, ptr::NonNull}; #[doc(inline)] pub use vtable_macro::*; @@ -230,6 +230,20 @@ impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> { } } + /// Create a new Pin> from a pinned reference. This is similar to `VRef::new` + pub fn new_pin>(value: core::pin::Pin<&'a X>) -> Pin { + // Since Value is pinned, this means it is safe to construct a Pin + unsafe { + Pin::new_unchecked(Self { + inner: Inner { + vtable: X::static_vtable() as *const T::VTable as *const u8, + ptr: value.get_ref() as *const X as *const u8, + }, + phantom: PhantomData, + }) + } + } + unsafe fn from_inner(inner: Inner) -> Self { Self { inner, phantom: PhantomData } } @@ -251,6 +265,17 @@ impl<'a, T: ?Sized + VTableMeta> VRef<'a, T> { None } } + + /// Return to a reference of the given type if the type is actually matching + pub fn downcast_pin>(this: Pin) -> Option> { + let inner = unsafe { Pin::into_inner_unchecked(this).inner }; + if inner.vtable == X::static_vtable() as *const _ as *const u8 { + // Safety: We just checked that the vtable fits + unsafe { Some(Pin::new_unchecked(&*(inner.ptr as *const X))) } + } else { + None + } + } } /// `VRefMut<'a MyTraitVTable>` can be thought as a `&'a mut dyn MyTrait` diff --git a/helper_crates/vtable/tests/test_vtable.rs b/helper_crates/vtable/tests/test_vtable.rs index ad208d6e2..44a42fe55 100644 --- a/helper_crates/vtable/tests/test_vtable.rs +++ b/helper_crates/vtable/tests/test_vtable.rs @@ -1,3 +1,4 @@ +use core::pin::Pin; use vtable::*; #[vtable] /// This is the actual doc @@ -157,3 +158,32 @@ fn test3() { new_vref!(let mut re_mut : VRefMut for Xxx = &mut p); assert_eq!(re_mut.ret_int(), 55); } + +#[test] +fn pin() { + #[vtable] + struct PinnedVTable { + my_func: fn(core::pin::Pin>, u32) -> u32, + my_func2: fn(std::pin::Pin>) -> u32, + my_func3: fn(Pin>, u32) -> u32, + } + + struct P(String, std::marker::PhantomPinned); + impl Pinned for P { + fn my_func(self: Pin<&Self>, p: u32) -> u32 { + self.0.len() as u32 + p + } + fn my_func2(self: Pin<&Self>) -> u32 { + self.0.len() as u32 + } + fn my_func3(self: Pin<&mut Self>, _p: u32) -> u32 { + self.0.len() as u32 + } + } + PinnedVTable_static!(static PVT for P); + + let b = Box::pin(P("hello".to_owned(), std::marker::PhantomPinned)); + let r = VRef::new_pin(b.as_ref()); + assert_eq!(r.as_ref().my_func(44), 44 + 5); + assert_eq!(r.as_ref().my_func2(), 5); +} diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index afa026e6b..a20590a39 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -463,10 +463,7 @@ fn generate_component(file: &mut File, component: &Rc, diag: &mut Dia declarations.push(Declaration::Var(Var { ty: "const sixtyfps::ComponentVTable".to_owned(), name: format!("{}::component_type", component_id), - init: Some( - "{ nullptr, sixtyfps::dummy_destory, visit_children, nullptr, compute_layout }" - .to_owned(), - ), + init: Some("{ visit_children, nullptr, compute_layout }".to_owned()), })); declarations.append(&mut file.declarations); diff --git a/sixtyfps_compiler/generator/rust.rs b/sixtyfps_compiler/generator/rust.rs index 67a2a403a..e00abd098 100644 --- a/sixtyfps_compiler/generator/rust.rs +++ b/sixtyfps_compiler/generator/rust.rs @@ -51,9 +51,9 @@ pub fn generate(component: &Rc, diag: &mut Diagnostics) -> Option) { let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component( - sixtyfps::re_exports::ComponentRef::new(self) + sixtyfps::re_exports::ComponentRef::new_pin(self) ); self.#prop_ident.emit(&eval_context, ()) } @@ -78,9 +78,9 @@ pub fn generate(component: &Rc, diag: &mut Diagnostics) -> Option #rust_property_type { + fn #getter_ident(self: ::core::pin::Pin<&Self>) -> #rust_property_type { let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component( - sixtyfps::re_exports::ComponentRef::new(self) + sixtyfps::re_exports::ComponentRef::new_pin(self) ); self.#prop_ident.get(&eval_context) } @@ -186,7 +186,7 @@ pub fn generate(component: &Rc, diag: &mut Diagnostics) -> Option().unwrap(); + let _self = context.get_component::<#component_id>().unwrap(); #tokens_for_expression; }); )); @@ -198,7 +198,7 @@ pub fn generate(component: &Rc, diag: &mut Diagnostics) -> Option().unwrap(); + let _self = context.get_component::<#component_id>().unwrap(); (#tokens_for_expression) as _ }); )); @@ -239,10 +239,12 @@ pub fn generate(component: &Rc, diag: &mut Diagnostics) -> Option, diag: &mut Diagnostics) -> Option, index: isize, visitor: sixtyfps::re_exports::ItemVisitorRefMut) { use sixtyfps::re_exports::*; let tree = &[#(#item_tree_array),*]; - sixtyfps::re_exports::visit_item_tree(self, VRef::new(self), tree, index, visitor, visit_dynamic); + sixtyfps::re_exports::visit_item_tree(self.get_ref(), VRef::new_pin(self), tree, index, visitor, visit_dynamic); #[allow(unused)] fn visit_dynamic(base: &#component_id, visitor: ItemVisitorRefMut, dyn_index: usize) { match dyn_index { @@ -289,9 +291,6 @@ pub fn generate(component: &Rc, diag: &mut Diagnostics) -> Option Self { - Default::default() - } #layouts } @@ -323,7 +322,7 @@ fn component_id(component: &Component) -> proc_macro2::Ident { /// to be used like: /// ```ignore /// let (access, context) = access_member(...) -/// quote!(context.#access.get(#context)) +/// quote!(#access.get(#context)) /// ``` fn access_member( element: &ElementRc, @@ -337,7 +336,7 @@ fn access_member( let component_id = component_id(&enclosing_component); if Rc::ptr_eq(component, &enclosing_component) { let name_ident = quote::format_ident!("{}", name); - let comp = quote!(component.downcast::<#component_id>().unwrap()); + let comp = quote!(#context.get_component::<#component_id>().unwrap()); if e.property_declarations.contains_key(name) { (quote!(#comp.#name_ident), context) } else { @@ -345,13 +344,7 @@ fn access_member( (quote!(#comp.#elem_ident.#name_ident), context) } } else { - let (access, new_context) = access_member( - element, - name, - &enclosing_component, - quote!(#context.parent_context.unwrap()), - ); - (quote!(parent_context.unwrap().#access), new_context) + access_member(element, name, &enclosing_component, quote!(#context.parent_context.unwrap())) } } @@ -377,7 +370,7 @@ fn compile_expression(e: &Expression, component: &Rc) -> TokenStream component, quote!(context), ); - quote!(context.#access.get(#context)) + quote!(#access.get(#context)) } Expression::RepeaterIndexReference { element } => { if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) { @@ -416,7 +409,7 @@ fn compile_expression(e: &Expression, component: &Rc) -> TokenStream component, quote!(context), ); - quote!(context.#access.emit(#context, ())) + quote!(#access.emit(#context, ())) } Expression::FunctionCall { function } => { if matches!(function.ty(), Type::Signal) { @@ -436,7 +429,7 @@ fn compile_expression(e: &Expression, component: &Rc) -> TokenStream ); let rhs = compile_expression(&*rhs, &component); let op = proc_macro2::Punct::new(*op, proc_macro2::Spacing::Alone); - quote!( context.#lhs.set(context.#lhs.get(#context) #op &((#rhs) as _) )) + quote!( #lhs.set(#lhs.get(#context) #op &((#rhs) as _) )) } _ => panic!("typechecking should make sure this was a PropertyReference"), }, @@ -547,10 +540,10 @@ fn compute_layout(component: &Component) -> TokenStream { } quote! { - fn layout_info(&self) -> sixtyfps::re_exports::LayoutInfo { + fn layout_info(self: core::pin::Pin<&Self>) -> sixtyfps::re_exports::LayoutInfo { todo!("Implement in rust.rs") } - fn compute_layout(&self, eval_context: &sixtyfps::re_exports::EvaluationContext) { + fn compute_layout(self: core::pin::Pin<&Self>, eval_context: &sixtyfps::re_exports::EvaluationContext) { #![allow(unused)] use sixtyfps::re_exports::*; let dummy = Property::::default(); diff --git a/sixtyfps_runtime/corelib/abi/datastructures.rs b/sixtyfps_runtime/corelib/abi/datastructures.rs index 09253b193..8dc8c8f6f 100644 --- a/sixtyfps_runtime/corelib/abi/datastructures.rs +++ b/sixtyfps_runtime/corelib/abi/datastructures.rs @@ -2,6 +2,7 @@ use super::slice::Slice; use crate::EvaluationContext; +use core::pin::Pin; use std::cell::Cell; use vtable::*; @@ -34,23 +35,21 @@ struct Point { #[vtable] #[repr(C)] pub struct ComponentVTable { - /// Allocate an instance of this component - pub create: extern "C" fn(&ComponentVTable) -> VBox, - - /// Destruct this component. - pub drop: extern "C" fn(VRefMut), - /// Visit the children of the item at index `index`. /// Note that the root item is at index 0, so passing 0 would visit the item under root (the children of root). /// If you want to visit the root item, you need to pass -1 as an index - pub visit_children_item: - extern "C" fn(VRef, index: isize, visitor: VRefMut), + pub visit_children_item: extern "C" fn( + core::pin::Pin>, + index: isize, + visitor: VRefMut, + ), /// Returns the layout info for this component - pub layout_info: extern "C" fn(VRef) -> LayoutInfo, + pub layout_info: extern "C" fn(core::pin::Pin>) -> LayoutInfo, /// Will compute the layout of - pub compute_layout: extern "C" fn(VRef, eval_context: &EvaluationContext), + pub compute_layout: + extern "C" fn(core::pin::Pin>, eval_context: &EvaluationContext), } /// This structure must be present in items that are Rendered and contains information. @@ -279,7 +278,7 @@ impl ComponentWindow { Self(window_impl) } /// Spins an event loop and renders the items of the provided component in this window. - pub fn run(&self, component: VRef) { + pub fn run(&self, component: Pin>) { let event_loop = crate::eventloop::EventLoop::new(); self.0.clone().map_window(&event_loop); @@ -309,7 +308,7 @@ pub unsafe extern "C" fn sixtyfps_component_window_drop(handle: *mut ComponentWi #[no_mangle] pub unsafe extern "C" fn sixtyfps_component_window_run( handle: *mut ComponentWindowOpaque, - component: vtable::VRef, + component: Pin>, ) { let window = &*(handle as *const ComponentWindow); window.run(component); @@ -327,7 +326,7 @@ pub struct ItemVisitorVTable { /// and `item` is a reference to the item itself visit_item: fn( VRefMut, - component: VRef, + component: Pin>, index: isize, item: VRef, ), @@ -335,10 +334,10 @@ pub struct ItemVisitorVTable { drop: fn(VRefMut), } -impl, isize, VRef)> ItemVisitor for T { +impl)> ItemVisitor for T { fn visit_item( &mut self, - component: VRef, + component: crate::ComponentRefPin, index: isize, item: VRef, ) { @@ -351,7 +350,7 @@ impl, isize, VRef)> ItemVisitor for T /// Safety: Assume a correct implementation of the item_tree array #[no_mangle] pub unsafe extern "C" fn sixtyfps_visit_item_tree( - component: VRef, + component: Pin>, item_tree: Slice>, index: isize, visitor: VRefMut, diff --git a/sixtyfps_runtime/corelib/abi/properties.rs b/sixtyfps_runtime/corelib/abi/properties.rs index bf4c6ec0c..029de1aaf 100644 --- a/sixtyfps_runtime/corelib/abi/properties.rs +++ b/sixtyfps_runtime/corelib/abi/properties.rs @@ -5,7 +5,7 @@ thin dst container, and intrusive linked list */ -use crate::abi::datastructures::ComponentRef; +use crate::ComponentRefPin; use core::cell::*; use core::ops::DerefMut; use std::rc::{Rc, Weak}; @@ -101,7 +101,7 @@ impl PropertyNotify for RefCell> { #[repr(C)] pub struct EvaluationContext<'a> { /// The component which contains the Property or the Signal - pub component: vtable::VRef<'a, crate::abi::datastructures::ComponentVTable>, + pub component: core::pin::Pin>, /// The context of the parent component pub parent_context: Option<&'a EvaluationContext<'a>>, @@ -112,15 +112,24 @@ impl<'a> EvaluationContext<'a> { /// /// The component need to be a root component, otherwise fetching properties /// might panic. - pub fn for_root_component(component: ComponentRef<'a>) -> Self { + pub fn for_root_component(component: ComponentRefPin<'a>) -> Self { Self { component, parent_context: None } } /// Create a context for a child component of a component within the current /// context. - pub fn child_context(&'a self, child: ComponentRef<'a>) -> Self { + pub fn child_context(&'a self, child: ComponentRefPin<'a>) -> Self { Self { component: child, parent_context: Some(self) } } + + /// Attempt to cast the component to the given type + pub fn get_component< + T: vtable::HasStaticVTable, + >( + &'a self, + ) -> Option> { + vtable::VRef::downcast_pin(self.component) + } } type PropertyHandle = Rc>>; @@ -275,7 +284,10 @@ fn properties_simple_test() { area: Property, } let dummy_eval_context = EvaluationContext::for_root_component(unsafe { - vtable::VRef::from_raw(core::ptr::NonNull::dangling(), core::ptr::NonNull::dangling()) + core::pin::Pin::new_unchecked(vtable::VRef::from_raw( + core::ptr::NonNull::dangling(), + core::ptr::NonNull::dangling(), + )) }); let compo = Rc::new(Component::default()); let w = Rc::downgrade(&compo); @@ -596,10 +608,10 @@ mod test { fn properties_test_animation_triggered_by_set() { let dummy_eval_context = EvaluationContext { component: unsafe { - vtable::VRef::from_raw( + core::pin::Pin::new_unchecked(vtable::VRef::from_raw( core::ptr::NonNull::dangling(), core::ptr::NonNull::dangling(), - ) + )) }, parent_context: None, }; @@ -638,10 +650,10 @@ mod test { fn properties_test_animation_triggered_by_binding() { let dummy_eval_context = EvaluationContext { component: unsafe { - vtable::VRef::from_raw( + core::pin::Pin::new_unchecked(vtable::VRef::from_raw( core::ptr::NonNull::dangling(), core::ptr::NonNull::dangling(), - ) + )) }, parent_context: None, }; diff --git a/sixtyfps_runtime/corelib/abi/signals.rs b/sixtyfps_runtime/corelib/abi/signals.rs index bf1c9d084..4bdad5e21 100644 --- a/sixtyfps_runtime/corelib/abi/signals.rs +++ b/sixtyfps_runtime/corelib/abi/signals.rs @@ -38,22 +38,23 @@ impl Signal { #[test] fn signal_simple_test() { + use std::pin::Pin; #[derive(Default)] struct Component { pressed: core::cell::Cell, clicked: Signal<()>, } impl crate::abi::datastructures::Component for Component { - fn create() -> Self { - Default::default() + fn visit_children_item( + self: Pin<&Self>, + _: isize, + _: crate::abi::datastructures::ItemVisitorRefMut, + ) { } - fn visit_children_item(&self, _: isize, _: crate::abi::datastructures::ItemVisitorRefMut) {} - fn layout_info(&self) -> crate::abi::datastructures::LayoutInfo { - unimplemented!() - } - fn compute_layout(&self, _: &crate::EvaluationContext) { + fn layout_info(self: Pin<&Self>) -> crate::abi::datastructures::LayoutInfo { unimplemented!() } + fn compute_layout(self: Pin<&Self>, _: &crate::EvaluationContext) {} } use crate::abi::datastructures::ComponentVTable; let mut c = Component::default(); @@ -62,10 +63,10 @@ fn signal_simple_test() { }); let vtable = ComponentVTable::new::(); let ctx = super::properties::EvaluationContext::for_root_component(unsafe { - vtable::VRef::from_raw( + Pin::new_unchecked(vtable::VRef::from_raw( core::ptr::NonNull::from(&vtable), core::ptr::NonNull::from(&c).cast(), - ) + )) }); c.clicked.emit(&ctx, ()); assert_eq!(c.pressed.get(), true); diff --git a/sixtyfps_runtime/corelib/eventloop.rs b/sixtyfps_runtime/corelib/eventloop.rs index 09d833f59..668c7fb14 100644 --- a/sixtyfps_runtime/corelib/eventloop.rs +++ b/sixtyfps_runtime/corelib/eventloop.rs @@ -5,12 +5,12 @@ use std::rc::{Rc, Weak}; use winit::platform::desktop::EventLoopExtDesktop; pub trait GenericWindow { - fn draw(&self, component: vtable::VRef); + fn draw(&self, component: core::pin::Pin); fn process_mouse_input( &self, pos: winit::dpi::PhysicalPosition, state: winit::event::ElementState, - component: vtable::VRef, + component: core::pin::Pin, ); fn window_handle(&self) -> std::cell::Ref<'_, winit::window::Window>; fn map_window(self: Rc, event_loop: &EventLoop); @@ -42,7 +42,7 @@ impl EventLoop { Self { winit_loop: winit::event_loop::EventLoop::new() } } #[allow(unused_mut)] // mut need changes for wasm - pub fn run(mut self, component: vtable::VRef) { + pub fn run(mut self, component: core::pin::Pin) { use winit::event::Event; use winit::event_loop::{ControlFlow, EventLoopWindowTarget}; diff --git a/sixtyfps_runtime/corelib/graphics.rs b/sixtyfps_runtime/corelib/graphics.rs index f4cc6616d..d0b211991 100644 --- a/sixtyfps_runtime/corelib/graphics.rs +++ b/sixtyfps_runtime/corelib/graphics.rs @@ -143,9 +143,9 @@ impl Drop for GraphicsWindow { impl crate::eventloop::GenericWindow for RefCell> { - fn draw(&self, component: vtable::VRef) { + fn draw(&self, component: crate::ComponentRefPin) { // FIXME: we should do that only if some property change - component.compute_layout(&crate::EvaluationContext::for_root_component(component)); + component.as_ref().compute_layout(&crate::EvaluationContext::for_root_component(component)); let mut this = self.borrow_mut(); @@ -189,7 +189,7 @@ impl crate::eventloop::GenericWindow &self, pos: winit::dpi::PhysicalPosition, state: winit::event::ElementState, - component: vtable::VRef, + component: crate::ComponentRefPin, ) { crate::input::process_mouse_event( component, diff --git a/sixtyfps_runtime/corelib/input.rs b/sixtyfps_runtime/corelib/input.rs index f9013400e..a4851c662 100644 --- a/sixtyfps_runtime/corelib/input.rs +++ b/sixtyfps_runtime/corelib/input.rs @@ -3,10 +3,11 @@ TODO: Keyboard events */ -use crate::abi::datastructures::{ComponentRef, MouseEvent}; +use crate::abi::datastructures::MouseEvent; +use crate::ComponentRefPin; use euclid::default::Vector2D; -pub fn process_mouse_event(component: ComponentRef<'_>, event: MouseEvent) { +pub fn process_mouse_event(component: ComponentRefPin, event: MouseEvent) { let offset = Vector2D::new(0., 0.); crate::item_tree::visit_items( diff --git a/sixtyfps_runtime/corelib/item_rendering.rs b/sixtyfps_runtime/corelib/item_rendering.rs index 5ca8d0685..5007530f9 100644 --- a/sixtyfps_runtime/corelib/item_rendering.rs +++ b/sixtyfps_runtime/corelib/item_rendering.rs @@ -38,7 +38,7 @@ pub(crate) fn update_item_rendering_data( } pub(crate) fn render_component_items( - component: vtable::VRef<'_, crate::abi::datastructures::ComponentVTable>, + component: crate::ComponentRefPin, frame: &mut Backend::Frame, rendering_cache: &RenderingCache, ) { diff --git a/sixtyfps_runtime/corelib/item_tree.rs b/sixtyfps_runtime/corelib/item_tree.rs index 15b829223..a014a1910 100644 --- a/sixtyfps_runtime/corelib/item_tree.rs +++ b/sixtyfps_runtime/corelib/item_tree.rs @@ -1,13 +1,12 @@ -use crate::abi::datastructures::{ - ComponentRef, ItemRef, ItemTreeNode, ItemVisitor, ItemVisitorVTable, -}; +use crate::abi::datastructures::{ItemRef, ItemTreeNode, ItemVisitor, ItemVisitorVTable}; +use crate::ComponentRefPin; use crate::EvaluationContext; /// Visit each items recursively /// /// The state parametter returned by the visitor is passed to each children. pub fn visit_items( - component: ComponentRef, + component: ComponentRefPin, mut visitor: impl FnMut(&EvaluationContext, ItemRef, &State) -> State, state: State, ) { @@ -21,7 +20,7 @@ fn visit_internal( index: isize, state: &State, ) { - let mut actual_visitor = |component: ComponentRef, index: isize, item: ItemRef| { + let mut actual_visitor = |component: ComponentRefPin, index: isize, item: ItemRef| { if component.as_ptr() == context.component.as_ptr() { let s = visitor(context, item, state); visit_internal(context, visitor, index, &s); @@ -32,7 +31,7 @@ fn visit_internal( } }; vtable::new_vref!(let mut actual_visitor : VRefMut for ItemVisitor = &mut actual_visitor); - context.component.visit_children_item(index, actual_visitor); + context.component.as_ref().visit_children_item(index, actual_visitor); } /// Visit the children within an array of ItemTreeNode @@ -45,7 +44,7 @@ fn visit_internal( /// Possibly we should generate code that directly call the visitor instead pub fn visit_item_tree( base: &Base, - component: ComponentRef, + component: ComponentRefPin, item_tree: &[ItemTreeNode], index: isize, mut visitor: vtable::VRefMut, diff --git a/sixtyfps_runtime/corelib/lib.rs b/sixtyfps_runtime/corelib/lib.rs index 759ce8407..42fe27854 100644 --- a/sixtyfps_runtime/corelib/lib.rs +++ b/sixtyfps_runtime/corelib/lib.rs @@ -45,5 +45,8 @@ pub use abi::properties::{EvaluationContext, Property}; #[doc(inline)] pub use abi::signals::Signal; +/// Type alias to the commonly use `Pin>>` +pub type ComponentRefPin<'a> = core::pin::Pin>; + pub mod eventloop; mod item_rendering; diff --git a/sixtyfps_runtime/interpreter/dynamic_component.rs b/sixtyfps_runtime/interpreter/dynamic_component.rs index 153cd20a8..bf9495aec 100644 --- a/sixtyfps_runtime/interpreter/dynamic_component.rs +++ b/sixtyfps_runtime/interpreter/dynamic_component.rs @@ -2,19 +2,50 @@ use crate::{dynamic_type, eval}; use core::convert::TryInto; use core::ptr::NonNull; -use dynamic_type::Instance; +use dynamic_type::{Instance, InstanceBox}; use object_tree::ElementRc; use sixtyfps_compilerlib::typeregister::Type; use sixtyfps_compilerlib::*; use sixtyfps_corelib::abi::datastructures::{ - ComponentBox, ComponentRef, ComponentVTable, ItemTreeNode, ItemVTable, ItemVisitorRefMut, - Resource, + ComponentVTable, ItemTreeNode, ItemVTable, ItemVisitorRefMut, Resource, }; use sixtyfps_corelib::abi::slice::Slice; use sixtyfps_corelib::rtti::PropertyInfo; +use sixtyfps_corelib::ComponentRefPin; use sixtyfps_corelib::{rtti, EvaluationContext, Property, SharedString, Signal}; use std::collections::HashMap; -use std::rc::Rc; +use std::{pin::Pin, rc::Rc}; + +pub struct ComponentBox { + instance: InstanceBox, + component_type: Rc, +} + +impl ComponentBox { + /// Borrow this component as a `Pin` + pub fn borrow(&self) -> ComponentRefPin { + unsafe { + Pin::new_unchecked(vtable::VRef::from_raw( + NonNull::from(&self.component_type.ct).cast(), + self.instance.as_ptr().cast(), + )) + } + } + + /// Borrow this component as a `Pin` + pub fn borrow_mut(&mut self) -> Pin { + unsafe { + Pin::new_unchecked(vtable::VRefMut::from_raw( + NonNull::from(&self.component_type.ct).cast(), + self.instance.as_ptr().cast(), + )) + } + } + + pub fn description(&self) -> Rc { + return self.component_type.clone(); + } +} pub(crate) struct ItemWithinComponent { offset: usize, @@ -47,18 +78,6 @@ pub(crate) struct RepeaterWithinComponent { type RepeaterVec = Vec; -/// A wrapper around a pointer to a Component, and its description. -/// -/// Safety: the `mem` member must be a component instantiated with the description of -/// this document. -/// -/// It is similar to ComponentBox, but instead of a ComponentVTable pointer, we -/// have direct access to the ComponentDescription, so we do not have to cast -pub struct ComponentImpl { - pub(crate) mem: *mut u8, - pub(crate) component_type: Rc, -} - /// ComponentDescription is a representation of a component suitable for interpretation /// /// It contains information about how to create and destroy the Component. @@ -81,7 +100,7 @@ pub struct ComponentDescription { } unsafe extern "C" fn visit_children_item( - component: ComponentRef, + component: ComponentRefPin, index: isize, v: ItemVisitorRefMut, ) { @@ -98,7 +117,7 @@ unsafe extern "C" fn visit_children_item( let rep_in_comp = &component_type.repeater[index]; let vec = &*(component.as_ptr().add(rep_in_comp.offset) as *const RepeaterVec); for x in vec { - x.visit_children_item(-1, visitor.borrow_mut()); + x.borrow().as_ref().visit_children_item(-1, visitor.borrow_mut()); } }, ); @@ -254,18 +273,12 @@ fn generate_component( } extern "C" fn layout_info( - _: ComponentRef, + _: ComponentRefPin, ) -> sixtyfps_corelib::abi::datastructures::LayoutInfo { todo!() } - let t = ComponentVTable { - create: component_create, - drop: component_destroy, - visit_children_item, - layout_info, - compute_layout, - }; + let t = ComponentVTable { visit_children_item, layout_info, compute_layout }; let t = ComponentDescription { ct: t, dynamic_type: builder.build(), @@ -280,36 +293,13 @@ fn generate_component( Rc::new(t) } -/// Safety: Can only be called for ComponentVTable which are in `MyComponentType` -unsafe extern "C" fn component_create(s: &ComponentVTable) -> ComponentBox { - // This is safe because we have an instance of ComponentVTable which is the first field of MyComponentType - // And the only way to get a MyComponentType is through the load function which returns a Rc - let component_type = Rc::::from_raw( - s as *const ComponentVTable as *const ComponentDescription, - ); - // We need to increment the ref-count, as from_raw doesn't do that. - std::mem::forget(component_type.clone()); - instentiate(component_type, None) -} - pub fn instentiate( component_type: Rc, parent_ctx: Option<&EvaluationContext>, ) -> ComponentBox { let instance = component_type.dynamic_type.clone().create_instance(); - let mem = instance as *mut u8; - - let ctx = Rc::new(ComponentImpl { mem, component_type: component_type.clone() }); - - let component_box = unsafe { - ComponentBox::from_raw( - NonNull::from(&ctx.component_type.ct).cast(), - NonNull::new(mem).unwrap().cast(), - ) - }; - - // The destructor of ComponentBox will take care of reducing the count - Rc::into_raw(component_type); + let mem = instance.as_ptr().as_ptr() as *mut u8; + let component_box = ComponentBox { instance, component_type: component_type.clone() }; let eval_context = if let Some(parent) = parent_ctx { parent.child_context(component_box.borrow()) @@ -317,7 +307,7 @@ pub fn instentiate( EvaluationContext::for_root_component(component_box.borrow()) }; - for item_within_component in ctx.component_type.items.values() { + for item_within_component in component_type.items.values() { unsafe { let item = item_within_component.item_from_component(mem); let elem = item_within_component.elem.borrow(); @@ -330,47 +320,47 @@ pub fn instentiate( .get(prop.as_str()) .map(|o| item.as_ptr().add(*o) as *mut u8) .or_else(|| { - ctx.component_type - .custom_signals - .get(prop.as_str()) - .map(|o| mem.add(*o)) + component_type.custom_signals.get(prop.as_str()).map(|o| mem.add(*o)) }) .unwrap_or_else(|| panic!("unkown signal {}", prop)) as *mut Signal<()>); let expr = expr.clone(); - let ctx = ctx.clone(); + let component_type = component_type.clone(); signal.set_handler(move |eval_context, _| { - eval::eval_expression(&expr, &*ctx, &eval_context); + eval::eval_expression(&expr, &*component_type, &eval_context); }) } else { if let Some(prop_rtti) = item_within_component.rtti.properties.get(prop.as_str()) { if expr.is_constant() { - prop_rtti.set(item, eval::eval_expression(expr, &*ctx, &eval_context)); + prop_rtti.set( + item, + eval::eval_expression(expr, &*component_type, &eval_context), + ); } else { let expr = expr.clone(); - let ctx = ctx.clone(); + let component_type = component_type.clone(); prop_rtti.set_binding( item, Box::new(move |eval_context| { - eval::eval_expression(&expr, &*ctx, eval_context) + eval::eval_expression(&expr, &*component_type, eval_context) }), ); } } else if let Some(PropertiesWithinComponent { offset, prop, .. }) = - ctx.component_type.custom_properties.get(prop.as_str()) + component_type.custom_properties.get(prop.as_str()) { if expr.is_constant() { - let v = eval::eval_expression(expr, &*ctx, &eval_context); + let v = eval::eval_expression(expr, &*component_type, &eval_context); prop.set(&*mem.add(*offset), v).unwrap(); } else { let expr = expr.clone(); - let ctx = ctx.clone(); + let component_type = component_type.clone(); prop.set_binding( &*mem.add(*offset), Box::new(move |eval_context| { - eval::eval_expression(&expr, &*ctx, eval_context) + eval::eval_expression(&expr, &*component_type, eval_context) }), ); } @@ -382,9 +372,9 @@ pub fn instentiate( } } - for rep_in_comp in &ctx.component_type.repeater { + for rep_in_comp in &component_type.repeater { let vec = unsafe { &mut *(mem.add(rep_in_comp.offset) as *mut RepeaterVec) }; - match eval::eval_expression(&rep_in_comp.model, &*ctx, &eval_context) { + match eval::eval_expression(&rep_in_comp.model, &*component_type, &eval_context) { crate::Value::Number(count) => { vec.resize_with(count.round() as usize, || { instentiate(rep_in_comp.component_to_repeat.clone(), Some(&eval_context)) @@ -422,16 +412,7 @@ pub fn instentiate( component_box } -unsafe extern "C" fn component_destroy(component_ref: vtable::VRefMut) { - // Take the reference count that the instentiate function leaked - let _vtable_rc = Rc::::from_raw(component_ref.get_vtable() - as *const ComponentVTable - as *const ComponentDescription); - - dynamic_type::TypeInfo::delete_instance(component_ref.as_ptr() as *mut dynamic_type::Instance); -} - -unsafe extern "C" fn compute_layout(component: ComponentRef, eval_context: &EvaluationContext) { +unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &EvaluationContext) { debug_assert!(component.as_ptr() == eval_context.component.as_ptr()); // This is fine since we can only be called with a component that with our vtable which is a ComponentDescription @@ -500,6 +481,7 @@ unsafe extern "C" fn compute_layout(component: ComponentRef, eval_context: &Eval /// Get the component description from a ComponentRef /// /// Safety: the component must have been created by the interpreter -pub unsafe fn get_component_type<'a>(component: ComponentRef<'a>) -> &'a ComponentDescription { - &*(component.get_vtable() as *const ComponentVTable as *const ComponentDescription) +pub unsafe fn get_component_type<'a>(component: ComponentRefPin<'a>) -> &'a ComponentDescription { + &*(Pin::into_inner_unchecked(component).get_vtable() as *const ComponentVTable + as *const ComponentDescription) } diff --git a/sixtyfps_runtime/interpreter/dynamic_type.rs b/sixtyfps_runtime/interpreter/dynamic_type.rs index eea1e06e2..c5d481443 100644 --- a/sixtyfps_runtime/interpreter/dynamic_type.rs +++ b/sixtyfps_runtime/interpreter/dynamic_type.rs @@ -117,7 +117,7 @@ impl TypeInfo { /// /// The instance will be allocated on the heap. /// The instance must be freed with `delete_instance` - pub fn create_instance(self: Rc) -> *mut Instance { + pub fn create_instance(self: Rc) -> InstanceBox { // Safety: the TypeInfo invariant means that the constructor can be called unsafe { let mem = std::alloc::alloc(self.mem_layout); @@ -127,14 +127,14 @@ impl TypeInfo { ctor(mem.add(f.offset)); } } - mem as *mut Instance + InstanceBox(core::ptr::NonNull::new_unchecked(mem as *mut Instance)) } } /// Drop and free the memory of this instance /// /// Saferty, the instance must have been created by `TypeInfo::create_instance` - pub unsafe fn delete_instance(instance: *mut Instance) { + unsafe fn delete_instance(instance: *mut Instance) { let type_info = (*instance).type_info.clone(); let mem = instance as *mut u8; for f in &type_info.fields { @@ -152,3 +152,19 @@ pub struct Instance { type_info: Rc, _opaque: [u8; 0], } + +/// A pointer to an Instance that automaticaly frees the memory after use +pub struct InstanceBox(core::ptr::NonNull); + +impl InstanceBox { + // return a pointer to the instance + pub fn as_ptr(&self) -> core::ptr::NonNull { + self.0 + } +} + +impl Drop for InstanceBox { + fn drop(&mut self) { + unsafe { TypeInfo::delete_instance(self.0.as_mut()) } + } +} diff --git a/sixtyfps_runtime/interpreter/eval.rs b/sixtyfps_runtime/interpreter/eval.rs index 406070ad0..a1689ae45 100644 --- a/sixtyfps_runtime/interpreter/eval.rs +++ b/sixtyfps_runtime/interpreter/eval.rs @@ -98,7 +98,7 @@ declare_value_conversion!(Object => [HashMap] ); /// Evaluate an expression and return a Value as the result of this expression pub fn eval_expression( e: &Expression, - ctx: &crate::ComponentImpl, + component_type: &crate::ComponentDescription, eval_context: &corelib::EvaluationContext, ) -> Value { match e { @@ -129,33 +129,47 @@ pub fn eval_expression( } Expression::RepeaterIndexReference { element } => { if element.upgrade().unwrap().borrow().base_type - == Type::Component(ctx.component_type.original.clone()) + == Type::Component(component_type.original.clone()) { - let x = &ctx.component_type.custom_properties["index"]; - unsafe { x.prop.get(&*ctx.mem.offset(x.offset as isize), &eval_context).unwrap() } + let x = &component_type.custom_properties["index"]; + unsafe { + x.prop + .get( + &*eval_context.component.as_ptr().offset(x.offset as isize), + &eval_context, + ) + .unwrap() + } } else { todo!(); } } Expression::RepeaterModelReference { element } => { if element.upgrade().unwrap().borrow().base_type - == Type::Component(ctx.component_type.original.clone()) + == Type::Component(component_type.original.clone()) { - let x = &ctx.component_type.custom_properties["model_data"]; - unsafe { x.prop.get(&*ctx.mem.offset(x.offset as isize), &eval_context).unwrap() } + let x = &component_type.custom_properties["model_data"]; + unsafe { + x.prop + .get( + &*eval_context.component.as_ptr().offset(x.offset as isize), + &eval_context, + ) + .unwrap() + } } else { todo!(); } } Expression::ObjectAccess { base, name } => { - if let Value::Object(mut o) = eval_expression(base, ctx, eval_context) { + if let Value::Object(mut o) = eval_expression(base, component_type, eval_context) { o.remove(name).unwrap_or(Value::Void) } else { Value::Void } } Expression::Cast { from, to } => { - let v = eval_expression(&*from, ctx, eval_context); + let v = eval_expression(&*from, component_type, eval_context); match (v, to) { (Value::Number(n), Type::Int32) => Value::Number(n.round()), (Value::Number(n), Type::String) => { @@ -167,7 +181,7 @@ pub fn eval_expression( Expression::CodeBlock(sub) => { let mut v = Value::Void; for e in sub { - v = eval_expression(e, ctx, eval_context); + v = eval_expression(e, component_type, eval_context); } v } @@ -203,7 +217,7 @@ pub fn eval_expression( Expression::SelfAssignment { lhs, rhs, op } => match &**lhs { Expression::PropertyReference(NamedReference { element, name }) => { let eval = |lhs| { - let rhs = eval_expression(&**rhs, ctx, eval_context); + let rhs = eval_expression(&**rhs, component_type, eval_context); match (lhs, rhs, op) { (Value::Number(a), Value::Number(b), '+') => Value::Number(a + b), (Value::Number(a), Value::Number(b), '-') => Value::Number(a - b), @@ -236,8 +250,8 @@ pub fn eval_expression( _ => panic!("typechecking should make sure this was a PropertyReference"), }, Expression::BinaryExpression { lhs, rhs, op } => { - let lhs = eval_expression(&**lhs, ctx, eval_context); - let rhs = eval_expression(&**rhs, ctx, eval_context); + let lhs = eval_expression(&**lhs, component_type, eval_context); + let rhs = eval_expression(&**rhs, component_type, eval_context); match (lhs, rhs, op) { (Value::Number(a), Value::Number(b), '+') => Value::Number(a + b), @@ -251,19 +265,21 @@ pub fn eval_expression( Value::Resource(Resource::AbsoluteFilePath(absolute_source_path.as_str().into())) } Expression::Condition { condition, true_expr, false_expr } => { - match eval_expression(&**condition, ctx, eval_context).try_into() as Result { - Ok(true) => eval_expression(&**true_expr, ctx, eval_context), - Ok(false) => eval_expression(&**false_expr, ctx, eval_context), + match eval_expression(&**condition, component_type, eval_context).try_into() + as Result + { + Ok(true) => eval_expression(&**true_expr, component_type, eval_context), + Ok(false) => eval_expression(&**false_expr, component_type, eval_context), _ => panic!("conditional expression did not evaluate to boolean"), } } - Expression::Array { values, .. } => { - Value::Array(values.iter().map(|e| eval_expression(e, ctx, eval_context)).collect()) - } + Expression::Array { values, .. } => Value::Array( + values.iter().map(|e| eval_expression(e, component_type, eval_context)).collect(), + ), Expression::Object { values, .. } => Value::Object( values .iter() - .map(|(k, v)| (k.clone(), eval_expression(v, ctx, eval_context))) + .map(|(k, v)| (k.clone(), eval_expression(v, component_type, eval_context))) .collect(), ), } diff --git a/sixtyfps_runtime/interpreter/lib.rs b/sixtyfps_runtime/interpreter/lib.rs index 966c59007..bb6e71d45 100644 --- a/sixtyfps_runtime/interpreter/lib.rs +++ b/sixtyfps_runtime/interpreter/lib.rs @@ -14,12 +14,10 @@ pub use dynamic_component::load; pub use dynamic_component::ComponentDescription; pub use eval::Value; -pub(crate) use dynamic_component::ComponentImpl; -use sixtyfps_corelib::{ - abi::datastructures::{ComponentBox, ComponentRef, ComponentRefMut}, - EvaluationContext, Signal, -}; -use std::{collections::HashMap, rc::Rc}; +pub use dynamic_component::ComponentBox; +use sixtyfps_corelib::abi::datastructures::{ComponentRef, ComponentRefMut}; +use sixtyfps_corelib::{ComponentRefPin, EvaluationContext, Signal}; +use std::{collections::HashMap, pin::Pin, rc::Rc}; impl ComponentDescription { /// The name of this Component as written in the .60 file @@ -49,7 +47,7 @@ impl ComponentDescription { /// or if the property with this name does not exist in this component pub fn set_property( &self, - component: ComponentRef, + component: ComponentRefPin, name: &str, value: Value, ) -> Result<(), ()> { @@ -96,7 +94,7 @@ impl ComponentDescription { /// or if the property with this name does not exist in this component pub fn set_signal_handler( &self, - component: ComponentRefMut, + component: Pin, name: &str, handler: Box, ) -> Result<(), ()> { diff --git a/tests/cases/arithmetic_expr.60 b/tests/cases/arithmetic_expr.60 index 7466b4603..7b62232e0 100644 --- a/tests/cases/arithmetic_expr.60 +++ b/tests/cases/arithmetic_expr.60 @@ -16,7 +16,8 @@ assert(instance.get_t3() == 42 - (3 + 2 * (42 + 2))); ```rust -let instance = TestCase::default(); +let instance = Box::pin(TestCase::default()); +let instance = instance.as_ref(); assert_eq!(instance.get_t1(), 4 + 3 * 2 + 2 - 50 - 2); assert_eq!(instance.get_t2(), 500 / 2 * 30 - 1); instance.set_a(42); diff --git a/tests/cases/cast_conditional.60 b/tests/cases/cast_conditional.60 index 4d3779706..0a72a1a5a 100644 --- a/tests/cases/cast_conditional.60 +++ b/tests/cases/cast_conditional.60 @@ -21,7 +21,8 @@ assert(instance.get_s1() == "123"); ```rust -let instance = TestCase::default(); +let instance = Box::pin(TestCase::default()); +let instance = instance.as_ref(); instance.set_condition(true); assert_eq!(instance.get_s1(), "abc"); assert_eq!(instance.get_s2(), "123"); diff --git a/tests/cases/color.60 b/tests/cases/color.60 index 446cf5e0b..e665f54c0 100644 --- a/tests/cases/color.60 +++ b/tests/cases/color.60 @@ -27,7 +27,8 @@ assert(t.get_b1() != t.get_r5()); ```rust -let t = Test::default(); +let t = Box::pin(Test::default()); +let t = t.as_ref(); assert_eq!(t.get_r1(), t.get_r2()); assert_eq!(t.get_r1(), t.get_r3()); assert_eq!(t.get_r1(), t.get_r4()); diff --git a/tests/cases/conditional_expr.60 b/tests/cases/conditional_expr.60 index 3a7c57208..ab41b1bb0 100644 --- a/tests/cases/conditional_expr.60 +++ b/tests/cases/conditional_expr.60 @@ -20,7 +20,8 @@ assert(instance.get_test_value2() == 3); ```rust -let instance = TestCase::default(); +let instance = Box::pin(TestCase::default()); +let instance = instance.as_ref(); instance.set_condition(true); assert_eq!(instance.get_test_value(), 1); assert_eq!(instance.get_test_value2(), 2); diff --git a/tests/cases/signal_handler.60 b/tests/cases/signal_handler.60 index 24fb2cb7a..e8d812f48 100644 --- a/tests/cases/signal_handler.60 +++ b/tests/cases/signal_handler.60 @@ -14,7 +14,8 @@ assert(instance.get_signal_emission_count() == 1); ```rust -let instance = TestCase::default(); +let instance = Box::pin(TestCase::default()); +let instance = instance.as_ref(); instance.set_signal_emission_count(0); assert_eq!(instance.get_signal_emission_count(), 0); instance.emit_test_signal();