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>();
#[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 (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
(fn_name, is_anon, fn_curry, environ, fn_def)
};
#[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();
(fn_name, false, fn_curry, environ)
};
@ -868,7 +868,7 @@ impl Engine {
caches,
&mut Scope::new(),
target,
environ.as_deref(),
_environ.as_deref(),
&fn_def,
args,
true,
@ -1043,13 +1043,13 @@ impl Engine {
let fn_ptr = arg_value.cast::<FnPtr>();
#[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 (fn_name, fn_curry, environ, fn_def) = fn_ptr.take_data();
(fn_name, is_anon, fn_curry, environ, fn_def)
};
#[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();
(fn_name, false, fn_curry, environ)
};
@ -1077,7 +1077,7 @@ impl Engine {
caches,
&mut Scope::new(),
&mut this_ptr,
environ.as_deref(),
_environ.as_deref(),
&fn_def,
args,
true,

View File

@ -2139,19 +2139,37 @@ impl Module {
let _ = result?;
// Encapsulated environment
#[cfg(not(feature = "no_function"))]
let environ = Shared::new(crate::func::EncapsulatedEnviron {
#[cfg(not(feature = "no_function"))]
lib: ast.shared_lib().clone(),
imports: imports.into_boxed_slice(),
#[cfg(not(feature = "no_function"))]
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
for (_name, mut value, mut aliases) in scope {
#[cfg(not(feature = "no_function"))]
if let Some(mut fn_ptr) = value.write_lock::<crate::FnPtr>() {
fn_ptr.set_encapsulated_environ(Some(environ.clone()));
}
update_encapsulated_environ(&mut value, &environ);
match aliases.len() {
0 => (),

View File

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