From af10087e8f1592cc11b69ee4407ef712675f6a1e Mon Sep 17 00:00:00 2001 From: Mikhail Razorvin Date: Mon, 15 Jul 2024 22:18:46 +0200 Subject: [PATCH 1/6] feat: added support to additional shared variant in dynamic --- src/func/call.rs | 3 +- src/serde/de.rs | 41 +++++++++---- src/serde/serialize.rs | 3 +- src/tests.rs | 4 +- src/types/dynamic.rs | 131 ++++++++++++++++++++++++++++++++--------- 5 files changed, 137 insertions(+), 45 deletions(-) diff --git a/src/func/call.rs b/src/func/call.rs index 3c002b89e..1a1882985 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1866,7 +1866,8 @@ impl Engine { } _ => (), }, - (Union::Variant(..), _) | (_, Union::Variant(..)) => (), + (Union::SharedVariant(..), _) | (_, Union::SharedVariant(..)) => (), + (Union::OwnedVariant(..), _) | (_, Union::OwnedVariant(..)) => (), _ => { if let Some((func, need_context)) = get_builtin_binary_op_fn(op_token, &lhs, &rhs) diff --git a/src/serde/de.rs b/src/serde/de.rs index dffe45653..c65204725 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -162,18 +162,35 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => self.type_error(), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_i8(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_i16(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_i32(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_i64(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_i128(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_u8(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_u16(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_u32(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_u64(visitor), - Union::Variant(ref value, ..) if value.is::() => self.deserialize_u128(visitor), - - Union::Variant(..) => self.type_error(), + Union::OwnedVariant(ref value, ..) if value.is::() => self.deserialize_i8(visitor), + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_i16(visitor) + } + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_i32(visitor) + } + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_i64(visitor) + } + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_i128(visitor) + } + Union::OwnedVariant(ref value, ..) if value.is::() => self.deserialize_u8(visitor), + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_u16(visitor) + } + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_u32(visitor) + } + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_u64(visitor) + } + Union::OwnedVariant(ref value, ..) if value.is::() => { + self.deserialize_u128(visitor) + } + + Union::OwnedVariant(..) => self.type_error(), + Union::SharedVariant(..) => self.type_error(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => self.type_error(), diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 252e3243d..9e8fb7935 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -71,7 +71,8 @@ impl Serialize for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()), - Union::Variant(ref v, ..) => ser.serialize_str((***v).type_name()), + Union::OwnedVariant(ref v, ..) => ser.serialize_str(v.type_name()), + Union::SharedVariant(ref v, ..) => ser.serialize_str(v.type_name()), #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] diff --git a/src/tests.rs b/src/tests.rs index fc4778ea6..6d87e2868 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -22,7 +22,7 @@ fn check_struct_sizes() { } else if IS_32_BIT { 12 } else { - 16 + 24 } ); assert_eq!( @@ -32,7 +32,7 @@ fn check_struct_sizes() { } else if IS_32_BIT { 12 } else { - 16 + 24 } ); assert_eq!( diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 1a94dcbe0..7ed2fba04 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -33,6 +33,52 @@ pub enum AccessMode { ReadOnly, } +pub struct SharedVariantPtr(*mut dyn Variant); +impl SharedVariantPtr { + fn type_id(&self) -> TypeId { + unsafe { (*self.0).type_id() } + } + + pub fn type_name(&self) -> &'static str { + unsafe { (*self.0).type_name() } + } + + fn as_any(&self) -> &dyn Any { + unsafe { (*self.0).as_any() } + } + + fn clone(&self) -> Self { + SharedVariantPtr(self.0) + } +} + +pub struct OwnedVariantBox(Box); +impl OwnedVariantBox { + fn type_id(&self) -> TypeId { + (*(*self).0).type_id() + } + + pub fn type_name(&self) -> &'static str { + (*(*self).0).type_name() + } + + fn as_any(&self) -> &dyn Any { + (*(*self).0).as_any() + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + (*(*self).0).as_any_mut() + } + + fn clone(&self) -> Self { + OwnedVariantBox(self.0.clone_object()) + } + + pub fn is(&self) -> bool { + self.0.is::() + } +} + /// Arbitrary data attached to a [`Dynamic`] value. #[cfg(target_pointer_width = "64")] pub type Tag = i32; @@ -87,9 +133,12 @@ pub enum Union { /// Any type as a trait object. /// + /// We needed to support both variants shared and owned + /// /// An extra level of redirection is used in order to avoid bloating the size of [`Dynamic`] /// because `Box` is a fat pointer. - Variant(Box>, Tag, AccessMode), + OwnedVariant(OwnedVariantBox, Tag, AccessMode), + SharedVariant(SharedVariantPtr, Tag, AccessMode), /// A _shared_ value of any type. #[cfg(not(feature = "no_closure"))] @@ -186,7 +235,8 @@ impl Dynamic { | Union::Char(_, tag, _) | Union::Int(_, tag, _) | Union::FnPtr(_, tag, _) - | Union::Variant(_, tag, _) => tag, + | Union::SharedVariant(_, tag, _) + | Union::OwnedVariant(_, tag, _) => tag, #[cfg(not(feature = "no_float"))] Union::Float(_, tag, _) => tag, @@ -211,7 +261,8 @@ impl Dynamic { | Union::Char(_, ref mut tag, _) | Union::Int(_, ref mut tag, _) | Union::FnPtr(_, ref mut tag, _) - | Union::Variant(_, ref mut tag, _) => *tag = value, + | Union::OwnedVariant(_, ref mut tag, _) + | Union::SharedVariant(_, ref mut tag, _) => *tag = value, #[cfg(not(feature = "no_float"))] Union::Float(_, ref mut tag, _) => *tag = value, @@ -233,7 +284,7 @@ impl Dynamic { #[inline(always)] #[must_use] pub const fn is_variant(&self) -> bool { - matches!(self.0, Union::Variant(..)) + matches!(self.0, Union::OwnedVariant(..)) || matches!(self.0, Union::SharedVariant(..)) } /// Is the value held by this [`Dynamic`] shared? /// @@ -333,7 +384,8 @@ impl Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => TypeId::of::(), - Union::Variant(ref v, ..) => (***v).type_id(), + Union::SharedVariant(ref v, ..) => v.type_id(), + Union::OwnedVariant(ref v, ..) => v.type_id(), #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell).unwrap()).type_id(), @@ -367,7 +419,8 @@ impl Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => "timestamp", - Union::Variant(ref v, ..) => (***v).type_name(), + Union::SharedVariant(ref v, ..) => v.type_name(), + Union::OwnedVariant(ref v, ..) => v.type_name(), #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell).unwrap()).type_name(), @@ -411,8 +464,12 @@ impl Hash for Dynamic { #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell).unwrap()).hash(state), - Union::Variant(ref v, ..) => { - let _value_any = (***v).as_any(); + Union::OwnedVariant(..) | Union::SharedVariant(..) => { + let _value_any = match self.0 { + Union::OwnedVariant(ref v, ..) => v.as_any(), + Union::SharedVariant(ref v, ..) => v.as_any(), + _ => unimplemented!("unknown type"), + }; #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] @@ -491,8 +548,12 @@ impl fmt::Display for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => f.write_str(""), - Union::Variant(ref v, ..) => { - let _value_any = (***v).as_any(); + Union::OwnedVariant(..) | Union::SharedVariant(..) => { + let (_value_any, _type_name) = match self.0 { + Union::OwnedVariant(ref v, ..) => (v.as_any(), v.type_name()), + Union::SharedVariant(ref v, ..) => (v.as_any(), v.type_name()), + _ => unimplemented!("unknown type"), + }; let _type_id = _value_any.type_id(); #[cfg(not(feature = "only_i32"))] @@ -549,7 +610,7 @@ impl fmt::Display for Dynamic { }; } - f.write_str((***v).type_name()) + f.write_str(_type_name) } #[cfg(not(feature = "no_closure"))] @@ -659,8 +720,12 @@ impl fmt::Debug for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => write!(f, ""), - Union::Variant(ref v, ..) => { - let _value_any = (***v).as_any(); + Union::SharedVariant(..) | Union::OwnedVariant(..) => { + let (_value_any, _type_name) = match self.0 { + Union::OwnedVariant(ref v, ..) => (v.as_any(), v.type_name()), + Union::SharedVariant(ref v, ..) => (v.as_any(), v.type_name()), + _ => unimplemented!("unknown type"), + }; let _type_id = _value_any.type_id(); #[cfg(not(feature = "only_i32"))] @@ -717,7 +782,7 @@ impl fmt::Debug for Dynamic { }; } - f.write_str((***v).type_name()) + f.write_str(_type_name) } #[cfg(not(feature = "no_closure"))] @@ -836,11 +901,13 @@ impl Clone for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref v, tag, ..) => Self(Union::TimeStamp(v.clone(), tag, ReadWrite)), - Union::Variant(ref v, tag, ..) => Self(Union::Variant( - v.as_ref().as_ref().clone_object().into(), - tag, - ReadWrite, - )), + Union::OwnedVariant(ref v, tag, ..) => { + Self(Union::OwnedVariant(v.clone(), tag, ReadWrite)) + } + + Union::SharedVariant(ref v, tag, ..) => { + Self(Union::SharedVariant(v.clone(), tag, ReadWrite)) + } #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, tag, ..) => Self(Union::Shared(cell.clone(), tag, ReadWrite)), @@ -1082,7 +1149,8 @@ impl Dynamic { | Union::Char(.., access) | Union::Int(.., access) | Union::FnPtr(.., access) - | Union::Variant(.., access) => access, + | Union::SharedVariant(.., access) + | Union::OwnedVariant(.., access) => access, #[cfg(not(feature = "no_float"))] Union::Float(.., access) => access, @@ -1107,7 +1175,8 @@ impl Dynamic { | Union::Char(.., ref mut access) | Union::Int(.., ref mut access) | Union::FnPtr(.., ref mut access) - | Union::Variant(.., ref mut access) => *access = typ, + | Union::SharedVariant(.., ref mut access) + | Union::OwnedVariant(.., ref mut access) => *access = typ, #[cfg(not(feature = "no_float"))] Union::Float(.., ref mut access) => *access = typ, @@ -1220,8 +1289,12 @@ impl Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => false, - Union::Variant(ref v, ..) => { - let _value_any = (***v).as_any(); + Union::SharedVariant(..) | Union::OwnedVariant(..) => { + let _value_any = match self.0 { + Union::OwnedVariant(ref v, ..) => v.as_any(), + Union::SharedVariant(ref v, ..) => v.as_any(), + _ => unimplemented!("unknown type"), + }; let _type_id = _value_any.type_id(); #[cfg(not(feature = "only_i32"))] @@ -1400,8 +1473,8 @@ impl Dynamic { #[cfg(not(feature = "no_closure"))] reify! { value => |v: crate::Shared>| return v.into() } - Self(Union::Variant( - Box::new(Box::new(value)), + Self(Union::OwnedVariant( + OwnedVariantBox(Box::new(value)), DEFAULT_TAG_VALUE, ReadWrite, )) @@ -1590,8 +1663,8 @@ impl Dynamic { } match self.0 { - Union::Variant(v, ..) if TypeId::of::() == (**v).type_id() => { - Ok((*v).as_boxed_any().downcast().map(|x| *x).unwrap()) + Union::OwnedVariant(v, ..) if TypeId::of::() == v.type_id() => { + Ok(v.0.as_boxed_any().downcast().map(|x| *x).unwrap()) } _ => Err(self), } @@ -1939,7 +2012,7 @@ impl Dynamic { } match self.0 { - Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::(), + Union::OwnedVariant(ref v, ..) => v.as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, _ => None, @@ -2042,7 +2115,7 @@ impl Dynamic { } match self.0 { - Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::(), + Union::OwnedVariant(ref mut v, ..) => v.as_any_mut().downcast_mut::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, _ => None, From 70f52be2f09b456fdbb292f7774be58b86f986a8 Mon Sep 17 00:00:00 2001 From: Mikhail Razorvin Date: Tue, 16 Jul 2024 13:15:38 +0200 Subject: [PATCH 2/6] feat: allowse shared getter --- benches/eval_type.rs | 4 +- examples/simple_fn.rs | 91 +++++++++++++++++++++++++++++++++++++++++-- src/api/deprecated.rs | 4 +- src/api/register.rs | 11 +++--- src/func/register.rs | 4 +- src/lib.rs | 2 - src/module/mod.rs | 1 + tests/arrays.rs | 2 +- tests/closures.rs | 2 +- tests/constants.rs | 2 +- tests/expressions.rs | 4 +- tests/float.rs | 2 +- tests/get_set.rs | 16 ++++---- tests/side_effects.rs | 2 +- tests/switch.rs | 2 +- tests/var_scope.rs | 2 +- 16 files changed, 118 insertions(+), 33 deletions(-) diff --git a/benches/eval_type.rs b/benches/eval_type.rs index 7cee4394e..d943071f5 100644 --- a/benches/eval_type.rs +++ b/benches/eval_type.rs @@ -12,7 +12,7 @@ struct Test { } impl Test { - pub fn get_x(&mut self) -> INT { + pub fn get_x(&self) -> INT { self.x } pub fn action(&mut self) { @@ -21,7 +21,7 @@ impl Test { pub fn update(&mut self, val: INT) { self.x = val; } - pub fn get_nest(&mut self) -> Test { + pub fn get_nest(&self) -> Test { Test { x: 9 } } } diff --git a/examples/simple_fn.rs b/examples/simple_fn.rs index 887cad30b..c8b18d623 100644 --- a/examples/simple_fn.rs +++ b/examples/simple_fn.rs @@ -1,18 +1,103 @@ //! An example showing how to register a simple Rust function. -use rhai::{Engine, EvalAltResult}; +use std::ops::Deref; + +use rhai::{Dynamic, Engine, EvalAltResult}; +use rhai::{Scope, INT}; fn add(x: i64, y: i64) -> i64 { x + y } +#[derive(Clone)] +struct TestStruct { + x: INT, + y: INT, + array: Vec, +} + +impl TestStruct { + fn get_x(&mut self) -> INT { + self.x + } + + fn set_x(&mut self, new_x: INT) { + self.x = new_x; + } + + fn get_y(&mut self) -> INT { + self.y + } + + fn new() -> Self { + Self { + x: 1, + y: 123, + array: vec![1, 2, 3, 4, 5], + } + } +} + +#[derive(Clone)] +struct UnsafeShared { + ptr: *const T, +} + +impl Deref for UnsafeShared { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr } + } +} + +/// as +#[repr(transparent)] +pub struct RhaiRef { + value: T, +} + +impl Clone for RhaiRef { + fn clone(&self) -> Self { + unimplemented!() + } +} + +unsafe impl Send for RhaiRef {} +unsafe impl Sync for RhaiRef {} + +impl Deref for RhaiRef { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl RhaiRef { + fn get_y(&self) -> INT { + self.y + } +} + fn main() -> Result<(), Box> { let mut engine = Engine::new(); + let mut scope = Scope::new(); + let target = TestStruct::new(); + + scope.set_value("ts", unsafe { Dynamic::from_ref(&target) }); + engine.register_get("y", RhaiRef::::get_y); - engine.register_fn("add", add); + println!("{}", std::any::type_name::<&RhaiRef>()); - let result = engine.eval::("add(40, 2)")?; + let result = engine.eval_with_scope::( + &mut scope, + r#" + ts.y() + "#, + )?; + drop(target); println!("Answer: {result}"); // prints 42 Ok(()) diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 755560312..e838d06a5 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -12,7 +12,7 @@ use std::any::TypeId; use std::prelude::v1::*; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::func::register::Mut; +use crate::func::register::{Mut, Ref}; #[cfg(not(feature = "no_std"))] #[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))] @@ -221,7 +221,7 @@ impl Engine { pub fn register_get_result( &mut self, name: impl AsRef, - get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, true> + SendSync + 'static, + get_fn: impl RhaiNativeFunc<(Ref,), 1, X, R, true> + SendSync + 'static, ) -> &mut Self { self.register_get(name, get_fn) } diff --git a/src/api/register.rs b/src/api/register.rs index 245b2eec2..bbfa12af6 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -11,7 +11,7 @@ use std::any::{type_name, TypeId}; use std::prelude::v1::*; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::func::register::Mut; +use crate::func::register::{Mut, Ref}; impl Engine { /// Get a mutable reference to the global namespace module @@ -74,7 +74,6 @@ impl Engine { func: impl RhaiNativeFunc + SendSync + 'static, ) -> &mut Self { FuncRegistration::new(name.into()).register_into_engine(self, func); - self } /// Register a function of the [`Engine`]. @@ -279,7 +278,7 @@ impl Engine { /// Self { field: 1 } /// } /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self) -> i64 { + /// fn get_field(&self) -> i64 { /// self.field /// } /// } @@ -305,7 +304,7 @@ impl Engine { pub fn register_get( &mut self, name: impl AsRef, - get_fn: impl RhaiNativeFunc<(Mut,), 1, X, R, F> + SendSync + 'static, + get_fn: impl RhaiNativeFunc<(Ref,), 1, X, R, F> + SendSync + 'static, ) -> &mut Self { self.register_fn(crate::engine::make_getter(name.as_ref()), get_fn) } @@ -380,7 +379,7 @@ impl Engine { /// Self { field: 1 } /// } /// // Even a getter must start with `&mut self` and not `&self`. - /// fn get_field(&mut self) -> i64 { + /// fn get_field(&self) -> i64 { /// self.field /// } /// fn set_field(&mut self, new_val: i64) { @@ -417,7 +416,7 @@ impl Engine { >( &mut self, name: impl AsRef, - get_fn: impl RhaiNativeFunc<(Mut,), 1, X1, R, F1> + SendSync + 'static, + get_fn: impl RhaiNativeFunc<(Ref,), 1, X1, R, F1> + SendSync + 'static, set_fn: impl RhaiNativeFunc<(Mut, R), 2, X2, (), F2> + SendSync + 'static, ) -> &mut Self { self.register_get(&name, get_fn).register_set(&name, set_fn) diff --git a/src/func/register.rs b/src/func/register.rs index f67452461..f36f8369c 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -35,7 +35,7 @@ use std::{ /// /// These types are not actually used anywhere. pub struct Mut(T); -//pub struct Ref(T); +pub struct Ref(T); /// Dereference into [`DynamicWriteLock`] #[inline(always)] @@ -230,6 +230,8 @@ macro_rules! def_register { ($p0:ident:$n0:expr $(, $p:ident: $n:expr)*) => { def_register!(imp Pure : $n0 ; $p0 => $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => $p => by_value)*); def_register!(imp Method : $n0 ; $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => $p => by_value)*); + + def_register!(imp Method : $n0 ; $p0 => & $p0 => Ref<$p0> => & $p0 => by_ref $(, $p => $p => $p => $p => by_value)*); // ^ RhaiFunc constructor // ^ number of arguments ^ first parameter passed through // ^ others passed by value (by_value) diff --git a/src/lib.rs b/src/lib.rs index 525df595c..2b9211225 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -323,8 +323,6 @@ pub use module::resolvers as module_resolvers; #[cfg(not(feature = "no_optimize"))] pub use optimizer::OptimizationLevel; -// Expose internal data structures. - #[cfg(feature = "internals")] pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant}; diff --git a/src/module/mod.rs b/src/module/mod.rs index 4c8ba66ce..e1a77c67c 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -407,6 +407,7 @@ impl FuncRegistration { .map(|ty| format!("_: {}", engine.format_param_type(ty))) .collect::>(); + if FUNC::return_type() != TypeId::of::<()>() { param_type_names .push(engine.format_param_type(FUNC::return_type_name()).into()); diff --git a/tests/arrays.rs b/tests/arrays.rs index 35e15d250..c80b3eb33 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -187,7 +187,7 @@ fn test_array_with_structs() { self.x += 1000; } - fn get_x(&mut self) -> INT { + fn get_x(&self) -> INT { self.x } diff --git a/tests/closures.rs b/tests/closures.rs index 39bf88565..fffff83a0 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -328,7 +328,7 @@ fn test_closures_shared_obj() { // Register API on TestStruct engine .register_type_with_name::("TestStruct") - .register_get_set("data", |p: &mut TestStruct| *p.borrow(), |p: &mut TestStruct, value: INT| *p.borrow_mut() = value) + .register_get_set("data", |p: &TestStruct| *p.borrow(), |p: &mut TestStruct, value: INT| *p.borrow_mut() = value) .register_fn("+=", |p1: &mut TestStruct, p2: TestStruct| *p1.borrow_mut() += *p2.borrow()) .register_fn("-=", |p1: &mut TestStruct, p2: TestStruct| *p1.borrow_mut() -= *p2.borrow()); diff --git a/tests/constants.rs b/tests/constants.rs index cb8c810b2..a882866b9 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -46,7 +46,7 @@ fn test_constant_mut() { engine .register_type_with_name::("TestStruct") .register_fn("new_ts", || TestStruct(123)) - .register_get("value", |obj: &mut TestStruct| obj.0) + .register_get("value", |obj: &TestStruct| obj.0) .register_set("value", set_value) .register_fn("update_value", set_value); diff --git a/tests/expressions.rs b/tests/expressions.rs index 99c43875e..991493ef8 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -75,10 +75,10 @@ fn test_expressions_eval() { } impl AGENT { - pub fn get_gender(&mut self) -> String { + pub fn get_gender(&self) -> String { self.gender.clone() } - pub fn get_age(&mut self) -> INT { + pub fn get_age(&self) -> INT { self.age } } diff --git a/tests/float.rs b/tests/float.rs index 8796b296b..416879a9f 100644 --- a/tests/float.rs +++ b/tests/float.rs @@ -44,7 +44,7 @@ fn test_struct_with_float() { self.x += 5.789; } - fn get_x(&mut self) -> FLOAT { + fn get_x(&self) -> FLOAT { self.x } diff --git a/tests/get_set.rs b/tests/get_set.rs index d5d7e591f..532ce99f3 100644 --- a/tests/get_set.rs +++ b/tests/get_set.rs @@ -11,7 +11,7 @@ fn test_get_set() { } impl TestStruct { - fn get_x(&mut self) -> INT { + fn get_x(&self) -> INT { self.x } @@ -19,7 +19,7 @@ fn test_get_set() { self.x = new_x; } - fn get_y(&mut self) -> INT { + fn get_y(&self) -> INT { self.y } @@ -61,7 +61,7 @@ fn test_get_set_chain_with_write_back() { } impl TestChild { - fn get_x(&mut self) -> INT { + fn get_x(&self) -> INT { self.x } @@ -80,7 +80,7 @@ fn test_get_set_chain_with_write_back() { } impl TestParent { - fn get_child(&mut self) -> TestChild { + fn get_child(&self) -> TestChild { self.child.clone() } @@ -128,7 +128,7 @@ fn test_get_set_op_assignment() { struct Num(INT); impl Num { - fn get(&mut self) -> INT { + fn get(&self) -> INT { self.0 } fn set(&mut self, x: INT) { @@ -163,9 +163,9 @@ fn test_get_set_chain_without_write_back() { engine .register_type::() - .register_get_set("value", |t: &mut Inner| t.value, |_: NativeCallContext, _: &mut Inner, new: INT| panic!("Inner::value setter called with {}", new)) + .register_get_set("value", |t: &Inner| t.value, |_: NativeCallContext, _: &mut Inner, new: INT| panic!("Inner::value setter called with {}", new)) .register_type::() - .register_get_set("inner", |_: NativeCallContext, t: &mut Outer| t.inner.clone(), |_: &mut Outer, new: Inner| panic!("Outer::inner setter called with {:?}", new)); + .register_get_set("inner", |_: NativeCallContext, t: &Outer| t.inner.clone(), |_: &mut Outer, new: Inner| panic!("Outer::inner setter called with {:?}", new)); #[cfg(not(feature = "no_index"))] engine.register_indexer_get_set(|t: &mut Outer, n: INT| Inner { value: t.inner.value * n }, |_: &mut Outer, n: INT, new: Inner| panic!("Outer::inner index setter called with {} and {:?}", n, new)); @@ -193,7 +193,7 @@ fn test_get_set_collection() { .register_iterator::() .register_fn("new_bag", MyBag::new) .register_fn("len", |col: &mut MyBag| col.len() as INT) - .register_get("len", |col: &mut MyBag| col.len() as INT) + .register_get("len", |col: &MyBag| col.len() as INT) .register_fn("clear", |col: &mut MyBag| col.clear()) .register_fn("contains", |col: &mut MyBag, item: INT| col.contains(&item)) .register_fn("add", |col: &mut MyBag, item: MyItem| col.insert(item)) diff --git a/tests/side_effects.rs b/tests/side_effects.rs index 31e55bbfa..5a8039b80 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -45,7 +45,7 @@ fn test_side_effects_command() { let val = command.get(); command.action(val + x); }); - engine.register_get("value", |command: &mut API| command.lock().unwrap().get()); + engine.register_get("value", |command: &API| command.lock().unwrap().get()); assert_eq!( engine diff --git a/tests/switch.rs b/tests/switch.rs index 94915713c..02db4c9d4 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -146,7 +146,7 @@ mod test_switch_enum { } impl MyEnum { - fn get_enum_data(&mut self) -> Array { + fn get_enum_data(&self) -> Array { match self { Self::Foo => vec!["Foo".into()] as Array, Self::Bar(num) => vec!["Bar".into(), (*num).into()] as Array, diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 2d83387e1..962fe2d1a 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -444,7 +444,7 @@ fn test_var_scope_cloning() { } let mut engine = Engine::new(); - engine.register_get_set("field", |foo: &mut Foo| foo.field, |foo: &mut Foo, value| foo.field = value); + engine.register_get_set("field", |foo: &Foo| foo.field, |foo: &mut Foo, value| foo.field = value); let mut scope = Scope::new(); scope.push("foo", Foo { field: 1 }); From a73334616b07ed57542ea067a4eecaacc95c0992 Mon Sep 17 00:00:00 2001 From: Mikhail Razorvin Date: Tue, 16 Jul 2024 17:55:45 +0200 Subject: [PATCH 3/6] feat: finished basic integration --- examples/simple_fn.rs | 82 +++++++++++-------------------------------- src/func/call.rs | 6 ++++ src/func/register.rs | 14 +++++--- src/lib.rs | 3 ++ src/types/dynamic.rs | 55 +++++++++++++++++++++++------ 5 files changed, 84 insertions(+), 76 deletions(-) diff --git a/examples/simple_fn.rs b/examples/simple_fn.rs index c8b18d623..9bcbce69f 100644 --- a/examples/simple_fn.rs +++ b/examples/simple_fn.rs @@ -1,15 +1,12 @@ //! An example showing how to register a simple Rust function. -use std::ops::Deref; - -use rhai::{Dynamic, Engine, EvalAltResult}; +use rhai::{dynamic_scope, Dynamic, Engine, EvalAltResult}; use rhai::{Scope, INT}; fn add(x: i64, y: i64) -> i64 { x + y } -#[derive(Clone)] struct TestStruct { x: INT, y: INT, @@ -17,85 +14,48 @@ struct TestStruct { } impl TestStruct { - fn get_x(&mut self) -> INT { - self.x - } - - fn set_x(&mut self, new_x: INT) { - self.x = new_x; - } - - fn get_y(&mut self) -> INT { - self.y - } - fn new() -> Self { Self { x: 1, - y: 123, + y: 833, array: vec![1, 2, 3, 4, 5], } } -} - -#[derive(Clone)] -struct UnsafeShared { - ptr: *const T, -} -impl Deref for UnsafeShared { - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &*self.ptr } + fn get_y(&self) -> INT { + self.y } } -/// as -#[repr(transparent)] -pub struct RhaiRef { - value: T, -} - -impl Clone for RhaiRef { +impl Clone for TestStruct { fn clone(&self) -> Self { unimplemented!() } } - -unsafe impl Send for RhaiRef {} -unsafe impl Sync for RhaiRef {} - -impl Deref for RhaiRef { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.value - } -} - -impl RhaiRef { - fn get_y(&self) -> INT { - self.y - } -} - fn main() -> Result<(), Box> { let mut engine = Engine::new(); let mut scope = Scope::new(); let target = TestStruct::new(); - scope.set_value("ts", unsafe { Dynamic::from_ref(&target) }); - engine.register_get("y", RhaiRef::::get_y); + let result = dynamic_scope(|| { + scope.set_value("ts", unsafe { Dynamic::from_ref(&target) }); + engine.register_get("y", TestStruct::get_y); + + engine.register_fn("xx1", |_: TestStruct| {}); + engine.register_fn("xx2", |_: &'static TestStruct| {}); - println!("{}", std::any::type_name::<&RhaiRef>()); + // THIS CAUSE ERROR + // engine.register_get("xx2", |x: &TestStruct| -> &'static INT { &x.y }); - let result = engine.eval_with_scope::( - &mut scope, - r#" - ts.y() + engine.eval_with_scope::( + &mut scope, + r#" + ts.y + // xx1(ts) // <- this is impossible + // xx2(ts) // <- this is also impossible "#, - )?; + ) + })?; drop(target); println!("Answer: {result}"); // prints 42 diff --git a/src/func/call.rs b/src/func/call.rs index 1a1882985..785cd5107 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -174,6 +174,9 @@ impl Engine { allow_dynamic: bool, ) -> Option<&'s FnResolutionCacheEntry> { let mut hash = args.as_deref().map_or(hash_base, |args| { + args.iter() + .map(|a| a.type_name()) + .for_each(|x| println!("{x}")); calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id())) }); @@ -353,6 +356,9 @@ impl Engine { ) -> RhaiResultOf<(Dynamic, bool)> { self.track_operation(global, pos)?; + println!("---------------"); + println!("{}", name); + // Check if function access already in the cache let local_entry = &mut None; let a = Some(&mut *args); diff --git a/src/func/register.rs b/src/func/register.rs index f36f8369c..c44439d15 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -8,7 +8,7 @@ use super::call::FnCallArgs; use super::function::RhaiFunc; use super::native::{SendSync, Shared}; -use crate::types::dynamic::{DynamicWriteLock, Union, Variant}; +use crate::types::dynamic::{DynamicReadLock, DynamicWriteLock, Union, Variant}; use crate::{Dynamic, Identifier, NativeCallContext, RhaiResultOf}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -44,6 +44,12 @@ pub fn by_ref(data: &mut Dynamic) -> DynamicWriteLock { data.write_lock::().unwrap() } +#[inline(always)] +pub fn by_ref_read(data: &mut Dynamic) -> DynamicReadLock { + // Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data. + data.read_lock::().unwrap() +} + /// Dereference into value. #[inline(always)] #[must_use] @@ -145,7 +151,7 @@ macro_rules! def_register { #[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] } #[inline(always)] fn into_rhai_function(self, is_pure: bool, is_volatile: bool) -> RhaiFunc { - RhaiFunc::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| { + RhaiFunc::$abi { func: Shared::new(move |ctx: _, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! let mut drain = args.iter_mut(); $(let mut $par = $clone(drain.next().unwrap()); )* @@ -174,6 +180,7 @@ macro_rules! def_register { let mut drain = args.iter_mut(); $(let mut $par = $clone(drain.next().unwrap()); )* + // Call the function with each argument value let r = self(ctx, $($arg),*); @@ -230,8 +237,7 @@ macro_rules! def_register { ($p0:ident:$n0:expr $(, $p:ident: $n:expr)*) => { def_register!(imp Pure : $n0 ; $p0 => $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => $p => by_value)*); def_register!(imp Method : $n0 ; $p0 => &mut $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => $p => by_value)*); - - def_register!(imp Method : $n0 ; $p0 => & $p0 => Ref<$p0> => & $p0 => by_ref $(, $p => $p => $p => $p => by_value)*); + def_register!(imp Method : $n0 ; $p0 => & $p0 => Ref<$p0> => & $p0 => by_ref_read $(, $p => $p => $p => $p => by_value)*); // ^ RhaiFunc constructor // ^ number of arguments ^ first parameter passed through // ^ others passed by value (by_value) diff --git a/src/lib.rs b/src/lib.rs index 2b9211225..2dade0c9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,6 +92,7 @@ #![allow(clippy::enum_glob_use)] // Sometimes useful to import all `Tokens` etc. #![allow(clippy::no_effect_underscore_binding)] // Underscored variables may be used by code within feature guards #![allow(clippy::semicolon_if_nothing_returned)] // One-liner `match` cases are sometimes formatted as multi-line blocks +#![feature(thread_local)] #[cfg(feature = "no_std")] extern crate alloc; @@ -323,6 +324,8 @@ pub use module::resolvers as module_resolvers; #[cfg(not(feature = "no_optimize"))] pub use optimizer::OptimizationLevel; +pub use types::dynamic::dynamic_scope; + #[cfg(feature = "internals")] pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant}; diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 7ed2fba04..20ab6cbe4 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -26,6 +26,7 @@ pub use instant::Instant; /// Exported under the `internals` feature only. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] #[non_exhaustive] +#[repr(u8)] pub enum AccessMode { /// Mutable. ReadWrite, @@ -33,7 +34,7 @@ pub enum AccessMode { ReadOnly, } -pub struct SharedVariantPtr(*mut dyn Variant); +pub struct SharedVariantPtr(*const dyn Variant); impl SharedVariantPtr { fn type_id(&self) -> TypeId { unsafe { (*self.0).type_id() } @@ -79,6 +80,19 @@ impl OwnedVariantBox { } } +#[thread_local] +pub static DYNAMIC_GENERATION: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(0); + +/// dynamic scope +pub fn dynamic_scope(func: impl FnOnce() -> R) -> R { + let current_generation = DYNAMIC_GENERATION.load(core::sync::atomic::Ordering::Acquire); + let result = func(); + if current_generation != DYNAMIC_GENERATION.fetch_add(1, core::sync::atomic::Ordering::AcqRel) { + panic!("nested dynnamic scopes not supproted yet"); + } + result +} + /// Arbitrary data attached to a [`Dynamic`] value. #[cfg(target_pointer_width = "64")] pub type Tag = i32; @@ -138,7 +152,7 @@ pub enum Union { /// An extra level of redirection is used in order to avoid bloating the size of [`Dynamic`] /// because `Box` is a fat pointer. OwnedVariant(OwnedVariantBox, Tag, AccessMode), - SharedVariant(SharedVariantPtr, Tag, AccessMode), + SharedVariant(SharedVariantPtr, Tag, AccessMode, u16), /// A _shared_ value of any type. #[cfg(not(feature = "no_closure"))] @@ -235,7 +249,7 @@ impl Dynamic { | Union::Char(_, tag, _) | Union::Int(_, tag, _) | Union::FnPtr(_, tag, _) - | Union::SharedVariant(_, tag, _) + | Union::SharedVariant(_, tag, _, _) | Union::OwnedVariant(_, tag, _) => tag, #[cfg(not(feature = "no_float"))] @@ -262,7 +276,7 @@ impl Dynamic { | Union::Int(_, ref mut tag, _) | Union::FnPtr(_, ref mut tag, _) | Union::OwnedVariant(_, ref mut tag, _) - | Union::SharedVariant(_, ref mut tag, _) => *tag = value, + | Union::SharedVariant(_, ref mut tag, _, _) => *tag = value, #[cfg(not(feature = "no_float"))] Union::Float(_, ref mut tag, _) => *tag = value, @@ -905,9 +919,12 @@ impl Clone for Dynamic { Self(Union::OwnedVariant(v.clone(), tag, ReadWrite)) } - Union::SharedVariant(ref v, tag, ..) => { - Self(Union::SharedVariant(v.clone(), tag, ReadWrite)) - } + Union::SharedVariant(ref v, tag, ..) => Self(Union::SharedVariant( + v.clone(), + tag, + ReadWrite, + DYNAMIC_GENERATION.load(core::sync::atomic::Ordering::Relaxed), + )), #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, tag, ..) => Self(Union::Shared(cell.clone(), tag, ReadWrite)), @@ -1149,7 +1166,7 @@ impl Dynamic { | Union::Char(.., access) | Union::Int(.., access) | Union::FnPtr(.., access) - | Union::SharedVariant(.., access) + | Union::SharedVariant(.., access, _) | Union::OwnedVariant(.., access) => access, #[cfg(not(feature = "no_float"))] @@ -1175,7 +1192,7 @@ impl Dynamic { | Union::Char(.., ref mut access) | Union::Int(.., ref mut access) | Union::FnPtr(.., ref mut access) - | Union::SharedVariant(.., ref mut access) + | Union::SharedVariant(.., ref mut access, _) | Union::OwnedVariant(.., ref mut access) => *access = typ, #[cfg(not(feature = "no_float"))] @@ -1375,6 +1392,18 @@ impl Dynamic { } } } + + /// + #[inline] + pub unsafe fn from_ref(value: &T) -> Self { + Self(Union::SharedVariant( + SharedVariantPtr(value as &dyn Variant as *const dyn Variant), + 0, + ReadOnly, + DYNAMIC_GENERATION.load(core::sync::atomic::Ordering::Relaxed) as u16, + )) + } + /// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is. /// /// # Arrays @@ -1543,7 +1572,7 @@ impl Dynamic { #[inline(always)] #[must_use] #[allow(unused_mut)] - pub fn try_cast(mut self) -> Option { + pub fn try_cast(mut self) -> Option { self.try_cast_result().ok() } /// Convert the [`Dynamic`] value into specific type. @@ -1566,7 +1595,7 @@ impl Dynamic { /// /// These normally shouldn't occur since most operations in Rhai are single-threaded. #[allow(unused_mut)] - pub fn try_cast_result(mut self) -> Result { + pub fn try_cast_result(mut self) -> Result { // Coded this way in order to maximally leverage potentials for dead-code removal. #[cfg(not(feature = "no_closure"))] @@ -2014,6 +2043,10 @@ impl Dynamic { match self.0 { Union::OwnedVariant(ref v, ..) => v.as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] + Union::SharedVariant(ref v, .., generation) => (generation + == DYNAMIC_GENERATION.load(core::sync::atomic::Ordering::Relaxed)) + .then(|| v.as_any().downcast_ref::()) + .flatten(), Union::Shared(..) => None, _ => None, } From 3ea3980ae6cb2967f36e4bcbe7671f8f60861372 Mon Sep 17 00:00:00 2001 From: Mikhail Razorvin Date: Tue, 16 Jul 2024 18:01:10 +0200 Subject: [PATCH 4/6] feat: restored variant name --- src/func/call.rs | 2 +- src/serde/de.rs | 22 +++++++++--------- src/serde/serialize.rs | 2 +- src/types/dynamic.rs | 52 ++++++++++++++++++++---------------------- 4 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/func/call.rs b/src/func/call.rs index 785cd5107..98abc65bd 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1873,7 +1873,7 @@ impl Engine { _ => (), }, (Union::SharedVariant(..), _) | (_, Union::SharedVariant(..)) => (), - (Union::OwnedVariant(..), _) | (_, Union::OwnedVariant(..)) => (), + (Union::Variant(..), _) | (_, Union::Variant(..)) => (), _ => { if let Some((func, need_context)) = get_builtin_binary_op_fn(op_token, &lhs, &rhs) diff --git a/src/serde/de.rs b/src/serde/de.rs index c65204725..97224e677 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -162,34 +162,34 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => self.type_error(), - Union::OwnedVariant(ref value, ..) if value.is::() => self.deserialize_i8(visitor), - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => self.deserialize_i8(visitor), + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_i16(visitor) } - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_i32(visitor) } - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_i64(visitor) } - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_i128(visitor) } - Union::OwnedVariant(ref value, ..) if value.is::() => self.deserialize_u8(visitor), - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => self.deserialize_u8(visitor), + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_u16(visitor) } - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_u32(visitor) } - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_u64(visitor) } - Union::OwnedVariant(ref value, ..) if value.is::() => { + Union::Variant(ref value, ..) if value.is::() => { self.deserialize_u128(visitor) } - Union::OwnedVariant(..) => self.type_error(), + Union::Variant(..) => self.type_error(), Union::SharedVariant(..) => self.type_error(), #[cfg(not(feature = "no_closure"))] diff --git a/src/serde/serialize.rs b/src/serde/serialize.rs index 9e8fb7935..05463c982 100644 --- a/src/serde/serialize.rs +++ b/src/serde/serialize.rs @@ -71,7 +71,7 @@ impl Serialize for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()), - Union::OwnedVariant(ref v, ..) => ser.serialize_str(v.type_name()), + Union::Variant(ref v, ..) => ser.serialize_str(v.type_name()), Union::SharedVariant(ref v, ..) => ser.serialize_str(v.type_name()), #[cfg(not(feature = "no_closure"))] diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 20ab6cbe4..bf84bb408 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -53,8 +53,8 @@ impl SharedVariantPtr { } } -pub struct OwnedVariantBox(Box); -impl OwnedVariantBox { +pub struct OwnedVariant(Box); +impl OwnedVariant { fn type_id(&self) -> TypeId { (*(*self).0).type_id() } @@ -72,7 +72,7 @@ impl OwnedVariantBox { } fn clone(&self) -> Self { - OwnedVariantBox(self.0.clone_object()) + OwnedVariant(self.0.clone_object()) } pub fn is(&self) -> bool { @@ -151,7 +151,7 @@ pub enum Union { /// /// An extra level of redirection is used in order to avoid bloating the size of [`Dynamic`] /// because `Box` is a fat pointer. - OwnedVariant(OwnedVariantBox, Tag, AccessMode), + Variant(OwnedVariant, Tag, AccessMode), SharedVariant(SharedVariantPtr, Tag, AccessMode, u16), /// A _shared_ value of any type. @@ -250,7 +250,7 @@ impl Dynamic { | Union::Int(_, tag, _) | Union::FnPtr(_, tag, _) | Union::SharedVariant(_, tag, _, _) - | Union::OwnedVariant(_, tag, _) => tag, + | Union::Variant(_, tag, _) => tag, #[cfg(not(feature = "no_float"))] Union::Float(_, tag, _) => tag, @@ -275,7 +275,7 @@ impl Dynamic { | Union::Char(_, ref mut tag, _) | Union::Int(_, ref mut tag, _) | Union::FnPtr(_, ref mut tag, _) - | Union::OwnedVariant(_, ref mut tag, _) + | Union::Variant(_, ref mut tag, _) | Union::SharedVariant(_, ref mut tag, _, _) => *tag = value, #[cfg(not(feature = "no_float"))] @@ -298,7 +298,7 @@ impl Dynamic { #[inline(always)] #[must_use] pub const fn is_variant(&self) -> bool { - matches!(self.0, Union::OwnedVariant(..)) || matches!(self.0, Union::SharedVariant(..)) + matches!(self.0, Union::Variant(..)) || matches!(self.0, Union::SharedVariant(..)) } /// Is the value held by this [`Dynamic`] shared? /// @@ -399,7 +399,7 @@ impl Dynamic { Union::TimeStamp(..) => TypeId::of::(), Union::SharedVariant(ref v, ..) => v.type_id(), - Union::OwnedVariant(ref v, ..) => v.type_id(), + Union::Variant(ref v, ..) => v.type_id(), #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell).unwrap()).type_id(), @@ -434,7 +434,7 @@ impl Dynamic { Union::TimeStamp(..) => "timestamp", Union::SharedVariant(ref v, ..) => v.type_name(), - Union::OwnedVariant(ref v, ..) => v.type_name(), + Union::Variant(ref v, ..) => v.type_name(), #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell).unwrap()).type_name(), @@ -478,9 +478,9 @@ impl Hash for Dynamic { #[cfg(not(feature = "no_closure"))] Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell).unwrap()).hash(state), - Union::OwnedVariant(..) | Union::SharedVariant(..) => { + Union::Variant(..) | Union::SharedVariant(..) => { let _value_any = match self.0 { - Union::OwnedVariant(ref v, ..) => v.as_any(), + Union::Variant(ref v, ..) => v.as_any(), Union::SharedVariant(ref v, ..) => v.as_any(), _ => unimplemented!("unknown type"), }; @@ -562,9 +562,9 @@ impl fmt::Display for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => f.write_str(""), - Union::OwnedVariant(..) | Union::SharedVariant(..) => { + Union::Variant(..) | Union::SharedVariant(..) => { let (_value_any, _type_name) = match self.0 { - Union::OwnedVariant(ref v, ..) => (v.as_any(), v.type_name()), + Union::Variant(ref v, ..) => (v.as_any(), v.type_name()), Union::SharedVariant(ref v, ..) => (v.as_any(), v.type_name()), _ => unimplemented!("unknown type"), }; @@ -734,9 +734,9 @@ impl fmt::Debug for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => write!(f, ""), - Union::SharedVariant(..) | Union::OwnedVariant(..) => { + Union::SharedVariant(..) | Union::Variant(..) => { let (_value_any, _type_name) = match self.0 { - Union::OwnedVariant(ref v, ..) => (v.as_any(), v.type_name()), + Union::Variant(ref v, ..) => (v.as_any(), v.type_name()), Union::SharedVariant(ref v, ..) => (v.as_any(), v.type_name()), _ => unimplemented!("unknown type"), }; @@ -915,9 +915,7 @@ impl Clone for Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(ref v, tag, ..) => Self(Union::TimeStamp(v.clone(), tag, ReadWrite)), - Union::OwnedVariant(ref v, tag, ..) => { - Self(Union::OwnedVariant(v.clone(), tag, ReadWrite)) - } + Union::Variant(ref v, tag, ..) => Self(Union::Variant(v.clone(), tag, ReadWrite)), Union::SharedVariant(ref v, tag, ..) => Self(Union::SharedVariant( v.clone(), @@ -1167,7 +1165,7 @@ impl Dynamic { | Union::Int(.., access) | Union::FnPtr(.., access) | Union::SharedVariant(.., access, _) - | Union::OwnedVariant(.., access) => access, + | Union::Variant(.., access) => access, #[cfg(not(feature = "no_float"))] Union::Float(.., access) => access, @@ -1193,7 +1191,7 @@ impl Dynamic { | Union::Int(.., ref mut access) | Union::FnPtr(.., ref mut access) | Union::SharedVariant(.., ref mut access, _) - | Union::OwnedVariant(.., ref mut access) => *access = typ, + | Union::Variant(.., ref mut access) => *access = typ, #[cfg(not(feature = "no_float"))] Union::Float(.., ref mut access) => *access = typ, @@ -1306,9 +1304,9 @@ impl Dynamic { #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => false, - Union::SharedVariant(..) | Union::OwnedVariant(..) => { + Union::SharedVariant(..) | Union::Variant(..) => { let _value_any = match self.0 { - Union::OwnedVariant(ref v, ..) => v.as_any(), + Union::Variant(ref v, ..) => v.as_any(), Union::SharedVariant(ref v, ..) => v.as_any(), _ => unimplemented!("unknown type"), }; @@ -1502,8 +1500,8 @@ impl Dynamic { #[cfg(not(feature = "no_closure"))] reify! { value => |v: crate::Shared>| return v.into() } - Self(Union::OwnedVariant( - OwnedVariantBox(Box::new(value)), + Self(Union::Variant( + OwnedVariant(Box::new(value)), DEFAULT_TAG_VALUE, ReadWrite, )) @@ -1692,7 +1690,7 @@ impl Dynamic { } match self.0 { - Union::OwnedVariant(v, ..) if TypeId::of::() == v.type_id() => { + Union::Variant(v, ..) if TypeId::of::() == v.type_id() => { Ok(v.0.as_boxed_any().downcast().map(|x| *x).unwrap()) } _ => Err(self), @@ -2041,7 +2039,7 @@ impl Dynamic { } match self.0 { - Union::OwnedVariant(ref v, ..) => v.as_any().downcast_ref::(), + Union::Variant(ref v, ..) => v.as_any().downcast_ref::(), #[cfg(not(feature = "no_closure"))] Union::SharedVariant(ref v, .., generation) => (generation == DYNAMIC_GENERATION.load(core::sync::atomic::Ordering::Relaxed)) @@ -2148,7 +2146,7 @@ impl Dynamic { } match self.0 { - Union::OwnedVariant(ref mut v, ..) => v.as_any_mut().downcast_mut::(), + Union::Variant(ref mut v, ..) => v.as_any_mut().downcast_mut::(), #[cfg(not(feature = "no_closure"))] Union::Shared(..) => None, _ => None, From 1d3a260a18826ced5fae96242e755911369bc864 Mon Sep 17 00:00:00 2001 From: Mikhail Razorvin Date: Tue, 16 Jul 2024 18:40:15 +0200 Subject: [PATCH 5/6] feat: added conditional compilation for unsafe code --- Cargo.toml | 2 ++ src/types/dynamic.rs | 55 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d43de4210..99249f402 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,6 +130,8 @@ unstable = [] ## Running under a testing environment. testing-environ = [] +dangerous = [] + [[bin]] name = "rhai-repl" required-features = ["rustyline"] diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index bf84bb408..bcedfe0b2 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -34,27 +34,54 @@ pub enum AccessMode { ReadOnly, } -pub struct SharedVariantPtr(*const dyn Variant); +pub struct SharedVariantPtr(#[cfg(feature = "dangerous")] *const dyn Variant); impl SharedVariantPtr { fn type_id(&self) -> TypeId { - unsafe { (*self.0).type_id() } + #[cfg(feature = "dangerous")] + unsafe { + (*self.0).type_id() + } + + #[cfg(not(feature = "dangerous"))] + unimplemented!("required `feature = dangerous`") } pub fn type_name(&self) -> &'static str { - unsafe { (*self.0).type_name() } + #[cfg(feature = "dangerous")] + unsafe { + (*self.0).type_name() + } + + #[cfg(not(feature = "dangerous"))] + unimplemented!("required `feature = dangerous`") } fn as_any(&self) -> &dyn Any { - unsafe { (*self.0).as_any() } + #[cfg(feature = "dangerous")] + unsafe { + (*self.0).as_any() + } + + #[cfg(not(feature = "dangerous"))] + unimplemented!("required `feature = dangerous`") } fn clone(&self) -> Self { - SharedVariantPtr(self.0) + #[cfg(feature = "dangerous")] + let _clone = SharedVariantPtr(self.0); + #[cfg(not(feature = "dangerous"))] + let _clone = unimplemented!("required `feature = dangerous`"); + #[allow(unreachable_code)] + _clone } } pub struct OwnedVariant(Box); impl OwnedVariant { + fn new(boxed: Box) -> Self { + OwnedVariant(boxed) + } + fn type_id(&self) -> TypeId { (*(*self).0).type_id() } @@ -67,6 +94,10 @@ impl OwnedVariant { (*(*self).0).as_any() } + fn as_boxed_any(self) -> Box { + self.0.as_boxed_any() + } + fn as_any_mut(&mut self) -> &mut dyn Any { (*(*self).0).as_any_mut() } @@ -249,8 +280,8 @@ impl Dynamic { | Union::Char(_, tag, _) | Union::Int(_, tag, _) | Union::FnPtr(_, tag, _) - | Union::SharedVariant(_, tag, _, _) - | Union::Variant(_, tag, _) => tag, + | Union::Variant(_, tag, _) + | Union::SharedVariant(_, tag, _, _) => tag, #[cfg(not(feature = "no_float"))] Union::Float(_, tag, _) => tag, @@ -1392,7 +1423,7 @@ impl Dynamic { } /// - #[inline] + #[cfg(feature = "dangerous")] pub unsafe fn from_ref(value: &T) -> Self { Self(Union::SharedVariant( SharedVariantPtr(value as &dyn Variant as *const dyn Variant), @@ -1501,7 +1532,7 @@ impl Dynamic { reify! { value => |v: crate::Shared>| return v.into() } Self(Union::Variant( - OwnedVariant(Box::new(value)), + OwnedVariant::new(Box::new(value)), DEFAULT_TAG_VALUE, ReadWrite, )) @@ -1570,7 +1601,7 @@ impl Dynamic { #[inline(always)] #[must_use] #[allow(unused_mut)] - pub fn try_cast(mut self) -> Option { + pub fn try_cast(mut self) -> Option { self.try_cast_result().ok() } /// Convert the [`Dynamic`] value into specific type. @@ -1593,7 +1624,7 @@ impl Dynamic { /// /// These normally shouldn't occur since most operations in Rhai are single-threaded. #[allow(unused_mut)] - pub fn try_cast_result(mut self) -> Result { + pub fn try_cast_result(mut self) -> Result { // Coded this way in order to maximally leverage potentials for dead-code removal. #[cfg(not(feature = "no_closure"))] @@ -1691,7 +1722,7 @@ impl Dynamic { match self.0 { Union::Variant(v, ..) if TypeId::of::() == v.type_id() => { - Ok(v.0.as_boxed_any().downcast().map(|x| *x).unwrap()) + Ok(v.as_boxed_any().downcast().map(|x| *x).unwrap()) } _ => Err(self), } From 74779a66525adfed5c53464cc2ed1b986585141e Mon Sep 17 00:00:00 2001 From: Mikhail Razorvin Date: Tue, 16 Jul 2024 18:41:14 +0200 Subject: [PATCH 6/6] ref: de --- src/serde/de.rs | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/src/serde/de.rs b/src/serde/de.rs index 97224e677..07b07456b 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -163,31 +163,15 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { Union::TimeStamp(..) => self.type_error(), Union::Variant(ref value, ..) if value.is::() => self.deserialize_i8(visitor), - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_i16(visitor) - } - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_i32(visitor) - } - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_i64(visitor) - } - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_i128(visitor) - } + Union::Variant(ref value, ..) if value.is::() => self.deserialize_i16(visitor), + Union::Variant(ref value, ..) if value.is::() => self.deserialize_i32(visitor), + Union::Variant(ref value, ..) if value.is::() => self.deserialize_i64(visitor), + Union::Variant(ref value, ..) if value.is::() => self.deserialize_i128(visitor), Union::Variant(ref value, ..) if value.is::() => self.deserialize_u8(visitor), - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_u16(visitor) - } - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_u32(visitor) - } - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_u64(visitor) - } - Union::Variant(ref value, ..) if value.is::() => { - self.deserialize_u128(visitor) - } + Union::Variant(ref value, ..) if value.is::() => self.deserialize_u16(visitor), + Union::Variant(ref value, ..) if value.is::() => self.deserialize_u32(visitor), + Union::Variant(ref value, ..) if value.is::() => self.deserialize_u64(visitor), + Union::Variant(ref value, ..) if value.is::() => self.deserialize_u128(visitor), Union::Variant(..) => self.type_error(), Union::SharedVariant(..) => self.type_error(),