diff --git a/src/fn_call.rs b/src/fn_call.rs index ca1aef51..6672d746 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -36,6 +36,7 @@ use crate::engine::{Map, Target, FN_GET, FN_SET}; use crate::stdlib::{ any::{type_name, TypeId}, boxed::Box, + collections::HashSet, convert::TryFrom, format, iter::{empty, once}, @@ -109,22 +110,20 @@ fn restore_first_arg<'a>(old_this_ptr: Option<&'a mut Dynamic>, args: &mut FnCal // Add captured variables into scope #[cfg(not(feature = "no_capture"))] fn add_captured_variables_into_scope<'s>( - externals: &[String], - captured: &'s Scope<'s>, + externals: &HashSet, + captured: Scope<'s>, scope: &mut Scope<'s>, ) { - externals - .iter() - .map(|var_name| captured.get_entry(var_name)) - .filter(Option::is_some) - .map(Option::unwrap) + captured + .into_iter() + .filter(|ScopeEntry { name, .. }| externals.contains(name.as_ref())) .for_each( |ScopeEntry { name, typ, value, .. }| { match typ { - ScopeEntryType::Normal => scope.push(name.clone(), value.clone()), - ScopeEntryType::Constant => scope.push_constant(name.clone(), value.clone()), + ScopeEntryType::Normal => scope.push(name, value), + ScopeEntryType::Constant => scope.push_constant(name, value), }; }, ); @@ -451,7 +450,7 @@ impl Engine { // Add captured variables into scope #[cfg(not(feature = "no_capture"))] - if let Some(captured) = &capture { + if let Some(captured) = capture { add_captured_variables_into_scope(&func.externals, captured, scope); } @@ -801,7 +800,7 @@ impl Engine { let mut args: StaticVec<_>; let mut is_ref = false; let capture = if capture && !scope.is_empty() { - Some(scope.clone()) + Some(scope.flatten_clone()) } else { None }; @@ -875,7 +874,7 @@ impl Engine { #[cfg(not(feature = "no_capture"))] let capture = if capture && !scope.is_empty() { - Some(scope.clone()) + Some(scope.flatten_clone()) } else { None }; @@ -952,7 +951,7 @@ impl Engine { // Add captured variables into scope #[cfg(not(feature = "no_capture"))] - if let Some(captured) = &capture { + if let Some(captured) = capture { add_captured_variables_into_scope(&func.externals, captured, scope); } diff --git a/src/parser.rs b/src/parser.rs index c8f4e314..3fd6365b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -25,7 +25,7 @@ use crate::stdlib::{ borrow::Cow, boxed::Box, char, - collections::HashMap, + collections::{HashMap, HashSet}, fmt, format, hash::{Hash, Hasher}, iter::empty, @@ -355,7 +355,7 @@ impl fmt::Display for FnAccess { /// ## WARNING /// /// This type is volatile and may change. -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone)] pub struct ScriptFnDef { /// Function name. pub name: ImmutableString, @@ -365,7 +365,7 @@ pub struct ScriptFnDef { pub params: StaticVec, /// Access to external variables. #[cfg(not(feature = "no_capture"))] - pub externals: StaticVec, + pub externals: HashSet, /// Function body. pub body: Stmt, /// Position of the function definition. diff --git a/src/scope.rs b/src/scope.rs index 3e43b850..b2024564 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -4,7 +4,9 @@ use crate::any::{Dynamic, Variant}; use crate::parser::{map_dynamic_to_expr, Expr}; use crate::token::Position; -use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec}; +use crate::stdlib::{ + borrow::Cow, boxed::Box, collections::HashMap, iter, string::String, vec::Vec, +}; /// Type of an entry in the Scope. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] @@ -389,13 +391,28 @@ impl<'a> Scope<'a> { self } + /// Clone the Scope, keeping only the last instances of each variable name. + /// Shadowed variables are omitted in the copy. + #[cfg(not(feature = "no_capture"))] + pub(crate) fn flatten_clone(&self) -> Self { + let mut entries: HashMap<&str, Entry> = Default::default(); + + self.0.iter().rev().for_each(|entry| { + entries + .entry(entry.name.as_ref()) + .or_insert_with(|| entry.clone()); + }); + + Self(entries.into_iter().map(|(_, v)| v).collect()) + } + /// Get an iterator to entries in the Scope. #[cfg(not(feature = "no_module"))] pub(crate) fn into_iter(self) -> impl Iterator> { self.0.into_iter() } - /// Get an iterator to entries in the Scope. + /// Get an iterator to entries in the Scope in reverse order. pub(crate) fn to_iter(&self) -> impl Iterator { self.0.iter().rev() // Always search a Scope in reverse order }