Skip to content

Commit

Permalink
Merge pull request #716 from schungx/master
Browse files Browse the repository at this point in the history
Dynamic deserialization.
  • Loading branch information
schungx committed Apr 29, 2023
2 parents aa47dd2 + ae59a33 commit c1d1072
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 339 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Enhancements
------------

* `Engine::is_symbol_disabled` is added to test whether a particular keyword/symbol is disabled.
* Support is added to deserialize a `Dynamic` value containing custom types or shared values back into another `Dynamic` (essentially a straight cloned copy).


Version 1.13.0
Expand Down
496 changes: 199 additions & 297 deletions src/func/call.rs

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions src/func/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::plugin::PluginFunction;
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
use crate::types::dynamic::Variant;
use crate::{
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf,
StaticVec, VarDefInfo, ERR,
calc_fn_hash, Dynamic, Engine, EvalContext, FnArgsVec, FuncArgs, Position, RhaiResult,
RhaiResultOf, StaticVec, VarDefInfo, ERR,
};
use std::any::{type_name, TypeId};
#[cfg(feature = "no_std")]
Expand Down Expand Up @@ -309,9 +309,9 @@ impl<'a> NativeCallContext<'a> {
let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values);

let mut args: StaticVec<_> = arg_values.iter_mut().collect();
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();

