From e343641d70271e2c22c74f355ad0d16ce8eb3fe6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Jul 2024 10:45:01 +0800 Subject: [PATCH 1/4] New as_XXX_ref API for Dynamic --- CHANGELOG.md | 1 + src/api/json.rs | 2 +- src/bin/rhai-dbg.rs | 4 +- src/func/builtin.rs | 53 ++++---- src/packages/array_basic.rs | 4 +- src/serde/de.rs | 6 +- src/serde/ser.rs | 2 +- src/types/dynamic.rs | 257 ++++++++++++++++++++++++++++++------ src/types/error.rs | 5 +- tests/debugging.rs | 2 +- tests/parse_json.rs | 4 +- 11 files changed, 256 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4e59c686..b585bdacf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Bug fixes Enhancements ------------ +* New `as_immutable_string_ref`, `as_array_ref`, `as_blob_ref`, `as_map_ref` plus their `_mut` variants for `Dynamic`. * The `break`, `return` and `throw` statements can now be simply used as `switch` case statement expressions. Previously it is required that the statement be wrapped in a block. diff --git a/src/api/json.rs b/src/api/json.rs index 2aa8740fe..b50749dc5 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -52,7 +52,7 @@ impl Engine { /// assert_eq!(map["b"].as_int().expect("b should exist"), 42); /// assert_eq!(map["d"].as_unit().expect("d should exist"), ()); /// - /// let c = map["c"].read_lock::().expect("c should exist"); + /// let c = map["c"].as_map().expect("c should exist"); /// assert_eq!(c["x"].as_bool().expect("x should be bool"), false); /// assert_eq!(c["y"].as_bool().expect("y should be bool"), true); /// assert_eq!(c["z"].as_char().expect("z should be char"), '$'); diff --git a/src/bin/rhai-dbg.rs b/src/bin/rhai-dbg.rs index 72ffe8eab..69ad15837 100644 --- a/src/bin/rhai-dbg.rs +++ b/src/bin/rhai-dbg.rs @@ -1,5 +1,5 @@ use rhai::debugger::{BreakPoint, DebuggerCommand, DebuggerEvent}; -use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString, Position, Scope, INT}; +use rhai::{Dynamic, Engine, EvalAltResult, Position, Scope, INT}; use std::{ env, @@ -63,7 +63,7 @@ fn print_current_source( .global_runtime_state_mut() .debugger_mut() .state_mut() - .write_lock::() + .as_immutable_string_ref_mut() .unwrap(); let src = source.unwrap_or(""); if src != current_source { diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 80cebac33..061eb6e77 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -236,8 +236,8 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< return match op { Plus => Some(( |_ctx, args| { - let s1 = &*args[0].read_lock::().unwrap(); - let s2 = &*args[1].read_lock::().unwrap(); + let s1 = &*args[0].as_immutable_string_ref().unwrap(); + let s2 = &*args[1].as_immutable_string_ref().unwrap(); #[cfg(not(feature = "unchecked"))] _ctx.unwrap() @@ -294,11 +294,11 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< return match op { Plus => Some(( |_ctx, args| { - let b2 = &*args[1].read_lock::().unwrap(); + let b2 = &*args[1].as_blob_ref().unwrap(); if b2.is_empty() { return Ok(args[0].flatten_clone()); } - let b1 = &*args[0].read_lock::().unwrap(); + let b1 = &*args[0].as_blob_ref().unwrap(); if b1.is_empty() { return Ok(args[1].flatten_clone()); } @@ -475,7 +475,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if (type1, type2) == (TypeId::of::(), TypeId::of::()) { fn get_s1s2(args: &FnCallArgs) -> ([Option; 2], [Option; 2]) { let x = args[0].as_char().unwrap(); - let y = &*args[1].read_lock::().unwrap(); + let y = &*args[1].as_immutable_string_ref().unwrap(); let s1 = [Some(x), None]; let mut y = y.chars(); let s2 = [y.next(), y.next()]; @@ -486,7 +486,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< Plus => Some(( |_ctx, args| { let x = args[0].as_char().unwrap(); - let y = &*args[1].read_lock::().unwrap(); + let y = &*args[1].as_immutable_string_ref().unwrap(); let mut result = SmartString::new_const(); result.push(x); @@ -511,7 +511,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< // string op char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { fn get_s1s2(args: &FnCallArgs) -> ([Option; 2], [Option; 2]) { - let x = &*args[0].read_lock::().unwrap(); + let x = &*args[0].as_immutable_string_ref().unwrap(); let y = args[1].as_char().unwrap(); let mut x = x.chars(); let s1 = [x.next(), x.next()]; @@ -522,7 +522,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< return match op { Plus => Some(( |_ctx, args| { - let x = &*args[0].read_lock::().unwrap(); + let x = &*args[0].as_immutable_string_ref().unwrap(); let y = args[1].as_char().unwrap(); let result = x + y; @@ -535,7 +535,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< )), Minus => Some(( |_, args| { - let x = &*args[0].read_lock::().unwrap(); + let x = &*args[0].as_immutable_string_ref().unwrap(); let y = args[1].as_char().unwrap(); Ok((x - y).into()) }, @@ -576,13 +576,11 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< // blob #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { - use crate::Blob; - if type2 == TypeId::of::() { return match op { Plus => Some(( |_ctx, args| { - let mut blob = args[0].read_lock::().unwrap().clone(); + let mut blob = args[0].as_blob_ref().unwrap().clone(); let mut buf = [0_u8; 4]; let x = args[1].as_char().unwrap().encode_utf8(&mut buf); @@ -794,8 +792,8 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt PlusAssign => Some(( |_ctx, args| { let (first, second) = args.split_first_mut().unwrap(); - let x = &mut *first.write_lock::().unwrap(); - let y = &*second[0].read_lock::().unwrap(); + let x = &mut *first.as_immutable_string_ref_mut().unwrap(); + let y = &*second[0].as_immutable_string_ref().unwrap(); #[cfg(not(feature = "unchecked"))] if !x.is_empty() && !y.is_empty() { @@ -812,8 +810,8 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt MinusAssign => Some(( |_, args| { let (first, second) = args.split_first_mut().unwrap(); - let x = &mut *first.write_lock::().unwrap(); - let y = &*second[0].read_lock::().unwrap(); + let x = &mut *first.as_immutable_string_ref_mut().unwrap(); + let y = &*second[0].as_immutable_string_ref().unwrap(); *x -= y; Ok(Dynamic::UNIT) }, @@ -827,7 +825,6 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { #[allow(clippy::wildcard_imports)] use crate::packages::array_basic::array_functions::*; - use crate::Array; return match op { PlusAssign => Some(( @@ -839,14 +836,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt } #[cfg(not(feature = "unchecked"))] - if !args[0].read_lock::().unwrap().is_empty() { + if !args[0].as_array_ref().unwrap().is_empty() { _ctx.unwrap().engine().check_data_size( &*args[0].read_lock().unwrap(), crate::Position::NONE, )?; } - let array = &mut *args[0].write_lock::().unwrap(); + let array = &mut *args[0].as_array_ref_mut().unwrap(); append(array, x); @@ -862,13 +859,12 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { #[allow(clippy::wildcard_imports)] use crate::packages::blob_basic::blob_functions::*; - use crate::Blob; return match op { PlusAssign => Some(( |_ctx, args| { let blob2 = args[1].take().into_blob().unwrap(); - let blob1 = &mut *args[0].write_lock::().unwrap(); + let blob1 = &mut *args[0].as_blob_ref_mut().unwrap(); #[cfg(not(feature = "unchecked"))] _ctx.unwrap() @@ -958,7 +954,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt |_ctx, args| { let mut buf = [0_u8; 4]; let ch = &*args[1].as_char().unwrap().encode_utf8(&mut buf); - let mut x = args[0].write_lock::().unwrap(); + let mut x = args[0].as_immutable_string_ref_mut().unwrap(); #[cfg(not(feature = "unchecked"))] _ctx.unwrap() @@ -981,7 +977,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt PlusAssign => Some(( |_ctx, args| { let ch = { - let s = &*args[1].read_lock::().unwrap(); + let s = &*args[1].as_immutable_string_ref().unwrap(); if s.is_empty() { return Ok(Dynamic::UNIT); @@ -1013,14 +1009,13 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { #[allow(clippy::wildcard_imports)] use crate::packages::array_basic::array_functions::*; - use crate::Array; return match op { PlusAssign => Some(( |_ctx, args| { { let x = args[1].take(); - let array = &mut *args[0].write_lock::().unwrap(); + let array = &mut *args[0].as_array_ref_mut().unwrap(); push(array, x); } @@ -1050,7 +1045,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt PlusAssign => Some(( |_ctx, args| { let x = args[1].as_int().unwrap(); - let blob = &mut *args[0].write_lock::().unwrap(); + let blob = &mut *args[0].as_blob_ref_mut().unwrap(); #[cfg(not(feature = "unchecked"))] _ctx.unwrap() @@ -1076,7 +1071,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt PlusAssign => Some(( |_ctx, args| { let x = args[1].as_char().unwrap(); - let blob = &mut *args[0].write_lock::().unwrap(); + let blob = &mut *args[0].as_blob_ref_mut().unwrap(); #[cfg(not(feature = "unchecked"))] _ctx.unwrap() @@ -1102,8 +1097,8 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt PlusAssign => Some(( |_ctx, args| { let (first, second) = args.split_first_mut().unwrap(); - let blob = &mut *first.write_lock::().unwrap(); - let s = &*second[0].read_lock::().unwrap(); + let blob = &mut *first.as_blob_ref_mut().unwrap(); + let s = &*second[0].as_immutable_string_ref().unwrap(); if s.is_empty() { return Ok(Dynamic::UNIT); diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 88deb3941..f1606d85f 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -1638,8 +1638,8 @@ pub mod array_functions { } if type_id == TypeId::of::() { array.sort_by(|a, b| { - let a = &*a.read_lock::().unwrap(); - let b = &*b.read_lock::().unwrap(); + let a = &*a.as_immutable_string_ref().unwrap(); + let b = &*b.as_immutable_string_ref().unwrap(); a.cmp(b) }); return Ok(()); diff --git a/src/serde/de.rs b/src/serde/de.rs index dffe45653..edae25461 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -470,9 +470,9 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> { _variants: &'static [&'static str], visitor: V, ) -> RhaiResultOf { - match self.0.read_lock::() { - Some(s) => visitor.visit_enum(s.into_deserializer()), - None => { + match self.0.as_immutable_string_ref() { + Ok(s) => visitor.visit_enum(s.into_deserializer()), + Err(_) => { #[cfg(not(feature = "no_object"))] return self.0.downcast_ref::().map_or_else( || self.type_error(), diff --git a/src/serde/ser.rs b/src/serde/ser.rs index c4030dbde..a0d7098b2 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -73,7 +73,7 @@ impl DynamicSerializer { /// assert!(value.is::()); /// /// let map = value.cast::(); -/// let point = map["d"].read_lock::().unwrap(); +/// let point = map["d"].as_map().unwrap(); /// assert_eq!(*point["x"].read_lock::().unwrap(), 123.456); /// assert_eq!(*point["y"].read_lock::().unwrap(), 999.0); /// # } diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 1a94dcbe0..d0bf775bb 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -22,6 +22,12 @@ pub use std::time::Instant; #[cfg(all(target_family = "wasm", target_os = "unknown"))] pub use instant::Instant; +#[cfg(not(feature = "no_index"))] +use crate::{Array, Blob}; + +#[cfg(not(feature = "no_object"))] +use crate::Map; + /// _(internals)_ Modes of access. /// Exported under the `internals` feature only. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] @@ -72,13 +78,13 @@ pub enum Union { Decimal(Box, Tag, AccessMode), /// An array value. #[cfg(not(feature = "no_index"))] - Array(Box, Tag, AccessMode), + Array(Box, Tag, AccessMode), /// An blob (byte array). #[cfg(not(feature = "no_index"))] - Blob(Box, Tag, AccessMode), + Blob(Box, Tag, AccessMode), /// An object map value. #[cfg(not(feature = "no_object"))] - Map(Box, Tag, AccessMode), + Map(Box, Tag, AccessMode), /// A function pointer. FnPtr(Box, Tag, AccessMode), /// A timestamp value. @@ -280,15 +286,15 @@ impl Dynamic { return matches!(self.0, Union::Str(..)); } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::Array(..)); } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::Blob(..)); } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return matches!(self.0, Union::Map(..)); } #[cfg(feature = "decimal")] @@ -324,11 +330,11 @@ impl Dynamic { #[cfg(feature = "decimal")] Union::Decimal(..) => TypeId::of::(), #[cfg(not(feature = "no_index"))] - Union::Array(..) => TypeId::of::(), + Union::Array(..) => TypeId::of::(), #[cfg(not(feature = "no_index"))] - Union::Blob(..) => TypeId::of::(), + Union::Blob(..) => TypeId::of::(), #[cfg(not(feature = "no_object"))] - Union::Map(..) => TypeId::of::(), + Union::Map(..) => TypeId::of::(), Union::FnPtr(..) => TypeId::of::(), #[cfg(not(feature = "no_time"))] Union::TimeStamp(..) => TypeId::of::(), @@ -1045,22 +1051,22 @@ impl Dynamic { pub fn from_decimal(value: rust_decimal::Decimal) -> Self { Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } - /// Create a [`Dynamic`] from an [`Array`][crate::Array]. + /// Create a [`Dynamic`] from an [`Array`]. #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn from_array(array: crate::Array) -> Self { + pub fn from_array(array: Array) -> Self { Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite)) } - /// Create a [`Dynamic`] from a [`Blob`][crate::Blob]. + /// Create a [`Dynamic`] from a [`Blob`]. #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn from_blob(blob: crate::Blob) -> Self { + pub fn from_blob(blob: Blob) -> Self { Self(Union::Blob(blob.into(), DEFAULT_TAG_VALUE, ReadWrite)) } - /// Create a [`Dynamic`] from a [`Map`][crate::Map]. + /// Create a [`Dynamic`] from a [`Map`]. #[cfg(not(feature = "no_object"))] #[inline(always)] - pub fn from_map(map: crate::Map) -> Self { + pub fn from_map(map: Map) -> Self { Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite)) } /// Create a new [`Dynamic`] from an [`Instant`]. @@ -1306,20 +1312,20 @@ impl Dynamic { /// /// # Arrays /// - /// Beware that you need to pass in an [`Array`][crate::Array] type for it to be recognized as - /// an [`Array`][crate::Array]. A [`Vec`][Vec] does not get automatically converted to an - /// [`Array`][crate::Array], but will be a custom type instead (stored as a trait object). + /// Beware that you need to pass in an [`Array`] type for it to be recognized as + /// an [`Array`]. A [`Vec`][Vec] does not get automatically converted to an + /// [`Array`], but will be a custom type instead (stored as a trait object). /// /// Use `array.into()` or `array.into_iter()` to convert a [`Vec`][Vec] into a [`Dynamic`] as - /// an [`Array`][crate::Array] value. See the examples for details. + /// an [`Array`] value. See the examples for details. /// /// # Hash Maps /// /// Similarly, passing in a [`HashMap`][std::collections::HashMap] or - /// [`BTreeMap`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] but a + /// [`BTreeMap`][std::collections::BTreeMap] will not get a [`Map`] but a /// custom type. /// - /// Again, use `map.into()` to get a [`Dynamic`] with a [`Map`][crate::Map] value. + /// Again, use `map.into()` to get a [`Dynamic`] with a [`Map`] value. /// See the examples for details. /// /// # Examples @@ -1387,12 +1393,12 @@ impl Dynamic { reify! { value => |v: ()| return v.into() } #[cfg(not(feature = "no_index"))] - reify! { value => |v: crate::Array| return v.into() } + reify! { value => |v: Array| return v.into() } #[cfg(not(feature = "no_index"))] // don't use blob.into() because it'll be converted into an Array - reify! { value => |v: crate::Blob| return Self::from_blob(v) } + reify! { value => |v: Blob| return Self::from_blob(v) } #[cfg(not(feature = "no_object"))] - reify! { value => |v: crate::Map| return v.into() } + reify! { value => |v: Map| return v.into() } reify! { value => |v: FnPtr| return v.into() } #[cfg(not(feature = "no_time"))] @@ -1555,21 +1561,21 @@ impl Dynamic { }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Array(a, ..) => Ok(reify! { *a => !!! T }), _ => Err(self), }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Blob(b, ..) => Ok(reify! { *b => !!! T }), _ => Err(self), }; } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Map(m, ..) => Ok(reify! { *m => !!! T }), _ => Err(self), @@ -1895,21 +1901,21 @@ impl Dynamic { }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Array(ref v, ..) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Blob(ref v, ..) => v.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Map(ref v, ..) => v.as_ref().as_any().downcast_ref::(), _ => None, @@ -1998,21 +2004,21 @@ impl Dynamic { }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Array(ref mut v, ..) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Blob(ref mut v, ..) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Map(ref mut v, ..) => v.as_mut().as_any_mut().downcast_mut::(), _ => None, @@ -2208,7 +2214,7 @@ impl Dynamic { _ => false, } } - /// Return `true` if the [`Dynamic`] holds an [`Array`][crate::Array]. + /// Return `true` if the [`Dynamic`] holds an [`Array`]. /// /// Not available under `no_index`. /// @@ -2233,7 +2239,7 @@ impl Dynamic { _ => false, } } - /// Return `true` if the [`Dynamic`] holds a [`Blob`][crate::Blob]. + /// Return `true` if the [`Dynamic`] holds a [`Blob`]. /// /// Not available under `no_index`. /// @@ -2258,7 +2264,7 @@ impl Dynamic { _ => false, } } - /// Return `true` if the [`Dynamic`] holds a [`Map`][crate::Map]. + /// Return `true` if the [`Dynamic`] holds a [`Map`]. /// /// Not available under `no_object`. /// @@ -2503,6 +2509,177 @@ impl Dynamic { _ => Err(self.type_name()), } } + /// Cast the [`Dynamic`] as an [`ImmutableString`]. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[inline] + pub fn as_immutable_string_ref( + &self, + ) -> Result + '_, &'static str> { + self.read_lock::() + .ok_or_else(|| self.type_name()) + } + /// Cast the [`Dynamic`] as a mutable reference to an [`ImmutableString`]. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[inline] + pub fn as_immutable_string_ref_mut( + &mut self, + ) -> Result + '_, &'static str> { + let type_name = self.type_name(); + self.write_lock::().ok_or(type_name) + } + /// Cast the [`Dynamic`] as an [`Array`]. + /// + /// Not available under `no_index`. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[cfg(not(feature = "no_index"))] + #[inline(always)] + pub fn as_array_ref(&self) -> Result + '_, &'static str> { + self.read_lock::().ok_or_else(|| self.type_name()) + } + /// Cast the [`Dynamic`] as a mutable reference to an [`Array`]. + /// + /// Not available under `no_index`. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[cfg(not(feature = "no_index"))] + #[inline(always)] + pub fn as_array_ref_mut(&mut self) -> Result + '_, &'static str> { + let type_name = self.type_name(); + self.write_lock::().ok_or(type_name) + } + /// Cast the [`Dynamic`] as a [`Blob`]. + /// + /// Not available under `no_index`. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[cfg(not(feature = "no_index"))] + #[inline(always)] + pub fn as_blob_ref(&self) -> Result + '_, &'static str> { + self.read_lock::().ok_or_else(|| self.type_name()) + } + /// Cast the [`Dynamic`] as a mutable reference to a [`Blob`]. + /// + /// Not available under `no_index`. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[cfg(not(feature = "no_index"))] + #[inline(always)] + pub fn as_blob_ref_mut(&mut self) -> Result + '_, &'static str> { + let type_name = self.type_name(); + self.write_lock::().ok_or(type_name) + } + /// Cast the [`Dynamic`] as a [`Map`]. + /// + /// Not available under `no_object`. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[cfg(not(feature = "no_object"))] + #[inline(always)] + pub fn as_map_ref(&self) -> Result + '_, &'static str> { + self.read_lock::().ok_or_else(|| self.type_name()) + } + /// Cast the [`Dynamic`] as a mutable reference to a [`Map`]. + /// + /// Not available under `no_object`. + /// + /// # Errors + /// + /// Returns the name of the actual type as an error if the cast fails. + /// + /// # Shared Value + /// + /// Under the `sync` feature, a _shared_ value may deadlock. + /// Otherwise, the data may currently be borrowed for write (so its type cannot be determined). + /// + /// Under these circumstances, the cast also fails. + /// + /// These normally shouldn't occur since most operations in Rhai are single-threaded. + #[cfg(not(feature = "no_object"))] + #[inline(always)] + pub fn as_map_ref_mut(&mut self) -> Result + '_, &'static str> { + let type_name = self.type_name(); + self.write_lock::().ok_or(type_name) + } /// Convert the [`Dynamic`] into a [`String`]. /// /// If there are other references to the same string, a cloned copy is returned. @@ -2552,7 +2729,7 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Convert the [`Dynamic`] into an [`Array`][crate::Array]. + /// Convert the [`Dynamic`] into an [`Array`]. /// /// Not available under `no_index`. /// @@ -2570,7 +2747,7 @@ impl Dynamic { /// These normally shouldn't occur since most operations in Rhai are single-threaded. #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn into_array(self) -> Result { + pub fn into_array(self) -> Result { match self.0 { Union::Array(a, ..) => Ok(*a), #[cfg(not(feature = "no_closure"))] @@ -2638,7 +2815,7 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Convert the [`Dynamic`] into a [`Blob`][crate::Blob]. + /// Convert the [`Dynamic`] into a [`Blob`]. /// /// Not available under `no_index`. /// @@ -2656,7 +2833,7 @@ impl Dynamic { /// These normally shouldn't occur since most operations in Rhai are single-threaded. #[cfg(not(feature = "no_index"))] #[inline(always)] - pub fn into_blob(self) -> Result { + pub fn into_blob(self) -> Result { match self.0 { Union::Blob(b, ..) => Ok(*b), #[cfg(not(feature = "no_closure"))] diff --git a/src/types/error.rs b/src/types/error.rs index 9ae277185..330d432af 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -1,6 +1,6 @@ //! Module containing error definitions for the evaluation process. -use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT}; +use crate::{Dynamic, ParseErrorType, Position, INT}; #[cfg(feature = "no_std")] use core_error::Error; #[cfg(not(feature = "no_std"))] @@ -179,8 +179,7 @@ impl fmt::Display for EvalAltResult { Self::ErrorRuntime(d, ..) if d.is_unit() => f.write_str("Runtime error")?, Self::ErrorRuntime(d, ..) - if d.read_lock::() - .map_or(false, |v| v.is_empty()) => + if d.as_immutable_string_ref().map_or(false, |v| v.is_empty()) => { write!(f, "Runtime error")? } diff --git a/tests/debugging.rs b/tests/debugging.rs index 811666e8f..67bd5bf9e 100644 --- a/tests/debugging.rs +++ b/tests/debugging.rs @@ -58,7 +58,7 @@ fn test_debugger_state() { println!("Current state = {}", context.global_runtime_state().debugger().state()); // Modify state - let mut state = context.global_runtime_state_mut().debugger_mut().state_mut().write_lock::().unwrap(); + let mut state = context.global_runtime_state_mut().debugger_mut().state_mut().as_map_ref_mut().unwrap(); let hello = state.get("hello").unwrap().as_int().unwrap(); state.insert("hello".into(), (hello + 1).into()); state.insert("foo".into(), true.into()); diff --git a/tests/parse_json.rs b/tests/parse_json.rs index 911c39282..5786e26aa 100644 --- a/tests/parse_json.rs +++ b/tests/parse_json.rs @@ -40,7 +40,7 @@ mod without_metadata { assert_eq!(map["age"].as_int().expect("age should exist"), 43); assert_eq!(map["phones"].clone().into_typed_array::().expect("phones should exist"), ["+44 1234567", "+44 2345678"]); - let address = map["address"].read_lock::().expect("address should exist"); + let address = map["address"].as_map().expect("address should exist"); assert_eq!(address["city"].clone().into_immutable_string().expect("address.city should exist"), "London"); assert_eq!(address["street"].clone().into_immutable_string().expect("address.street should exist"), "10 Downing Street"); } @@ -135,7 +135,7 @@ mod with_metadata { assert_eq!(map["age"].as_int().expect("age should exist"), 43); assert_eq!(map["phones"].clone().into_typed_array::().expect("phones should exist"), ["+44 1234567", "+44 2345678"]); - let address = map["address"].read_lock::().expect("address should exist"); + let address = map["address"].as_map_ref().expect("address should exist"); assert_eq!(address["city"].clone().into_immutable_string().expect("address.city should exist"), "London"); assert_eq!(address["street"].clone().into_immutable_string().expect("address.street should exist"), "10 Downing Street"); } From 4b8b3e3106de01b8d9fd66f3d513a244f6a7d4cd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Jul 2024 10:53:52 +0800 Subject: [PATCH 2/4] Fix tests. --- src/api/json.rs | 2 +- src/serde/ser.rs | 2 +- tests/parse_json.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/json.rs b/src/api/json.rs index b50749dc5..30f8d7f56 100644 --- a/src/api/json.rs +++ b/src/api/json.rs @@ -52,7 +52,7 @@ impl Engine { /// assert_eq!(map["b"].as_int().expect("b should exist"), 42); /// assert_eq!(map["d"].as_unit().expect("d should exist"), ()); /// - /// let c = map["c"].as_map().expect("c should exist"); + /// let c = map["c"].as_map_ref().expect("c should exist"); /// assert_eq!(c["x"].as_bool().expect("x should be bool"), false); /// assert_eq!(c["y"].as_bool().expect("y should be bool"), true); /// assert_eq!(c["z"].as_char().expect("z should be char"), '$'); diff --git a/src/serde/ser.rs b/src/serde/ser.rs index a0d7098b2..d314165fe 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -73,7 +73,7 @@ impl DynamicSerializer { /// assert!(value.is::()); /// /// let map = value.cast::(); -/// let point = map["d"].as_map().unwrap(); +/// let point = map["d"].as_map_ref().unwrap(); /// assert_eq!(*point["x"].read_lock::().unwrap(), 123.456); /// assert_eq!(*point["y"].read_lock::().unwrap(), 999.0); /// # } diff --git a/tests/parse_json.rs b/tests/parse_json.rs index 5786e26aa..08b1e7eaf 100644 --- a/tests/parse_json.rs +++ b/tests/parse_json.rs @@ -40,7 +40,7 @@ mod without_metadata { assert_eq!(map["age"].as_int().expect("age should exist"), 43); assert_eq!(map["phones"].clone().into_typed_array::().expect("phones should exist"), ["+44 1234567", "+44 2345678"]); - let address = map["address"].as_map().expect("address should exist"); + let address = map["address"].as_map_ref().expect("address should exist"); assert_eq!(address["city"].clone().into_immutable_string().expect("address.city should exist"), "London"); assert_eq!(address["street"].clone().into_immutable_string().expect("address.street should exist"), "10 Downing Street"); } From 19b8c5327b4a40f7db0d20c61b8b4640fc86d62c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Jul 2024 11:48:24 +0800 Subject: [PATCH 3/4] Fix test output for new compiler version. --- codegen/src/function.rs | 6 +----- codegen/src/module.rs | 4 ---- codegen/ui_tests/export_fn_cfg.stderr | 11 +++++++++++ codegen/ui_tests/rhai_mod_inner_cfg_false.stderr | 11 +++++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index e216c0d04..b3bfde58b 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -5,12 +5,8 @@ use syn::{ spanned::Spanned, }; -#[cfg(no_std)] -use alloc::format; -#[cfg(not(no_std))] -use std::format; - use std::borrow::Cow; +use std::format; use crate::attrs::{ExportInfo, ExportScope, ExportedParams}; diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 92dda1e0f..cd7e2b795 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -1,11 +1,7 @@ use quote::{quote, ToTokens}; use syn::{parse::Parse, parse::ParseStream}; -#[cfg(no_std)] -use core::mem; -#[cfg(not(no_std))] use std::mem; - use std::borrow::Cow; use crate::attrs::{AttrItem, ExportInfo, ExportScope, ExportedParams}; diff --git a/codegen/ui_tests/export_fn_cfg.stderr b/codegen/ui_tests/export_fn_cfg.stderr index 498c95d4f..2dc96ff23 100644 --- a/codegen/ui_tests/export_fn_cfg.stderr +++ b/codegen/ui_tests/export_fn_cfg.stderr @@ -4,6 +4,17 @@ error: `cfg` attributes are not allowed for `export_fn` 10 | #[cfg(not(feature = "foo"))] | ^ +warning: unexpected `cfg` condition value: `foo` + --> ui_tests/export_fn_cfg.rs:10:11 + | +10 | #[cfg(not(feature = "foo"))] + | ^^^^^^^^^^^^^^^ + | + = note: expected values for `feature` are: `default` and `metadata` + = help: consider adding `foo` as a feature in `Cargo.toml` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + error[E0425]: cannot find function `test_fn` in this scope --> ui_tests/export_fn_cfg.rs:18:8 | diff --git a/codegen/ui_tests/rhai_mod_inner_cfg_false.stderr b/codegen/ui_tests/rhai_mod_inner_cfg_false.stderr index 2c6d5659e..8fb2f445f 100644 --- a/codegen/ui_tests/rhai_mod_inner_cfg_false.stderr +++ b/codegen/ui_tests/rhai_mod_inner_cfg_false.stderr @@ -10,3 +10,14 @@ note: found an item that was configured out 12 | pub mod test_mod { | ^^^^^^^^ = note: the item is gated behind the `unset_feature` feature + +warning: unexpected `cfg` condition value: `unset_feature` + --> ui_tests/rhai_mod_inner_cfg_false.rs:11:11 + | +11 | #[cfg(feature = "unset_feature")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected values for `feature` are: `default` and `metadata` + = help: consider adding `unset_feature` as a feature in `Cargo.toml` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default From a6e1ba188e6175fe27792bbe2debd07dcb45d8ba Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 26 Jul 2024 12:05:35 +0800 Subject: [PATCH 4/4] Fix formatting --- codegen/src/module.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index cd7e2b795..b31f98289 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -1,8 +1,8 @@ use quote::{quote, ToTokens}; use syn::{parse::Parse, parse::ParseStream}; -use std::mem; use std::borrow::Cow; +use std::mem; use crate::attrs::{AttrItem, ExportInfo, ExportScope, ExportedParams}; use crate::function::ExportedFn;