diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d899cc..52567abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,8 @@ Enhancements 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`. -* `AST::shared_lib` is changed to return `&Shared` while `AST::lib` is deprecated. +* `AST::merge_filtered` and `AST::combine_filtered` are no longer exported under `no_function`. Version 1.2.1 diff --git a/src/api/eval.rs b/src/api/eval.rs index 8cd86735..6356c1e8 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -3,7 +3,7 @@ use crate::engine::{EvalState, Imports}; use crate::parser::ParseState; 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; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -217,7 +217,7 @@ impl Engine { } #[cfg(not(feature = "no_module"))] { - mods.embedded_module_resolver = ast.resolver(); + mods.embedded_module_resolver = ast.resolver().cloned(); } let statements = ast.statements(); @@ -226,7 +226,15 @@ impl Engine { 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) } } diff --git a/src/api/mod.rs b/src/api/mod.rs index 875db590..b2c7c8f4 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -86,12 +86,16 @@ impl Engine { }) .collect(); - #[cfg(feature = "no_function")] - let lib = crate::StaticVec::new_const(); - 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, + ) } } diff --git a/src/api/run.rs b/src/api/run.rs index 844ad074..84e2222d 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -2,7 +2,7 @@ use crate::engine::{EvalState, Imports}; use crate::parser::ParseState; -use crate::{Engine, EvalAltResult, Scope, AST}; +use crate::{Engine, EvalAltResult, Module, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -59,12 +59,20 @@ impl Engine { } #[cfg(not(feature = "no_module"))] { - mods.embedded_module_resolver = ast.resolver(); + mods.embedded_module_resolver = ast.resolver().cloned(); } let statements = ast.statements(); 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)?; } Ok(()) diff --git a/src/ast.rs b/src/ast.rs index b9a2852a..bc6f99f2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,7 +1,6 @@ //! Module defining the AST (abstract syntax tree). use crate::calc_fn_hash; -use crate::func::native::shared_make_mut; use crate::module::NamespaceRef; use crate::tokenizer::Token; use crate::types::dynamic::Union; @@ -187,10 +186,9 @@ pub struct AST { /// Global statements. body: StmtBlock, /// Script-defined functions. + #[cfg(not(feature = "no_function"))] functions: Shared, /// Embedded module resolver, if any. - /// - /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] resolver: Option>, } @@ -208,11 +206,12 @@ impl AST { #[must_use] pub fn new( statements: impl IntoIterator, - functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into>, ) -> Self { Self { source: None, body: StmtBlock::new(statements, Position::NONE), + #[cfg(not(feature = "no_function"))] functions: functions.into(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -225,6 +224,7 @@ impl AST { Self { source: None, body: StmtBlock::NONE, + #[cfg(not(feature = "no_function"))] functions: Module::new().into(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -235,12 +235,13 @@ impl AST { #[must_use] pub fn new_with_source( statements: impl IntoIterator, - functions: impl Into>, + #[cfg(not(feature = "no_function"))] functions: impl Into>, source: impl Into, ) -> Self { Self { source: Some(source.into()), body: StmtBlock::new(statements, Position::NONE), + #[cfg(not(feature = "no_function"))] functions: functions.into(), #[cfg(not(feature = "no_module"))] resolver: None, @@ -262,6 +263,7 @@ impl AST { #[inline] pub fn set_source(&mut self, source: impl Into) -> &mut Self { let source = source.into(); + #[cfg(not(feature = "no_function"))] Shared::get_mut(&mut self.functions) .as_mut() .map(|m| m.set_id(source.clone())); @@ -296,9 +298,17 @@ impl AST { pub(crate) fn statements_mut(&mut self) -> &mut StaticVec { &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. #[cfg(not(feature = "internals"))] - #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] @@ -308,9 +318,8 @@ impl AST { /// _(internals)_ Get the internal shared [`Module`] containing all script-defined functions. /// Exported under the `internals` feature only. /// - /// Not available under `no_function` or `no_module`. + /// Not available under `no_function`. #[cfg(feature = "internals")] - #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] @@ -318,25 +327,25 @@ impl AST { &self.functions } /// Get the embedded [module resolver][`ModuleResolver`]. - #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "internals"))] + #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] pub(crate) fn resolver( &self, - ) -> Option> { - self.resolver.clone() + ) -> Option<&Shared> { + self.resolver.as_ref() } /// _(internals)_ Get the embedded [module resolver][crate::ModuleResolver]. /// Exported under the `internals` feature only. /// /// Not available under `no_module`. - #[cfg(not(feature = "no_module"))] #[cfg(feature = "internals")] + #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn resolver(&self) -> Option> { - self.resolver.clone() + pub fn resolver(&self) -> Option<&Shared> { + self.resolver.as_ref() } /// Set the embedded [module resolver][`ModuleResolver`]. #[cfg(not(feature = "no_module"))] @@ -391,6 +400,7 @@ impl AST { Self { source: self.source.clone(), body: self.body.clone(), + #[cfg(not(feature = "no_function"))] functions: Module::new().into(), #[cfg(not(feature = "no_module"))] resolver: self.resolver.clone(), @@ -448,7 +458,7 @@ impl AST { #[inline(always)] #[must_use] 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. /// @@ -500,11 +510,13 @@ impl AST { /// ``` #[inline(always)] 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 /// is returned. /// + /// Not available under `no_function`. + /// /// 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. /// Of course, if the first [`AST`] uses a `return` statement at the end, then @@ -518,8 +530,6 @@ impl AST { /// /// ``` /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { /// use rhai::Engine; /// /// let engine = Engine::new(); @@ -550,45 +560,67 @@ impl AST { /// /// // Evaluate it /// assert_eq!(engine.eval_ast::(&ast)?, "42!"); - /// # } /// # Ok(()) /// # } /// ``` - #[inline] + #[cfg(not(feature = "no_function"))] + #[inline(always)] #[must_use] pub fn merge_filtered( &self, other: &Self, filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, ) -> Self { - let Self { - body, functions, .. - } = self; - - let merged = match (body.is_empty(), other.body.is_empty()) { + self.merge_filtered_impl(other, filter) + } + /// Merge two [`AST`] into one. Both [`AST`]'s are untouched and a new, merged, version + /// is returned. + #[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) => { - let mut body = body.clone(); + let mut body = self.body.clone(); body.0.extend(other.body.0.iter().cloned()); body } - (false, true) => body.clone(), + (false, true) => self.body.clone(), (true, false) => other.body.clone(), (true, true) => StmtBlock::NONE, }; let source = other.source.clone().or_else(|| self.source.clone()); - let mut functions = functions.as_ref().clone(); - functions.merge_filtered(&other.functions, &filter); + #[cfg(not(feature = "no_function"))] + let functions = { + let mut functions = self.functions.as_ref().clone(); + functions.merge_filtered(&other.functions, &_filter); + functions + }; 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 { - 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. /// + /// Not available under `no_function`. + /// /// 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. /// Of course, if the first [`AST`] uses a `return` statement at the end, then @@ -602,8 +634,6 @@ impl AST { /// /// ``` /// # fn main() -> Result<(), Box> { - /// # #[cfg(not(feature = "no_function"))] - /// # { /// use rhai::Engine; /// /// let engine = Engine::new(); @@ -634,20 +664,31 @@ impl AST { /// /// // Evaluate it /// assert_eq!(engine.eval_ast::(&ast1)?, "42!"); - /// # } /// # Ok(()) /// # } /// ``` - #[inline] + #[cfg(not(feature = "no_function"))] + #[inline(always)] pub fn combine_filtered( &mut self, other: Self, 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 { self.body.0.extend(other.body.0.into_iter()); + #[cfg(not(feature = "no_function"))] 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 } @@ -683,7 +724,8 @@ impl AST { filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, ) -> &mut Self { 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 } @@ -875,6 +917,7 @@ impl AsRef<[Stmt]> for AST { } } +#[cfg(not(feature = "no_function"))] impl AsRef for AST { #[inline(always)] fn as_ref(&self) -> &Module { @@ -882,6 +925,7 @@ impl AsRef for AST { } } +#[cfg(not(feature = "no_function"))] impl AsRef> for AST { #[inline(always)] fn as_ref(&self) -> &Shared { @@ -2456,6 +2500,7 @@ impl AST { /// This method will be removed in the next major version. #[deprecated(since = "1.3.0", note = "use `shared_lib` instead")] #[cfg(feature = "internals")] + #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] pub fn lib(&self) -> &crate::Module { diff --git a/src/func/call.rs b/src/func/call.rs index 8d768f79..25079ab6 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -13,7 +13,7 @@ use crate::tokenizer::Token; use crate::{ ast::{Expr, Stmt}, 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")] use std::prelude::v1::*; @@ -554,14 +554,14 @@ impl Engine { let mut lib_merged = StaticVec::with_capacity(lib.len() + 1); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); - let lib = if let Some(ref env_lib) = fn_def.lib { - if env_lib.is_empty() { + let lib = if let Some(ref fn_lib) = fn_def.lib { + if fn_lib.is_empty() { lib } else { 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.as_ref() + &lib_merged } } else { lib @@ -886,8 +886,9 @@ impl Engine { )?; // If new functions are defined within the eval string, it is an error + #[cfg(not(feature = "no_function"))] if !ast.shared_lib().is_empty() { - return Err(ParseErrorType::WrongFnDefinition.into()); + return Err(crate::ParseErrorType::WrongFnDefinition.into()); } let statements = ast.statements(); diff --git a/src/module/mod.rs b/src/module/mod.rs index 62f2ed51..47d12f14 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1460,7 +1460,7 @@ impl Module { // Non-private functions defined become module functions #[cfg(not(feature = "no_function"))] - if !ast.shared_lib().functions.is_empty() { + if ast.has_functions() { ast.shared_lib() .functions .values() diff --git a/src/optimizer.rs b/src/optimizer.rs index ce10d07b..73d70857 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1,7 +1,7 @@ //! Module implementing the [`AST`] optimizer. #![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::{ 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::{ 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")] use std::prelude::v1::*; @@ -59,8 +59,9 @@ struct OptimizerState<'a> { propagate_constants: bool, /// An [`Engine`] instance for eager function evaluation. engine: &'a Engine, - /// [Module] containing script-defined functions. - lib: &'a [&'a Module], + /// [Module][crate::Module] containing script-defined functions. + #[cfg(not(feature = "no_function"))] + lib: &'a [&'a crate::Module], /// Optimization level. optimization_level: OptimizationLevel, } @@ -70,7 +71,7 @@ impl<'a> OptimizerState<'a> { #[inline(always)] pub const fn new( engine: &'a Engine, - lib: &'a [&'a Module], + #[cfg(not(feature = "no_function"))] lib: &'a [&'a crate::Module], optimization_level: OptimizationLevel, ) -> Self { Self { @@ -78,6 +79,7 @@ impl<'a> OptimizerState<'a> { variables: StaticVec::new_const(), propagate_constants: true, engine, + #[cfg(not(feature = "no_function"))] lib, optimization_level, } @@ -139,11 +141,16 @@ impl<'a> OptimizerState<'a> { fn_name: impl AsRef, arg_values: &mut [Dynamic], ) -> Option { + #[cfg(not(feature = "no_function"))] + let lib = self.lib; + #[cfg(feature = "no_function")] + let lib = &[]; + self.engine .call_native_fn( &mut Imports::new(), &mut EvalState::new(), - self.lib, + lib, &fn_name, calc_fn_hash(&fn_name, arg_values.len()), &mut arg_values.iter_mut().collect::>(), @@ -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 let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) .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"); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { @@ -1091,7 +1103,7 @@ fn optimize_top_level( statements: StaticVec, engine: &Engine, scope: &Scope, - lib: &[&Module], + #[cfg(not(feature = "no_function"))] lib: &[&crate::Module], optimization_level: OptimizationLevel, ) -> StaticVec { let mut statements = statements; @@ -1103,7 +1115,12 @@ fn optimize_top_level( } // 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 scope.iter().for_each(|(name, constant, value)| { @@ -1123,7 +1140,9 @@ pub fn optimize_into_ast( engine: &Engine, scope: &Scope, statements: StaticVec, - functions: StaticVec>, + #[cfg(not(feature = "no_function"))] functions: StaticVec< + crate::Shared, + >, optimization_level: OptimizationLevel, ) -> AST { let level = if cfg!(feature = "no_optimize") { @@ -1133,19 +1152,18 @@ pub fn optimize_into_ast( }; let mut statements = statements; - let _functions = functions; #[cfg(not(feature = "no_function"))] let lib = { - let mut module = Module::new(); + let mut module = crate::Module::new(); if level != OptimizationLevel::None { // 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() - .map(|fn_def| ScriptFnDef { + .map(|fn_def| crate::ast::ScriptFnDef { name: fn_def.name.clone(), access: fn_def.access, body: crate::ast::StmtBlock::NONE, @@ -1163,7 +1181,7 @@ pub fn optimize_into_ast( let lib2 = &[&lib2]; - _functions + functions .into_iter() .map(|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); }); } else { - _functions.into_iter().for_each(|fn_def| { + functions.into_iter().for_each(|fn_def| { module.set_script_fn(fn_def); }); } @@ -1187,18 +1205,21 @@ pub fn optimize_into_ast( module }; - #[cfg(feature = "no_function")] - let lib = Module::new(); - statements.shrink_to_fit(); AST::new( match level { OptimizationLevel::None => statements, - OptimizationLevel::Simple | OptimizationLevel::Full => { - optimize_top_level(statements, engine, &scope, &[&lib], level) - } + OptimizationLevel::Simple | OptimizationLevel::Full => optimize_top_level( + statements, + engine, + &scope, + #[cfg(not(feature = "no_function"))] + &[&lib], + level, + ), }, + #[cfg(not(feature = "no_function"))] lib, ) } diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 93f6aa16..37bb6eea 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -90,7 +90,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { .map(|&s| s.into()) .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(), |mut list, (_, _, _, _, f)| { list.push(make_metadata(&dict, None, f).into()); @@ -122,8 +122,8 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array { } 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 } diff --git a/src/parser.rs b/src/parser.rs index 6ce9314a..f207c7fb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3264,6 +3264,7 @@ impl Engine { self, _scope, statements, + #[cfg(not(feature = "no_function"))] StaticVec::new_const(), optimization_level, )); @@ -3351,6 +3352,7 @@ impl Engine { self, _scope, statements, + #[cfg(not(feature = "no_function"))] _lib, optimization_level, )); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index f96bf579..91aa892f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -764,6 +764,9 @@ impl Token { #[cfg(not(feature = "no_function"))] "private" => Private, + #[cfg(feature = "no_function")] + "fn" | "private" => Reserved(syntax.into()), + #[cfg(not(feature = "no_module"))] "import" => Import, #[cfg(not(feature = "no_module"))] @@ -771,9 +774,6 @@ impl Token { #[cfg(not(feature = "no_module"))] "as" => As, - #[cfg(feature = "no_function")] - "fn" | "private" => Reserved(syntax.into()), - #[cfg(feature = "no_module")] "import" | "export" | "as" => Reserved(syntax.into()), diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 3b26e1d0..835935cb 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -3,8 +3,8 @@ use crate::tokenizer::is_valid_identifier; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, NativeCallContext, Position, RhaiResult, - StaticVec, AST, + Dynamic, Engine, EvalAltResult, FuncArgs, Identifier, Module, NativeCallContext, Position, + RhaiResult, StaticVec, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -137,12 +137,21 @@ impl FnPtr { ast: &AST, args: impl FuncArgs, ) -> Result> { + let _ast = ast; let mut arg_values = crate::StaticVec::new_const(); 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)] - 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)?; diff --git a/tests/closures.rs b/tests/closures.rs index e87606db..5213bf3f 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -1,5 +1,5 @@ #![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::cell::RefCell; use std::mem::take; @@ -318,30 +318,18 @@ fn test_closures_shared_obj() -> Result<(), Box> { fn test_closures_external() -> Result<(), Box> { let engine = Engine::new(); - let mut ast = engine.compile( + let ast = engine.compile( r#" let test = "hello"; |x| test + x "#, )?; - // Save the function pointer together with captured variables let fn_ptr = engine.eval_ast::(&ast)?; - // Get rid of the script, retaining only functions - ast.retain_functions(|_, _, _, _| true); + let f = move |x: INT| -> String { fn_ptr.call(&engine, &ast, (x,)).unwrap() }; - // Create function namespace from the 'AST' - 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())); + assert_eq!(f(42), "hello42"); Ok(()) } diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 1b617e7d..1a1c05db 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -69,6 +69,7 @@ fn test_optimizer_run() -> Result<(), Box> { } #[cfg(not(feature = "no_module"))] +#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_position"))] #[test] fn test_optimizer_parse() -> Result<(), Box> {