Remove fields and parameters under no_function.

This commit is contained in:
Stephen Chung 2021-11-29 09:43:35 +08:00
parent a3e79c0bd0
commit 95dc2ad502
14 changed files with 191 additions and 105 deletions

View File

@ -18,9 +18,8 @@ Enhancements
Deprecated API's Deprecated API's
---------------- ----------------
* The internal `no_smartstring` feature is removed since `SmartString` now supports `no-std`.
* `NativeCallContext::new` is deprecated because it is simpler to call a function pointer via `FnPtr::call`. * `NativeCallContext::new` is deprecated because it is simpler to call a function pointer via `FnPtr::call`.
* `AST::shared_lib` is changed to return `&Shared<Module>` while `AST::lib` is deprecated. * `AST::merge_filtered` and `AST::combine_filtered` are no longer exported under `no_function`.
Version 1.2.1 Version 1.2.1

View File

@ -3,7 +3,7 @@
use crate::engine::{EvalState, Imports}; use crate::engine::{EvalState, Imports};
use crate::parser::ParseState; use crate::parser::ParseState;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{Dynamic, Engine, EvalAltResult, Position, RhaiResult, Scope, AST}; use crate::{Dynamic, Engine, EvalAltResult, Module, Position, RhaiResult, Scope, AST};
use std::any::type_name; use std::any::type_name;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -217,7 +217,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
mods.embedded_module_resolver = ast.resolver(); mods.embedded_module_resolver = ast.resolver().cloned();
} }
let statements = ast.statements(); let statements = ast.statements();
@ -226,7 +226,15 @@ impl Engine {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
} }
let lib = &[ast.as_ref()]; let lib = [
#[cfg(not(feature = "no_function"))]
ast.as_ref(),
];
let lib = if lib.first().map(|m: &&Module| m.is_empty()).unwrap_or(true) {
&lib[0..0]
} else {
&lib
};
self.eval_global_statements(scope, mods, &mut state, statements, lib, level) self.eval_global_statements(scope, mods, &mut state, statements, lib, level)
} }
} }

View File

@ -86,12 +86,16 @@ impl Engine {
}) })
.collect(); .collect();
#[cfg(feature = "no_function")]
let lib = crate::StaticVec::new_const();
let statements = std::mem::take(ast.statements_mut()); let statements = std::mem::take(ast.statements_mut());
crate::optimizer::optimize_into_ast(self, scope, statements, lib, optimization_level) crate::optimizer::optimize_into_ast(
self,
scope,
statements,
#[cfg(not(feature = "no_function"))]
lib,
optimization_level,
)
} }
} }

View File

@ -2,7 +2,7 @@
use crate::engine::{EvalState, Imports}; use crate::engine::{EvalState, Imports};
use crate::parser::ParseState; use crate::parser::ParseState;
use crate::{Engine, EvalAltResult, Scope, AST}; use crate::{Engine, EvalAltResult, Module, Scope, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -59,12 +59,20 @@ impl Engine {
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
mods.embedded_module_resolver = ast.resolver(); mods.embedded_module_resolver = ast.resolver().cloned();
} }
let statements = ast.statements(); let statements = ast.statements();
if !statements.is_empty() { if !statements.is_empty() {
let lib = &[ast.as_ref()]; let lib = [
#[cfg(not(feature = "no_function"))]
ast.as_ref(),
];
let lib = if lib.first().map(|m: &&Module| m.is_empty()).unwrap_or(true) {
&lib[0..0]
} else {
&lib
};
self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?; self.eval_global_statements(scope, mods, &mut state, statements, lib, 0)?;
} }
Ok(()) Ok(())

View File

