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
----------------
* 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<Module>` while `AST::lib` is deprecated.
* `AST::merge_filtered` and `AST::combine_filtered` are no longer exported under `no_function`.
Version 1.2.1

View File

@ -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)
}
}

View File

@ -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,
)
}
}

View File

@ -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(())

View File

@ -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<Module>,
/// Embedded module resolver, if any.
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
}
@ -208,11 +206,12 @@ impl AST {
#[must_use]
pub fn new(
statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>,
#[cfg(not(feature = "no_function"))] functions: impl Into<Shared<Module>>,
) -> 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<Item = Stmt>,
functions: impl Into<Shared<Module>>,
#[cfg(not(feature = "no_function"))] functions: impl Into<Shared<Module>>,
source: impl Into<Identifier>,
) -> 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<Identifier>) -> &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<Stmt> {
&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<Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.clone()
) -> Option<&Shared<crate::module::resolvers::StaticModuleResolver>> {
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<Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.clone()
pub fn resolver(&self) -> Option<&Shared<crate::module::resolvers::StaticModuleResolver>> {
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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<rhai::EvalAltResult>> {
/// # #[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::<String>(&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<Module> for AST {
#[inline(always)]
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 {
#[inline(always)]
fn as_ref(&self) -> &Shared<Module> {
@ -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 {

View File

@ -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();

View File

@ -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()

View File

@ -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<str>,
arg_values: &mut [Dynamic],
) -> Option<Dynamic> {
#[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::<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 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<Stmt>,
engine: &Engine,
scope: &Scope,
lib: &[&Module],
#[cfg(not(feature = "no_function"))] lib: &[&crate::Module],
optimization_level: OptimizationLevel,
) -> StaticVec<Stmt> {
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<Stmt>,
functions: StaticVec<Shared<ScriptFnDef>>,
#[cfg(not(feature = "no_function"))] functions: StaticVec<
crate::Shared<crate::ast::ScriptFnDef>,
>,
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,
)
}

View File

@ -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
}

View File

@ -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,
));

View File

@ -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()),

View File

@ -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<T, Box<EvalAltResult>> {
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)?;

View File

@ -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<EvalAltResult>> {
fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
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::<FnPtr>(&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(())
}

View File

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