Add has_override and script calls to function resolution cache.
This commit is contained in:
parent
a54b88a8b0
commit
7b87f81850
122
src/fn_call.rs
122
src/fn_call.rs
@ -479,6 +479,7 @@ impl Engine {
|
|||||||
pub(crate) fn has_override_by_name_and_arguments(
|
pub(crate) fn has_override_by_name_and_arguments(
|
||||||
&self,
|
&self,
|
||||||
mods: Option<&Imports>,
|
mods: Option<&Imports>,
|
||||||
|
state: Option<&mut State>,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
arg_types: impl AsRef<[TypeId]>,
|
arg_types: impl AsRef<[TypeId]>,
|
||||||
@ -488,7 +489,7 @@ impl Engine {
|
|||||||
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned());
|
let hash_fn = calc_native_fn_hash(empty(), fn_name, arg_types.iter().cloned());
|
||||||
let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len());
|
let hash_script = calc_script_fn_hash(empty(), fn_name, arg_types.len());
|
||||||
|
|
||||||
self.has_override(mods, lib, hash_fn, hash_script, pub_only)
|
self.has_override(mods, state, lib, hash_fn, hash_script, pub_only)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has a system function an override?
|
// Has a system function an override?
|
||||||
@ -496,23 +497,54 @@ impl Engine {
|
|||||||
pub(crate) fn has_override(
|
pub(crate) fn has_override(
|
||||||
&self,
|
&self,
|
||||||
mods: Option<&Imports>,
|
mods: Option<&Imports>,
|
||||||
|
mut state: Option<&mut State>,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
hash_fn: Option<NonZeroU64>,
|
hash_fn: Option<NonZeroU64>,
|
||||||
hash_script: Option<NonZeroU64>,
|
hash_script: Option<NonZeroU64>,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
// Check if it is already in the cache
|
||||||
|
if let Some(state) = state.as_mut() {
|
||||||
|
if let Some(hash) = hash_script {
|
||||||
|
match state.functions_cache.get(&hash) {
|
||||||
|
Some(v) => return v.is_some(),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(hash) = hash_fn {
|
||||||
|
match state.functions_cache.get(&hash) {
|
||||||
|
Some(v) => return v.is_some(),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// First check script-defined functions
|
// First check script-defined functions
|
||||||
hash_script.map(|hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))).unwrap_or(false)
|
let r = hash_script.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only)))
|
||||||
//|| hash_fn.map(|hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))).unwrap_or(false)
|
//|| hash_fn.map_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only)))
|
||||||
// Then check registered functions
|
// Then check registered functions
|
||||||
//|| hash_script.map(|hash| self.global_namespace.contains_fn(hash, pub_only)).unwrap_or(false)
|
//|| hash_script.map_or(false, |hash| self.global_namespace.contains_fn(hash, pub_only))
|
||||||
|| hash_fn.map(|hash| self.global_namespace.contains_fn(hash, false)).unwrap_or(false)
|
|| hash_fn.map_or(false, |hash| self.global_namespace.contains_fn(hash, false))
|
||||||
// Then check packages
|
// Then check packages
|
||||||
|| hash_script.map(|hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))).unwrap_or(false)
|
|| hash_script.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false)))
|
||||||
|| hash_fn.map(|hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))).unwrap_or(false)
|
|| hash_fn.map_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false)))
|
||||||
// Then check imported modules
|
// Then check imported modules
|
||||||
|| hash_script.map(|hash| mods.map(|m| m.contains_fn(hash)).unwrap_or(false)).unwrap_or(false)
|
|| hash_script.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash)))
|
||||||
|| hash_fn.map(|hash| mods.map(|m| m.contains_fn(hash)).unwrap_or(false)).unwrap_or(false)
|
|| hash_fn.map_or(false, |hash| mods.map_or(false, |m| m.contains_fn(hash)));
|
||||||
|
|
||||||
|
// If there is no override, put that information into the cache
|
||||||
|
if !r {
|
||||||
|
if let Some(state) = state.as_mut() {
|
||||||
|
if let Some(hash) = hash_script {
|
||||||
|
state.functions_cache.insert(hash, None);
|
||||||
|
}
|
||||||
|
if let Some(hash) = hash_fn {
|
||||||
|
state.functions_cache.insert(hash, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
/// Perform an actual function call, native Rust or scripted, taking care of special functions.
|
||||||
@ -551,7 +583,14 @@ impl Engine {
|
|||||||
// type_of
|
// type_of
|
||||||
KEYWORD_TYPE_OF
|
KEYWORD_TYPE_OF
|
||||||
if args.len() == 1
|
if args.len() == 1
|
||||||
&& !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) =>
|
&& !self.has_override(
|
||||||
|
Some(mods),
|
||||||
|
Some(state),
|
||||||
|
lib,
|
||||||
|
hash_fn,
|
||||||
|
hash_script,
|
||||||
|
pub_only,
|
||||||
|
) =>
|
||||||
{
|
{
|
||||||
Ok((
|
Ok((
|
||||||
self.map_type_name(args[0].type_name()).to_string().into(),
|
self.map_type_name(args[0].type_name()).to_string().into(),
|
||||||
@ -563,7 +602,14 @@ impl Engine {
|
|||||||
// by a function pointer so it isn't caught at parse time.
|
// by a function pointer so it isn't caught at parse time.
|
||||||
KEYWORD_FN_PTR | KEYWORD_EVAL
|
KEYWORD_FN_PTR | KEYWORD_EVAL
|
||||||
if args.len() == 1
|
if args.len() == 1
|
||||||
&& !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) =>
|
&& !self.has_override(
|
||||||
|
Some(mods),
|
||||||
|
Some(state),
|
||||||
|
lib,
|
||||||
|
hash_fn,
|
||||||
|
hash_script,
|
||||||
|
pub_only,
|
||||||
|
) =>
|
||||||
{
|
{
|
||||||
EvalAltResult::ErrorRuntime(
|
EvalAltResult::ErrorRuntime(
|
||||||
format!(
|
format!(
|
||||||
@ -579,25 +625,31 @@ impl Engine {
|
|||||||
// Script-like function found
|
// Script-like function found
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ if hash_script.is_some()
|
_ if hash_script.is_some()
|
||||||
&& self.has_override(Some(mods), lib, None, hash_script, pub_only) =>
|
&& self.has_override(Some(mods), Some(state), lib, None, hash_script, pub_only) =>
|
||||||
{
|
{
|
||||||
let hash_script = hash_script.unwrap();
|
let hash_script = hash_script.unwrap();
|
||||||
|
|
||||||
// Get function
|
// Check if function access already in the cache
|
||||||
let (func, mut source) = lib
|
let (func, source) = state
|
||||||
.iter()
|
.functions_cache
|
||||||
.find_map(|&m| {
|
.entry(hash_script)
|
||||||
m.get_fn(hash_script, pub_only)
|
.or_insert_with(|| {
|
||||||
.map(|f| (f, m.id_raw().cloned()))
|
lib.iter()
|
||||||
|
.find_map(|&m| {
|
||||||
|
m.get_fn(hash_script, pub_only)
|
||||||
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
||||||
|
})
|
||||||
|
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
|
||||||
|
.or_else(|| {
|
||||||
|
self.global_modules.iter().find_map(|m| {
|
||||||
|
m.get_fn(hash_script, false)
|
||||||
|
.map(|f| (f.clone(), m.id_raw().cloned()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
//.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.id_raw().clone()))))
|
||||||
})
|
})
|
||||||
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
|
.as_ref()
|
||||||
.or_else(|| {
|
.map(|(f, s)| (f.clone(), s.clone()))
|
||||||
self.global_modules.iter().find_map(|m| {
|
|
||||||
m.get_fn(hash_script, false)
|
|
||||||
.map(|f| (f, m.id_raw().cloned()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
//.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.id_raw().clone()))))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(func.is_script());
|
assert!(func.is_script());
|
||||||
@ -624,7 +676,8 @@ impl Engine {
|
|||||||
// Method call of script function - map first argument to `this`
|
// Method call of script function - map first argument to `this`
|
||||||
let (first, rest) = args.split_first_mut().unwrap();
|
let (first, rest) = args.split_first_mut().unwrap();
|
||||||
|
|
||||||
mem::swap(&mut state.source, &mut source);
|
let orig_source = mem::take(&mut state.source);
|
||||||
|
state.source = source;
|
||||||
|
|
||||||
let level = _level + 1;
|
let level = _level + 1;
|
||||||
|
|
||||||
@ -641,7 +694,7 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Restore the original source
|
// Restore the original source
|
||||||
state.source = source;
|
state.source = orig_source;
|
||||||
|
|
||||||
result?
|
result?
|
||||||
} else {
|
} else {
|
||||||
@ -650,7 +703,8 @@ impl Engine {
|
|||||||
let mut backup: ArgBackup = Default::default();
|
let mut backup: ArgBackup = Default::default();
|
||||||
backup.change_first_arg_to_copy(is_ref, args);
|
backup.change_first_arg_to_copy(is_ref, args);
|
||||||
|
|
||||||
mem::swap(&mut state.source, &mut source);
|
let orig_source = mem::take(&mut state.source);
|
||||||
|
state.source = source;
|
||||||
|
|
||||||
let level = _level + 1;
|
let level = _level + 1;
|
||||||
|
|
||||||
@ -658,7 +712,7 @@ impl Engine {
|
|||||||
.call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level);
|
.call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level);
|
||||||
|
|
||||||
// Restore the original source
|
// Restore the original source
|
||||||
state.source = source;
|
state.source = orig_source;
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
backup.restore_first_arg(args);
|
backup.restore_first_arg(args);
|
||||||
@ -926,7 +980,7 @@ impl Engine {
|
|||||||
|
|
||||||
if name == KEYWORD_FN_PTR_CALL
|
if name == KEYWORD_FN_PTR_CALL
|
||||||
&& args_expr.len() >= 1
|
&& args_expr.len() >= 1
|
||||||
&& !self.has_override(Some(mods), lib, None, hash_script, pub_only)
|
&& !self.has_override(Some(mods), Some(state), lib, None, hash_script, pub_only)
|
||||||
{
|
{
|
||||||
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
let fn_ptr = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?;
|
||||||
|
|
||||||
@ -956,7 +1010,7 @@ impl Engine {
|
|||||||
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
|
if name == KEYWORD_FN_PTR && args_expr.len() == 1 {
|
||||||
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) {
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
return self
|
return self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?
|
||||||
@ -1008,7 +1062,7 @@ impl Engine {
|
|||||||
if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 {
|
if name == KEYWORD_IS_DEF_VAR && args_expr.len() == 1 {
|
||||||
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) {
|
||||||
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.as_str().map_err(|err| {
|
||||||
@ -1022,7 +1076,7 @@ impl Engine {
|
|||||||
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
||||||
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
if !self.has_override(Some(mods), lib, hash_fn, hash_script, pub_only) {
|
if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let script =
|
let script =
|
||||||
|
@ -619,8 +619,7 @@ impl Module {
|
|||||||
if public_only {
|
if public_only {
|
||||||
self.functions
|
self.functions
|
||||||
.get(&hash_fn)
|
.get(&hash_fn)
|
||||||
.map(|FuncInfo { access, .. }| access.is_public())
|
.map_or(false, |FuncInfo { access, .. }| access.is_public())
|
||||||
.unwrap_or(false)
|
|
||||||
} else {
|
} else {
|
||||||
self.functions.contains_key(&hash_fn)
|
self.functions.contains_key(&hash_fn)
|
||||||
}
|
}
|
||||||
|
@ -674,7 +674,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
// Search for overloaded operators (can override built-in).
|
// Search for overloaded operators (can override built-in).
|
||||||
if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
|
if !state.engine.has_override_by_name_and_arguments(Some(&state.mods), None, state.lib, x.name.as_ref(), arg_types.as_ref(), false) {
|
||||||
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(result) = run_builtin_binary_op(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.ok().flatten()
|
.ok().flatten()
|
||||||
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
.and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
|
@ -31,8 +31,9 @@ mod fn_ptr_functions {
|
|||||||
false
|
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);
|
||||||
|
|
||||||
ctx.engine()
|
ctx.engine()
|
||||||
.has_override(ctx.mods, ctx.lib, None, hash_script, true)
|
.has_override(ctx.mods, None, ctx.lib, None, hash_script, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1731,8 +1731,7 @@ fn parse_binary_op(
|
|||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.get(c)
|
.get(c)
|
||||||
.map(Option::is_some)
|
.map_or(false, Option::is_some)
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
state.engine.custom_keywords.get(c).unwrap().unwrap().get()
|
state.engine.custom_keywords.get(c).unwrap().unwrap().get()
|
||||||
} else {
|
} else {
|
||||||
@ -1763,8 +1762,7 @@ fn parse_binary_op(
|
|||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.get(c)
|
.get(c)
|
||||||
.map(Option::is_some)
|
.map_or(false, Option::is_some)
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
{
|
||||||
state.engine.custom_keywords.get(c).unwrap().unwrap().get()
|
state.engine.custom_keywords.get(c).unwrap().unwrap().get()
|
||||||
} else {
|
} else {
|
||||||
@ -1875,8 +1873,7 @@ fn parse_binary_op(
|
|||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.get(&s)
|
.get(&s)
|
||||||
.map(Option::is_some)
|
.map_or(false, Option::is_some) =>
|
||||||
.unwrap_or(false) =>
|
|
||||||
{
|
{
|
||||||
let hash_script = if is_valid_identifier(s.chars()) {
|
let hash_script = if is_valid_identifier(s.chars()) {
|
||||||
// Accept non-native functions for custom operators
|
// Accept non-native functions for custom operators
|
||||||
|
@ -149,8 +149,7 @@ impl Engine {
|
|||||||
s if segments.is_empty()
|
s if segments.is_empty()
|
||||||
&& token
|
&& token
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|v| v.is_keyword() || v.is_reserved())
|
.map_or(false, |v| v.is_keyword() || v.is_reserved()) =>
|
||||||
.unwrap_or(false) =>
|
|
||||||
{
|
{
|
||||||
return Err(LexError::ImproperSymbol(
|
return Err(LexError::ImproperSymbol(
|
||||||
s.to_string(),
|
s.to_string(),
|
||||||
|
Loading…
Reference in New Issue
Block a user