Move encapsulated environment out of ScriptFnDef.

This commit is contained in:
Stephen Chung 2022-12-12 16:06:24 +08:00
parent 397b5eb39d
commit 67a7ab4069
14 changed files with 116 additions and 79 deletions

View File

@ -265,6 +265,7 @@ impl Engine {
caches, caches,
scope, scope,
this_ptr, this_ptr,
None,
fn_def, fn_def,
args, args,
rewind_scope, rewind_scope,

View File

@ -750,10 +750,8 @@ impl AST {
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> { pub fn iter_fn_def(&self) -> impl Iterator<Item = &crate::Shared<super::ScriptFnDef>> {
self.lib self.lib.iter_script_fn().map(|(.., fn_def)| fn_def)
.iter_script_fn()
.map(|(.., fn_def)| fn_def.as_ref())
} }
/// Iterate through all function definitions. /// Iterate through all function definitions.
/// ///
@ -762,10 +760,8 @@ impl AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[allow(dead_code)] #[allow(dead_code)]
#[inline] #[inline]
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> { pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &crate::Shared<super::ScriptFnDef>> {
self.lib self.lib.iter_script_fn().map(|(.., fn_def)| fn_def)
.iter_script_fn()
.map(|(.., fn_def)| fn_def.as_ref())
} }
/// Iterate through all function definitions. /// Iterate through all function definitions.
/// ///

View File

@ -19,9 +19,6 @@ pub use ident::Ident;
pub use namespace::Namespace; pub use namespace::Namespace;
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
pub use namespace_none::Namespace; pub use namespace_none::Namespace;
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))]
pub use script_fn::EncapsulatedEnviron;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use script_fn::{ScriptFnDef, ScriptFnMetadata}; pub use script_fn::{ScriptFnDef, ScriptFnMetadata};
pub use stmt::{ pub use stmt::{

View File

@ -7,34 +7,12 @@ use crate::{FnArgsVec, ImmutableString};
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{fmt, hash::Hash}; use std::{fmt, hash::Hash};
/// _(internals)_ Encapsulated AST environment.
/// Exported under the `internals` feature only.
///
/// 1) other functions defined within the same AST
/// 2) the stack of imported [modules][crate::Module]
/// 3) global constants
///
/// Not available under `no_module` or `no_function`.
#[cfg(not(feature = "no_module"))]
#[derive(Debug, Clone)]
pub struct EncapsulatedEnviron {
/// Functions defined within the same [`AST`][crate::AST].
pub lib: crate::SharedModule,
/// Imported [modules][crate::Module].
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
/// Globally-defined constants.
pub constants: Option<crate::eval::SharedGlobalConstants>,
}
/// _(internals)_ A type containing information on a script-defined function. /// _(internals)_ A type containing information on a script-defined function.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ScriptFnDef { pub struct ScriptFnDef {
/// Function body. /// Function body.
pub body: StmtBlock, pub body: StmtBlock,
/// Encapsulated AST environment, if any.
#[cfg(not(feature = "no_module"))]
pub environ: Option<crate::Shared<EncapsulatedEnviron>>,
/// Function name. /// Function name.
pub name: ImmutableString, pub name: ImmutableString,
/// Function access mode. /// Function access mode.

View File

@ -644,9 +644,10 @@ impl Engine {
// Script function call // Script function call
assert!(func.is_script()); assert!(func.is_script());
let func = func.get_script_fn_def().expect("script-defined function"); let f = func.get_script_fn_def().expect("script-defined function");
let environ = func.get_encapsulated_environ();
if func.body.is_empty() { if f.body.is_empty() {
return Ok((Dynamic::UNIT, false)); return Ok((Dynamic::UNIT, false));
} }
@ -666,7 +667,7 @@ impl Engine {
let (first_arg, rest_args) = _args.split_first_mut().unwrap(); let (first_arg, rest_args) = _args.split_first_mut().unwrap();
self.call_script_fn( self.call_script_fn(
global, caches, scope, first_arg, func, rest_args, true, pos, global, caches, scope, first_arg, environ, f, rest_args, true, pos,
) )
} else { } else {
// Normal call of script function // Normal call of script function
@ -683,7 +684,17 @@ impl Engine {
let mut this_ptr = Dynamic::NULL; let mut this_ptr = Dynamic::NULL;
self.call_script_fn(global, caches, scope, &mut this_ptr, func, args, true, pos) self.call_script_fn(
global,
caches,
scope,
&mut this_ptr,
environ,
f,
args,
true,
pos,
)
} }
.map(|r| (r, false)); .map(|r| (r, false));
} }
@ -770,6 +781,7 @@ impl Engine {
caches, caches,
&mut Scope::new(), &mut Scope::new(),
&mut this_ptr, &mut this_ptr,
None,
fn_def, fn_def,
args, args,
true, true,
@ -856,6 +868,7 @@ impl Engine {
caches, caches,
&mut Scope::new(), &mut Scope::new(),
target, target,
None,
&fn_def, &fn_def,
args, args,
true, true,
@ -1064,6 +1077,7 @@ impl Engine {
caches, caches,
&mut Scope::new(), &mut Scope::new(),
&mut this_ptr, &mut this_ptr,
None,
&fn_def, &fn_def,
args, args,
true, true,
@ -1441,15 +1455,27 @@ impl Engine {
match func { match func {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Some(f) if f.is_script() => { Some(func) if func.is_script() => {
let f = f.get_script_fn_def().expect("script-defined function"); let f = func.get_script_fn_def().expect("script-defined function");
let environ = func.get_encapsulated_environ();
let scope = &mut Scope::new(); let scope = &mut Scope::new();
let mut this_ptr = Dynamic::NULL; let mut this_ptr = Dynamic::NULL;
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned()); let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
auto_restore!(global => move |g| g.source = orig_source); auto_restore!(global => move |g| g.source = orig_source);
self.call_script_fn(global, caches, scope, &mut this_ptr, f, args, true, pos) self.call_script_fn(
global,
caches,
scope,
&mut this_ptr,
environ,
f,
args,
true,
pos,
)
} }
Some(f) if f.is_plugin_fn() => { Some(f) if f.is_plugin_fn() => {

View File

@ -8,6 +8,26 @@ use std::fmt;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
/// _(internals)_ Encapsulated AST environment.
/// Exported under the `internals` feature only.
///
/// 1) functions defined within the same AST
/// 2) the stack of imported [modules][crate::Module]
/// 3) global constants
#[derive(Debug, Clone)]
pub struct EncapsulatedEnviron {
/// Functions defined within the same [`AST`][crate::AST].
#[cfg(not(feature = "no_function"))]
pub lib: crate::SharedModule,
/// Imported [modules][crate::Module].
#[cfg(not(feature = "no_module"))]
pub imports: Box<[(crate::ImmutableString, crate::SharedModule)]>,
/// Globally-defined constants.
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))]
pub constants: Option<crate::eval::SharedGlobalConstants>,
}
/// _(internals)_ A type encapsulating a function callable by Rhai. /// _(internals)_ A type encapsulating a function callable by Rhai.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Clone)] #[derive(Clone)]
@ -24,7 +44,10 @@ pub enum CallableFunction {
Plugin(Shared<FnPlugin>), Plugin(Shared<FnPlugin>),
/// A script-defined function. /// A script-defined function.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Script(Shared<crate::ast::ScriptFnDef>), Script(
Shared<crate::ast::ScriptFnDef>,
Option<Shared<EncapsulatedEnviron>>,
),
} }
impl fmt::Debug for CallableFunction { impl fmt::Debug for CallableFunction {
@ -38,7 +61,7 @@ impl fmt::Debug for CallableFunction {
Self::Plugin(..) => f.write_str("PluginFunction"), Self::Plugin(..) => f.write_str("PluginFunction"),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), Self::Script(fn_def, ..) => fmt::Debug::fmt(fn_def, f),
} }
} }
} }
@ -52,7 +75,7 @@ impl fmt::Display for CallableFunction {
Self::Plugin(..) => f.write_str("PluginFunction"), Self::Plugin(..) => f.write_str("PluginFunction"),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(s) => fmt::Display::fmt(s, f), Self::Script(fn_def, ..) => fmt::Display::fmt(fn_def, f),
} }
} }
} }
@ -160,7 +183,7 @@ impl CallableFunction {
Self::Plugin(..) | Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => { Self::Plugin(..) | Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => {
FnAccess::Public FnAccess::Public
} }
Self::Script(f) => f.access, Self::Script(f, ..) => f.access,
} }
} }
/// Get a shared reference to a native Rust function. /// Get a shared reference to a native Rust function.
@ -184,7 +207,20 @@ impl CallableFunction {
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> { pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
match self { match self {
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) | Self::Plugin(..) => None, Self::Pure(..) | Self::Method(..) | Self::Iterator(..) | Self::Plugin(..) => None,
Self::Script(f) => Some(f), Self::Script(f, ..) => Some(f),
}
}
/// Get a reference to the shared encapsulated environment of the function definition.
///
/// Not available under `no_function` or `no_module`.
#[inline]
#[must_use]
pub fn get_encapsulated_environ(&self) -> Option<&EncapsulatedEnviron> {
match self {
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) | Self::Plugin(..) => None,
#[cfg(not(feature = "no_function"))]
Self::Script(.., environ) => environ.as_deref(),
} }
} }
/// Get a reference to an iterator function. /// Get a reference to an iterator function.
@ -217,7 +253,7 @@ impl CallableFunction {
impl From<crate::ast::ScriptFnDef> for CallableFunction { impl From<crate::ast::ScriptFnDef> for CallableFunction {
#[inline(always)] #[inline(always)]
fn from(func: crate::ast::ScriptFnDef) -> Self { fn from(func: crate::ast::ScriptFnDef) -> Self {
Self::Script(func.into()) Self::Script(func.into(), None)
} }
} }
@ -225,7 +261,7 @@ impl From<crate::ast::ScriptFnDef> for CallableFunction {
impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction { impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
#[inline(always)] #[inline(always)]
fn from(func: Shared<crate::ast::ScriptFnDef>) -> Self { fn from(func: Shared<crate::ast::ScriptFnDef>) -> Self {
Self::Script(func) Self::Script(func, None)
} }
} }

View File

@ -18,7 +18,7 @@ pub use call::ensure_no_data_race;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use call::is_anonymous_fn; pub use call::is_anonymous_fn;
pub use call::FnCallArgs; pub use call::FnCallArgs;
pub use callable_function::CallableFunction; pub use callable_function::{CallableFunction, EncapsulatedEnviron};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use func::Func; pub use func::Func;
pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap}; pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap};

