Recursively store encapsulated environment into values.

This commit is contained in:
Stephen Chung 2022-12-13 13:06:28 +08:00
parent d350a948e7
commit db7410776a
3 changed files with 53 additions and 35 deletions

View File

@ -834,13 +834,13 @@ impl Engine {
let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>(); let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>();
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let (fn_name, is_anon, fn_curry, environ, fn_def) = { let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
let is_anon = fn_ptr.is_anonymous(); let is_anon = fn_ptr.is_anonymous();
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data(); let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
(fn_name, is_anon, fn_curry, environ, fn_def) (fn_name, is_anon, fn_curry, environ, fn_def)
}; };
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let (fn_name, is_anon, environ, fn_curry) = { let (fn_name, is_anon, fn_curry, _environ) = {
let (fn_name, fn_curry, environ) = fn_ptr.take_data(); let (fn_name, fn_curry, environ) = fn_ptr.take_data();
(fn_name, false, fn_curry, environ) (fn_name, false, fn_curry, environ)
}; };
@ -868,7 +868,7 @@ impl Engine {
caches, caches,
&mut Scope::new(), &mut Scope::new(),
target, target,
environ.as_deref(), _environ.as_deref(),
&fn_def, &fn_def,
args, args,
true, true,
@ -1043,13 +1043,13 @@ impl Engine {
let fn_ptr = arg_value.cast::<FnPtr>(); let fn_ptr = arg_value.cast::<FnPtr>();
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let (fn_name, is_anon, fn_curry, environ, fn_def) = { let (fn_name, is_anon, fn_curry, _environ, fn_def) = {
let is_anon = fn_ptr.is_anonymous(); let is_anon = fn_ptr.is_anonymous();
let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data(); let (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
(fn_name, is_anon, fn_curry, environ, fn_def) (fn_name, is_anon, fn_curry, environ, fn_def)
}; };
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let (fn_name, is_anon, fn_curry, environ) = { let (fn_name, is_anon, fn_curry, _environ) = {
let (fn_name, fn_curry, environ) = fn_ptr.take_data(); let (fn_name, fn_curry, environ) = fn_ptr.take_data();
(fn_name, false, fn_curry, environ) (fn_name, false, fn_curry, environ)
}; };
@ -1077,7 +1077,7 @@ impl Engine {
caches, caches,
&mut Scope::new(), &mut Scope::new(),
&mut this_ptr, &mut this_ptr,
environ.as_deref(), _environ.as_deref(),
&fn_def, &fn_def,
args, args,
true, true,

View File

@ -2139,19 +2139,37 @@ impl Module {
let _ = result?; let _ = result?;
// Encapsulated environment // Encapsulated environment
#[cfg(not(feature = "no_function"))]
let environ = Shared::new(crate::func::EncapsulatedEnviron { let environ = Shared::new(crate::func::EncapsulatedEnviron {
#[cfg(not(feature = "no_function"))]
lib: ast.shared_lib().clone(), lib: ast.shared_lib().clone(),
imports: imports.into_boxed_slice(), imports: imports.into_boxed_slice(),
#[cfg(not(feature = "no_function"))]
constants, constants,
}); });
fn update_encapsulated_environ(
value: &mut Dynamic,
environ: &Shared<crate::func::EncapsulatedEnviron>,
) {
match value.0 {
#[cfg(not(feature = "no_index"))]
crate::types::dynamic::Union::Array(ref mut a, _, _) => a
.iter_mut()
.for_each(|v| update_encapsulated_environ(v, environ)),
#[cfg(not(feature = "no_object"))]
crate::types::dynamic::Union::Map(ref mut map, _, _) => map
.values_mut()
.for_each(|v| update_encapsulated_environ(v, environ)),
crate::types::dynamic::Union::FnPtr(ref mut fn_ptr, _, _) => {
fn_ptr.set_encapsulated_environ(Some(environ.clone()))
}
_ => (),
}
}
// Variables with an alias left in the scope become module variables // Variables with an alias left in the scope become module variables
for (_name, mut value, mut aliases) in scope { for (_name, mut value, mut aliases) in scope {
#[cfg(not(feature = "no_function"))] update_encapsulated_environ(&mut value, &environ);
if let Some(mut fn_ptr) = value.write_lock::<crate::FnPtr>() {
fn_ptr.set_encapsulated_environ(Some(environ.clone()));
}
match aliases.len() { match aliases.len() {
0 => (), 0 => (),

View File

@ -1,11 +1,12 @@
//! The `FnPtr` type. //! The `FnPtr` type.
use crate::eval::GlobalRuntimeState; use crate::eval::GlobalRuntimeState;
use crate::func::EncapsulatedEnviron;
use crate::tokenizer::is_valid_function_name; use crate::tokenizer::is_valid_function_name;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
RhaiResult, RhaiResultOf, StaticVec, AST, ERR, RhaiResult, RhaiResultOf, Shared, StaticVec, AST, ERR,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -13,7 +14,7 @@ use std::{
any::{type_name, TypeId}, any::{type_name, TypeId},
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
fmt, fmt,
hash::Hash, hash::{Hash, Hasher},
mem, mem,
}; };
@ -23,23 +24,23 @@ use std::{
pub struct FnPtr { pub struct FnPtr {
name: ImmutableString, name: ImmutableString,
curry: StaticVec<Dynamic>, curry: StaticVec<Dynamic>,
environ: Option<crate::Shared<crate::func::EncapsulatedEnviron>>, environ: Option<Shared<EncapsulatedEnviron>>,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
fn_def: Option<crate::Shared<crate::ast::ScriptFnDef>>, fn_def: Option<Shared<crate::ast::ScriptFnDef>>,
} }
impl Hash for FnPtr { impl Hash for FnPtr {
#[inline(always)] #[inline(always)]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state); self.name.hash(state);
self.curry.hash(state); self.curry.hash(state);
// Hash the shared [`EncapsulatedEnviron`] by hashing its shared pointer.
self.environ.as_ref().map(|e| Shared::as_ptr(e)).hash(state);
// Hash the linked [`ScriptFnDef`][crate::ast::ScriptFnDef] by hashing its shared pointer. // Hash the linked [`ScriptFnDef`][crate::ast::ScriptFnDef] by hashing its shared pointer.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
self.fn_def self.fn_def.as_ref().map(|f| Shared::as_ptr(f)).hash(state);
.as_ref()
.map(|f| crate::Shared::as_ptr(f))
.hash(state);
} }
} }
@ -102,8 +103,8 @@ impl FnPtr {
) -> ( ) -> (
ImmutableString, ImmutableString,
StaticVec<Dynamic>, StaticVec<Dynamic>,
Option<crate::Shared<crate::func::EncapsulatedEnviron>>, Option<Shared<EncapsulatedEnviron>>,
Option<crate::Shared<crate::ast::ScriptFnDef>>, Option<Shared<crate::ast::ScriptFnDef>>,
) { ) {
(self.name, self.curry, self.environ, self.fn_def) (self.name, self.curry, self.environ, self.fn_def)
} }
@ -116,7 +117,7 @@ impl FnPtr {
) -> ( ) -> (
ImmutableString, ImmutableString,
StaticVec<Dynamic>, StaticVec<Dynamic>,
Option<crate::Shared<crate::func::EncapsulatedEnviron>>, Option<Shared<EncapsulatedEnviron>>,
) { ) {
(self.name, self.curry, self.environ) (self.name, self.curry, self.environ)
} }
@ -192,7 +193,7 @@ impl FnPtr {
args: impl FuncArgs, args: impl FuncArgs,
) -> RhaiResultOf<T> { ) -> RhaiResultOf<T> {
let _ast = ast; let _ast = ast;
let mut arg_values = crate::StaticVec::new_const(); let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values); args.parse(&mut arg_values);
let global = &mut GlobalRuntimeState::new(engine); let global = &mut GlobalRuntimeState::new(engine);
@ -228,7 +229,7 @@ impl FnPtr {
context: &NativeCallContext, context: &NativeCallContext,
args: impl FuncArgs, args: impl FuncArgs,
) -> RhaiResultOf<T> { ) -> RhaiResultOf<T> {
let mut arg_values = crate::StaticVec::new_const(); let mut arg_values = StaticVec::new_const();
args.parse(&mut arg_values); args.parse(&mut arg_values);
self.call_raw(context, None, arg_values).and_then(|result| { self.call_raw(context, None, arg_values).and_then(|result| {
@ -316,17 +317,19 @@ impl FnPtr {
context.call_fn_raw(self.fn_name(), is_method, is_method, args) context.call_fn_raw(self.fn_name(), is_method, is_method, args)
} }
/// Get a reference to the [encapsulated environment][crate::func::EncapsulatedEnviron]. /// Get a reference to the [encapsulated environment][EncapsulatedEnviron].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub(crate) fn encapsulated_environ(&self) -> Option<&crate::func::EncapsulatedEnviron> { #[allow(dead_code)]
pub(crate) fn encapsulated_environ(&self) -> Option<&EncapsulatedEnviron> {
self.environ.as_deref() self.environ.as_deref()
} }
/// Set a reference to the [encapsulated environment][crate::func::EncapsulatedEnviron]. /// Set a reference to the [encapsulated environment][EncapsulatedEnviron].
#[inline(always)] #[inline(always)]
#[allow(dead_code)]
pub(crate) fn set_encapsulated_environ( pub(crate) fn set_encapsulated_environ(
&mut self, &mut self,
value: Option<impl Into<crate::Shared<crate::func::EncapsulatedEnviron>>>, value: Option<impl Into<Shared<EncapsulatedEnviron>>>,
) { ) {
self.environ = value.map(Into::into); self.environ = value.map(Into::into);
} }
@ -334,16 +337,13 @@ impl FnPtr {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub(crate) fn fn_def(&self) -> Option<&crate::Shared<crate::ast::ScriptFnDef>> { pub(crate) fn fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
self.fn_def.as_ref() self.fn_def.as_ref()
} }
/// Set a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef]. /// Set a reference to the linked [`ScriptFnDef`][crate::ast::ScriptFnDef].
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub(crate) fn set_fn_def( pub(crate) fn set_fn_def(&mut self, value: Option<impl Into<Shared<crate::ast::ScriptFnDef>>>) {
&mut self,
value: Option<impl Into<crate::Shared<crate::ast::ScriptFnDef>>>,
) {
self.fn_def = value.map(Into::into); self.fn_def = value.map(Into::into);
} }
} }
@ -374,7 +374,7 @@ impl TryFrom<ImmutableString> for FnPtr {
} }
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
impl<T: Into<crate::Shared<crate::ast::ScriptFnDef>>> From<T> for FnPtr { impl<T: Into<Shared<crate::ast::ScriptFnDef>>> From<T> for FnPtr {
#[inline(always)] #[inline(always)]
fn from(value: T) -> Self { fn from(value: T) -> Self {
let fn_def = value.into(); let fn_def = value.into();