Merge pull request #370 from schungx/master
Fix shared string operators bug.
This commit is contained in:
commit
c3301d079f
@ -16,6 +16,7 @@ Bug fixes
|
|||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
* `Dynamic::as_str` is removed because it does not properly handle shared values.
|
||||||
* Zero step in the `range` function now raises an error instead of creating an infinite stream.
|
* Zero step in the `range` function now raises an error instead of creating an infinite stream.
|
||||||
* Error variable captured by `catch` is now an _object map_ containing error fields.
|
* Error variable captured by `catch` is now an _object map_ containing error fields.
|
||||||
* `EvalAltResult::clear_position` is renamed `EvalAltResult::take_position` and returns the position taken.
|
* `EvalAltResult::clear_position` is renamed `EvalAltResult::take_position` and returns the position taken.
|
||||||
@ -34,6 +35,7 @@ Enhancements
|
|||||||
* `range` function now supports negative step and decreasing streams (i.e. to < from).
|
* `range` function now supports negative step and decreasing streams (i.e. to < from).
|
||||||
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
* More information is provided to the error variable captured by the `catch` statement in an _object map_.
|
||||||
* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported.
|
* Previously, `private` functions in an `AST` cannot be called with `call_fn` etc. This is inconvenient when trying to call a function inside a script which also serves as a loadable module exporting part (but not all) of the functions. Now, all functions (`private` or not) can be called in an `AST`. The `private` keyword is relegated to preventing a function from being exported.
|
||||||
|
* `Dynamic::as_unit` just for completeness sake.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.13
|
Version 0.19.13
|
||||||
|
141
src/dynamic.rs
141
src/dynamic.rs
@ -43,7 +43,10 @@ mod private {
|
|||||||
impl<T: Any + Clone + SendSync> Sealed for T {}
|
impl<T: Any + Clone + SendSync> Sealed for T {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to represent any type.
|
/// _(INTERNALS)_ Trait to represent any type.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// This trait is sealed and cannot be implemented.
|
||||||
///
|
///
|
||||||
/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type.
|
/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type.
|
||||||
/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`].
|
/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`].
|
||||||
@ -68,7 +71,10 @@ pub trait Variant: Any + private::Sealed {
|
|||||||
fn clone_into_dynamic(&self) -> Dynamic;
|
fn clone_into_dynamic(&self) -> Dynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to represent any type.
|
/// _(INTERNALS)_ Trait to represent any type.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// This trait is sealed and cannot be implemented.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub trait Variant: Any + Send + Sync + private::Sealed {
|
pub trait Variant: Any + Send + Sync + private::Sealed {
|
||||||
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
||||||
@ -134,17 +140,6 @@ pub enum AccessMode {
|
|||||||
ReadOnly,
|
ReadOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccessMode {
|
|
||||||
/// Is the access type `ReadOnly`?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_read_only(self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::ReadWrite => false,
|
|
||||||
Self::ReadOnly => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dynamic type containing any value.
|
/// Dynamic type containing any value.
|
||||||
pub struct Dynamic(pub(crate) Union);
|
pub struct Dynamic(pub(crate) Union);
|
||||||
|
|
||||||
@ -337,11 +332,14 @@ impl Dynamic {
|
|||||||
Union::Variant(value, _) => (***value).type_id(),
|
Union::Variant(value, _) => (***value).type_id(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
Union::Shared(cell, _) => {
|
||||||
Union::Shared(cell, _) => (*cell.borrow()).type_id(),
|
#[cfg(not(feature = "sync"))]
|
||||||
#[cfg(not(feature = "no_closure"))]
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell, _) => (*cell.read().unwrap()).type_id(),
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
|
(*value).type_id()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the name of the type of the value held by this [`Dynamic`].
|
/// Get the name of the type of the value held by this [`Dynamic`].
|
||||||
@ -408,11 +406,14 @@ impl Hash for Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
Union::Shared(cell, _) => {
|
||||||
Union::Shared(cell, _) => (*cell.borrow()).hash(state),
|
#[cfg(not(feature = "sync"))]
|
||||||
#[cfg(not(feature = "no_closure"))]
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell, _) => (*cell.read().unwrap()).hash(state),
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
|
(*value).hash(state)
|
||||||
|
}
|
||||||
|
|
||||||
_ => unimplemented!("{} cannot be hashed", self.type_name()),
|
_ => unimplemented!("{} cannot be hashed", self.type_name()),
|
||||||
}
|
}
|
||||||
@ -748,17 +749,25 @@ impl Dynamic {
|
|||||||
pub fn is_read_only(&self) -> bool {
|
pub fn is_read_only(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, access) if access.is_read_only() => true,
|
Union::Shared(_, AccessMode::ReadOnly) => true,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
Union::Shared(ref cell, _) => {
|
||||||
Union::Shared(ref cell, _) => cell.borrow().access_mode().is_read_only(),
|
#[cfg(not(feature = "sync"))]
|
||||||
|
let value = cell.borrow();
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
match value.access_mode() {
|
||||||
#[cfg(feature = "sync")]
|
AccessMode::ReadWrite => false,
|
||||||
Union::Shared(ref cell, _) => cell.read().unwrap().access_mode().is_read_only(),
|
AccessMode::ReadOnly => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => self.access_mode().is_read_only(),
|
_ => match self.access_mode() {
|
||||||
|
AccessMode::ReadWrite => false,
|
||||||
|
AccessMode::ReadOnly => true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Can this [`Dynamic`] be hashed?
|
/// Can this [`Dynamic`] be hashed?
|
||||||
@ -778,11 +787,14 @@ impl Dynamic {
|
|||||||
Union::Map(_, _) => true,
|
Union::Map(_, _) => true,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
Union::Shared(cell, _) => {
|
||||||
Union::Shared(cell, _) => cell.borrow().is_hashable(),
|
#[cfg(not(feature = "sync"))]
|
||||||
#[cfg(not(feature = "no_closure"))]
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell, _) => cell.read().unwrap().is_hashable(),
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
|
value.is_hashable()
|
||||||
|
}
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -1129,10 +1141,11 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell, _) => {
|
Union::Shared(cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return cell.borrow().clone();
|
let value = cell.borrow();
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return cell.read().unwrap().clone();
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
|
value.clone()
|
||||||
}
|
}
|
||||||
_ => self.clone(),
|
_ => self.clone(),
|
||||||
}
|
}
|
||||||
@ -1150,9 +1163,11 @@ impl Dynamic {
|
|||||||
Union::Shared(cell, _) => crate::fn_native::shared_try_take(cell).map_or_else(
|
Union::Shared(cell, _) => crate::fn_native::shared_try_take(cell).map_or_else(
|
||||||
|cell| {
|
|cell| {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return cell.borrow().clone();
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return cell.read().unwrap().clone();
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
|
value.clone()
|
||||||
},
|
},
|
||||||
|value| {
|
|value| {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -1200,16 +1215,16 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, _) => {
|
Union::Shared(ref cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let data = cell.borrow();
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let data = cell.read().unwrap();
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
let type_id = (*data).type_id();
|
let type_id = (*value).type_id();
|
||||||
|
|
||||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(DynamicReadLock(DynamicReadLockInner::Guard(data)))
|
Some(DynamicReadLock(DynamicReadLockInner::Guard(value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => self
|
_ => self
|
||||||
@ -1232,16 +1247,16 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, _) => {
|
Union::Shared(ref cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let data = cell.borrow_mut();
|
let value = cell.borrow_mut();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let data = cell.write().unwrap();
|
let value = cell.write().unwrap();
|
||||||
|
|
||||||
let type_id = (*data).type_id();
|
let type_id = (*value).type_id();
|
||||||
|
|
||||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(DynamicWriteLock(DynamicWriteLockInner::Guard(data)))
|
Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => self
|
_ => self
|
||||||
@ -1439,6 +1454,17 @@ impl Dynamic {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Cast the [`Dynamic`] as a unit `()` and return it.
|
||||||
|
/// Returns the name of the actual type if the cast fails.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_unit(&self) -> Result<(), &'static str> {
|
||||||
|
match self.0 {
|
||||||
|
Union::Unit(value, _) => Ok(value),
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
|
_ => Err(self.type_name()),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Cast the [`Dynamic`] as the system integer type [`INT`] and return it.
|
/// Cast the [`Dynamic`] as the system integer type [`INT`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1500,15 +1526,18 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Cast the [`Dynamic`] as a [`String`] and return the string slice.
|
/// Cast the [`Dynamic`] as an [`ImmutableString`] and return it.
|
||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
///
|
///
|
||||||
/// Fails if `self` is _shared_.
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_str(&self) -> Result<&str, &'static str> {
|
pub(crate) fn as_str(&self) -> Result<&str, &'static str> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Str(s, _) => Ok(s),
|
Union::Str(s, _) => Ok(s),
|
||||||
Union::FnPtr(f, _) => Ok(f.fn_name()),
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Union::Shared(_, _) => panic!("as_str() cannot be called on shared values"),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1529,13 +1558,13 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell, _) => {
|
Union::Shared(cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let data = cell.borrow();
|
let value = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
let data = cell.read().unwrap();
|
let value = cell.read().unwrap();
|
||||||
|
|
||||||
match &data.0 {
|
match &value.0 {
|
||||||
Union::Str(s, _) => Ok(s.clone()),
|
Union::Str(s, _) => Ok(s.clone()),
|
||||||
_ => Err((*data).type_name()),
|
_ => Err((*value).type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
|
@ -1893,7 +1893,7 @@ impl Engine {
|
|||||||
if mods
|
if mods
|
||||||
.scan_raw()
|
.scan_raw()
|
||||||
.skip(_mods_len)
|
.skip(_mods_len)
|
||||||
.any(|(_, m)| m.has_namespace(crate::FnNamespace::Global, true))
|
.any(|(_, m)| m.contains_indexed_global_functions())
|
||||||
{
|
{
|
||||||
if _restore_fn_resolution_cache {
|
if _restore_fn_resolution_cache {
|
||||||
// When new module is imported with global functions and there is already
|
// When new module is imported with global functions and there is already
|
||||||
|
@ -70,6 +70,13 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
let types_pair = (type1, type2);
|
let types_pair = (type1, type2);
|
||||||
|
|
||||||
macro_rules! impl_op {
|
macro_rules! impl_op {
|
||||||
|
($xx:ident $op:tt $yy:ident) => {
|
||||||
|
return Some(|_, args| {
|
||||||
|
let x = &*args[0].read_lock::<$xx>().unwrap();
|
||||||
|
let y = &*args[1].read_lock::<$yy>().unwrap();
|
||||||
|
Ok((x $op y).into())
|
||||||
|
})
|
||||||
|
};
|
||||||
($func:ident ( $op:tt )) => {
|
($func:ident ( $op:tt )) => {
|
||||||
return Some(|_, args| {
|
return Some(|_, args| {
|
||||||
let (x, y) = $func(args);
|
let (x, y) = $func(args);
|
||||||
@ -327,26 +334,14 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
|
|
||||||
if type1 == TypeId::of::<ImmutableString>() {
|
if type1 == TypeId::of::<ImmutableString>() {
|
||||||
match op {
|
match op {
|
||||||
"+" => {
|
"+" => impl_op!(ImmutableString + ImmutableString),
|
||||||
return Some(|_, args| {
|
"-" => impl_op!(ImmutableString - ImmutableString),
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
"==" => impl_op!(ImmutableString == ImmutableString),
|
||||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
"!=" => impl_op!(ImmutableString != ImmutableString),
|
||||||
Ok((x + y).into())
|
">" => impl_op!(ImmutableString > ImmutableString),
|
||||||
})
|
">=" => impl_op!(ImmutableString >= ImmutableString),
|
||||||
}
|
"<" => impl_op!(ImmutableString < ImmutableString),
|
||||||
"-" => {
|
"<=" => impl_op!(ImmutableString <= ImmutableString),
|
||||||
return Some(|_, args| {
|
|
||||||
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
|
|
||||||
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
|
|
||||||
Ok((x - y).into())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
"==" => impl_op!(&str => as_str == as_str),
|
|
||||||
"!=" => impl_op!(&str => as_str != as_str),
|
|
||||||
">" => impl_op!(&str => as_str > as_str),
|
|
||||||
">=" => impl_op!(&str => as_str >= as_str),
|
|
||||||
"<" => impl_op!(&str => as_str < as_str),
|
|
||||||
"<=" => impl_op!(&str => as_str <= as_str),
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
107
src/fn_call.rs
107
src/fn_call.rs
@ -188,42 +188,6 @@ impl Engine {
|
|||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
is_op_assignment: bool,
|
is_op_assignment: bool,
|
||||||
) -> &'s Option<(CallableFunction, Option<ImmutableString>)> {
|
) -> &'s Option<(CallableFunction, Option<ImmutableString>)> {
|
||||||
fn find_function(
|
|
||||||
engine: &Engine,
|
|
||||||
hash: NonZeroU64,
|
|
||||||
mods: &Imports,
|
|
||||||
lib: &[&Module],
|
|
||||||
) -> Option<(CallableFunction, Option<ImmutableString>)> {
|
|
||||||
lib.iter()
|
|
||||||
.find_map(|m| {
|
|
||||||
m.get_fn(hash, false)
|
|
||||||
.map(|f| (f.clone(), m.id_raw().cloned()))
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
|
||||||
engine
|
|
||||||
.global_namespace
|
|
||||||
.get_fn(hash, false)
|
|
||||||
.cloned()
|
|
||||||
.map(|f| (f, None))
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
|
||||||
engine.global_modules.iter().find_map(|m| {
|
|
||||||
m.get_fn(hash, false)
|
|
||||||
.map(|f| (f.clone(), m.id_raw().cloned()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
|
||||||
mods.get_fn(hash)
|
|
||||||
.map(|(f, source)| (f.clone(), source.cloned()))
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
|
||||||
engine.global_sub_modules.values().find_map(|m| {
|
|
||||||
m.get_qualified_fn(hash)
|
|
||||||
.map(|f| (f.clone(), m.id_raw().cloned()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
&*state
|
&*state
|
||||||
.fn_resolution_cache_mut()
|
.fn_resolution_cache_mut()
|
||||||
.entry(hash)
|
.entry(hash)
|
||||||
@ -237,7 +201,36 @@ impl Engine {
|
|||||||
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match find_function(self, hash, mods, lib) {
|
let func = lib
|
||||||
|
.iter()
|
||||||
|
.find_map(|m| {
|
||||||
|
m.get_fn(hash, false)
|
||||||
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
self.global_namespace
|
||||||
|
.get_fn(hash, false)
|
||||||
|
.cloned()
|
||||||
|
.map(|f| (f, None))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
self.global_modules.iter().find_map(|m| {
|
||||||
|
m.get_fn(hash, false)
|
||||||
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
mods.get_fn(hash)
|
||||||
|
.map(|(f, source)| (f.clone(), source.cloned()))
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
self.global_sub_modules.values().find_map(|m| {
|
||||||
|
m.get_qualified_fn(hash)
|
||||||
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
match func {
|
||||||
// Specific version found
|
// Specific version found
|
||||||
Some(f) => return Some(f),
|
Some(f) => return Some(f),
|
||||||
|
|
||||||
@ -360,17 +353,17 @@ impl Engine {
|
|||||||
// See if the function match print/debug (which requires special processing)
|
// See if the function match print/debug (which requires special processing)
|
||||||
return Ok(match fn_name {
|
return Ok(match fn_name {
|
||||||
KEYWORD_PRINT => {
|
KEYWORD_PRINT => {
|
||||||
let text = result.as_str().map_err(|typ| {
|
let text = result.take_immutable_string().map_err(|typ| {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
((self.print)(text).into(), false)
|
((self.print)(&text).into(), false)
|
||||||
}
|
}
|
||||||
KEYWORD_DEBUG => {
|
KEYWORD_DEBUG => {
|
||||||
let text = result.as_str().map_err(|typ| {
|
let text = result.take_immutable_string().map_err(|typ| {
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
typ.into(),
|
typ.into(),
|
||||||
@ -378,7 +371,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let source = state.source.as_ref().map(|s| s.as_str());
|
let source = state.source.as_ref().map(|s| s.as_str());
|
||||||
((self.debug)(text, source, pos).into(), false)
|
((self.debug)(&text, source, pos).into(), false)
|
||||||
}
|
}
|
||||||
_ => (result, func.is_method()),
|
_ => (result, func.is_method()),
|
||||||
});
|
});
|
||||||
@ -520,18 +513,16 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Merge in encapsulated environment, if any
|
// Merge in encapsulated environment, if any
|
||||||
let mut lib_merged: StaticVec<_>;
|
let lib_merged;
|
||||||
let mut unified = false;
|
|
||||||
|
|
||||||
let unified_lib = if let Some(ref env_lib) = fn_def.lib {
|
let (unified_lib, unified) = if let Some(ref env_lib) = fn_def.lib {
|
||||||
unified = true;
|
|
||||||
state.push_fn_resolution_cache();
|
state.push_fn_resolution_cache();
|
||||||
lib_merged = Default::default();
|
lib_merged = once(env_lib.as_ref())
|
||||||
lib_merged.push(env_lib.as_ref());
|
.chain(lib.iter().cloned())
|
||||||
lib_merged.extend(lib.iter().cloned());
|
.collect::<StaticVec<_>>();
|
||||||
lib_merged.as_ref()
|
(lib_merged.as_ref(), true)
|
||||||
} else {
|
} else {
|
||||||
lib
|
(lib, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -686,7 +677,7 @@ impl Engine {
|
|||||||
crate::engine::KEYWORD_IS_DEF_FN
|
crate::engine::KEYWORD_IS_DEF_FN
|
||||||
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<INT>() =>
|
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<INT>() =>
|
||||||
{
|
{
|
||||||
let fn_name = args[0].as_str().unwrap();
|
let fn_name = mem::take(args[0]).take_immutable_string().unwrap();
|
||||||
let num_params = args[1].as_int().unwrap();
|
let num_params = args[1].as_int().unwrap();
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
@ -694,7 +685,7 @@ impl Engine {
|
|||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
} else {
|
} else {
|
||||||
let hash_script =
|
let hash_script =
|
||||||
calc_script_fn_hash(empty(), fn_name, num_params as usize);
|
calc_script_fn_hash(empty(), &fn_name, num_params as usize);
|
||||||
self.has_override(Some(mods), state, lib, None, hash_script)
|
self.has_override(Some(mods), state, lib, None, hash_script)
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
@ -1140,7 +1131,7 @@ impl Engine {
|
|||||||
crate::engine::KEYWORD_IS_DEF_FN if args_expr.len() == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if args_expr.len() == 2 => {
|
||||||
let fn_name =
|
let fn_name =
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let fn_name = fn_name.as_str().map_err(|err| {
|
let fn_name = fn_name.take_immutable_string().map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
let num_params =
|
let num_params =
|
||||||
@ -1152,7 +1143,7 @@ impl Engine {
|
|||||||
return Ok(if num_params < 0 {
|
return Ok(if num_params < 0 {
|
||||||
Dynamic::FALSE
|
Dynamic::FALSE
|
||||||
} else {
|
} else {
|
||||||
let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize);
|
let hash_script = calc_script_fn_hash(empty(), &fn_name, num_params as usize);
|
||||||
self.has_override(Some(mods), state, lib, None, hash_script)
|
self.has_override(Some(mods), state, lib, None, hash_script)
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
@ -1162,10 +1153,10 @@ impl Engine {
|
|||||||
KEYWORD_IS_DEF_VAR if args_expr.len() == 1 => {
|
KEYWORD_IS_DEF_VAR if args_expr.len() == 1 => {
|
||||||
let var_name =
|
let var_name =
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
let var_name = var_name.as_str().map_err(|err| {
|
let var_name = var_name.take_immutable_string().map_err(|err| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
self.make_type_mismatch_err::<ImmutableString>(err, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
return Ok(scope.contains(var_name).into());
|
return Ok(scope.contains(&var_name).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle eval()
|
// Handle eval()
|
||||||
@ -1177,7 +1168,7 @@ impl Engine {
|
|||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let script =
|
let script =
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?;
|
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?;
|
||||||
let script = script.as_str().map_err(|typ| {
|
let script = script.take_immutable_string().map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
|
||||||
})?;
|
})?;
|
||||||
let result = self.eval_script_expr_in_place(
|
let result = self.eval_script_expr_in_place(
|
||||||
@ -1185,7 +1176,7 @@ impl Engine {
|
|||||||
mods,
|
mods,
|
||||||
state,
|
state,
|
||||||
lib,
|
lib,
|
||||||
script,
|
&script,
|
||||||
script_pos,
|
script_pos,
|
||||||
level + 1,
|
level + 1,
|
||||||
);
|
);
|
||||||
|
@ -180,6 +180,10 @@ pub mod serde;
|
|||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
pub use optimize::OptimizationLevel;
|
pub use optimize::OptimizationLevel;
|
||||||
|
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[deprecated = "this type is volatile and may change"]
|
||||||
|
pub use dynamic::Variant;
|
||||||
|
|
||||||
// Expose internal data structures.
|
// Expose internal data structures.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated = "this type is volatile and may change"]
|
#[deprecated = "this type is volatile and may change"]
|
||||||
|
@ -127,6 +127,8 @@ pub struct Module {
|
|||||||
all_type_iterators: HashMap<TypeId, IteratorFn>,
|
all_type_iterators: HashMap<TypeId, IteratorFn>,
|
||||||
/// Is the [`Module`] indexed?
|
/// Is the [`Module`] indexed?
|
||||||
indexed: bool,
|
indexed: bool,
|
||||||
|
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
|
||||||
|
contains_indexed_global_functions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Module {
|
impl Default for Module {
|
||||||
@ -141,6 +143,7 @@ impl Default for Module {
|
|||||||
type_iterators: Default::default(),
|
type_iterators: Default::default(),
|
||||||
all_type_iterators: Default::default(),
|
all_type_iterators: Default::default(),
|
||||||
indexed: false,
|
indexed: false,
|
||||||
|
contains_indexed_global_functions: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,6 +437,7 @@ impl Module {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.variables.insert(name.into(), Dynamic::from(value));
|
self.variables.insert(name.into(), Dynamic::from(value));
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,6 +481,7 @@ impl Module {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
hash_script
|
hash_script
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,6 +526,7 @@ impl Module {
|
|||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
|
|
||||||
&mut self.modules
|
&mut self.modules
|
||||||
}
|
}
|
||||||
@ -581,6 +587,7 @@ impl Module {
|
|||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.modules.insert(name.into(), sub_module.into());
|
self.modules.insert(name.into(), sub_module.into());
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,6 +656,7 @@ impl Module {
|
|||||||
f.namespace = namespace;
|
f.namespace = namespace;
|
||||||
}
|
}
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,6 +712,7 @@ impl Module {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
|
|
||||||
hash_fn
|
hash_fn
|
||||||
}
|
}
|
||||||
@ -1497,6 +1506,7 @@ impl Module {
|
|||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1515,6 +1525,7 @@ impl Module {
|
|||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1542,6 +1553,7 @@ impl Module {
|
|||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1602,6 +1614,7 @@ impl Module {
|
|||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1634,6 +1647,7 @@ impl Module {
|
|||||||
self.all_variables.clear();
|
self.all_variables.clear();
|
||||||
self.all_type_iterators.clear();
|
self.all_type_iterators.clear();
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1820,29 +1834,14 @@ impl Module {
|
|||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Are there functions (or type iterators) marked for the specified namespace?
|
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
|
||||||
pub fn has_namespace(&self, namespace: FnNamespace, recursive: bool) -> bool {
|
///
|
||||||
// Type iterators are default global
|
/// # Panics
|
||||||
if !self.type_iterators.is_empty() {
|
///
|
||||||
return true;
|
/// Panics if the [`Module`] is not yet indexed via [`build_index`][Module::build_index].
|
||||||
}
|
#[inline(always)]
|
||||||
// Any function marked global?
|
pub fn contains_indexed_global_functions(&self) -> bool {
|
||||||
if self.functions.values().any(|f| f.namespace == namespace) {
|
self.contains_indexed_global_functions
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan sub-modules
|
|
||||||
if recursive {
|
|
||||||
if self
|
|
||||||
.modules
|
|
||||||
.values()
|
|
||||||
.any(|m| m.has_namespace(namespace, recursive))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scan through all the sub-modules in the [`Module`] and build a hash index of all
|
/// Scan through all the sub-modules in the [`Module`] and build a hash index of all
|
||||||
@ -1857,11 +1856,15 @@ impl Module {
|
|||||||
variables: &mut HashMap<NonZeroU64, Dynamic, StraightHasherBuilder>,
|
variables: &mut HashMap<NonZeroU64, Dynamic, StraightHasherBuilder>,
|
||||||
functions: &mut HashMap<NonZeroU64, CallableFunction, StraightHasherBuilder>,
|
functions: &mut HashMap<NonZeroU64, CallableFunction, StraightHasherBuilder>,
|
||||||
type_iterators: &mut HashMap<TypeId, IteratorFn>,
|
type_iterators: &mut HashMap<TypeId, IteratorFn>,
|
||||||
) {
|
) -> bool {
|
||||||
|
let mut contains_indexed_global_functions = false;
|
||||||
|
|
||||||
module.modules.iter().for_each(|(name, m)| {
|
module.modules.iter().for_each(|(name, m)| {
|
||||||
// Index all the sub-modules first.
|
// Index all the sub-modules first.
|
||||||
qualifiers.push(name);
|
qualifiers.push(name);
|
||||||
index_module(m, qualifiers, variables, functions, type_iterators);
|
if index_module(m, qualifiers, variables, functions, type_iterators) {
|
||||||
|
contains_indexed_global_functions = true;
|
||||||
|
}
|
||||||
qualifiers.pop();
|
qualifiers.pop();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1875,6 +1878,7 @@ impl Module {
|
|||||||
// Index type iterators
|
// Index type iterators
|
||||||
module.type_iterators.iter().for_each(|(&type_id, func)| {
|
module.type_iterators.iter().for_each(|(&type_id, func)| {
|
||||||
type_iterators.insert(type_id, func.clone());
|
type_iterators.insert(type_id, func.clone());
|
||||||
|
contains_indexed_global_functions = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Index all Rust functions
|
// Index all Rust functions
|
||||||
@ -1895,6 +1899,7 @@ impl Module {
|
|||||||
FnNamespace::Global => {
|
FnNamespace::Global => {
|
||||||
// Flatten all functions with global namespace
|
// Flatten all functions with global namespace
|
||||||
functions.insert(hash, func.clone());
|
functions.insert(hash, func.clone());
|
||||||
|
contains_indexed_global_functions = true;
|
||||||
}
|
}
|
||||||
FnNamespace::Internal => (),
|
FnNamespace::Internal => (),
|
||||||
}
|
}
|
||||||
@ -1927,6 +1932,8 @@ impl Module {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
contains_indexed_global_functions
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.indexed {
|
if !self.indexed {
|
||||||
@ -1937,7 +1944,7 @@ impl Module {
|
|||||||
|
|
||||||
qualifiers.push("root");
|
qualifiers.push("root");
|
||||||
|
|
||||||
index_module(
|
self.contains_indexed_global_functions = index_module(
|
||||||
self,
|
self,
|
||||||
&mut qualifiers,
|
&mut qualifiers,
|
||||||
&mut variables,
|
&mut variables,
|
||||||
@ -1968,6 +1975,7 @@ impl Module {
|
|||||||
pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self {
|
pub fn set_iter(&mut self, typ: TypeId, func: IteratorFn) -> &mut Self {
|
||||||
self.type_iterators.insert(typ, func);
|
self.type_iterators.insert(typ, func);
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
|
self.contains_indexed_global_functions = false;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,17 +113,16 @@ impl<'a> State<'a> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (n, access, expr) in self.variables.iter().rev() {
|
self.variables.iter().rev().find_map(|(n, access, expr)| {
|
||||||
if n == name {
|
if n == name {
|
||||||
return if access.is_read_only() {
|
match access {
|
||||||
Some(expr)
|
AccessMode::ReadWrite => None,
|
||||||
} else {
|
AccessMode::ReadOnly => Some(expr),
|
||||||
None
|
}
|
||||||
};
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
|
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
|
||||||
|
|
||||||
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
||||||
let s = d.as_str().unwrap();
|
let s = &*d.read_lock::<ImmutableString>().unwrap();
|
||||||
write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
|
write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
|
||||||
}
|
}
|
||||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
||||||
|
@ -440,8 +440,8 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
|||||||
_variants: &'static [&'static str],
|
_variants: &'static [&'static str],
|
||||||
visitor: V,
|
visitor: V,
|
||||||
) -> Result<V::Value, Box<EvalAltResult>> {
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
if let Ok(s) = self.value.as_str() {
|
if let Some(s) = self.value.read_lock::<ImmutableString>() {
|
||||||
visitor.visit_enum(s.into_deserializer())
|
visitor.visit_enum(s.as_str().into_deserializer())
|
||||||
} else {
|
} else {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if let Some(map) = self.value.downcast_ref::<Map>() {
|
if let Some(map) = self.value.downcast_ref::<Map>() {
|
||||||
|
@ -300,7 +300,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
|
|||||||
// Closure 'f' captures: the engine, the AST, and the curried function pointer
|
// Closure 'f' captures: the engine, the AST, and the curried function pointer
|
||||||
let f = move |x: INT| fn_ptr.call_dynamic(context, None, [x.into()]);
|
let f = move |x: INT| fn_ptr.call_dynamic(context, None, [x.into()]);
|
||||||
|
|
||||||
assert_eq!(f(42)?.as_str(), Ok("hello42"));
|
assert_eq!(f(42)?.take_string(), Ok("hello42".to_string()));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,12 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
sub_module2.set_var("answer", 41 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
|
|
||||||
let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1));
|
let hash_inc = sub_module2.set_fn_1_mut("inc", FnNamespace::Internal, |x: &mut INT| Ok(*x + 1));
|
||||||
assert!(!sub_module2.has_namespace(FnNamespace::Global, true));
|
sub_module2.build_index();
|
||||||
|
assert!(!sub_module2.contains_indexed_global_functions());
|
||||||
|
|
||||||
sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1));
|
sub_module2.set_fn_1_mut("super_inc", FnNamespace::Global, |x: &mut INT| Ok(*x + 1));
|
||||||
assert!(sub_module2.has_namespace(FnNamespace::Global, true));
|
sub_module2.build_index();
|
||||||
|
assert!(sub_module2.contains_indexed_global_functions());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
sub_module2.set_getter_fn("doubled", |x: &mut INT| Ok(*x * 2));
|
sub_module2.set_getter_fn("doubled", |x: &mut INT| Ok(*x * 2));
|
||||||
@ -35,9 +37,9 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
sub_module.set_sub_module("universe", sub_module2);
|
sub_module.set_sub_module("universe", sub_module2);
|
||||||
module.set_sub_module("life", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
module.set_var("MYSTIC_NUMBER", Dynamic::from(42 as INT));
|
module.set_var("MYSTIC_NUMBER", Dynamic::from(42 as INT));
|
||||||
|
module.build_index();
|
||||||
|
|
||||||
assert!(module.has_namespace(FnNamespace::Global, true));
|
assert!(module.contains_indexed_global_functions());
|
||||||
assert!(!module.has_namespace(FnNamespace::Global, false));
|
|
||||||
|
|
||||||
assert!(module.contains_sub_module("life"));
|
assert!(module.contains_sub_module("life"));
|
||||||
let m = module.get_sub_module("life").unwrap();
|
let m = module.get_sub_module("life").unwrap();
|
||||||
|
@ -5,7 +5,6 @@ use rhai::{
|
|||||||
Dynamic, Engine, EvalAltResult, ImmutableString, INT,
|
Dynamic, Engine, EvalAltResult, ImmutableString, INT,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use rhai::Array;
|
use rhai::Array;
|
||||||
@ -95,13 +94,13 @@ fn test_serde_ser_struct() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let mut map = d.cast::<Map>();
|
let mut map = d.cast::<Map>();
|
||||||
let obj = map.remove("obj").unwrap().cast::<Map>();
|
let obj = map.remove("obj").unwrap().cast::<Map>();
|
||||||
let seq = map.remove("seq").unwrap().cast::<Array>();
|
let mut seq = map.remove("seq").unwrap().cast::<Array>();
|
||||||
|
|
||||||
assert_eq!(Ok(123), obj["a"].as_int());
|
assert_eq!(Ok(123), obj["a"].as_int());
|
||||||
assert!(obj["b"].as_bool().unwrap());
|
assert!(obj["b"].as_bool().unwrap());
|
||||||
assert_eq!(Ok(42), map["int"].as_int());
|
assert_eq!(Ok(42), map["int"].as_int());
|
||||||
assert_eq!(seq.len(), 3);
|
assert_eq!(seq.len(), 3);
|
||||||
assert_eq!(Ok("kitty"), seq[1].as_str());
|
assert_eq!("kitty", seq.remove(1).take_string().unwrap());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -115,10 +114,10 @@ fn test_serde_ser_unit_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let d = to_dynamic(MyEnum::VariantFoo)?;
|
let d = to_dynamic(MyEnum::VariantFoo)?;
|
||||||
assert_eq!(Ok("VariantFoo"), d.as_str());
|
assert_eq!("VariantFoo", d.take_string().unwrap());
|
||||||
|
|
||||||
let d = to_dynamic(MyEnum::VariantBar)?;
|
let d = to_dynamic(MyEnum::VariantBar)?;
|
||||||
assert_eq!(Ok("VariantBar"), d.as_str());
|
assert_eq!("VariantBar", d.take_string().unwrap());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -141,7 +140,13 @@ fn test_serde_ser_externally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
assert_eq!(Ok("VariantUnit"), to_dynamic(MyEnum::VariantUnit)?.as_str());
|
assert_eq!(
|
||||||
|
"VariantUnit",
|
||||||
|
to_dynamic(MyEnum::VariantUnit)?
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -193,13 +198,24 @@ fn test_serde_ser_internally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok("VariantEmptyStruct"),
|
"VariantEmptyStruct",
|
||||||
map.remove("tag").unwrap().as_str()
|
map.remove("tag")
|
||||||
|
.unwrap()
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
);
|
);
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
|
|
||||||
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
||||||
assert_eq!(Ok("VariantStruct"), map.remove("tag").unwrap().as_str());
|
assert_eq!(
|
||||||
|
"VariantStruct",
|
||||||
|
map.remove("tag")
|
||||||
|
.unwrap()
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
);
|
||||||
assert_eq!(Ok(123), map.remove("a").unwrap().as_int());
|
assert_eq!(Ok(123), map.remove("a").unwrap().as_int());
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
|
|
||||||
@ -225,20 +241,41 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut map = to_dynamic(MyEnum::VariantUnit)?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantUnit)?.cast::<Map>();
|
||||||
assert_eq!(Ok("VariantUnit"), map.remove("tag").unwrap().as_str());
|
assert_eq!(
|
||||||
|
"VariantUnit",
|
||||||
|
map.remove("tag")
|
||||||
|
.unwrap()
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
);
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
let mut map = to_dynamic(MyEnum::VariantUnitTuple())?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantUnitTuple())?.cast::<Map>();
|
||||||
assert_eq!(Ok("VariantUnitTuple"), map.remove("tag").unwrap().as_str());
|
assert_eq!(
|
||||||
|
"VariantUnitTuple",
|
||||||
|
map.remove("tag")
|
||||||
|
.unwrap()
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
);
|
||||||
let content = map.remove("content").unwrap().cast::<Array>();
|
let content = map.remove("content").unwrap().cast::<Array>();
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
assert!(content.is_empty());
|
assert!(content.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut map = to_dynamic(MyEnum::VariantNewtype(123))?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantNewtype(123))?.cast::<Map>();
|
||||||
assert_eq!(Ok("VariantNewtype"), map.remove("tag").unwrap().as_str());
|
assert_eq!(
|
||||||
|
"VariantNewtype",
|
||||||
|
map.remove("tag")
|
||||||
|
.unwrap()
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
);
|
||||||
let content = map.remove("content").unwrap();
|
let content = map.remove("content").unwrap();
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
assert_eq!(Ok(123), content.as_int());
|
assert_eq!(Ok(123), content.as_int());
|
||||||
@ -246,7 +283,14 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
let mut map = to_dynamic(MyEnum::VariantTuple(123, 456))?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantTuple(123, 456))?.cast::<Map>();
|
||||||
assert_eq!(Ok("VariantTuple"), map.remove("tag").unwrap().as_str());
|
assert_eq!(
|
||||||
|
"VariantTuple",
|
||||||
|
map.remove("tag")
|
||||||
|
.unwrap()
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
);
|
||||||
let content = map.remove("content").unwrap().cast::<Array>();
|
let content = map.remove("content").unwrap().cast::<Array>();
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
assert_eq!(2, content.len());
|
assert_eq!(2, content.len());
|
||||||
@ -256,15 +300,22 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantEmptyStruct {})?.cast::<Map>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok("VariantEmptyStruct"),
|
"VariantEmptyStruct",
|
||||||
map.remove("tag").unwrap().as_str()
|
map.remove("tag")
|
||||||
|
.unwrap()
|
||||||
|
.take_immutable_string()
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
);
|
);
|
||||||
let map_inner = map.remove("content").unwrap().cast::<Map>();
|
let map_inner = map.remove("content").unwrap().cast::<Map>();
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
assert!(map_inner.is_empty());
|
assert!(map_inner.is_empty());
|
||||||
|
|
||||||
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
||||||
assert_eq!(Ok("VariantStruct"), map.remove("tag").unwrap().as_str());
|
assert_eq!(
|
||||||
|
"VariantStruct",
|
||||||
|
map.remove("tag").unwrap().take_string().unwrap()
|
||||||
|
);
|
||||||
let mut map_inner = map.remove("content").unwrap().cast::<Map>();
|
let mut map_inner = map.remove("content").unwrap().cast::<Map>();
|
||||||
assert!(map.is_empty());
|
assert!(map.is_empty());
|
||||||
assert_eq!(Ok(123), map_inner.remove("a").unwrap().as_int());
|
assert_eq!(Ok(123), map_inner.remove("a").unwrap().as_int());
|
||||||
|
Loading…
Reference in New Issue
Block a user