Add has_override and script calls to function resolution cache.

This commit is contained in:
Stephen Chung 2021-02-07 15:09:27 +08:00
parent a54b88a8b0
commit 7b87f81850
6 changed files with 96 additions and 46 deletions

View File

@ -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 =

View File

@ -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)
} }

View File

@ -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))

View File

@ -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)
} }
} }
} }

View File

@ -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

View File

@ -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(),