self._call_fn_raw(fn_name, &mut args, false, false, false)
self._call_fn_raw(fn_name, args, false, false, false)
.and_then(|result| {
// Bail out early if the return type needs no cast
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
Expand Down Expand Up @@ -340,9 +340,9 @@ impl<'a> NativeCallContext<'a> {
let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values);

let mut args: StaticVec<_> = arg_values.iter_mut().collect();
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();

self._call_fn_raw(fn_name, &mut args, true, false, false)
self._call_fn_raw(fn_name, args, true, false, false)
.and_then(|result| {
// Bail out early if the return type needs no cast
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
Expand Down
2 changes: 1 addition & 1 deletion src/func/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
}
if TypeId::of::<T>() == TypeId::of::<String>() {
// If T is `String`, data must be `ImmutableString`, so map directly to it
return reify! { data.take().into_string().expect("`ImmutableString`") => T };
return reify! { data.take().into_string().expect("`ImmutableString`") => !!! T };
}

// We consume the argument and then replace it with () - the argument is not supposed to be used again.
Expand Down
14 changes: 8 additions & 6 deletions src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ impl FuncInfo {
}
}
} else {
let params: FnArgsVec<_> = self
let params = self
.metadata
.params_info
.iter()
Expand All @@ -134,7 +134,7 @@ impl FuncInfo {
};
result
})
.collect();
.collect::<FnArgsVec<_>>();
signature.push_str(&params.join(", "));
}
signature.push(')');
Expand Down Expand Up @@ -932,7 +932,10 @@ impl Module {
hash_fn: u64,
arg_names: impl IntoIterator<Item = S>,
) -> &mut Self {
let mut param_names: FnArgsVec<_> = arg_names.into_iter().map(Into::into).collect();
let mut param_names = arg_names
.into_iter()
.map(Into::into)
.collect::<FnArgsVec<_>>();

if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
let (param_names, return_type_name) = if param_names.len() > f.metadata.num_params {
Expand Down Expand Up @@ -1053,13 +1056,12 @@ impl Module {
let _arg_names = arg_names;
let is_method = func.is_method();

let mut param_types: FnArgsVec<_> = arg_types
let param_types = arg_types
.as_ref()
.iter()
.enumerate()
.map(|(i, &type_id)| Self::map_type(!is_method || i > 0, type_id))
.collect();
param_types.shrink_to_fit();
.collect::<FnArgsVec<_>>();

let is_dynamic = param_types
.iter()
Expand Down
7 changes: 3 additions & 4 deletions src/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,12 +1195,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
&& x.constant_args() // all arguments are constants
=> {
// First search for script-defined functions (can override built-in)
let _has_script_fn = false;
#[cfg(not(feature = "no_function"))]
let has_script_fn = !x.hashes.is_native_only() && state.global.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some();
#[cfg(feature = "no_function")]
let has_script_fn = false;
let _has_script_fn = !x.hashes.is_native_only() && state.global.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some();

if !has_script_fn {
if !_has_script_fn {
let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<FnArgsVec<_>>>().unwrap();

let result = match x.name.as_str() {
Expand Down
12 changes: 6 additions & 6 deletions src/packages/string_more.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,18 +265,18 @@ mod string_functions {
///
/// print(text); // prints "hello, world!"
///
/// x.truncate(10);
/// text.truncate(10);
///
/// print(text); // prints "hello, world!"
/// ```
pub fn truncate(string: &mut ImmutableString, len: INT) {
if len > 0 {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let len = len.min(MAX_USIZE_INT) as usize;
let chars: StaticVec<_> = string.chars().collect();
let copy = string.make_mut();
copy.clear();
copy.extend(chars.into_iter().take(len));
if let Some((index, _)) = string.char_indices().nth(len) {
let copy = string.make_mut();
copy.truncate(index);
}
} else {
clear(string);
}
Expand Down Expand Up @@ -1109,7 +1109,7 @@ mod string_functions {
copy.clear();
copy.extend(chars.iter().skip(offset).take(len));
}
/// Remove all characters from the string except until the `start` position.
/// Remove all characters from the string up to the `start` position.
///
/// * If `start` < 0, position counts from the end of the string (`-1` is the last character).
/// * If `start` < -length of string, the string is not modified.
Expand Down
9 changes: 9 additions & 0 deletions src/reify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
/// * `reify! { `_variable_ or _expression_` => |`_temp-variable_`: `_type_`|` _code_ `)`
/// * `reify! { `_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
/// * `reify! { `_variable_ or _expression_ `=>` _type_ `)`
///
/// * `reify! { `_expression_ `=> !!!` _type_ `)` (unsafe, no type checks!)
macro_rules! reify {
($old:ident => |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
#[allow(clippy::redundant_else)]
Expand Down Expand Up @@ -45,4 +47,11 @@ macro_rules! reify {
($old:expr => $t:ty) => {
reify! { $old => |v: $t| v, || unreachable!() }
};

($old:expr => !!! $t:ty) => {{
let old_value = $old;
let new_value: $t =
unsafe { std::mem::transmute_copy(&std::mem::ManuallyDrop::new(old_value)) };
new_value
}};
}
4 changes: 4 additions & 0 deletions src/serde/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
type Error = RhaiError;

fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if type_name::<V::Value>() == type_name::<Dynamic>() {
return Ok(reify! { self.0.clone() => !!! V::Value });
}

match self.0 .0 {
Union::Unit(..) => self.deserialize_unit(visitor),
Union::Bool(..) => self.deserialize_bool(visitor),
Expand Down
2 changes: 1 addition & 1 deletion src/serde/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl ModuleMetadata<'_> {

impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> {
fn from(module: &'a crate::Module) -> Self {
let mut functions: StaticVec<_> = module.iter_fn().map(Into::into).collect();
let mut functions = module.iter_fn().map(Into::into).collect::<StaticVec<_>>();
functions.sort();

Self {
Expand Down
34 changes: 17 additions & 17 deletions src/types/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1184,89 +1184,89 @@ impl Dynamic {
self.flatten_in_place();

if TypeId::of::<T>() == TypeId::of::<Self>() {
return Some(reify! { self => T });
return Some(reify! { self => !!! T });
}
if TypeId::of::<T>() == TypeId::of::<()>() {
return match self.0 {
Union::Unit(..) => Some(reify! { () => T }),
Union::Unit(..) => Some(reify! { () => !!! T }),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<INT>() {
return match self.0 {
Union::Int(n, ..) => Some(reify! { n => T }),
Union::Int(n, ..) => Some(reify! { n => !!! T }),
_ => None,
};
}
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
return match self.0 {
Union::Float(v, ..) => Some(reify! { *v => T }),
Union::Float(v, ..) => Some(reify! { *v => !!! T }),
_ => None,
};
}
#[cfg(feature = "decimal")]
if TypeId::of::<T>() == TypeId::of::<rust_decimal::Decimal>() {
return match self.0 {
Union::Decimal(v, ..) => Some(reify! { *v => T }),
Union::Decimal(v, ..) => Some(reify! { *v => !!! T }),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<bool>() {
return match self.0 {
Union::Bool(b, ..) => Some(reify! { b => T }),
Union::Bool(b, ..) => Some(reify! { b => !!! T }),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return match self.0 {
Union::Str(s, ..) => Some(reify! { s => T }),
Union::Str(s, ..) => Some(reify! { s => !!! T }),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<String>() {
return match self.0 {
Union::Str(s, ..) => Some(reify! { s.to_string() => T }),
Union::Str(s, ..) => Some(reify! { s.to_string() => !!! T }),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<char>() {
return match self.0 {
Union::Char(c, ..) => Some(reify! { c => T }),
Union::Char(c, ..) => Some(reify! { c => !!! T }),
_ => None,
};
}
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
return match self.0 {
Union::Array(a, ..) => Some(reify! { *a => T }),
Union::Array(a, ..) => Some(reify! { *a => !!! T }),
_ => None,
};
}
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<crate::Blob>() {
return match self.0 {
Union::Blob(b, ..) => Some(reify! { *b => T }),
Union::Blob(b, ..) => Some(reify! { *b => !!! T }),
_ => None,
};
}
#[cfg(not(feature = "no_object"))]
if TypeId::of::<T>() == TypeId::of::<crate::Map>() {
return match self.0 {
Union::Map(m, ..) => Some(reify! { *m => T }),
Union::Map(m, ..) => Some(reify! { *m => !!! T }),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
return match self.0 {
Union::FnPtr(f, ..) => Some(reify! { *f => T }),
Union::FnPtr(f, ..) => Some(reify! { *f => !!! T }),
_ => None,
};
}
#[cfg(not(feature = "no_time"))]
if TypeId::of::<T>() == TypeId::of::<Instant>() {
return match self.0 {
Union::TimeStamp(t, ..) => Some(reify! { *t => T }),
Union::TimeStamp(t, ..) => Some(reify! { *t => !!! T }),
_ => None,
};
}
Expand Down Expand Up @@ -1306,7 +1306,7 @@ impl Dynamic {
pub fn cast<T: Any + Clone>(self) -> T {
// Bail out early if the return type needs no cast
if TypeId::of::<T>() == TypeId::of::<Self>() {
return reify! { self => T };
return reify! { self => !!! T };
}

#[cfg(not(feature = "no_closure"))]
Expand Down Expand Up @@ -2038,7 +2038,7 @@ impl Dynamic {
})
.collect(),
Union::Blob(b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
Ok(reify! { *b => Vec<T> })
Ok(reify! { *b => !!! Vec<T> })
}
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => {
Expand All @@ -2061,7 +2061,7 @@ impl Dynamic {
.collect()
}
Union::Blob(ref b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
Ok(reify! { b.clone() => Vec<T> })
Ok(reify! { b.clone() => !!! Vec<T> })
}
_ => Err(cell.type_name()),
}
Expand Down
34 changes: 33 additions & 1 deletion tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use rhai::{
serde::{from_dynamic, to_dynamic},
Dynamic, Engine, EvalAltResult, ImmutableString, Scope, INT,
};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::json;
use std::sync::Arc;

#[cfg(not(feature = "no_index"))]
use rhai::Array;
Expand Down Expand Up @@ -383,6 +384,37 @@ fn test_serde_de_primary_types() -> Result<(), Box<EvalAltResult>> {
Ok(())
}

#[cfg(not(feature = "no_object"))]
#[test]
fn test_serde_de_variants() -> Result<(), Box<EvalAltResult>> {
#[derive(Debug)]
struct Foo;

#[derive(Debug, Deserialize)]
struct Bar {
#[serde(deserialize_with = "deserialize_foo")]
value: Arc<Foo>,
}

fn deserialize_foo<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Arc<Foo>, D::Error> {
let value = <Dynamic as Deserialize>::deserialize(deserializer)?;

value
.try_cast::<Arc<Foo>>()
.ok_or_else(|| serde::de::Error::custom("type error"))
}

let value = Arc::new(Foo);
let mut map = Map::new();
map.insert("value".into(), Dynamic::from(value.clone()));
let x = Dynamic::from(map);
let bar = from_dynamic::<Bar>(&x)?;

assert!(Arc::ptr_eq(&bar.value, &value));

Ok(())
}

#[test]
fn test_serde_de_integer_types() -> Result<(), Box<EvalAltResult>> {
assert_eq!(42, from_dynamic::<i8>(&Dynamic::from(42 as INT))?);
Expand Down

0 comments on commit c1d1072

Please sign in to comment.