View File

@ -4,6 +4,7 @@
use super::call::FnCallArgs; use super::call::FnCallArgs;
use crate::ast::ScriptFnDef; use crate::ast::ScriptFnDef;
use crate::eval::{Caches, GlobalRuntimeState}; use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::EncapsulatedEnviron;
use crate::{Dynamic, Engine, Position, RhaiResult, Scope, ERR}; use crate::{Dynamic, Engine, Position, RhaiResult, Scope, ERR};
use std::mem; use std::mem;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -28,6 +29,7 @@ impl Engine {
caches: &mut Caches, caches: &mut Caches,
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Dynamic, this_ptr: &mut Dynamic,
_environ: Option<&EncapsulatedEnviron>,
fn_def: &ScriptFnDef, fn_def: &ScriptFnDef,
args: &mut FnCallArgs, args: &mut FnCallArgs,
rewind_scope: bool, rewind_scope: bool,
@ -84,12 +86,12 @@ impl Engine {
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len(); let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let orig_constants = if let Some(ref environ) = fn_def.environ { let orig_constants = if let Some(environ) = _environ {
let crate::ast::EncapsulatedEnviron { let EncapsulatedEnviron {
ref lib, lib,
ref imports, imports,
ref constants, constants,
} = **environ; } = environ;
imports imports
.iter() .iter()
@ -124,8 +126,7 @@ impl Engine {
_ => Err(ERR::ErrorInFunctionCall( _ => Err(ERR::ErrorInFunctionCall(
fn_def.name.to_string(), fn_def.name.to_string(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
fn_def _environ
.environ
.as_deref() .as_deref()
.and_then(|environ| environ.lib.id()) .and_then(|environ| environ.lib.id())
.unwrap_or_else(|| global.source().unwrap_or("")) .unwrap_or_else(|| global.source().unwrap_or(""))

View File

@ -338,9 +338,7 @@ pub use ast::CustomExpr;
pub use ast::Namespace; pub use ast::Namespace;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))] pub use func::EncapsulatedEnviron;
#[cfg(not(feature = "no_function"))]
pub use ast::EncapsulatedEnviron;
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState}; pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState};

View File

@ -2184,31 +2184,32 @@ impl Module {
// Non-private functions defined become module functions // Non-private functions defined become module functions
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
{ {
let environ = Shared::new(crate::ast::EncapsulatedEnviron { let environ = Shared::new(crate::func::EncapsulatedEnviron {
lib: ast.shared_lib().clone(), lib: ast.shared_lib().clone(),
imports: imports.into_boxed_slice(), imports: imports.into_boxed_slice(),
constants, constants,
}); });
ast.shared_lib() ast.iter_fn_def()
.iter_fn() .filter(|&f| match f.access {
.filter(|&f| match f.metadata.access {
FnAccess::Public => true, FnAccess::Public => true,
FnAccess::Private => false, FnAccess::Private => false,
}) })
.filter(|&f| f.func.is_script())
.for_each(|f| { .for_each(|f| {
let mut func = f let hash = module.set_script_fn(f.clone());
.func
.get_script_fn_def()
.expect("script-defined function")
.as_ref()
.clone();
// Encapsulate AST environment // Encapsulate AST environment
func.environ = Some(environ.clone()); match module
.functions
module.set_script_fn(func); .as_mut()
.unwrap()
.get_mut(&hash)
.unwrap()
.func
{
CallableFunction::Script(.., ref mut e) => *e = Some(environ.clone()),
_ => (),
}
}); });
} }

View File

@ -1344,8 +1344,6 @@ impl Engine {
access: fn_def.access, access: fn_def.access,
body: crate::ast::StmtBlock::NONE, body: crate::ast::StmtBlock::NONE,
params: fn_def.params.clone(), params: fn_def.params.clone(),
#[cfg(not(feature = "no_module"))]
environ: None,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
comments: Box::default(), comments: Box::default(),

View File

@ -3629,8 +3629,6 @@ impl Engine {
access, access,
params, params,
body, body,
#[cfg(not(feature = "no_module"))]
environ: None,
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
comments: comments.into_iter().collect(), comments: comments.into_iter().collect(),
}) })
@ -3791,8 +3789,6 @@ impl Engine {
access: crate::FnAccess::Public, access: crate::FnAccess::Public,
params, params,
body: body.into(), body: body.into(),
#[cfg(not(feature = "no_module"))]
environ: None,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
comments: Box::default(), comments: Box::default(),

View File

@ -31,6 +31,14 @@ fn check_struct_sizes() {
size_of::<Option<ast::Stmt>>(), size_of::<Option<ast::Stmt>>(),
if IS_32_BIT { 12 } else { 16 } if IS_32_BIT { 12 } else { 16 }
); );
assert_eq!(
size_of::<CallableFunction>(),
if IS_32_BIT { 12 } else { 24 }
);
assert_eq!(
size_of::<module::FuncInfo>(),
if IS_32_BIT { 16 } else { 32 }
);
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
{ {

View File

@ -290,6 +290,7 @@ impl FnPtr {
caches, caches,
&mut crate::Scope::new(), &mut crate::Scope::new(),
this_ptr.unwrap_or(&mut null_ptr), this_ptr.unwrap_or(&mut null_ptr),
None,
&fn_def, &fn_def,
args, args,
true, true,