Use Scope::flatten_clone for capturing.

This commit is contained in:
Stephen Chung 2020-07-30 23:29:30 +08:00
parent 7d4620d0d9
commit a7ff207636
3 changed files with 34 additions and 18 deletions

View File

@ -36,6 +36,7 @@ use crate::engine::{Map, Target, FN_GET, FN_SET};
use crate::stdlib::{ use crate::stdlib::{
any::{type_name, TypeId}, any::{type_name, TypeId},
boxed::Box, boxed::Box,
collections::HashSet,
convert::TryFrom, convert::TryFrom,
format, format,
iter::{empty, once}, 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 // Add captured variables into scope
#[cfg(not(feature = "no_capture"))] #[cfg(not(feature = "no_capture"))]
fn add_captured_variables_into_scope<'s>( fn add_captured_variables_into_scope<'s>(
externals: &[String], externals: &HashSet<String>,
captured: &'s Scope<'s>, captured: Scope<'s>,
scope: &mut Scope<'s>, scope: &mut Scope<'s>,
) { ) {
externals captured
.iter() .into_iter()
.map(|var_name| captured.get_entry(var_name)) .filter(|ScopeEntry { name, .. }| externals.contains(name.as_ref()))
.filter(Option::is_some)
.map(Option::unwrap)
.for_each( .for_each(
|ScopeEntry { |ScopeEntry {
name, typ, value, .. name, typ, value, ..
}| { }| {
match typ { match typ {
ScopeEntryType::Normal => scope.push(name.clone(), value.clone()), ScopeEntryType::Normal => scope.push(name, value),
ScopeEntryType::Constant => scope.push_constant(name.clone(), value.clone()), ScopeEntryType::Constant => scope.push_constant(name, value),
}; };
}, },
); );
@ -451,7 +450,7 @@ impl Engine {
// Add captured variables into scope // Add captured variables into scope
#[cfg(not(feature = "no_capture"))] #[cfg(not(feature = "no_capture"))]
if let Some(captured) = &capture { if let Some(captured) = capture {
add_captured_variables_into_scope(&func.externals, captured, scope); add_captured_variables_into_scope(&func.externals, captured, scope);
} }
@ -801,7 +800,7 @@ impl Engine {
let mut args: StaticVec<_>; let mut args: StaticVec<_>;
let mut is_ref = false; let mut is_ref = false;
let capture = if capture && !scope.is_empty() { let capture = if capture && !scope.is_empty() {
Some(scope.clone()) Some(scope.flatten_clone())
} else { } else {
None None
}; };
@ -875,7 +874,7 @@ impl Engine {
#[cfg(not(feature = "no_capture"))] #[cfg(not(feature = "no_capture"))]
let capture = if capture && !scope.is_empty() { let capture = if capture && !scope.is_empty() {
Some(scope.clone()) Some(scope.flatten_clone())
} else { } else {
None None
}; };
@ -952,7 +951,7 @@ impl Engine {
// Add captured variables into scope // Add captured variables into scope
#[cfg(not(feature = "no_capture"))] #[cfg(not(feature = "no_capture"))]
if let Some(captured) = &capture { if let Some(captured) = capture {
add_captured_variables_into_scope(&func.externals, captured, scope); add_captured_variables_into_scope(&func.externals, captured, scope);
} }

View File

@ -25,7 +25,7 @@ use crate::stdlib::{
borrow::Cow, borrow::Cow,
boxed::Box, boxed::Box,
char, char,
collections::HashMap, collections::{HashMap, HashSet},
fmt, format, fmt, format,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
iter::empty, iter::empty,
@ -355,7 +355,7 @@ impl fmt::Display for FnAccess {
/// ## WARNING /// ## WARNING
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone)]
pub struct ScriptFnDef { pub struct ScriptFnDef {
/// Function name. /// Function name.
pub name: ImmutableString, pub name: ImmutableString,
@ -365,7 +365,7 @@ pub struct ScriptFnDef {
pub params: StaticVec<String>, pub params: StaticVec<String>,
/// Access to external variables. /// Access to external variables.
#[cfg(not(feature = "no_capture"))] #[cfg(not(feature = "no_capture"))]
pub externals: StaticVec<String>, pub externals: HashSet<String>,
/// Function body. /// Function body.
pub body: Stmt, pub body: Stmt,
/// Position of the function definition. /// Position of the function definition.

View File

@ -4,7 +4,9 @@ use crate::any::{Dynamic, Variant};
use crate::parser::{map_dynamic_to_expr, Expr}; use crate::parser::{map_dynamic_to_expr, Expr};
use crate::token::Position; 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. /// Type of an entry in the Scope.
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
@ -389,13 +391,28 @@ impl<'a> Scope<'a> {
self 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. /// Get an iterator to entries in the Scope.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> { pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
self.0.into_iter() 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<Item = &Entry> { pub(crate) fn to_iter(&self) -> impl Iterator<Item = &Entry> {
self.0.iter().rev() // Always search a Scope in reverse order self.0.iter().rev() // Always search a Scope in reverse order
} }