@ -1,7 +1,6 @@
//! Module defining the AST (abstract syntax tree). //! Module defining the AST (abstract syntax tree).
use crate::calc_fn_hash; use crate::calc_fn_hash;
use crate::func::native::shared_make_mut;
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
@ -187,10 +186,9 @@ pub struct AST {
/// Global statements. /// Global statements.
body: StmtBlock, body: StmtBlock,
/// Script-defined functions. /// Script-defined functions.
#[cfg(not(feature = "no_function"))]
functions: Shared<Module>, functions: Shared<Module>,
/// Embedded module resolver, if any. /// Embedded module resolver, if any.
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>, resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
} }
@ -208,11 +206,12 @@ impl AST {
#[must_use] #[must_use]
pub fn new( pub fn new(
statements: impl IntoIterator<Item = Stmt>, statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>, #[cfg(not(feature = "no_function"))] functions: impl Into<Shared<Module>>,
) -> Self { ) -> Self {
Self { Self {
source: None, source: None,
body: StmtBlock::new(statements, Position::NONE), body: StmtBlock::new(statements, Position::NONE),
#[cfg(not(feature = "no_function"))]
functions: functions.into(), functions: functions.into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: None, resolver: None,
@ -225,6 +224,7 @@ impl AST {
Self { Self {
source: None, source: None,
body: StmtBlock::NONE, body: StmtBlock::NONE,
#[cfg(not(feature = "no_function"))]
functions: Module::new().into(), functions: Module::new().into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: None, resolver: None,
@ -235,12 +235,13 @@ impl AST {
#[must_use] #[must_use]
pub fn new_with_source( pub fn new_with_source(
statements: impl IntoIterator<Item = Stmt>, statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>, #[cfg(not(feature = "no_function"))] functions: impl Into<Shared<Module>>,
source: impl Into<Identifier>, source: impl Into<Identifier>,
) -> Self { ) -> Self {
Self { Self {
source: Some(source.into()), source: Some(source.into()),
body: StmtBlock::new(statements, Position::NONE), body: StmtBlock::new(statements, Position::NONE),
#[cfg(not(feature = "no_function"))]
functions: functions.into(), functions: functions.into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: None, resolver: None,
@ -262,6 +263,7 @@ impl AST {
#[inline] #[inline]
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self { pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
let source = source.into(); let source = source.into();
#[cfg(not(feature = "no_function"))]
Shared::get_mut(&mut self.functions) Shared::get_mut(&mut self.functions)
.as_mut() .as_mut()
.map(|m| m.set_id(source.clone())); .map(|m| m.set_id(source.clone()));
@ -296,9 +298,17 @@ impl AST {
pub(crate) fn statements_mut(&mut self) -> &mut StaticVec<Stmt> { pub(crate) fn statements_mut(&mut self) -> &mut StaticVec<Stmt> {
&mut self.body.0 &mut self.body.0
} }
/// Does this [`AST`] contain script-defined functions?
///
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub fn has_functions(&self) -> bool {
!self.functions.is_empty()
}
/// Get the internal shared [`Module`] containing all script-defined functions. /// Get the internal shared [`Module`] containing all script-defined functions.
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
@ -308,9 +318,8 @@ impl AST {
/// _(internals)_ Get the internal shared [`Module`] containing all script-defined functions. /// _(internals)_ Get the internal shared [`Module`] containing all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
/// Not available under `no_function` or `no_module`. /// Not available under `no_function`.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
@ -318,25 +327,25 @@ impl AST {
&self.functions &self.functions
} }
/// Get the embedded [module resolver][`ModuleResolver`]. /// Get the embedded [module resolver][`ModuleResolver`].
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub(crate) fn resolver( pub(crate) fn resolver(
&self, &self,
) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> { ) -> Option<&Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.clone() self.resolver.as_ref()
} }
/// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver]. /// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver].
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn resolver(&self) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> { pub fn resolver(&self) -> Option<&Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.clone() self.resolver.as_ref()
} }
/// Set the embedded [module resolver][`ModuleResolver`]. /// Set the embedded [module resolver][`ModuleResolver`].
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -391,6 +400,7 @@ impl AST {
Self { Self {
source: self.source.clone(), source: self.source.clone(),
body: self.body.clone(), body: self.body.clone(),
#[cfg(not(feature = "no_function"))]
functions: Module::new().into(), functions: Module::new().into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: self.resolver.clone(), resolver: self.resolver.clone(),
@ -448,7 +458,7 @@ impl AST {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn merge(&self, other: &Self) -> Self { pub fn merge(&self, other: &Self) -> Self {
self.merge_filtered(other, |_, _, _, _, _| true) self.merge_filtered_impl(other, |_, _, _, _, _| true)
} }
/// Combine one [`AST`] with another. The second [`AST`] is consumed. /// Combine one [`AST`] with another. The second [`AST`] is consumed.
/// ///
@ -500,11 +510,13 @@ impl AST {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn combine(&mut self, other: Self) -> &mut Self { pub fn combine(&mut self, other: Self) -> &mut Self {
self.combine_filtered(other, |_, _, _, _, _| true) self.combine_filtered_impl(other, |_, _, _, _, _| true)
} }
/// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
/// is returned. /// is returned.
/// ///
/// Not available under `no_function`.
///
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
/// Of course, if the first [`AST`] uses a `return` statement at the end, then /// Of course, if the first [`AST`] uses a `return` statement at the end, then
@ -518,8 +530,6 @@ impl AST {
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let engine = Engine::new(); /// let engine = Engine::new();
@ -550,45 +560,67 @@ impl AST {
/// ///
/// // Evaluate it /// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast)?, "42!"); /// assert_eq!(engine.eval_ast::<String>(&ast)?, "42!");
/// # }
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline] #[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use] #[must_use]
pub fn merge_filtered( pub fn merge_filtered(
&self, &self,
other: &Self, other: &Self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self { ) -> Self {
let Self { self.merge_filtered_impl(other, filter)
body, functions, .. }
} = self; /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version
/// is returned.
let merged = match (body.is_empty(), other.body.is_empty()) { #[inline]
#[must_use]
fn merge_filtered_impl(
&self,
other: &Self,
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self {
let merged = match (self.body.is_empty(), other.body.is_empty()) {
(false, false) => { (false, false) => {
let mut body = body.clone(); let mut body = self.body.clone();
body.0.extend(other.body.0.iter().cloned()); body.0.extend(other.body.0.iter().cloned());
body body
} }
(false, true) => body.clone(), (false, true) => self.body.clone(),
(true, false) => other.body.clone(), (true, false) => other.body.clone(),
(true, true) => StmtBlock::NONE, (true, true) => StmtBlock::NONE,
}; };
let source = other.source.clone().or_else(|| self.source.clone()); let source = other.source.clone().or_else(|| self.source.clone());
let mut functions = functions.as_ref().clone(); #[cfg(not(feature = "no_function"))]
functions.merge_filtered(&other.functions, &filter); let functions = {
let mut functions = self.functions.as_ref().clone();
functions.merge_filtered(&other.functions, &_filter);
functions
};
if let Some(source) = source { if let Some(source) = source {
Self::new_with_source(merged.0, functions, source) Self::new_with_source(
merged.0,
#[cfg(not(feature = "no_function"))]
functions,
source,
)
} else { } else {
Self::new(merged.0, functions) Self::new(
merged.0,
#[cfg(not(feature = "no_function"))]
functions,
)
} }
} }
/// Combine one [`AST`] with another. The second [`AST`] is consumed. /// Combine one [`AST`] with another. The second [`AST`] is consumed.
/// ///
/// Not available under `no_function`.
///
/// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_. /// Statements in the second [`AST`] are simply appended to the end of the first _without any processing_.
/// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried. /// Thus, the return value of the first [`AST`] (if using expression-statement syntax) is buried.
/// Of course, if the first [`AST`] uses a `return` statement at the end, then /// Of course, if the first [`AST`] uses a `return` statement at the end, then
@ -602,8 +634,6 @@ impl AST {
/// ///
/// ``` /// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> { /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # #[cfg(not(feature = "no_function"))]
/// # {
/// use rhai::Engine; /// use rhai::Engine;
/// ///
/// let engine = Engine::new(); /// let engine = Engine::new();
@ -634,20 +664,31 @@ impl AST {
/// ///
/// // Evaluate it /// // Evaluate it
/// assert_eq!(engine.eval_ast::<String>(&ast1)?, "42!"); /// assert_eq!(engine.eval_ast::<String>(&ast1)?, "42!");
/// # }
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline] #[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn combine_filtered( pub fn combine_filtered(
&mut self, &mut self,
other: Self, other: Self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> &mut Self {
self.combine_filtered_impl(other, filter)
}
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
#[inline]
fn combine_filtered_impl(
&mut self,
other: Self,
_filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> &mut Self { ) -> &mut Self {
self.body.0.extend(other.body.0.into_iter()); self.body.0.extend(other.body.0.into_iter());
#[cfg(not(feature = "no_function"))]
if !other.functions.is_empty() { if !other.functions.is_empty() {
shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &filter); crate::func::native::shared_make_mut(&mut self.functions)
.merge_filtered(&other.functions, &_filter);
} }
self self
} }
@ -683,7 +724,8 @@ impl AST {
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
) -> &mut Self { ) -> &mut Self {
if !self.functions.is_empty() { if !self.functions.is_empty() {
shared_make_mut(&mut self.functions).retain_script_functions(filter); crate::func::native::shared_make_mut(&mut self.functions)
.retain_script_functions(filter);
} }
self self
} }
@ -875,6 +917,7 @@ impl AsRef<[Stmt]> for AST {
} }
} }
#[cfg(not(feature = "no_function"))]
impl AsRef<Module> for AST { impl AsRef<Module> for AST {
#[inline(always)] #[inline(always)]
fn as_ref(&self) -> &Module { fn as_ref(&self) -> &Module {
@ -882,6 +925,7 @@ impl AsRef<Module> for AST {
} }
} }
#[cfg(not(feature = "no_function"))]
impl AsRef<Shared<Module>> for AST { impl AsRef<Shared<Module>> for AST {
#[inline(always)] #[inline(always)]
fn as_ref(&self) -> &Shared<Module> { fn as_ref(&self) -> &Shared<Module> {
@ -2456,6 +2500,7 @@ impl AST {
/// This method will be removed in the next major version. /// This method will be removed in the next major version.
#[deprecated(since = "1.3.0", note = "use `shared_lib` instead")] #[deprecated(since = "1.3.0", note = "use `shared_lib` instead")]
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn lib(&self) -> &crate::Module { pub fn lib(&self) -> &crate::Module {

View File

@ -13,7 +13,7 @@ use crate::tokenizer::Token;
use crate::{ use crate::{
ast::{Expr, Stmt}, ast::{Expr, Stmt},
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, EvalAltResult, FnPtr, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, EvalAltResult, FnPtr,
Identifier, ImmutableString, Module, ParseErrorType, Position, RhaiResult, Scope, StaticVec, Identifier, ImmutableString, Module, Position, RhaiResult, Scope, StaticVec,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -554,14 +554,14 @@ impl Engine {
let mut lib_merged = StaticVec::with_capacity(lib.len() + 1); let mut lib_merged = StaticVec::with_capacity(lib.len() + 1);
let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len();
let lib = if let Some(ref env_lib) = fn_def.lib { let lib = if let Some(ref fn_lib) = fn_def.lib {
if env_lib.is_empty() { if fn_lib.is_empty() {
lib lib
} else { } else {
state.push_fn_resolution_cache(); state.push_fn_resolution_cache();
lib_merged.push(env_lib.as_ref()); lib_merged.push(fn_lib.as_ref());
lib_merged.extend(lib.iter().cloned()); lib_merged.extend(lib.iter().cloned());
lib_merged.as_ref() &lib_merged
} }
} else { } else {
lib lib
@ -886,8 +886,9 @@ impl Engine {
)?; )?;
// If new functions are defined within the eval string, it is an error // If new functions are defined within the eval string, it is an error
#[cfg(not(feature = "no_function"))]
if !ast.shared_lib().is_empty() { if !ast.shared_lib().is_empty() {
return Err(ParseErrorType::WrongFnDefinition.into()); return Err(crate::ParseErrorType::WrongFnDefinition.into());
} }
let statements = ast.statements(); let statements = ast.statements();

View File

@ -1460,7 +1460,7 @@ 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"))]
if !ast.shared_lib().functions.is_empty() { if ast.has_functions() {
ast.shared_lib() ast.shared_lib()
.functions .functions
.values() .values()

View File

@ -1,7 +1,7 @@
//! Module implementing the [`AST`] optimizer. //! Module implementing the [`AST`] optimizer.
#![cfg(not(feature = "no_optimize"))] #![cfg(not(feature = "no_optimize"))]
use crate::ast::{Expr, OpAssignment, ScriptFnDef, Stmt, AST_OPTION_FLAGS::*}; use crate::ast::{Expr, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
use crate::engine::{ use crate::engine::{
EvalState, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF, EvalState, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
}; };
@ -11,7 +11,7 @@ use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode; use crate::types::dynamic::AccessMode;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString,
Module, Position, Scope, Shared, StaticVec, AST, Position, Scope, StaticVec, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -59,8 +59,9 @@ struct OptimizerState<'a> {
propagate_constants: bool, propagate_constants: bool,
/// An [`Engine`] instance for eager function evaluation. /// An [`Engine`] instance for eager function evaluation.
engine: &'a Engine, engine: &'a Engine,
/// [Module] containing script-defined functions. /// [Module][crate::Module] containing script-defined functions.
lib: &'a [&'a Module], #[cfg(not(feature = "no_function"))]
lib: &'a [&'a crate::Module],
/// Optimization level. /// Optimization level.
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
} }
@ -70,7 +71,7 @@ impl<'a> OptimizerState<'a> {
#[inline(always)] #[inline(always)]
pub const fn new( pub const fn new(
engine: &'a Engine, engine: &'a Engine,
lib: &'a [&'a Module], #[cfg(not(feature = "no_function"))] lib: &'a [&'a crate::Module],
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> Self { ) -> Self {
Self { Self {
@ -78,6 +79,7 @@ impl<'a> OptimizerState<'a> {
variables: StaticVec::new_const(), variables: StaticVec::new_const(),
propagate_constants: true, propagate_constants: true,
engine, engine,
#[cfg(not(feature = "no_function"))]
lib, lib,
optimization_level, optimization_level,
} }
@ -139,11 +141,16 @@ impl<'a> OptimizerState<'a> {
fn_name: impl AsRef<str>, fn_name: impl AsRef<str>,
arg_values: &mut [Dynamic], arg_values: &mut [Dynamic],
) -> Option<Dynamic> { ) -> Option<Dynamic> {
#[cfg(not(feature = "no_function"))]
let lib = self.lib;
#[cfg(feature = "no_function")]
let lib = &[];
self.engine self.engine
.call_native_fn( .call_native_fn(
&mut Imports::new(), &mut Imports::new(),
&mut EvalState::new(), &mut EvalState::new(),
self.lib, lib,
&fn_name, &fn_name,
calc_fn_hash(&fn_name, arg_values.len()), calc_fn_hash(&fn_name, arg_values.len()),
&mut arg_values.iter_mut().collect::<StaticVec<_>>(), &mut arg_values.iter_mut().collect::<StaticVec<_>>(),
@ -992,7 +999,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
_ if x.args.len() == 2 && !state.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => { _ if x.args.len() == 2 && !state.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => {
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
let context = (state.engine, x.name.as_str(), state.lib).into(); #[cfg(not(feature = "no_function"))]
let lib = state.lib;
#[cfg(feature = "no_function")]
let lib = &[];
let context = (state.engine, x.name.as_str(), lib).into();
let (first, second) = arg_values.split_first_mut().expect("not empty"); let (first, second) = arg_values.split_first_mut().expect("not empty");
(f)(context, &mut [ first, &mut second[0] ]).ok() (f)(context, &mut [ first, &mut second[0] ]).ok()
}) { }) {
@ -1091,7 +1103,7 @@ fn optimize_top_level(
statements: StaticVec<Stmt>, statements: StaticVec<Stmt>,
engine: &Engine, engine: &Engine,
scope: &Scope, scope: &Scope,
lib: &[&Module], #[cfg(not(feature = "no_function"))] lib: &[&crate::Module],
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> StaticVec<Stmt> { ) -> StaticVec<Stmt> {
let mut statements = statements; let mut statements = statements;
@ -1103,7 +1115,12 @@ fn optimize_top_level(
} }
// Set up the state // Set up the state
let mut state = OptimizerState::new(engine, lib, optimization_level); let mut state = OptimizerState::new(
engine,
#[cfg(not(feature = "no_function"))]
lib,
optimization_level,
);
// Add constants and variables from the scope // Add constants and variables from the scope
scope.iter().for_each(|(name, constant, value)| { scope.iter().for_each(|(name, constant, value)| {
@ -1123,7 +1140,9 @@ pub fn optimize_into_ast(
engine: &Engine, engine: &Engine,
scope: &Scope, scope: &Scope,
statements: StaticVec<Stmt>, statements: StaticVec<Stmt>,
functions: StaticVec<Shared<ScriptFnDef>>, #[cfg(not(feature = "no_function"))] functions: StaticVec<
crate::Shared<crate::ast::ScriptFnDef>,
>,
optimization_level: OptimizationLevel, optimization_level: OptimizationLevel,
) -> AST { ) -> AST {
let level = if cfg!(feature = "no_optimize") { let level = if cfg!(feature = "no_optimize") {
@ -1133,19 +1152,18 @@ pub fn optimize_into_ast(
}; };
let mut statements = statements; let mut statements = statements;
let _functions = functions;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let lib = { let lib = {
let mut module = Module::new(); let mut module = crate::Module::new();
if level != OptimizationLevel::None { if level != OptimizationLevel::None {
// We only need the script library's signatures for optimization purposes // We only need the script library's signatures for optimization purposes
let mut lib2 = Module::new(); let mut lib2 = crate::Module::new();
_functions functions
.iter() .iter()
.map(|fn_def| ScriptFnDef { .map(|fn_def| crate::ast::ScriptFnDef {
name: fn_def.name.clone(), name: fn_def.name.clone(),
access: fn_def.access, access: fn_def.access,
body: crate::ast::StmtBlock::NONE, body: crate::ast::StmtBlock::NONE,
@ -1163,7 +1181,7 @@ pub fn optimize_into_ast(
let lib2 = &[&lib2]; let lib2 = &[&lib2];
_functions functions
.into_iter() .into_iter()
.map(|fn_def| { .map(|fn_def| {
let mut fn_def = crate::func::native::shared_take_or_clone(fn_def); let mut fn_def = crate::func::native::shared_take_or_clone(fn_def);
@ -1179,7 +1197,7 @@ pub fn optimize_into_ast(
module.set_script_fn(fn_def); module.set_script_fn(fn_def);
}); });
} else { } else {
_functions.into_iter().for_each(|fn_def| { functions.into_iter().for_each(|fn_def| {
module.set_script_fn(fn_def); module.set_script_fn(fn_def);
}); });
} }
@ -1187,18 +1205,21 @@ pub fn optimize_into_ast(
module module
}; };
#[cfg(feature = "no_function")]
let lib = Module::new();
statements.shrink_to_fit(); statements.shrink_to_fit();
AST::new( AST::new(
match level { match level {
OptimizationLevel::None => statements, OptimizationLevel::None => statements,
OptimizationLevel::Simple | OptimizationLevel::Full => { OptimizationLevel::Simple | OptimizationLevel::Full => optimize_top_level(
optimize_top_level(statements, engine, &scope, &[&lib], level) statements,
} engine,
&scope,
#[cfg(not(feature = "no_function"))]
&[&lib],
level,
),
}, },
#[cfg(not(feature = "no_function"))]
lib, lib,
) )
} }

View File

@ -90,7 +90,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
.map(|&s| s.into()) .map(|&s| s.into())
.collect(); .collect();
let mut list = ctx.iter_namespaces().flat_map(Module::iter_script_fn).fold( let mut _list = ctx.iter_namespaces().flat_map(Module::iter_script_fn).fold(
Array::new(), Array::new(),
|mut list, (_, _, _, _, f)| { |mut list, (_, _, _, _, f)| {
list.push(make_metadata(&dict, None, f).into()); list.push(make_metadata(&dict, None, f).into());
@ -122,8 +122,8 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
} }
ctx.iter_imports_raw() ctx.iter_imports_raw()
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref())); .for_each(|(ns, m)| scan_module(&mut _list, &dict, ns.clone(), m.as_ref()));
} }
list _list
} }

View File

@ -3264,6 +3264,7 @@ impl Engine {
self, self,
_scope, _scope,
statements, statements,
#[cfg(not(feature = "no_function"))]
StaticVec::new_const(), StaticVec::new_const(),
optimization_level, optimization_level,
)); ));
@ -3351,6 +3352,7 @@ impl Engine {
self, self,
_scope, _scope,
statements, statements,
#[cfg(not(feature = "no_function"))]
_lib, _lib,
optimization_level, optimization_level,
)); ));

View File

@ -764,6 +764,9 @@ impl Token {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
"private" => Private, "private" => Private,
#[cfg(feature = "no_function")]
"fn" | "private" => Reserved(syntax.into()),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
"import" => Import, "import" => Import,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -771,9 +774,6 @@ impl Token {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
"as" => As, "as" => As,
#[cfg(feature = "no_function")]
"fn" | "private" => Reserved(syntax.into()),
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
"import" | "export" | "as" => Reserved(syntax.into()), "import" | "export" | "as" => Reserved(syntax.into()),

View File

@ -3,8 +3,8 @@
use crate::tokenizer::is_valid_identifier; use crate::tokenizer::is_valid_identifier;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, NativeCallContext, Position, RhaiResult, Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, Module, NativeCallContext, Position,
StaticVec, AST, RhaiResult, StaticVec, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -137,12 +137,21 @@ impl FnPtr {
ast: &AST, ast: &AST,
args: impl FuncArgs, args: impl FuncArgs,
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
let _ast = ast;
let mut arg_values = crate::StaticVec::new_const(); let mut arg_values = crate::StaticVec::new_const();
args.parse(&mut arg_values); args.parse(&mut arg_values);
let lib = [ast.as_ref()]; let lib = [
#[cfg(not(feature = "no_function"))]
_ast.as_ref(),
];
let lib = if lib.first().map(|m: &&Module| m.is_empty()).unwrap_or(true) {
&lib[0..0]
} else {
&lib
};
#[allow(deprecated)] #[allow(deprecated)]
let ctx = NativeCallContext::new(engine, self.fn_name(), &lib); let ctx = NativeCallContext::new(engine, self.fn_name(), lib);
let result = self.call_dynamic(&ctx, None, arg_values)?; let result = self.call_dynamic(&ctx, None, arg_values)?;

View File

@ -1,5 +1,5 @@
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use rhai::{Engine, EvalAltResult, FnPtr, NativeCallContext, ParseErrorType, Scope, INT}; use rhai::{Engine, EvalAltResult, FnPtr, ParseErrorType, Scope, INT};
use std::any::TypeId; use std::any::TypeId;
use std::cell::RefCell; use std::cell::RefCell;
use std::mem::take; use std::mem::take;
@ -318,30 +318,18 @@ fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
fn test_closures_external() -> Result<(), Box<EvalAltResult>> { fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
let mut ast = engine.compile( let ast = engine.compile(
r#" r#"
let test = "hello"; let test = "hello";
|x| test + x |x| test + x
"#, "#,
)?; )?;
// Save the function pointer together with captured variables
let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?; let fn_ptr = engine.eval_ast::<FnPtr>(&ast)?;
// Get rid of the script, retaining only functions let f = move |x: INT| -> String { fn_ptr.call(&engine, &ast, (x,)).unwrap() };
ast.retain_functions(|_, _, _, _| true);
// Create function namespace from the 'AST' assert_eq!(f(42), "hello42");
let lib = [ast.as_ref()];
// Create native call context
let fn_name = fn_ptr.fn_name().to_string();
let context = NativeCallContext::new(&engine, &fn_name, &lib);
// Closure 'f' captures: the engine, the AST, and the curried function pointer
let f = move |x: INT| fn_ptr.call_dynamic(&context, None, [x.into()]);
assert_eq!(f(42)?.into_string(), Ok("hello42".to_string()));
Ok(()) Ok(())
} }

View File

@ -69,6 +69,7 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
#[test] #[test]
fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> { fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {