commit
c1d1072c74
@ -28,6 +28,7 @@ Enhancements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* `Engine::is_symbol_disabled` is added to test whether a particular keyword/symbol is disabled.
|
* `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
|
Version 1.13.0
|
||||||
|
484
src/func/call.rs
484
src/func/call.rs
@ -576,39 +576,32 @@ impl Engine {
|
|||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
// These may be redirected from method style calls.
|
// These may be redirected from method style calls.
|
||||||
if hashes.is_native_only() {
|
if hashes.is_native_only() {
|
||||||
let error = match fn_name {
|
loop {
|
||||||
|
match fn_name {
|
||||||
// Handle type_of()
|
// Handle type_of()
|
||||||
KEYWORD_TYPE_OF => {
|
KEYWORD_TYPE_OF if args.len() == 1 => {
|
||||||
if args.len() == 1 {
|
|
||||||
let typ = self.get_interned_string(self.map_type_name(args[0].type_name()));
|
let typ = self.get_interned_string(self.map_type_name(args[0].type_name()));
|
||||||
return Ok((typ.into(), false));
|
return Ok((typ.into(), false));
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED => {
|
crate::engine::KEYWORD_IS_SHARED if args.len() == 1 => {
|
||||||
if args.len() == 1 {
|
return Ok((args[0].is_shared().into(), false))
|
||||||
return Ok((args[0].is_shared().into(), false));
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
crate::engine::KEYWORD_IS_SHARED => (),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN => true,
|
crate::engine::KEYWORD_IS_DEF_FN => (),
|
||||||
|
|
||||||
KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL
|
KEYWORD_TYPE_OF | KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR
|
||||||
| KEYWORD_FN_PTR_CURRY => true,
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => (),
|
||||||
|
|
||||||
_ => false,
|
_ => break,
|
||||||
};
|
}
|
||||||
|
|
||||||
if error {
|
let sig = self.gen_fn_call_signature(fn_name, args);
|
||||||
return Err(ERR::ErrorFunctionNotFound(
|
return Err(ERR::ErrorFunctionNotFound(sig, pos).into());
|
||||||
self.gen_fn_call_signature(fn_name, args),
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,31 +617,27 @@ impl Engine {
|
|||||||
let hash = hashes.script();
|
let hash = hashes.script();
|
||||||
let local_entry = &mut None;
|
let local_entry = &mut None;
|
||||||
|
|
||||||
|
let mut resolved = None;
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
let resolved = if _is_method_call && !args.is_empty() {
|
if _is_method_call && !args.is_empty() {
|
||||||
let typed_hash =
|
let typed_hash =
|
||||||
crate::calc_typed_method_hash(hash, self.map_type_name(args[0].type_name()));
|
crate::calc_typed_method_hash(hash, self.map_type_name(args[0].type_name()));
|
||||||
self.resolve_fn(global, caches, local_entry, None, typed_hash, None, false)
|
resolved =
|
||||||
} else {
|
self.resolve_fn(global, caches, local_entry, None, typed_hash, None, false);
|
||||||
None
|
}
|
||||||
};
|
|
||||||
#[cfg(feature = "no_object")]
|
|
||||||
let resolved = None;
|
|
||||||
|
|
||||||
let resolved = if resolved.is_none() {
|
if resolved.is_none() {
|
||||||
self.resolve_fn(global, caches, local_entry, None, hash, None, false)
|
resolved = self.resolve_fn(global, caches, local_entry, None, hash, None, false);
|
||||||
} else {
|
}
|
||||||
resolved
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(FnResolutionCacheEntry { func, ref source }) = resolved.cloned() {
|
if let Some(FnResolutionCacheEntry { func, source }) = resolved.cloned() {
|
||||||
// Script function call
|
// Script function call
|
||||||
debug_assert!(func.is_script());
|
debug_assert!(func.is_script());
|
||||||
|
|
||||||
let f = func.get_script_fn_def().expect("script-defined function");
|
|
||||||
let environ = func.get_encapsulated_environ();
|
let environ = func.get_encapsulated_environ();
|
||||||
|
let func = func.get_script_fn_def().expect("script-defined function");
|
||||||
|
|
||||||
if f.body.is_empty() {
|
if func.body.is_empty() {
|
||||||
return Ok((Dynamic::UNIT, false));
|
return Ok((Dynamic::UNIT, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,23 +649,17 @@ impl Engine {
|
|||||||
&mut empty_scope
|
&mut empty_scope
|
||||||
};
|
};
|
||||||
|
|
||||||
let orig_source = mem::replace(&mut global.source, source.clone());
|
let orig_source = mem::replace(&mut global.source, source);
|
||||||
defer! { global => move |g| g.source = orig_source }
|
defer! { global => move |g| g.source = orig_source }
|
||||||
|
|
||||||
return if _is_method_call {
|
return if _is_method_call {
|
||||||
// Method call of script function - map first argument to `this`
|
use std::ops::DerefMut;
|
||||||
let (first_arg, rest_args) = args.split_first_mut().unwrap();
|
|
||||||
|
|
||||||
|
// Method call of script function - map first argument to `this`
|
||||||
|
let (first_arg, args) = args.split_first_mut().unwrap();
|
||||||
|
let this_ptr = Some(first_arg.deref_mut());
|
||||||
self.call_script_fn(
|
self.call_script_fn(
|
||||||
global,
|
global, caches, scope, this_ptr, environ, func, args, true, pos,
|
||||||
caches,
|
|
||||||
scope,
|
|
||||||
Some(first_arg),
|
|
||||||
environ,
|
|
||||||
f,
|
|
||||||
rest_args,
|
|
||||||
true,
|
|
||||||
pos,
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Normal call of script function
|
// Normal call of script function
|
||||||
@ -691,7 +674,7 @@ impl Engine {
|
|||||||
|
|
||||||
defer! { args = (args) if swap => move |a| backup.restore_first_arg(a) }
|
defer! { args = (args) if swap => move |a| backup.restore_first_arg(a) }
|
||||||
|
|
||||||
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
self.call_script_fn(global, caches, scope, None, environ, func, args, true, pos)
|
||||||
}
|
}
|
||||||
.map(|r| (r, false));
|
.map(|r| (r, false));
|
||||||
}
|
}
|
||||||
@ -748,55 +731,47 @@ impl Engine {
|
|||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
mut hash: FnCallHashes,
|
mut hash: FnCallHashes,
|
||||||
target: &mut crate::eval::Target,
|
target: &mut crate::eval::Target,
|
||||||
mut call_args: &mut [Dynamic],
|
call_args: &mut [Dynamic],
|
||||||
first_arg_pos: Position,
|
first_arg_pos: Position,
|
||||||
fn_call_pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
let is_ref_mut = target.is_ref();
|
|
||||||
|
|
||||||
let (result, updated) = match fn_name {
|
let (result, updated) = match fn_name {
|
||||||
// Handle fn_ptr.call(...)
|
// Handle fn_ptr.call(...)
|
||||||
KEYWORD_FN_PTR_CALL if target.is_fnptr() => {
|
KEYWORD_FN_PTR_CALL if target.is_fnptr() => {
|
||||||
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
|
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
|
||||||
|
|
||||||
// Arguments are passed as-is, adding the curried arguments
|
// Arguments are passed as-is, adding the curried arguments
|
||||||
let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len());
|
let mut curry = fn_ptr.curry().iter().cloned().collect::<FnArgsVec<_>>();
|
||||||
curry.extend(fn_ptr.curry().iter().cloned());
|
let args = &mut curry
|
||||||
let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len());
|
.iter_mut()
|
||||||
args.extend(curry.iter_mut());
|
.chain(call_args.iter_mut())
|
||||||
args.extend(call_args.iter_mut());
|
.collect::<FnArgsVec<_>>();
|
||||||
|
|
||||||
// Linked to scripted function?
|
let _fn_def = ();
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let fn_def = fn_ptr.fn_def();
|
let _fn_def = fn_ptr.fn_def();
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
let fn_def = ();
|
|
||||||
|
|
||||||
match fn_def {
|
match _fn_def {
|
||||||
|
// Linked to scripted function - short-circuit
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Some(fn_def) if fn_def.params.len() == args.len() => self
|
Some(fn_def) if fn_def.params.len() == args.len() => {
|
||||||
.call_script_fn(
|
let scope = &mut Scope::new();
|
||||||
global,
|
let environ = fn_ptr.encapsulated_environ().map(|r| r.as_ref());
|
||||||
caches,
|
|
||||||
&mut Scope::new(),
|
self.call_script_fn(
|
||||||
None,
|
global, caches, scope, None, environ, fn_def, args, true, pos,
|
||||||
fn_ptr.encapsulated_environ().map(|r| r.as_ref()),
|
|
||||||
fn_def,
|
|
||||||
args,
|
|
||||||
true,
|
|
||||||
fn_call_pos,
|
|
||||||
)
|
)
|
||||||
.map(|v| (v, false)),
|
.map(|v| (v, false))
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
let _is_anon = false;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
let _is_anon = fn_ptr.is_anonymous();
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
let is_anon = false;
|
|
||||||
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
// Recalculate hashes
|
// Recalculate hashes
|
||||||
let new_hash = if !is_anon && !is_valid_function_name(fn_name) {
|
let new_hash = if !_is_anon && !is_valid_function_name(fn_name) {
|
||||||
FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args.len()))
|
FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args.len()))
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args.len()))
|
FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args.len()))
|
||||||
@ -804,153 +779,105 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
global,
|
global, caches, None, fn_name, None, new_hash, args, false, false, pos,
|
||||||
caches,
|
|
||||||
None,
|
|
||||||
fn_name,
|
|
||||||
None,
|
|
||||||
new_hash,
|
|
||||||
args,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
fn_call_pos,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle obj.call()
|
||||||
|
KEYWORD_FN_PTR_CALL if call_args.is_empty() => {
|
||||||
|
return Err(self
|
||||||
|
.make_type_mismatch_err::<FnPtr>(self.map_type_name(target.type_name()), pos))
|
||||||
|
}
|
||||||
|
|
||||||
// Handle obj.call(fn_ptr, ...)
|
// Handle obj.call(fn_ptr, ...)
|
||||||
KEYWORD_FN_PTR_CALL => {
|
KEYWORD_FN_PTR_CALL => {
|
||||||
if call_args.is_empty() {
|
debug_assert!(!call_args.is_empty());
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
|
||||||
self.map_type_name(target.type_name()),
|
|
||||||
fn_call_pos,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if !call_args[0].is_fnptr() {
|
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
|
||||||
self.map_type_name(call_args[0].type_name()),
|
|
||||||
first_arg_pos,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
let fn_ptr = call_args[0].take().cast::<FnPtr>();
|
let typ = call_args[0].type_name();
|
||||||
|
let fn_ptr = call_args[0].take().try_cast::<FnPtr>().ok_or_else(|| {
|
||||||
let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
|
self.make_type_mismatch_err::<FnPtr>(self.map_type_name(typ), first_arg_pos)
|
||||||
#[cfg(not(feature = "no_function"))]
|
})?;
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
let is_anon = false;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
|
let (is_anon, (fn_name, fn_curry, environ, fn_def)) =
|
||||||
|
(fn_ptr.is_anonymous(), fn_ptr.take_data());
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let (fn_name, fn_curry, environ) = fn_ptr.take_data();
|
let (is_anon, (fn_name, fn_curry, _), fn_def) = (false, fn_ptr.take_data(), ());
|
||||||
|
|
||||||
(
|
// Adding the curried arguments and the remaining arguments
|
||||||
fn_name,
|
let mut curry = fn_curry.into_iter().collect::<FnArgsVec<_>>();
|
||||||
is_anon,
|
let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len());
|
||||||
fn_curry,
|
|
||||||
environ,
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
fn_def,
|
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Replace the first argument with the object pointer, adding the curried arguments
|
|
||||||
call_args = &mut call_args[1..];
|
|
||||||
|
|
||||||
let mut curry = FnArgsVec::with_capacity(fn_curry.len());
|
|
||||||
curry.extend(fn_curry.into_iter());
|
|
||||||
let args = &mut FnArgsVec::with_capacity(curry.len() + call_args.len() + 1);
|
|
||||||
args.extend(curry.iter_mut());
|
args.extend(curry.iter_mut());
|
||||||
args.extend(call_args.iter_mut());
|
args.extend(call_args.iter_mut().skip(1));
|
||||||
|
|
||||||
// Linked to scripted function?
|
|
||||||
match fn_def {
|
match fn_def {
|
||||||
|
// Linked to scripted function - short-circuit
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Some(fn_def) if fn_def.params.len() == args.len() => {
|
Some(fn_def) if fn_def.params.len() == args.len() => {
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
ensure_no_data_race(&fn_def.name, args, false)?;
|
ensure_no_data_race(&fn_def.name, args, false)?;
|
||||||
|
|
||||||
|
let scope = &mut Scope::new();
|
||||||
|
let this_ptr = Some(target.as_mut());
|
||||||
|
let environ = environ.as_deref();
|
||||||
|
|
||||||
self.call_script_fn(
|
self.call_script_fn(
|
||||||
global,
|
global, caches, scope, this_ptr, environ, &fn_def, args, true, pos,
|
||||||
caches,
|
|
||||||
&mut Scope::new(),
|
|
||||||
Some(target),
|
|
||||||
_environ.as_deref(),
|
|
||||||
&fn_def,
|
|
||||||
args,
|
|
||||||
true,
|
|
||||||
fn_call_pos,
|
|
||||||
)
|
)
|
||||||
.map(|v| (v, false))
|
.map(|v| (v, false))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
let name = fn_name.as_str();
|
||||||
|
let is_ref_mut = target.is_ref();
|
||||||
|
|
||||||
// Add the first argument with the object pointer
|
// Add the first argument with the object pointer
|
||||||
args.insert(0, target.as_mut());
|
args.insert(0, target.as_mut());
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
|
let num_args = args.len();
|
||||||
|
|
||||||
let new_hash = match is_anon {
|
let new_hash = match is_anon {
|
||||||
false if !is_valid_function_name(&fn_name) => {
|
false if !is_valid_function_name(name) => {
|
||||||
FnCallHashes::from_native_only(calc_fn_hash(
|
FnCallHashes::from_native_only(calc_fn_hash(None, name, num_args))
|
||||||
None,
|
|
||||||
&fn_name,
|
|
||||||
args.len(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ => FnCallHashes::from_script_and_native(
|
_ => FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(None, &fn_name, args.len() - 1),
|
calc_fn_hash(None, name, num_args - 1),
|
||||||
calc_fn_hash(None, &fn_name, args.len()),
|
calc_fn_hash(None, name, num_args),
|
||||||
),
|
),
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
_ => FnCallHashes::from_native_only(calc_fn_hash(
|
_ => FnCallHashes::from_native_only(calc_fn_hash(None, name, num_args)),
|
||||||
None,
|
|
||||||
&fn_name,
|
|
||||||
args.len(),
|
|
||||||
)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
global,
|
global, caches, None, name, None, new_hash, args, is_ref_mut, true, pos,
|
||||||
caches,
|
|
||||||
None,
|
|
||||||
&fn_name,
|
|
||||||
None,
|
|
||||||
new_hash,
|
|
||||||
args,
|
|
||||||
is_ref_mut,
|
|
||||||
true,
|
|
||||||
fn_call_pos,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KEYWORD_FN_PTR_CURRY => {
|
|
||||||
if !target.is_fnptr() {
|
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
|
||||||
self.map_type_name(target.type_name()),
|
|
||||||
fn_call_pos,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`").clone();
|
// Handle fn_ptr.curry(...)
|
||||||
|
KEYWORD_FN_PTR_CURRY => {
|
||||||
|
let typ = target.type_name();
|
||||||
|
let mut fn_ptr = target
|
||||||
|
.read_lock::<FnPtr>()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
self.make_type_mismatch_err::<FnPtr>(self.map_type_name(typ), pos)
|
||||||
|
})?
|
||||||
|
.clone();
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
call_args.iter_mut().map(mem::take).for_each(|value| {
|
fn_ptr.extend(call_args.iter_mut().map(mem::take));
|
||||||
fn_ptr.add_curry(value);
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok((fn_ptr.into(), false))
|
Ok((fn_ptr.into(), false))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_shared()
|
// Handle var.is_shared()
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if call_args.is_empty() => {
|
crate::engine::KEYWORD_IS_SHARED if call_args.is_empty() => {
|
||||||
return Ok((target.is_shared().into(), false));
|
return Ok((target.is_shared().into(), false));
|
||||||
@ -960,7 +887,7 @@ impl Engine {
|
|||||||
let mut fn_name = fn_name;
|
let mut fn_name = fn_name;
|
||||||
let _redirected;
|
let _redirected;
|
||||||
let mut _linked = None;
|
let mut _linked = None;
|
||||||
let mut _arg_values: FnArgsVec<_>;
|
let mut _arg_values;
|
||||||
let mut call_args = call_args;
|
let mut call_args = call_args;
|
||||||
|
|
||||||
// Check if it is a map method call in OOP style
|
// Check if it is a map method call in OOP style
|
||||||
@ -979,17 +906,16 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.chain(call_args.iter_mut().map(mem::take))
|
.chain(call_args.iter_mut().map(mem::take))
|
||||||
.collect();
|
.collect::<FnArgsVec<_>>();
|
||||||
call_args = &mut _arg_values;
|
call_args = &mut _arg_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Linked to scripted function?
|
let _fn_def = ();
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let fn_def = fn_ptr.fn_def();
|
let _fn_def = fn_ptr.fn_def();
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
let fn_def = ();
|
|
||||||
|
|
||||||
match fn_def {
|
match _fn_def {
|
||||||
|
// Linked to scripted function
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Some(fn_def) if fn_def.params.len() == call_args.len() => {
|
Some(fn_def) if fn_def.params.len() == call_args.len() => {
|
||||||
_linked = Some((
|
_linked = Some((
|
||||||
@ -998,27 +924,27 @@ impl Engine {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
let _is_anon = false;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
let _is_anon = fn_ptr.is_anonymous();
|
||||||
#[cfg(feature = "no_function")]
|
|
||||||
let is_anon = false;
|
|
||||||
|
|
||||||
// Recalculate the hash based on the new function name and new arguments
|
// Recalculate the hash based on the new function name and new arguments
|
||||||
let args_len = call_args.len() + 1;
|
let num_args = call_args.len() + 1;
|
||||||
hash = match is_anon {
|
|
||||||
|
hash = match _is_anon {
|
||||||
false if !is_valid_function_name(fn_name) => {
|
false if !is_valid_function_name(fn_name) => {
|
||||||
FnCallHashes::from_native_only(calc_fn_hash(
|
FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
None, fn_name, args_len,
|
None, fn_name, num_args,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ => FnCallHashes::from_script_and_native(
|
_ => FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(None, fn_name, args_len - 1),
|
calc_fn_hash(None, fn_name, num_args - 1),
|
||||||
calc_fn_hash(None, fn_name, args_len),
|
calc_fn_hash(None, fn_name, num_args),
|
||||||
),
|
),
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
_ => FnCallHashes::from_native_only(calc_fn_hash(
|
_ => FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
None, fn_name, args_len,
|
None, fn_name, num_args,
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1030,39 +956,29 @@ impl Engine {
|
|||||||
match _linked {
|
match _linked {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Some((fn_def, environ)) => {
|
Some((fn_def, environ)) => {
|
||||||
// Linked to scripted function
|
// Linked to scripted function - short-circuit
|
||||||
|
let scope = &mut Scope::new();
|
||||||
|
let environ = environ.as_deref();
|
||||||
|
let this_ptr = Some(target.as_mut());
|
||||||
|
let args = &mut call_args.iter_mut().collect::<FnArgsVec<_>>();
|
||||||
|
|
||||||
self.call_script_fn(
|
self.call_script_fn(
|
||||||
global,
|
global, caches, scope, this_ptr, environ, &*fn_def, args, true, pos,
|
||||||
caches,
|
|
||||||
&mut Scope::new(),
|
|
||||||
Some(target),
|
|
||||||
environ.as_deref(),
|
|
||||||
&*fn_def,
|
|
||||||
&mut call_args.iter_mut().collect::<FnArgsVec<_>>(),
|
|
||||||
true,
|
|
||||||
fn_call_pos,
|
|
||||||
)
|
)
|
||||||
.map(|v| (v, false))
|
.map(|v| (v, false))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
Some(()) => unreachable!(),
|
Some(()) => unreachable!(),
|
||||||
None => {
|
None => {
|
||||||
|
let is_ref_mut = target.is_ref();
|
||||||
|
|
||||||
// Attached object pointer in front of the arguments
|
// Attached object pointer in front of the arguments
|
||||||
let mut args = FnArgsVec::with_capacity(call_args.len() + 1);
|
let args = &mut std::iter::once(target.as_mut())
|
||||||
args.push(target.as_mut());
|
.chain(call_args.iter_mut())
|
||||||
args.extend(call_args.iter_mut());
|
.collect::<FnArgsVec<_>>();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
global,
|
global, caches, None, fn_name, None, hash, args, is_ref_mut, true, pos,
|
||||||
caches,
|
|
||||||
None,
|
|
||||||
fn_name,
|
|
||||||
None,
|
|
||||||
hash,
|
|
||||||
&mut args,
|
|
||||||
is_ref_mut,
|
|
||||||
true,
|
|
||||||
fn_call_pos,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1071,7 +987,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Propagate the changed value back to the source if necessary
|
// Propagate the changed value back to the source if necessary
|
||||||
if updated {
|
if updated {
|
||||||
target.propagate_changed_value(fn_call_pos)?;
|
target.propagate_changed_value(pos)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((result, updated))
|
Ok((result, updated))
|
||||||
@ -1093,8 +1009,8 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut first_arg = first_arg;
|
let mut first_arg = first_arg;
|
||||||
let mut a_expr = args_expr;
|
let mut args_expr = args_expr;
|
||||||
let mut total_args = usize::from(first_arg.is_some()) + a_expr.len();
|
let mut num_args = usize::from(first_arg.is_some()) + args_expr.len();
|
||||||
let mut curry = FnArgsVec::new_const();
|
let mut curry = FnArgsVec::new_const();
|
||||||
let mut name = fn_name;
|
let mut name = fn_name;
|
||||||
let mut hashes = hashes;
|
let mut hashes = hashes;
|
||||||
@ -1104,48 +1020,38 @@ impl Engine {
|
|||||||
_ if op_token.is_some() => (),
|
_ if op_token.is_some() => (),
|
||||||
|
|
||||||
// Handle call(fn_ptr, ...)
|
// Handle call(fn_ptr, ...)
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if num_args >= 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (first_arg_value, first_arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
||||||
|
|
||||||
if !arg_value.is_fnptr() {
|
let typ = first_arg_value.type_name();
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
let fn_ptr = first_arg_value.try_cast::<FnPtr>().ok_or_else(|| {
|
||||||
self.map_type_name(arg_value.type_name()),
|
self.make_type_mismatch_err::<FnPtr>(self.map_type_name(typ), first_arg_pos)
|
||||||
arg_pos,
|
})?;
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let fn_ptr = arg_value.cast::<FnPtr>();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
|
let (is_anon, (fn_name, fn_curry, _environ, fn_def)) =
|
||||||
let is_anon = fn_ptr.is_anonymous();
|
(fn_ptr.is_anonymous(), fn_ptr.take_data());
|
||||||
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
|
|
||||||
(fn_name, is_anon, fn_curry, environ, fn_def)
|
|
||||||
};
|
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let (fn_name, is_anon, fn_curry, _environ) = {
|
let (is_anon, (fn_name, fn_curry, _environ)) = (false, fn_ptr.take_data());
|
||||||
let (fn_name, fn_curry, environ) = fn_ptr.take_data();
|
|
||||||
(fn_name, false, fn_curry, environ)
|
|
||||||
};
|
|
||||||
|
|
||||||
curry.extend(fn_curry.into_iter());
|
curry.extend(fn_curry.into_iter());
|
||||||
|
|
||||||
// Linked to scripted function?
|
// Linked to scripted function - short-circuit
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if let Some(fn_def) = fn_def {
|
if let Some(fn_def) = fn_def {
|
||||||
if fn_def.params.len() == curry.len() + a_expr.len() {
|
if fn_def.params.len() == curry.len() + args_expr.len() {
|
||||||
// Evaluate arguments
|
// Evaluate arguments
|
||||||
let mut arg_values = curry
|
let mut arg_values =
|
||||||
.into_iter()
|
FnArgsVec::with_capacity(curry.len() + args_expr.len());
|
||||||
.map(Ok)
|
arg_values.extend(curry);
|
||||||
.chain(a_expr.iter().map(|expr| -> Result<_, crate::RhaiError> {
|
for expr in args_expr {
|
||||||
let this_ptr = this_ptr.as_deref_mut();
|
let this_ptr = this_ptr.as_deref_mut();
|
||||||
self.get_arg_value(global, caches, scope, this_ptr, expr)
|
let (value, _) =
|
||||||
.map(|(v, ..)| v)
|
self.get_arg_value(global, caches, scope, this_ptr, expr)?;
|
||||||
}))
|
arg_values.push(value);
|
||||||
.collect::<RhaiResultOf<FnArgsVec<_>>>()?;
|
}
|
||||||
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
|
let args = &mut arg_values.iter_mut().collect::<FnArgsVec<_>>();
|
||||||
let scope = &mut Scope::new();
|
let scope = &mut Scope::new();
|
||||||
let environ = _environ.as_deref();
|
let environ = _environ.as_deref();
|
||||||
@ -1161,14 +1067,14 @@ impl Engine {
|
|||||||
name = &redirected;
|
name = &redirected;
|
||||||
|
|
||||||
// Shift the arguments
|
// Shift the arguments
|
||||||
first_arg = a_expr.get(0);
|
first_arg = args_expr.get(0);
|
||||||
if !a_expr.is_empty() {
|
if !args_expr.is_empty() {
|
||||||
a_expr = &a_expr[1..];
|
args_expr = &args_expr[1..];
|
||||||
}
|
}
|
||||||
total_args -= 1;
|
num_args -= 1;
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let args_len = total_args + curry.len();
|
let args_len = num_args + curry.len();
|
||||||
|
|
||||||
hashes = if !is_anon && !is_valid_function_name(name) {
|
hashes = if !is_anon && !is_valid_function_name(name) {
|
||||||
FnCallHashes::from_native_only(calc_fn_hash(None, name, args_len))
|
FnCallHashes::from_native_only(calc_fn_hash(None, name, args_len))
|
||||||
@ -1176,8 +1082,9 @@ impl Engine {
|
|||||||
FnCallHashes::from_hash(calc_fn_hash(None, name, args_len))
|
FnCallHashes::from_hash(calc_fn_hash(None, name, args_len))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Fn(fn_name)
|
// Handle Fn(fn_name)
|
||||||
KEYWORD_FN_PTR if total_args == 1 => {
|
KEYWORD_FN_PTR if num_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr, arg)?;
|
self.get_arg_value(global, caches, scope, this_ptr, arg)?;
|
||||||
@ -1192,22 +1099,18 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle curry(x, ...)
|
// Handle curry(x, ...)
|
||||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
KEYWORD_FN_PTR_CURRY if num_args > 1 => {
|
||||||
let first = first_arg.unwrap();
|
let first = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (first_arg_value, first_arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
||||||
|
|
||||||
if !arg_value.is_fnptr() {
|
let typ = first_arg_value.type_name();
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
let mut fn_ptr = first_arg_value.try_cast::<FnPtr>().ok_or_else(|| {
|
||||||
self.map_type_name(arg_value.type_name()),
|
self.make_type_mismatch_err::<FnPtr>(self.map_type_name(typ), first_arg_pos)
|
||||||
arg_pos,
|
})?;
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut fn_ptr = arg_value.cast::<FnPtr>();
|
|
||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
for expr in a_expr {
|
for expr in args_expr {
|
||||||
let (value, ..) =
|
let (value, ..) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||||
fn_ptr.add_curry(value);
|
fn_ptr.add_curry(value);
|
||||||
@ -1218,7 +1121,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle is_shared(var)
|
// Handle is_shared(var)
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if num_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, ..) =
|
let (arg_value, ..) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
||||||
@ -1227,7 +1130,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Handle is_def_fn(fn_name, arity)
|
// Handle is_def_fn(fn_name, arity)
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if num_args == 2 => {
|
||||||
let first = first_arg.unwrap();
|
let first = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
||||||
@ -1237,7 +1140,7 @@ impl Engine {
|
|||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
|
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr, &a_expr[0])?;
|
self.get_arg_value(global, caches, scope, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
let num_params = arg_value
|
let num_params = arg_value
|
||||||
.as_int()
|
.as_int()
|
||||||
@ -1255,7 +1158,7 @@ impl Engine {
|
|||||||
// Handle is_def_fn(this_type, fn_name, arity)
|
// Handle is_def_fn(this_type, fn_name, arity)
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if total_args == 3 => {
|
crate::engine::KEYWORD_IS_DEF_FN if num_args == 3 => {
|
||||||
let first = first_arg.unwrap();
|
let first = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
||||||
@ -1264,15 +1167,20 @@ impl Engine {
|
|||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
|
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) = self.get_arg_value(
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &a_expr[0])?;
|
global,
|
||||||
|
caches,
|
||||||
|
scope,
|
||||||
|
this_ptr.as_deref_mut(),
|
||||||
|
&args_expr[0],
|
||||||
|
)?;
|
||||||
|
|
||||||
let fn_name = arg_value
|
let fn_name = arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
|
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr, &a_expr[1])?;
|
self.get_arg_value(global, caches, scope, this_ptr, &args_expr[1])?;
|
||||||
|
|
||||||
let num_params = arg_value
|
let num_params = arg_value
|
||||||
.as_int()
|
.as_int()
|
||||||
@ -1291,7 +1199,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_var(fn_name)
|
// Handle is_def_var(fn_name)
|
||||||
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
KEYWORD_IS_DEF_VAR if num_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr, arg)?;
|
self.get_arg_value(global, caches, scope, this_ptr, arg)?;
|
||||||
@ -1302,7 +1210,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle eval(script)
|
// Handle eval(script)
|
||||||
KEYWORD_EVAL if total_args == 1 => {
|
KEYWORD_EVAL if num_args == 1 => {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1345,8 +1253,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call - except for Fn, curry, call and eval (handled above)
|
// Normal function call - except for Fn, curry, call and eval (handled above)
|
||||||
let mut arg_values = FnArgsVec::with_capacity(total_args);
|
let mut arg_values = FnArgsVec::with_capacity(num_args);
|
||||||
let mut args = FnArgsVec::with_capacity(total_args + curry.len());
|
let mut args = FnArgsVec::with_capacity(num_args + curry.len());
|
||||||
let mut is_ref_mut = false;
|
let mut is_ref_mut = false;
|
||||||
|
|
||||||
// Capture parent scope?
|
// Capture parent scope?
|
||||||
@ -1354,7 +1262,7 @@ impl Engine {
|
|||||||
// If so, do it separately because we cannot convert the first argument (if it is a simple
|
// If so, do it separately because we cannot convert the first argument (if it is a simple
|
||||||
// variable access) to &mut because `scope` is needed.
|
// variable access) to &mut because `scope` is needed.
|
||||||
if capture_scope && !scope.is_empty() {
|
if capture_scope && !scope.is_empty() {
|
||||||
for expr in first_arg.iter().copied().chain(a_expr.iter()) {
|
for expr in first_arg.iter().copied().chain(args_expr.iter()) {
|
||||||
let (value, ..) =
|
let (value, ..) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||||
arg_values.push(value.flatten());
|
arg_values.push(value.flatten());
|
||||||
@ -1374,18 +1282,20 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call with blank scope
|
// Call with blank scope
|
||||||
if total_args > 0 || !curry.is_empty() {
|
if num_args > 0 || !curry.is_empty() {
|
||||||
// If the first argument is a variable, and there is no curried arguments,
|
// If the first argument is a variable, and there is no curried arguments,
|
||||||
// convert to method-call style in order to leverage potential &mut first argument and
|
// convert to method-call style in order to leverage potential &mut first argument and
|
||||||
// avoid cloning the value
|
// avoid cloning the value
|
||||||
if curry.is_empty() && first_arg.map_or(false, |expr| expr.is_variable_access(false)) {
|
if curry.is_empty() && first_arg.map_or(false, |expr| expr.is_variable_access(false)) {
|
||||||
let first_expr = first_arg.unwrap();
|
let first_expr = first_arg.unwrap();
|
||||||
|
|
||||||
|
self.track_operation(global, first_expr.position())?;
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), first_expr)?;
|
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), first_expr)?;
|
||||||
|
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
for expr in a_expr {
|
for expr in args_expr {
|
||||||
let (value, ..) =
|
let (value, ..) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||||
arg_values.push(value.flatten());
|
arg_values.push(value.flatten());
|
||||||
@ -1398,8 +1308,6 @@ impl Engine {
|
|||||||
target = target.into_owned();
|
target = target.into_owned();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.track_operation(global, first_expr.position())?;
|
|
||||||
|
|
||||||
if target.is_shared() || target.is_temp_value() {
|
if target.is_shared() || target.is_temp_value() {
|
||||||
arg_values.insert(0, target.take_or_clone().flatten());
|
arg_values.insert(0, target.take_or_clone().flatten());
|
||||||
} else {
|
} else {
|
||||||
@ -1410,7 +1318,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// func(..., ...)
|
// func(..., ...)
|
||||||
for expr in first_arg.into_iter().chain(a_expr.iter()) {
|
for expr in first_arg.into_iter().chain(args_expr.iter()) {
|
||||||
let (value, ..) =
|
let (value, ..) =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||||
arg_values.push(value.flatten());
|
arg_values.push(value.flatten());
|
||||||
@ -1450,14 +1358,13 @@ impl Engine {
|
|||||||
// If so, convert to method-call style in order to leverage potential &mut first argument
|
// If so, convert to method-call style in order to leverage potential &mut first argument
|
||||||
// and avoid cloning the value
|
// and avoid cloning the value
|
||||||
if !args_expr.is_empty() && args_expr[0].is_variable_access(true) {
|
if !args_expr.is_empty() && args_expr[0].is_variable_access(true) {
|
||||||
|
// Get target reference to first argument
|
||||||
|
let first_arg = &args_expr[0];
|
||||||
|
|
||||||
|
self.track_operation(global, first_arg.position())?;
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(
|
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), first_arg)?;
|
||||||
global,
|
|
||||||
caches,
|
|
||||||
scope,
|
|
||||||
this_ptr.as_deref_mut(),
|
|
||||||
&args_expr[0],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
arg_values.push(Dynamic::UNIT);
|
arg_values.push(Dynamic::UNIT);
|
||||||
@ -1468,11 +1375,6 @@ impl Engine {
|
|||||||
arg_values.push(value.flatten());
|
arg_values.push(value.flatten());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get target reference to first argument
|
|
||||||
let first_arg = &args_expr[0];
|
|
||||||
|
|
||||||
self.track_operation(global, first_arg.position())?;
|
|
||||||
|
|
||||||
let target = self.search_scope_only(global, caches, scope, this_ptr, first_arg)?;
|
let target = self.search_scope_only(global, caches, scope, this_ptr, first_arg)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
@ -7,8 +7,8 @@ use crate::plugin::PluginFunction;
|
|||||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf,
|
calc_fn_hash, Dynamic, Engine, EvalContext, FnArgsVec, FuncArgs, Position, RhaiResult,
|
||||||
StaticVec, VarDefInfo, ERR,
|
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
||||||
};
|
};
|
||||||
use std::any::{type_name, TypeId};
|
use std::any::{type_name, TypeId};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -309,9 +309,9 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
let mut arg_values = StaticVec::new_const();
|
let mut arg_values = StaticVec::new_const();
|
||||||
args.parse(&mut arg_values);
|
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| {
|
.and_then(|result| {
|
||||||
// Bail out early if the return type needs no cast
|
// Bail out early if the return type needs no cast
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
@ -340,9 +340,9 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
let mut arg_values = StaticVec::new_const();
|
let mut arg_values = StaticVec::new_const();
|
||||||
args.parse(&mut arg_values);
|
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| {
|
.and_then(|result| {
|
||||||
// Bail out early if the return type needs no cast
|
// Bail out early if the return type needs no cast
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
@ -57,7 +57,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
// 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.
|
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||||
|
@ -118,7 +118,7 @@ impl FuncInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let params: FnArgsVec<_> = self
|
let params = self
|
||||||
.metadata
|
.metadata
|
||||||
.params_info
|
.params_info
|
||||||
.iter()
|
.iter()
|
||||||
@ -134,7 +134,7 @@ impl FuncInfo {
|
|||||||
};
|
};
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<FnArgsVec<_>>();
|
||||||
signature.push_str(¶ms.join(", "));
|
signature.push_str(¶ms.join(", "));
|
||||||
}
|
}
|
||||||
signature.push(')');
|
signature.push(')');
|
||||||
@ -932,7 +932,10 @@ impl Module {
|
|||||||
hash_fn: u64,
|
hash_fn: u64,
|
||||||
arg_names: impl IntoIterator<Item = S>,
|
arg_names: impl IntoIterator<Item = S>,
|
||||||
) -> &mut Self {
|
) -> &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)) {
|
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 {
|
let (param_names, return_type_name) = if param_names.len() > f.metadata.num_params {
|
||||||
@ -1053,13 +1056,12 @@ impl Module {
|
|||||||
let _arg_names = arg_names;
|
let _arg_names = arg_names;
|
||||||
let is_method = func.is_method();
|
let is_method = func.is_method();
|
||||||
|
|
||||||
let mut param_types: FnArgsVec<_> = arg_types
|
let param_types = arg_types
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, &type_id)| Self::map_type(!is_method || i > 0, type_id))
|
.map(|(i, &type_id)| Self::map_type(!is_method || i > 0, type_id))
|
||||||
.collect();
|
.collect::<FnArgsVec<_>>();
|
||||||
param_types.shrink_to_fit();
|
|
||||||
|
|
||||||
let is_dynamic = param_types
|
let is_dynamic = param_types
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1195,12 +1195,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
&& x.constant_args() // all arguments are constants
|
&& x.constant_args() // all arguments are constants
|
||||||
=> {
|
=> {
|
||||||
// First search for script-defined functions (can override built-in)
|
// First search for script-defined functions (can override built-in)
|
||||||
|
let _has_script_fn = false;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[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();
|
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;
|
|
||||||
|
|
||||||
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 arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<FnArgsVec<_>>>().unwrap();
|
||||||
|
|
||||||
let result = match x.name.as_str() {
|
let result = match x.name.as_str() {
|
||||||
|
@ -265,7 +265,7 @@ mod string_functions {
|
|||||||
///
|
///
|
||||||
/// print(text); // prints "hello, world!"
|
/// print(text); // prints "hello, world!"
|
||||||
///
|
///
|
||||||
/// x.truncate(10);
|
/// text.truncate(10);
|
||||||
///
|
///
|
||||||
/// print(text); // prints "hello, world!"
|
/// print(text); // prints "hello, world!"
|
||||||
/// ```
|
/// ```
|
||||||
@ -273,10 +273,10 @@ mod string_functions {
|
|||||||
if len > 0 {
|
if len > 0 {
|
||||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||||
let len = len.min(MAX_USIZE_INT) as usize;
|
let len = len.min(MAX_USIZE_INT) as usize;
|
||||||
let chars: StaticVec<_> = string.chars().collect();
|
if let Some((index, _)) = string.char_indices().nth(len) {
|
||||||
let copy = string.make_mut();
|
let copy = string.make_mut();
|
||||||
copy.clear();
|
copy.truncate(index);
|
||||||
copy.extend(chars.into_iter().take(len));
|
}
|
||||||
} else {
|
} else {
|
||||||
clear(string);
|
clear(string);
|
||||||
}
|
}
|
||||||
@ -1109,7 +1109,7 @@ mod string_functions {
|
|||||||
copy.clear();
|
copy.clear();
|
||||||
copy.extend(chars.iter().skip(offset).take(len));
|
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` < 0, position counts from the end of the string (`-1` is the last character).
|
||||||
/// * If `start` < -length of string, the string is not modified.
|
/// * If `start` < -length of string, the string is not modified.
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
/// * `reify! { `_variable_ or _expression_` => |`_temp-variable_`: `_type_`|` _code_ `)`
|
/// * `reify! { `_variable_ or _expression_` => |`_temp-variable_`: `_type_`|` _code_ `)`
|
||||||
/// * `reify! { `_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
|
/// * `reify! { `_variable_ or _expression_ `=>` `Option<`_type_`>` `)`
|
||||||
/// * `reify! { `_variable_ or _expression_ `=>` _type_ `)`
|
/// * `reify! { `_variable_ or _expression_ `=>` _type_ `)`
|
||||||
|
///
|
||||||
|
/// * `reify! { `_expression_ `=> !!!` _type_ `)` (unsafe, no type checks!)
|
||||||
macro_rules! reify {
|
macro_rules! reify {
|
||||||
($old:ident => |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
($old:ident => |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
|
||||||
#[allow(clippy::redundant_else)]
|
#[allow(clippy::redundant_else)]
|
||||||
@ -45,4 +47,11 @@ macro_rules! reify {
|
|||||||
($old:expr => $t:ty) => {
|
($old:expr => $t:ty) => {
|
||||||
reify! { $old => |v: $t| v, || unreachable!() }
|
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
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,10 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
|||||||
type Error = RhaiError;
|
type Error = RhaiError;
|
||||||
|
|
||||||
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
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 {
|
match self.0 .0 {
|
||||||
Union::Unit(..) => self.deserialize_unit(visitor),
|
Union::Unit(..) => self.deserialize_unit(visitor),
|
||||||
Union::Bool(..) => self.deserialize_bool(visitor),
|
Union::Bool(..) => self.deserialize_bool(visitor),
|
||||||
|
@ -156,7 +156,7 @@ impl ModuleMetadata<'_> {
|
|||||||
|
|
||||||
impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> {
|
impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> {
|
||||||
fn from(module: &'a crate::Module) -> Self {
|
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();
|
functions.sort();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -1184,89 +1184,89 @@ impl Dynamic {
|
|||||||
self.flatten_in_place();
|
self.flatten_in_place();
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||||
return Some(reify! { self => T });
|
return Some(reify! { self => !!! T });
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Unit(..) => Some(reify! { () => T }),
|
Union::Unit(..) => Some(reify! { () => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Int(n, ..) => Some(reify! { n => T }),
|
Union::Int(n, ..) => Some(reify! { n => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Float(v, ..) => Some(reify! { *v => T }),
|
Union::Float(v, ..) => Some(reify! { *v => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
if TypeId::of::<T>() == TypeId::of::<rust_decimal::Decimal>() {
|
if TypeId::of::<T>() == TypeId::of::<rust_decimal::Decimal>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Decimal(v, ..) => Some(reify! { *v => T }),
|
Union::Decimal(v, ..) => Some(reify! { *v => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Bool(b, ..) => Some(reify! { b => T }),
|
Union::Bool(b, ..) => Some(reify! { b => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Str(s, ..) => Some(reify! { s => T }),
|
Union::Str(s, ..) => Some(reify! { s => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Str(s, ..) => Some(reify! { s.to_string() => T }),
|
Union::Str(s, ..) => Some(reify! { s.to_string() => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Char(c, ..) => Some(reify! { c => T }),
|
Union::Char(c, ..) => Some(reify! { c => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Array(a, ..) => Some(reify! { *a => T }),
|
Union::Array(a, ..) => Some(reify! { *a => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Blob>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Blob>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Blob(b, ..) => Some(reify! { *b => T }),
|
Union::Blob(b, ..) => Some(reify! { *b => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<crate::Map>() {
|
if TypeId::of::<T>() == TypeId::of::<crate::Map>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Map(m, ..) => Some(reify! { *m => T }),
|
Union::Map(m, ..) => Some(reify! { *m => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::FnPtr(f, ..) => Some(reify! { *f => T }),
|
Union::FnPtr(f, ..) => Some(reify! { *f => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_time"))]
|
#[cfg(not(feature = "no_time"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::TimeStamp(t, ..) => Some(reify! { *t => T }),
|
Union::TimeStamp(t, ..) => Some(reify! { *t => !!! T }),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1306,7 +1306,7 @@ impl Dynamic {
|
|||||||
pub fn cast<T: Any + Clone>(self) -> T {
|
pub fn cast<T: Any + Clone>(self) -> T {
|
||||||
// Bail out early if the return type needs no cast
|
// Bail out early if the return type needs no cast
|
||||||
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
if TypeId::of::<T>() == TypeId::of::<Self>() {
|
||||||
return reify! { self => T };
|
return reify! { self => !!! T };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -2038,7 +2038,7 @@ impl Dynamic {
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
Union::Blob(b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
|
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"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => {
|
Union::Shared(ref cell, ..) => {
|
||||||
@ -2061,7 +2061,7 @@ impl Dynamic {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
Union::Blob(ref b, ..) if TypeId::of::<T>() == TypeId::of::<u8>() => {
|
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()),
|
_ => Err(cell.type_name()),
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,9 @@ use rhai::{
|
|||||||
serde::{from_dynamic, to_dynamic},
|
serde::{from_dynamic, to_dynamic},
|
||||||
Dynamic, Engine, EvalAltResult, ImmutableString, Scope, INT,
|
Dynamic, Engine, EvalAltResult, ImmutableString, Scope, INT,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use rhai::Array;
|
use rhai::Array;
|
||||||
@ -383,6 +384,37 @@ fn test_serde_de_primary_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn test_serde_de_integer_types() -> Result<(), Box<EvalAltResult>> {
|
fn test_serde_de_integer_types() -> Result<(), Box<EvalAltResult>> {
|
||||||
assert_eq!(42, from_dynamic::<i8>(&Dynamic::from(42 as INT))?);
|
assert_eq!(42, from_dynamic::<i8>(&Dynamic::from(42 as INT))?);
|
||||||
|
Loading…
Reference in New Issue
Block a user