Limit modules loading.
This commit is contained in:
parent
55c97eb649
commit
be97047e51
20
README.md
20
README.md
@ -23,8 +23,8 @@ Rhai's current features set:
|
|||||||
* Relatively little `unsafe` code (yes there are some for performance reasons)
|
* Relatively little `unsafe` code (yes there are some for performance reasons)
|
||||||
* Sand-boxed (the scripting [`Engine`] can be declared immutable which cannot mutate the containing environment
|
* Sand-boxed (the scripting [`Engine`] can be declared immutable which cannot mutate the containing environment
|
||||||
unless explicitly allowed via `RefCell` etc.)
|
unless explicitly allowed via `RefCell` etc.)
|
||||||
* Rugged (protection against [stack-overflow](#maximum-stack-depth) and [runaway scripts](#maximum-number-of-operations))
|
* Rugged (protection against [stack-overflow](#maximum-stack-depth) and [runaway scripts](#maximum-number-of-operations) etc.)
|
||||||
* Able to set limits on script resource usage (e.g. see [tracking progress](#tracking-progress))
|
* Able to track script evaluation [progress](#tracking-progress) and manually terminate a script run
|
||||||
* [`no-std`](#optional-features) support
|
* [`no-std`](#optional-features) support
|
||||||
* [Function overloading](#function-overloading)
|
* [Function overloading](#function-overloading)
|
||||||
* [Operator overloading](#operator-overloading)
|
* [Operator overloading](#operator-overloading)
|
||||||
@ -2268,6 +2268,7 @@ The most important resources to watch out for are:
|
|||||||
floating-point representations, in order to crash the system.
|
floating-point representations, in order to crash the system.
|
||||||
* **Files**: A malignant script may continuously `import` an external module within an infinite loop,
|
* **Files**: A malignant script may continuously `import` an external module within an infinite loop,
|
||||||
thereby putting heavy load on the file-system (or even the network if the file is not local).
|
thereby putting heavy load on the file-system (or even the network if the file is not local).
|
||||||
|
Even when modules are not created from files, they still typically consume a lot of resources to load.
|
||||||
* **Data**: A malignant script may attempt to read from and/or write to data that it does not own. If this happens,
|
* **Data**: A malignant script may attempt to read from and/or write to data that it does not own. If this happens,
|
||||||
it is a severe security breach and may put the entire system at risk.
|
it is a severe security breach and may put the entire system at risk.
|
||||||
|
|
||||||
@ -2319,6 +2320,19 @@ engine.on_progress(|count| { // 'count' is the number of operatio
|
|||||||
The closure passed to `Engine::on_progress` will be called once every operation.
|
The closure passed to `Engine::on_progress` will be called once every operation.
|
||||||
Return `false` to terminate the script immediately.
|
Return `false` to terminate the script immediately.
|
||||||
|
|
||||||
|
### Maximum number of modules
|
||||||
|
|
||||||
|
Rhai by default does not limit how many [modules] are loaded via the `import` statement.
|
||||||
|
This can be changed via the `Engine::set_max_modules` method, with zero being unlimited (the default).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine.set_max_modules(5); // allow loading only up to 5 modules
|
||||||
|
|
||||||
|
engine.set_max_modules(0); // allow unlimited modules
|
||||||
|
```
|
||||||
|
|
||||||
### Maximum stack depth
|
### Maximum stack depth
|
||||||
|
|
||||||
Rhai by default limits function calls to a maximum depth of 256 levels (28 levels in debug build).
|
Rhai by default limits function calls to a maximum depth of 256 levels (28 levels in debug build).
|
||||||
@ -2646,7 +2660,7 @@ let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run
|
|||||||
Or override it from Rust:
|
Or override it from Rust:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn alt_eval(script: String) -> Result<(), EvalAltResult> {
|
fn alt_eval(script: String) -> Result<(), Box<EvalAltResult>> {
|
||||||
Err(format!("eval is evil! I refuse to run {}", script).into())
|
Err(format!("eval is evil! I refuse to run {}", script).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ use crate::stdlib::{
|
|||||||
any::{type_name, Any, TypeId},
|
any::{type_name, Any, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt, mem, ptr,
|
fmt,
|
||||||
string::String,
|
string::String,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@ -27,7 +27,7 @@ use crate::stdlib::{
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
use crate::stdlib::time::Instant;
|
use crate::stdlib::time::Instant;
|
||||||
|
|
||||||
/// A trait to represent any type.
|
/// Trait to represent any type.
|
||||||
///
|
///
|
||||||
/// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type.
|
/// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type.
|
||||||
/// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`.
|
/// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`.
|
||||||
@ -81,7 +81,7 @@ impl<T: Any + Clone> Variant for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait to represent any type.
|
/// Trait to represent any type.
|
||||||
///
|
///
|
||||||
/// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`),
|
/// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`),
|
||||||
/// `bool`, `String`, `char`, `Vec<T>` (into `Array`) and `HashMap<String, T>` (into `Map`).
|
/// `bool`, `String`, `char`, `Vec<T>` (into `Array`) and `HashMap<String, T>` (into `Map`).
|
||||||
@ -142,7 +142,7 @@ impl dyn Variant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dynamic type containing any value.
|
/// Dynamic type containing any value.
|
||||||
pub struct Dynamic(pub(crate) Union);
|
pub struct Dynamic(pub(crate) Union);
|
||||||
|
|
||||||
/// Internal `Dynamic` representation.
|
/// Internal `Dynamic` representation.
|
||||||
|
@ -21,7 +21,6 @@ use crate::engine::Map;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
|
||||||
mem,
|
mem,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
};
|
};
|
||||||
@ -1016,11 +1015,10 @@ impl Engine {
|
|||||||
.get_function_by_signature(name, args.len(), true)
|
.get_function_by_signature(name, args.len(), true)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.into(), pos)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.into(), pos)))?;
|
||||||
|
|
||||||
let mut state = State::new(fn_lib);
|
let state = State::new(fn_lib);
|
||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
|
|
||||||
let (result, _) =
|
let (result, _) = self.call_script_fn(Some(scope), state, name, fn_def, args, pos, 0)?;
|
||||||
self.call_script_fn(Some(scope), &mut state, name, fn_def, args, pos, 0, 0)?;
|
|
||||||
|
|
||||||
let return_type = self.map_type_name(result.type_name());
|
let return_type = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ use crate::parser::ModuleRef;
|
|||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
borrow::Cow,
|
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
format,
|
format,
|
||||||
@ -36,13 +35,13 @@ use crate::stdlib::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An dynamic array of `Dynamic` values.
|
/// Variable-sized array of `Dynamic` values.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_index` feature.
|
/// Not available under the `no_index` feature.
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub type Array = Vec<Dynamic>;
|
pub type Array = Vec<Dynamic>;
|
||||||
|
|
||||||
/// An dynamic hash map of `Dynamic` values with `String` keys.
|
/// Hash map of `Dynamic` values with `String` keys.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_object` feature.
|
/// Not available under the `no_object` feature.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -154,16 +153,20 @@ pub struct State<'a> {
|
|||||||
|
|
||||||
/// Number of operations performed.
|
/// Number of operations performed.
|
||||||
pub operations: u64,
|
pub operations: u64,
|
||||||
|
|
||||||
|
/// Number of modules loaded.
|
||||||
|
pub modules: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
/// Create a new `State`.
|
/// Create a new `State`.
|
||||||
pub fn new(fn_lib: &'a FunctionsLib) -> Self {
|
pub fn new(fn_lib: &'a FunctionsLib) -> Self {
|
||||||
Self {
|
Self {
|
||||||
always_search: false,
|
|
||||||
fn_lib,
|
fn_lib,
|
||||||
|
always_search: false,
|
||||||
scope_level: 0,
|
scope_level: 0,
|
||||||
operations: 0,
|
operations: 0,
|
||||||
|
modules: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Does a certain script-defined function exist in the `State`?
|
/// Does a certain script-defined function exist in the `State`?
|
||||||
@ -322,8 +325,10 @@ pub struct Engine {
|
|||||||
///
|
///
|
||||||
/// Defaults to 28 for debug builds and 256 for non-debug builds.
|
/// Defaults to 28 for debug builds and 256 for non-debug builds.
|
||||||
pub(crate) max_call_stack_depth: usize,
|
pub(crate) max_call_stack_depth: usize,
|
||||||
/// Maximum number of operations to run.
|
/// Maximum number of operations allowed to run.
|
||||||
pub(crate) max_operations: Option<NonZeroU64>,
|
pub(crate) max_operations: Option<NonZeroU64>,
|
||||||
|
/// Maximum number of modules allowed to load.
|
||||||
|
pub(crate) max_modules: Option<NonZeroU64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Engine {
|
impl Default for Engine {
|
||||||
@ -363,6 +368,7 @@ impl Default for Engine {
|
|||||||
|
|
||||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
max_operations: None,
|
max_operations: None,
|
||||||
|
max_modules: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "no_stdlib")]
|
#[cfg(feature = "no_stdlib")]
|
||||||
@ -503,6 +509,7 @@ impl Engine {
|
|||||||
|
|
||||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||||
max_operations: None,
|
max_operations: None,
|
||||||
|
max_modules: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,6 +552,13 @@ impl Engine {
|
|||||||
pub fn set_max_operations(&mut self, operations: u64) {
|
pub fn set_max_operations(&mut self, operations: u64) {
|
||||||
self.max_operations = NonZeroU64::new(operations);
|
self.max_operations = NonZeroU64::new(operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the maximum number of imported modules allowed for a script (0 for unlimited).
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
pub fn set_max_modules(&mut self, modules: u64) {
|
||||||
|
self.max_modules = NonZeroU64::new(modules);
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the module resolution service used by the `Engine`.
|
/// Set the module resolution service used by the `Engine`.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_module` feature.
|
/// Not available under the `no_module` feature.
|
||||||
@ -582,11 +596,9 @@ impl Engine {
|
|||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if hashes.1 > 0 {
|
if hashes.1 > 0 {
|
||||||
if let Some(fn_def) = state.get_function(hashes.1) {
|
if let Some(fn_def) = state.get_function(hashes.1) {
|
||||||
let ops = state.operations;
|
let (result, state2) =
|
||||||
let (result, operations) =
|
self.call_script_fn(scope, *state, fn_name, fn_def, args, pos, level)?;
|
||||||
self.call_script_fn(scope, &state, fn_name, fn_def, args, pos, level, ops)?;
|
*state = state2;
|
||||||
state.operations = operations;
|
|
||||||
self.inc_operations(state, pos)?;
|
|
||||||
return Ok((result, false));
|
return Ok((result, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -701,24 +713,23 @@ impl Engine {
|
|||||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||||
pub(crate) fn call_script_fn<'s>(
|
pub(crate) fn call_script_fn<'s>(
|
||||||
&self,
|
&self,
|
||||||
scope: Option<&mut Scope<'s>>,
|
scope: Option<&mut Scope>,
|
||||||
state: &State,
|
mut state: State<'s>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
fn_def: &FnDef,
|
fn_def: &FnDef,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
operations: u64,
|
) -> Result<(Dynamic, State<'s>), Box<EvalAltResult>> {
|
||||||
) -> Result<(Dynamic, u64), Box<EvalAltResult>> {
|
self.inc_operations(&mut state, pos)?;
|
||||||
|
|
||||||
|
let orig_scope_level = state.scope_level;
|
||||||
|
state.scope_level += 1;
|
||||||
|
|
||||||
match scope {
|
match scope {
|
||||||
// Extern scope passed in which is not empty
|
// Extern scope passed in which is not empty
|
||||||
Some(scope) if scope.len() > 0 => {
|
Some(scope) if scope.len() > 0 => {
|
||||||
let scope_len = scope.len();
|
let scope_len = scope.len();
|
||||||
let mut local_state = State::new(state.fn_lib);
|
|
||||||
|
|
||||||
local_state.operations = operations;
|
|
||||||
self.inc_operations(&mut local_state, pos)?;
|
|
||||||
local_state.scope_level += 1;
|
|
||||||
|
|
||||||
// Put arguments into scope as variables
|
// Put arguments into scope as variables
|
||||||
scope.extend(
|
scope.extend(
|
||||||
@ -730,14 +741,14 @@ impl Engine {
|
|||||||
args.into_iter().map(|v| mem::take(*v)),
|
args.into_iter().map(|v| mem::take(*v)),
|
||||||
)
|
)
|
||||||
.map(|(name, value)| {
|
.map(|(name, value)| {
|
||||||
let var_name = unsafe_cast_var_name(name.as_str(), &local_state);
|
let var_name = unsafe_cast_var_name(name.as_str(), &mut state);
|
||||||
(var_name, ScopeEntryType::Normal, value)
|
(var_name, ScopeEntryType::Normal, value)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
let result = self
|
let result = self
|
||||||
.eval_stmt(scope, &mut local_state, &fn_def.body, level + 1)
|
.eval_stmt(scope, &mut state, &fn_def.body, level + 1)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Convert return statement to return value
|
// Convert return statement to return value
|
||||||
EvalAltResult::Return(x, _) => Ok(x),
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
@ -756,19 +767,14 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Remove all local variables
|
// Remove all local variables
|
||||||
// No need to reset `state.scope_level` because it is thrown away
|
|
||||||
scope.rewind(scope_len);
|
scope.rewind(scope_len);
|
||||||
|
state.scope_level = orig_scope_level;
|
||||||
|
|
||||||
return result.map(|v| (v, local_state.operations));
|
return result.map(|v| (v, state));
|
||||||
}
|
}
|
||||||
// No new scope - create internal scope
|
// No new scope - create internal scope
|
||||||
_ => {
|
_ => {
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
let mut local_state = State::new(state.fn_lib);
|
|
||||||
|
|
||||||
local_state.operations = operations;
|
|
||||||
self.inc_operations(&mut local_state, pos)?;
|
|
||||||
local_state.scope_level += 1;
|
|
||||||
|
|
||||||
// Put arguments into scope as variables
|
// Put arguments into scope as variables
|
||||||
scope.extend(
|
scope.extend(
|
||||||
@ -783,9 +789,8 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Evaluate the function at one higher level of call depth
|
// Evaluate the function at one higher level of call depth
|
||||||
// No need to reset `state.scope_level` because it is thrown away
|
let result = self
|
||||||
return self
|
.eval_stmt(&mut scope, &mut state, &fn_def.body, level + 1)
|
||||||
.eval_stmt(&mut scope, &mut local_state, &fn_def.body, level + 1)
|
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Convert return statement to return value
|
// Convert return statement to return value
|
||||||
EvalAltResult::Return(x, _) => Ok(x),
|
EvalAltResult::Return(x, _) => Ok(x),
|
||||||
@ -801,8 +806,10 @@ impl Engine {
|
|||||||
err,
|
err,
|
||||||
pos,
|
pos,
|
||||||
))),
|
))),
|
||||||
})
|
});
|
||||||
.map(|v| (v, local_state.operations));
|
|
||||||
|
state.scope_level = orig_scope_level;
|
||||||
|
return result.map(|v| (v, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1512,11 +1519,9 @@ impl Engine {
|
|||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if let Some(fn_def) = module.get_qualified_scripted_fn(*hash_fn_def) {
|
if let Some(fn_def) = module.get_qualified_scripted_fn(*hash_fn_def) {
|
||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
let ops = state.operations;
|
let (result, state2) =
|
||||||
let (result, operations) =
|
self.call_script_fn(None, *state, name, fn_def, args, *pos, level)?;
|
||||||
self.call_script_fn(None, state, name, fn_def, args, *pos, level, ops)?;
|
*state = state2;
|
||||||
state.operations = operations;
|
|
||||||
self.inc_operations(state, *pos)?;
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
// Then search in Rust functions
|
// Then search in Rust functions
|
||||||
@ -1792,13 +1797,20 @@ impl Engine {
|
|||||||
|
|
||||||
// Import statement
|
// Import statement
|
||||||
Stmt::Import(x) => {
|
Stmt::Import(x) => {
|
||||||
let (expr, (name, pos)) = x.as_ref();
|
|
||||||
|
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
unreachable!();
|
unreachable!();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
|
let (expr, (name, pos)) = x.as_ref();
|
||||||
|
|
||||||
|
// Guard against too many modules
|
||||||
|
if let Some(max) = self.max_modules {
|
||||||
|
if state.modules >= max.get() {
|
||||||
|
return Err(Box::new(EvalAltResult::ErrorTooManyModules(*pos)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(path) = self
|
if let Some(path) = self
|
||||||
.eval_expr(scope, state, &expr, level)?
|
.eval_expr(scope, state, &expr, level)?
|
||||||
.try_cast::<String>()
|
.try_cast::<String>()
|
||||||
@ -1810,7 +1822,10 @@ impl Engine {
|
|||||||
|
|
||||||
let mod_name = unsafe_cast_var_name(name, &state);
|
let mod_name = unsafe_cast_var_name(name, &state);
|
||||||
scope.push_module(mod_name, module);
|
scope.push_module(mod_name, module);
|
||||||
|
|
||||||
|
state.modules += 1;
|
||||||
self.inc_operations(state, *pos)?;
|
self.inc_operations(state, *pos)?;
|
||||||
|
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
|
Err(Box::new(EvalAltResult::ErrorModuleNotFound(
|
||||||
|
@ -11,7 +11,7 @@ use crate::scope::Scope;
|
|||||||
|
|
||||||
use crate::stdlib::{boxed::Box, string::ToString};
|
use crate::stdlib::{boxed::Box, string::ToString};
|
||||||
|
|
||||||
/// A trait to create a Rust anonymous function from a script.
|
/// Trait to create a Rust anonymous function from a script.
|
||||||
pub trait Func<ARGS, RET> {
|
pub trait Func<ARGS, RET> {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ pub enum NativeFunctionABI {
|
|||||||
Method,
|
Method,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait implemented by all native Rust functions that are callable by Rhai.
|
/// Trait implemented by all native Rust functions that are callable by Rhai.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub trait NativeCallable {
|
pub trait NativeCallable {
|
||||||
/// Get the ABI type of a native Rust function.
|
/// Get the ABI type of a native Rust function.
|
||||||
@ -91,7 +91,7 @@ pub trait NativeCallable {
|
|||||||
fn call(&self, args: &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
fn call(&self, args: &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait implemented by all native Rust functions that are callable by Rhai.
|
/// Trait implemented by all native Rust functions that are callable by Rhai.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub trait NativeCallable: Send + Sync {
|
pub trait NativeCallable: Send + Sync {
|
||||||
/// Get the ABI type of a native Rust function.
|
/// Get the ABI type of a native Rust function.
|
||||||
|
@ -10,7 +10,7 @@ use crate::result::EvalAltResult;
|
|||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
|
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
|
||||||
|
|
||||||
/// A trait to register custom functions with the `Engine`.
|
/// Trait to register custom functions with the `Engine`.
|
||||||
pub trait RegisterFn<FN, ARGS, RET> {
|
pub trait RegisterFn<FN, ARGS, RET> {
|
||||||
/// Register a custom function with the `Engine`.
|
/// Register a custom function with the `Engine`.
|
||||||
///
|
///
|
||||||
@ -42,7 +42,7 @@ pub trait RegisterFn<FN, ARGS, RET> {
|
|||||||
fn register_fn(&mut self, name: &str, f: FN);
|
fn register_fn(&mut self, name: &str, f: FN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait to register custom functions that return `Dynamic` values with the `Engine`.
|
/// Trait to register custom functions that return `Dynamic` values with the `Engine`.
|
||||||
pub trait RegisterDynamicFn<FN, ARGS> {
|
pub trait RegisterDynamicFn<FN, ARGS> {
|
||||||
/// Register a custom function returning `Dynamic` values with the `Engine`.
|
/// Register a custom function returning `Dynamic` values with the `Engine`.
|
||||||
///
|
///
|
||||||
@ -69,7 +69,7 @@ pub trait RegisterDynamicFn<FN, ARGS> {
|
|||||||
fn register_dynamic_fn(&mut self, name: &str, f: FN);
|
fn register_dynamic_fn(&mut self, name: &str, f: FN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait to register fallible custom functions returning `Result<_, Box<EvalAltResult>>` with the `Engine`.
|
/// Trait to register fallible custom functions returning `Result<_, Box<EvalAltResult>>` with the `Engine`.
|
||||||
pub trait RegisterResultFn<FN, ARGS, RET> {
|
pub trait RegisterResultFn<FN, ARGS, RET> {
|
||||||
/// Register a custom fallible function with the `Engine`.
|
/// Register a custom fallible function with the `Engine`.
|
||||||
///
|
///
|
||||||
|
@ -116,6 +116,7 @@ pub use parser::FLOAT;
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub use module::ModuleResolver;
|
pub use module::ModuleResolver;
|
||||||
|
|
||||||
|
/// Module containing all built-in _module resolvers_ available to Rhai.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub mod module_resolvers {
|
pub mod module_resolvers {
|
||||||
pub use crate::module::resolvers::*;
|
pub use crate::module::resolvers::*;
|
||||||
|
@ -769,7 +769,7 @@ impl ModuleRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait that encapsulates a module resolution service.
|
/// Trait that encapsulates a module resolution service.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub trait ModuleResolver {
|
pub trait ModuleResolver {
|
||||||
@ -783,7 +783,7 @@ pub trait ModuleResolver {
|
|||||||
) -> Result<Module, Box<EvalAltResult>>;
|
) -> Result<Module, Box<EvalAltResult>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait that encapsulates a module resolution service.
|
/// Trait that encapsulates a module resolution service.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub trait ModuleResolver: Send + Sync {
|
pub trait ModuleResolver: Send + Sync {
|
||||||
@ -812,7 +812,7 @@ mod file {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::stdlib::path::PathBuf;
|
use crate::stdlib::path::PathBuf;
|
||||||
|
|
||||||
/// A module resolution service that loads module script files from the file system.
|
/// Module resolution service that loads module script files from the file system.
|
||||||
///
|
///
|
||||||
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
/// The `new_with_path` and `new_with_path_and_extension` constructor functions
|
||||||
/// allow specification of a base directory with module path used as a relative path offset
|
/// allow specification of a base directory with module path used as a relative path offset
|
||||||
@ -949,7 +949,7 @@ mod file {
|
|||||||
mod stat {
|
mod stat {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// A module resolution service that serves modules added into it.
|
/// Module resolution service that serves modules added into it.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -13,7 +13,6 @@ use crate::token::Position;
|
|||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
|
||||||
iter::empty,
|
iter::empty,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! This module contains all built-in _packages_ available to Rhai, plus facilities to define custom packages.
|
//! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages.
|
||||||
|
|
||||||
use crate::fn_native::{NativeCallable, SharedIteratorFunction};
|
use crate::fn_native::{NativeCallable, SharedIteratorFunction};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
@ -89,7 +89,7 @@ impl PackagesCollection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This macro makes it easy to define a _package_ (which is basically a shared module)
|
/// Macro that makes it easy to define a _package_ (which is basically a shared module)
|
||||||
/// and register functions into it.
|
/// and register functions into it.
|
||||||
///
|
///
|
||||||
/// Functions can be added to the package using the standard module methods such as
|
/// Functions can be added to the package using the standard module methods such as
|
||||||
|
@ -81,6 +81,8 @@ pub enum EvalAltResult {
|
|||||||
ErrorArithmetic(String, Position),
|
ErrorArithmetic(String, Position),
|
||||||
/// Number of operations over maximum limit.
|
/// Number of operations over maximum limit.
|
||||||
ErrorTooManyOperations(Position),
|
ErrorTooManyOperations(Position),
|
||||||
|
/// Modules over maximum limit.
|
||||||
|
ErrorTooManyModules(Position),
|
||||||
/// Call stack over maximum limit.
|
/// Call stack over maximum limit.
|
||||||
ErrorStackOverflow(Position),
|
ErrorStackOverflow(Position),
|
||||||
/// The script is prematurely terminated.
|
/// The script is prematurely terminated.
|
||||||
@ -142,6 +144,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
||||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||||
Self::ErrorTooManyOperations(_) => "Too many operations",
|
Self::ErrorTooManyOperations(_) => "Too many operations",
|
||||||
|
Self::ErrorTooManyModules(_) => "Too many modules imported",
|
||||||
Self::ErrorStackOverflow(_) => "Stack overflow",
|
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||||
Self::ErrorTerminated(_) => "Script terminated.",
|
Self::ErrorTerminated(_) => "Script terminated.",
|
||||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||||
@ -190,6 +193,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
| Self::ErrorInExpr(pos)
|
| Self::ErrorInExpr(pos)
|
||||||
| Self::ErrorDotExpr(_, pos)
|
| Self::ErrorDotExpr(_, pos)
|
||||||
| Self::ErrorTooManyOperations(pos)
|
| Self::ErrorTooManyOperations(pos)
|
||||||
|
| Self::ErrorTooManyModules(pos)
|
||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorTerminated(pos) => write!(f, "{} ({})", desc, pos),
|
| Self::ErrorTerminated(pos) => write!(f, "{} ({})", desc, pos),
|
||||||
|
|
||||||
@ -308,6 +312,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorDotExpr(_, pos)
|
| Self::ErrorDotExpr(_, pos)
|
||||||
| Self::ErrorArithmetic(_, pos)
|
| Self::ErrorArithmetic(_, pos)
|
||||||
| Self::ErrorTooManyOperations(pos)
|
| Self::ErrorTooManyOperations(pos)
|
||||||
|
| Self::ErrorTooManyModules(pos)
|
||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorTerminated(pos)
|
| Self::ErrorTerminated(pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
@ -346,6 +351,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorDotExpr(_, pos)
|
| Self::ErrorDotExpr(_, pos)
|
||||||
| Self::ErrorArithmetic(_, pos)
|
| Self::ErrorArithmetic(_, pos)
|
||||||
| Self::ErrorTooManyOperations(pos)
|
| Self::ErrorTooManyOperations(pos)
|
||||||
|
| Self::ErrorTooManyModules(pos)
|
||||||
| Self::ErrorStackOverflow(pos)
|
| Self::ErrorStackOverflow(pos)
|
||||||
| Self::ErrorTerminated(pos)
|
| Self::ErrorTerminated(pos)
|
||||||
| Self::ErrorRuntime(_, pos)
|
| Self::ErrorRuntime(_, pos)
|
||||||
|
@ -36,7 +36,7 @@ pub struct Entry<'a> {
|
|||||||
pub expr: Option<Box<Expr>>,
|
pub expr: Option<Box<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type containing information about the current scope.
|
/// Type containing information about the current scope.
|
||||||
/// Useful for keeping state between `Engine` evaluation runs.
|
/// Useful for keeping state between `Engine` evaluation runs.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use crate::any::Variant;
|
use crate::any::Variant;
|
||||||
use crate::engine::State;
|
use crate::engine::State;
|
||||||
use crate::utils::StaticVec;
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
@ -10,7 +9,6 @@ use crate::stdlib::{
|
|||||||
boxed::Box,
|
boxed::Box,
|
||||||
mem, ptr,
|
mem, ptr,
|
||||||
string::ToString,
|
string::ToString,
|
||||||
vec::Vec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Cast a type into another type.
|
/// Cast a type into another type.
|
||||||
|
@ -72,13 +72,60 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r#"
|
r#"
|
||||||
import "hello" as h;
|
import "hello" as h1;
|
||||||
h::answer
|
import "hello" as h2;
|
||||||
|
h2::answer
|
||||||
"#
|
"#
|
||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
|
engine.set_max_modules(5);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.eval::<()>(
|
||||||
|
r#"
|
||||||
|
for x in range(0, 10) {
|
||||||
|
import "hello" as h;
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorTooManyModules(_)
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.eval::<()>(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
import "hello" as h;
|
||||||
|
}
|
||||||
|
|
||||||
|
for x in range(0, 10) {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorInFunctionCall(fn_name, _, _) if fn_name == "foo"
|
||||||
|
));
|
||||||
|
|
||||||
|
engine.set_max_modules(0);
|
||||||
|
|
||||||
|
engine.eval::<()>(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
import "hello" as h;
|
||||||
|
}
|
||||||
|
|
||||||
|
for x in range(0, 10) {
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user