From 7b87f8185076f61db22144d266e247d303d3d572 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 7 Feb 2021 15:09:27 +0800 Subject: [PATCH] Add has_override and script calls to function resolution cache. --- src/fn_call.rs | 122 ++++++++++++++++++++++++++++----------- src/module/mod.rs | 3 +- src/optimize.rs | 2 +- src/packages/fn_basic.rs | 3 +- src/parser.rs | 9 +-- src/syntax.rs | 3 +- 6 files changed, 96 insertions(+), 46 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index e309a8db..e038e845 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -479,6 +479,7 @@ impl Engine { pub(crate) fn has_override_by_name_and_arguments( &self, mods: Option<&Imports>, + state: Option<&mut State>, lib: &[&Module], fn_name: &str, 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_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? @@ -496,23 +497,54 @@ impl Engine { pub(crate) fn has_override( &self, mods: Option<&Imports>, + mut state: Option<&mut State>, lib: &[&Module], hash_fn: Option, hash_script: Option, pub_only: 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 - hash_script.map(|hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))).unwrap_or(false) - //|| hash_fn.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_or(false, |hash| lib.iter().any(|&m| m.contains_fn(hash, pub_only))) // Then check registered functions - //|| hash_script.map(|hash| self.global_namespace.contains_fn(hash, pub_only)).unwrap_or(false) - || hash_fn.map(|hash| self.global_namespace.contains_fn(hash, false)).unwrap_or(false) + //|| hash_script.map_or(false, |hash| self.global_namespace.contains_fn(hash, pub_only)) + || hash_fn.map_or(false, |hash| self.global_namespace.contains_fn(hash, false)) // Then check packages - || hash_script.map(|hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))).unwrap_or(false) - || hash_fn.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_or(false, |hash| self.global_modules.iter().any(|m| m.contains_fn(hash, false))) // Then check imported modules - || hash_script.map(|hash| mods.map(|m| m.contains_fn(hash)).unwrap_or(false)).unwrap_or(false) - || hash_fn.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_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. @@ -551,7 +583,14 @@ impl Engine { // type_of KEYWORD_TYPE_OF 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(( 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. KEYWORD_FN_PTR | KEYWORD_EVAL 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( format!( @@ -579,25 +625,31 @@ impl Engine { // Script-like function found #[cfg(not(feature = "no_function"))] _ 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(); - // Get function - let (func, mut source) = lib - .iter() - .find_map(|&m| { - m.get_fn(hash_script, pub_only) - .map(|f| (f, m.id_raw().cloned())) + // Check if function access already in the cache + let (func, source) = state + .functions_cache + .entry(hash_script) + .or_insert_with(|| { + 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)) - .or_else(|| { - 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())))) + .as_ref() + .map(|(f, s)| (f.clone(), s.clone())) .unwrap(); assert!(func.is_script()); @@ -624,7 +676,8 @@ impl Engine { // Method call of script function - map first argument to `this` 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; @@ -641,7 +694,7 @@ impl Engine { ); // Restore the original source - state.source = source; + state.source = orig_source; result? } else { @@ -650,7 +703,8 @@ impl Engine { let mut backup: ArgBackup = Default::default(); 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; @@ -658,7 +712,7 @@ impl Engine { .call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level); // Restore the original source - state.source = source; + state.source = orig_source; // Restore the original reference backup.restore_first_arg(args); @@ -926,7 +980,7 @@ impl Engine { if name == KEYWORD_FN_PTR_CALL && 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)?; @@ -956,7 +1010,7 @@ impl Engine { if name == KEYWORD_FN_PTR && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - 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 return self .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 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - 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 = self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; let var_name = var_name.as_str().map_err(|err| { @@ -1022,7 +1076,7 @@ impl Engine { if name == KEYWORD_EVAL && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); - 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 let prev_len = scope.len(); let script = diff --git a/src/module/mod.rs b/src/module/mod.rs index 64788b74..35e12393 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -619,8 +619,7 @@ impl Module { if public_only { self.functions .get(&hash_fn) - .map(|FuncInfo { access, .. }| access.is_public()) - .unwrap_or(false) + .map_or(false, |FuncInfo { access, .. }| access.is_public()) } else { self.functions.contains_key(&hash_fn) } diff --git a/src/optimize.rs b/src/optimize.rs index 6a0864f0..7a7296b4 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -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(); // 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]) .ok().flatten() .and_then(|result| map_dynamic_to_expr(result, *pos)) diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index ad1586ab..c2943498 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -31,8 +31,9 @@ mod fn_ptr_functions { false } else { let hash_script = calc_script_fn_hash(empty(), fn_name, num_params as usize); + ctx.engine() - .has_override(ctx.mods, ctx.lib, None, hash_script, true) + .has_override(ctx.mods, None, ctx.lib, None, hash_script, true) } } } diff --git a/src/parser.rs b/src/parser.rs index 41c72a88..dd4b5812 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1731,8 +1731,7 @@ fn parse_binary_op( .engine .custom_keywords .get(c) - .map(Option::is_some) - .unwrap_or(false) + .map_or(false, Option::is_some) { state.engine.custom_keywords.get(c).unwrap().unwrap().get() } else { @@ -1763,8 +1762,7 @@ fn parse_binary_op( .engine .custom_keywords .get(c) - .map(Option::is_some) - .unwrap_or(false) + .map_or(false, Option::is_some) { state.engine.custom_keywords.get(c).unwrap().unwrap().get() } else { @@ -1875,8 +1873,7 @@ fn parse_binary_op( .engine .custom_keywords .get(&s) - .map(Option::is_some) - .unwrap_or(false) => + .map_or(false, Option::is_some) => { let hash_script = if is_valid_identifier(s.chars()) { // Accept non-native functions for custom operators diff --git a/src/syntax.rs b/src/syntax.rs index 78ce64f7..1ec40876 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -149,8 +149,7 @@ impl Engine { s if segments.is_empty() && token .as_ref() - .map(|v| v.is_keyword() || v.is_reserved()) - .unwrap_or(false) => + .map_or(false, |v| v.is_keyword() || v.is_reserved()) => { return Err(LexError::ImproperSymbol( s.to_string(),