Limit function calls depth.
This commit is contained in:
parent
cc8554d095
commit
337a96394f
12
README.md
12
README.md
@ -651,6 +651,18 @@ fn main() -> Result<(), EvalAltResult>
|
||||
}
|
||||
```
|
||||
|
||||
Engine configuration options
|
||||
---------------------------
|
||||
|
||||
| Method | Description |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------------- |
|
||||
| `set_optimization_level` | Set the amount of script _optimizations_ performed. See [`script optimization`]. |
|
||||
| `set_max_call_levels` | Set the maximum number of function call levels (default 50) to avoid infinite recursion. |
|
||||
|
||||
[`script optimization`]: #script-optimization
|
||||
|
||||
-------
|
||||
|
||||
Rhai Language Guide
|
||||
===================
|
||||
|
||||
|
12
src/api.rs
12
src/api.rs
@ -726,11 +726,9 @@ impl<'e> Engine<'e> {
|
||||
statements
|
||||
};
|
||||
|
||||
let mut result = ().into_dynamic();
|
||||
|
||||
for stmt in statements {
|
||||
result = engine.eval_stmt(scope, stmt)?;
|
||||
}
|
||||
let result = statements.iter().try_fold(().into_dynamic(), |_, stmt| {
|
||||
engine.eval_stmt(scope, stmt, 0)
|
||||
})?;
|
||||
|
||||
if !retain_functions {
|
||||
engine.clear_functions();
|
||||
@ -829,7 +827,7 @@ impl<'e> Engine<'e> {
|
||||
|
||||
let result = statements
|
||||
.iter()
|
||||
.try_fold(().into_dynamic(), |_, o| self.eval_stmt(scope, o))
|
||||
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0))
|
||||
.map(|_| ());
|
||||
|
||||
if !retain_functions {
|
||||
@ -889,7 +887,7 @@ impl<'e> Engine<'e> {
|
||||
mut values: Vec<Dynamic>,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
let mut values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect();
|
||||
engine.call_fn_raw(name, &mut values, None, Position::none())
|
||||
engine.call_fn_raw(name, &mut values, None, Position::none(), 0)
|
||||
}
|
||||
|
||||
call_fn_internal(self, name, args.into_vec()).and_then(|b| {
|
||||
|
234
src/engine.rs
234
src/engine.rs
@ -32,6 +32,7 @@ pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result<Dynamic, EvalAltRes
|
||||
|
||||
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||
|
||||
pub(crate) const MAX_CALL_STACK_DEPTH: usize = 50;
|
||||
pub(crate) const KEYWORD_PRINT: &str = "print";
|
||||
pub(crate) const KEYWORD_DEBUG: &str = "debug";
|
||||
pub(crate) const KEYWORD_DUMP_AST: &str = "dump_ast";
|
||||
@ -127,22 +128,26 @@ impl FunctionsLib {
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct Engine<'e> {
|
||||
/// Optimize the AST after compilation
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
pub(crate) optimization_level: OptimizationLevel,
|
||||
/// A hashmap containing all compiled functions known to the engine
|
||||
/// A hashmap containing all compiled functions known to the engine.
|
||||
pub(crate) functions: HashMap<FnSpec<'e>, Box<FnAny>>,
|
||||
/// A hashmap containing all script-defined functions
|
||||
/// A hashmap containing all script-defined functions.
|
||||
pub(crate) fn_lib: FunctionsLib,
|
||||
/// A hashmap containing all iterators known to the engine
|
||||
/// A hashmap containing all iterators known to the engine.
|
||||
pub(crate) type_iterators: HashMap<TypeId, Box<IteratorFn>>,
|
||||
/// A hashmap mapping type names to pretty-print names
|
||||
/// A hashmap mapping type names to pretty-print names.
|
||||
pub(crate) type_names: HashMap<String, String>,
|
||||
|
||||
/// Closure for implementing the print commands
|
||||
/// Closure for implementing the print commands.
|
||||
pub(crate) on_print: Box<dyn FnMut(&str) + 'e>,
|
||||
/// Closure for implementing the debug commands
|
||||
/// Closure for implementing the debug commands.
|
||||
pub(crate) on_debug: Box<dyn FnMut(&str) + 'e>,
|
||||
|
||||
/// Optimize the AST after compilation.
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
pub(crate) optimization_level: OptimizationLevel,
|
||||
|
||||
/// Maximum levels of call-stack to prevent infinite recursion.
|
||||
pub(crate) max_call_stack_depth: usize,
|
||||
}
|
||||
|
||||
impl Default for Engine<'_> {
|
||||
@ -174,6 +179,8 @@ impl Default for Engine<'_> {
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
#[cfg(feature = "optimize_full")]
|
||||
optimization_level: OptimizationLevel::Full,
|
||||
|
||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||
};
|
||||
|
||||
engine.register_core_lib();
|
||||
@ -197,6 +204,12 @@ impl Engine<'_> {
|
||||
self.optimization_level = optimization_level
|
||||
}
|
||||
|
||||
/// Set the maximum levels of function calls allowed for a script in order to avoid
|
||||
/// infinite recursion and stack overflows.
|
||||
pub fn set_max_call_levels(&mut self, levels: usize) {
|
||||
self.max_call_stack_depth = levels
|
||||
}
|
||||
|
||||
/// Call a registered function
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
pub(crate) fn call_ext_fn_raw(
|
||||
@ -227,7 +240,12 @@ impl Engine<'_> {
|
||||
args: &mut FnCallArgs,
|
||||
def_val: Option<&Dynamic>,
|
||||
pos: Position,
|
||||
level: usize,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
if level >= self.max_call_stack_depth {
|
||||
return Err(EvalAltResult::ErrorStackOverflow(pos));
|
||||
}
|
||||
|
||||
// First search in script-defined functions (can override built-in)
|
||||
if let Some(fn_def) = self.fn_lib.get_function(fn_name, args.len()) {
|
||||
let mut scope = Scope::new();
|
||||
@ -241,11 +259,11 @@ impl Engine<'_> {
|
||||
.map(|(name, value)| (name, ScopeEntryType::Normal, value)),
|
||||
);
|
||||
|
||||
// Evaluate
|
||||
// Convert return statement to return value
|
||||
// Evaluate the function at one higher level of call depth
|
||||
return self
|
||||
.eval_stmt(&mut scope, &fn_def.body)
|
||||
.eval_stmt(&mut scope, &fn_def.body, level + 1)
|
||||
.or_else(|err| match err {
|
||||
// Convert return statement to return value
|
||||
EvalAltResult::Return(x, _) => Ok(x),
|
||||
err => Err(err.set_position(pos)),
|
||||
});
|
||||
@ -336,6 +354,7 @@ impl Engine<'_> {
|
||||
src: Option<ScopeSource>,
|
||||
target: Option<&mut Variant>,
|
||||
dot_rhs: &Expr,
|
||||
level: usize,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
// Get the `this` reference. Either `src` or `target` should be `Some`.
|
||||
fn get_this_ptr<'a>(
|
||||
@ -360,7 +379,7 @@ impl Engine<'_> {
|
||||
Expr::FunctionCall(fn_name, arg_expr_list, def_val, pos) => {
|
||||
let mut values = arg_expr_list
|
||||
.iter()
|
||||
.map(|arg_expr| self.eval_expr(scope, arg_expr))
|
||||
.map(|arg_expr| self.eval_expr(scope, arg_expr, level))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let this_ptr = get_this_ptr(scope, src, target);
|
||||
@ -369,14 +388,14 @@ impl Engine<'_> {
|
||||
.chain(values.iter_mut().map(Dynamic::as_mut))
|
||||
.collect();
|
||||
|
||||
self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos)
|
||||
self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos, 0)
|
||||
}
|
||||
|
||||
// xxx.id
|
||||
Expr::Property(id, pos) => {
|
||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||
let this_ptr = get_this_ptr(scope, src, target);
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||
}
|
||||
|
||||
// xxx.idx_lhs[idx_expr]
|
||||
@ -388,13 +407,13 @@ impl Engine<'_> {
|
||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||
let this_ptr = get_this_ptr(scope, src, target);
|
||||
(
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)?,
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?,
|
||||
*pos,
|
||||
)
|
||||
}
|
||||
// xxx.???[???][idx_expr]
|
||||
Expr::Index(_, _, _) => (
|
||||
self.get_dot_val_helper(scope, src, target, idx_lhs)?,
|
||||
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?,
|
||||
*op_pos,
|
||||
),
|
||||
// Syntax error
|
||||
@ -406,7 +425,7 @@ impl Engine<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
|
||||
.map(|(v, _)| v)
|
||||
}
|
||||
@ -418,9 +437,9 @@ impl Engine<'_> {
|
||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||
let this_ptr = get_this_ptr(scope, src, target);
|
||||
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||
.and_then(|mut v| {
|
||||
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs)
|
||||
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs, level)
|
||||
})
|
||||
}
|
||||
// xxx.idx_lhs[idx_expr].rhs
|
||||
@ -432,13 +451,13 @@ impl Engine<'_> {
|
||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||
let this_ptr = get_this_ptr(scope, src, target);
|
||||
(
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)?,
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)?,
|
||||
*pos,
|
||||
)
|
||||
}
|
||||
// xxx.???[???][idx_expr].rhs
|
||||
Expr::Index(_, _, _) => (
|
||||
self.get_dot_val_helper(scope, src, target, idx_lhs)?,
|
||||
self.get_dot_val_helper(scope, src, target, idx_lhs, level)?,
|
||||
*op_pos,
|
||||
),
|
||||
// Syntax error
|
||||
@ -450,10 +469,10 @@ impl Engine<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||
self.get_indexed_value(&val, idx, idx_expr.position(), *op_pos)
|
||||
.and_then(|(mut v, _)| {
|
||||
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs)
|
||||
self.get_dot_val_helper(scope, None, Some(v.as_mut()), rhs, level)
|
||||
})
|
||||
}
|
||||
// Syntax error
|
||||
@ -477,6 +496,7 @@ impl Engine<'_> {
|
||||
scope: &mut Scope,
|
||||
dot_lhs: &Expr,
|
||||
dot_rhs: &Expr,
|
||||
level: usize,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
match dot_lhs {
|
||||
// id.???
|
||||
@ -488,15 +508,16 @@ impl Engine<'_> {
|
||||
|
||||
// This is a variable property access (potential function call).
|
||||
// Use a direct index into `scope` to directly mutate the variable value.
|
||||
self.get_dot_val_helper(scope, Some(entry), None, dot_rhs)
|
||||
self.get_dot_val_helper(scope, Some(entry), None, dot_rhs, level)
|
||||
}
|
||||
|
||||
// idx_lhs[idx_expr].???
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||
let (src_type, src, idx, mut target) =
|
||||
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos)?;
|
||||
let val = self.get_dot_val_helper(scope, None, Some(target.as_mut()), dot_rhs);
|
||||
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?;
|
||||
let this_ptr = target.as_mut();
|
||||
let val = self.get_dot_val_helper(scope, None, Some(this_ptr), dot_rhs, level);
|
||||
|
||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||
if let Some(src) = src {
|
||||
@ -524,8 +545,9 @@ impl Engine<'_> {
|
||||
|
||||
// {expr}.???
|
||||
expr => {
|
||||
let mut target = self.eval_expr(scope, expr)?;
|
||||
self.get_dot_val_helper(scope, None, Some(target.as_mut()), dot_rhs)
|
||||
let mut target = self.eval_expr(scope, expr, level)?;
|
||||
let this_ptr = target.as_mut();
|
||||
self.get_dot_val_helper(scope, None, Some(this_ptr), dot_rhs, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -549,8 +571,9 @@ impl Engine<'_> {
|
||||
&mut self,
|
||||
scope: &mut Scope,
|
||||
idx_expr: &Expr,
|
||||
level: usize,
|
||||
) -> Result<INT, EvalAltResult> {
|
||||
self.eval_expr(scope, idx_expr)?
|
||||
self.eval_expr(scope, idx_expr, level)?
|
||||
.downcast::<INT>()
|
||||
.map(|v| *v)
|
||||
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))
|
||||
@ -612,8 +635,9 @@ impl Engine<'_> {
|
||||
lhs: &'a Expr,
|
||||
idx_expr: &Expr,
|
||||
op_pos: Position,
|
||||
level: usize,
|
||||
) -> Result<(IndexSourceType, Option<ScopeSource<'a>>, usize, Dynamic), EvalAltResult> {
|
||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||
|
||||
match lhs {
|
||||
// id[idx_expr]
|
||||
@ -638,7 +662,7 @@ impl Engine<'_> {
|
||||
|
||||
// (expr)[idx_expr]
|
||||
expr => {
|
||||
let val = self.eval_expr(scope, expr)?;
|
||||
let val = self.eval_expr(scope, expr, level)?;
|
||||
|
||||
self.get_indexed_value(&val, idx, idx_expr.position(), op_pos)
|
||||
.map(|(v, _)| (IndexSourceType::Expression, None, idx as usize, v))
|
||||
@ -727,18 +751,14 @@ impl Engine<'_> {
|
||||
this_ptr: &mut Variant,
|
||||
dot_rhs: &Expr,
|
||||
new_val: (&mut Dynamic, Position),
|
||||
level: usize,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
match dot_rhs {
|
||||
// xxx.id
|
||||
Expr::Property(id, pos) => {
|
||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||
|
||||
self.call_fn_raw(
|
||||
&set_fn_name,
|
||||
&mut [this_ptr, new_val.0.as_mut()],
|
||||
None,
|
||||
*pos,
|
||||
)
|
||||
let mut args = [this_ptr, new_val.0.as_mut()];
|
||||
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||
}
|
||||
|
||||
// xxx.lhs[idx_expr]
|
||||
@ -749,9 +769,9 @@ impl Engine<'_> {
|
||||
Expr::Property(id, pos) => {
|
||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||
.and_then(|v| {
|
||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||
Self::update_indexed_value(
|
||||
v,
|
||||
idx as usize,
|
||||
@ -761,7 +781,8 @@ impl Engine<'_> {
|
||||
})
|
||||
.and_then(|mut v| {
|
||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||
self.call_fn_raw(&set_fn_name, &mut [this_ptr, v.as_mut()], None, *pos)
|
||||
let mut args = [this_ptr, v.as_mut()];
|
||||
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -778,15 +799,15 @@ impl Engine<'_> {
|
||||
Expr::Property(id, pos) => {
|
||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||
.and_then(|mut v| {
|
||||
self.set_dot_val_helper(scope, v.as_mut(), rhs, new_val)
|
||||
self.set_dot_val_helper(scope, v.as_mut(), rhs, new_val, level)
|
||||
.map(|_| v) // Discard Ok return value
|
||||
})
|
||||
.and_then(|mut v| {
|
||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||
|
||||
self.call_fn_raw(&set_fn_name, &mut [this_ptr, v.as_mut()], None, *pos)
|
||||
let mut args = [this_ptr, v.as_mut()];
|
||||
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -798,26 +819,23 @@ impl Engine<'_> {
|
||||
Expr::Property(id, pos) => {
|
||||
let get_fn_name = format!("{}{}", FUNC_GETTER, id);
|
||||
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos)
|
||||
self.call_fn_raw(&get_fn_name, &mut [this_ptr], None, *pos, 0)
|
||||
.and_then(|v| {
|
||||
let idx = self.eval_index_value(scope, idx_expr)?;
|
||||
let idx = self.eval_index_value(scope, idx_expr, level)?;
|
||||
let (mut target, _) =
|
||||
self.get_indexed_value(&v, idx, idx_expr.position(), *op_pos)?;
|
||||
|
||||
let val_pos = new_val.1;
|
||||
self.set_dot_val_helper(scope, target.as_mut(), rhs, new_val)?;
|
||||
let this_ptr = target.as_mut();
|
||||
self.set_dot_val_helper(scope, this_ptr, rhs, new_val, level)?;
|
||||
|
||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||
Self::update_indexed_value(v, idx as usize, target, val_pos)
|
||||
})
|
||||
.and_then(|mut v| {
|
||||
let set_fn_name = format!("{}{}", FUNC_SETTER, id);
|
||||
self.call_fn_raw(
|
||||
&set_fn_name,
|
||||
&mut [this_ptr, v.as_mut()],
|
||||
None,
|
||||
*pos,
|
||||
)
|
||||
let mut args = [this_ptr, v.as_mut()];
|
||||
self.call_fn_raw(&set_fn_name, &mut args, None, *pos, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -851,6 +869,7 @@ impl Engine<'_> {
|
||||
dot_rhs: &Expr,
|
||||
new_val: (&mut Dynamic, Position),
|
||||
op_pos: Position,
|
||||
level: usize,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
match dot_lhs {
|
||||
// id.???
|
||||
@ -865,8 +884,8 @@ impl Engine<'_> {
|
||||
_ => {
|
||||
// Avoid referencing scope which is used below as mut
|
||||
let entry = ScopeSource { name: id, ..entry };
|
||||
|
||||
let val = self.set_dot_val_helper(scope, target.as_mut(), dot_rhs, new_val);
|
||||
let this_ptr = target.as_mut();
|
||||
let val = self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level);
|
||||
|
||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||
*scope.get_mut(entry) = target;
|
||||
@ -881,9 +900,10 @@ impl Engine<'_> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(lhs, idx_expr, op_pos) => {
|
||||
let (src_type, src, idx, mut target) =
|
||||
self.eval_index_expr(scope, lhs, idx_expr, *op_pos)?;
|
||||
self.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)?;
|
||||
let val_pos = new_val.1;
|
||||
let val = self.set_dot_val_helper(scope, target.as_mut(), dot_rhs, new_val);
|
||||
let this_ptr = target.as_mut();
|
||||
let val = self.set_dot_val_helper(scope, this_ptr, dot_rhs, new_val, level);
|
||||
|
||||
// In case the expression mutated `target`, we need to update it back into the scope because it is cloned.
|
||||
if let Some(src) = src {
|
||||
@ -918,7 +938,12 @@ impl Engine<'_> {
|
||||
}
|
||||
|
||||
/// Evaluate an expression
|
||||
fn eval_expr(&mut self, scope: &mut Scope, expr: &Expr) -> Result<Dynamic, EvalAltResult> {
|
||||
fn eval_expr(
|
||||
&mut self,
|
||||
scope: &mut Scope,
|
||||
expr: &Expr,
|
||||
level: usize,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
match expr {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Expr::FloatConstant(f, _) => Ok(f.into_dynamic()),
|
||||
@ -932,15 +957,15 @@ impl Engine<'_> {
|
||||
// lhs[idx_expr]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(lhs, idx_expr, op_pos) => self
|
||||
.eval_index_expr(scope, lhs, idx_expr, *op_pos)
|
||||
.eval_index_expr(scope, lhs, idx_expr, *op_pos, level)
|
||||
.map(|(_, _, _, x)| x),
|
||||
|
||||
// Statement block
|
||||
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt),
|
||||
Expr::Stmt(stmt, _) => self.eval_stmt(scope, stmt, level),
|
||||
|
||||
// lhs = rhs
|
||||
Expr::Assignment(lhs, rhs, op_pos) => {
|
||||
let mut rhs_val = self.eval_expr(scope, rhs)?;
|
||||
let mut rhs_val = self.eval_expr(scope, rhs, level)?;
|
||||
|
||||
match lhs.as_ref() {
|
||||
// name = rhs
|
||||
@ -974,7 +999,7 @@ impl Engine<'_> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(idx_lhs, idx_expr, op_pos) => {
|
||||
let (src_type, src, idx, _) =
|
||||
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos)?;
|
||||
self.eval_index_expr(scope, idx_lhs, idx_expr, *op_pos, level)?;
|
||||
|
||||
if let Some(src) = src {
|
||||
match src.typ {
|
||||
@ -1006,6 +1031,7 @@ impl Engine<'_> {
|
||||
dot_rhs,
|
||||
(&mut rhs_val, rhs.position()),
|
||||
*op_pos,
|
||||
level,
|
||||
),
|
||||
|
||||
// Error assignment to constant
|
||||
@ -1019,15 +1045,15 @@ impl Engine<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs),
|
||||
Expr::Dot(lhs, rhs, _) => self.get_dot_val(scope, lhs, rhs, level),
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Array(contents, _) => {
|
||||
let mut arr = Vec::new();
|
||||
|
||||
for item in contents {
|
||||
arr.push(self.eval_expr(scope, item)?);
|
||||
}
|
||||
contents.into_iter().try_for_each(|item| {
|
||||
self.eval_expr(scope, item, level).map(|val| arr.push(val))
|
||||
})?;
|
||||
|
||||
Ok(Box::new(arr))
|
||||
}
|
||||
@ -1053,25 +1079,22 @@ impl Engine<'_> {
|
||||
};
|
||||
|
||||
// Change the argument to a debug dump of the expressions
|
||||
let result = args_expr_list
|
||||
let mut result = args_expr_list
|
||||
.iter()
|
||||
.map(|expr| format!("{:#?}", expr))
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.into_dynamic();
|
||||
|
||||
// Redirect call to `print`
|
||||
self.call_fn_raw(
|
||||
KEYWORD_PRINT,
|
||||
&mut [result.join("\n").into_dynamic().as_mut()],
|
||||
None,
|
||||
pos,
|
||||
)
|
||||
self.call_fn_raw(KEYWORD_PRINT, &mut [result.as_mut()], None, pos, level)
|
||||
}
|
||||
|
||||
// type_of
|
||||
KEYWORD_TYPE_OF
|
||||
if args_expr_list.len() == 1 && !has_override(self, KEYWORD_TYPE_OF) =>
|
||||
{
|
||||
let r = self.eval_expr(scope, &args_expr_list[0])?;
|
||||
let r = self.eval_expr(scope, &args_expr_list[0], level)?;
|
||||
Ok(self
|
||||
.map_type_name((*r).type_name())
|
||||
.to_string()
|
||||
@ -1083,7 +1106,7 @@ impl Engine<'_> {
|
||||
if args_expr_list.len() == 1 && !has_override(self, KEYWORD_EVAL) =>
|
||||
{
|
||||
let pos = args_expr_list[0].position();
|
||||
let r = self.eval_expr(scope, &args_expr_list[0])?;
|
||||
let r = self.eval_expr(scope, &args_expr_list[0], level)?;
|
||||
|
||||
let script =
|
||||
r.downcast_ref::<String>()
|
||||
@ -1118,27 +1141,27 @@ impl Engine<'_> {
|
||||
_ => {
|
||||
let mut values = args_expr_list
|
||||
.iter()
|
||||
.map(|expr| self.eval_expr(scope, expr))
|
||||
.map(|expr| self.eval_expr(scope, expr, level))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let mut arg_values: Vec<_> =
|
||||
values.iter_mut().map(Dynamic::as_mut).collect();
|
||||
|
||||
self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos)
|
||||
self.call_fn_raw(fn_name, &mut arg_values, def_val.as_ref(), *pos, level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::And(lhs, rhs) => Ok(Box::new(
|
||||
*self
|
||||
.eval_expr(scope, &*lhs)?
|
||||
.eval_expr(scope, &*lhs, level)?
|
||||
.downcast::<bool>()
|
||||
.map_err(|_| {
|
||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position())
|
||||
})?
|
||||
&& // Short-circuit using &&
|
||||
*self
|
||||
.eval_expr(scope, &*rhs)?
|
||||
.eval_expr(scope, &*rhs, level)?
|
||||
.downcast::<bool>()
|
||||
.map_err(|_| {
|
||||
EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position())
|
||||
@ -1147,14 +1170,14 @@ impl Engine<'_> {
|
||||
|
||||
Expr::Or(lhs, rhs) => Ok(Box::new(
|
||||
*self
|
||||
.eval_expr(scope, &*lhs)?
|
||||
.eval_expr(scope, &*lhs, level)?
|
||||
.downcast::<bool>()
|
||||
.map_err(|_| {
|
||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position())
|
||||
})?
|
||||
|| // Short-circuit using ||
|
||||
*self
|
||||
.eval_expr(scope, &*rhs)?
|
||||
.eval_expr(scope, &*rhs, level)?
|
||||
.downcast::<bool>()
|
||||
.map_err(|_| {
|
||||
EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position())
|
||||
@ -1172,6 +1195,7 @@ impl Engine<'_> {
|
||||
&mut self,
|
||||
scope: &mut Scope,
|
||||
stmt: &Stmt,
|
||||
level: usize,
|
||||
) -> Result<Dynamic, EvalAltResult> {
|
||||
match stmt {
|
||||
// No-op
|
||||
@ -1179,7 +1203,7 @@ impl Engine<'_> {
|
||||
|
||||
// Expression as statement
|
||||
Stmt::Expr(expr) => {
|
||||
let result = self.eval_expr(scope, expr)?;
|
||||
let result = self.eval_expr(scope, expr, level)?;
|
||||
|
||||
Ok(if !matches!(expr.as_ref(), Expr::Assignment(_, _, _)) {
|
||||
result
|
||||
@ -1192,15 +1216,10 @@ impl Engine<'_> {
|
||||
// Block scope
|
||||
Stmt::Block(block, _) => {
|
||||
let prev_len = scope.len();
|
||||
let mut result: Result<Dynamic, EvalAltResult> = Ok(().into_dynamic());
|
||||
|
||||
for stmt in block.iter() {
|
||||
result = self.eval_stmt(scope, stmt);
|
||||
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let result = block.iter().try_fold(().into_dynamic(), |_, stmt| {
|
||||
self.eval_stmt(scope, stmt, level)
|
||||
});
|
||||
|
||||
scope.rewind(prev_len);
|
||||
|
||||
@ -1209,14 +1228,14 @@ impl Engine<'_> {
|
||||
|
||||
// If-else statement
|
||||
Stmt::IfThenElse(guard, if_body, else_body) => self
|
||||
.eval_expr(scope, guard)?
|
||||
.eval_expr(scope, guard, level)?
|
||||
.downcast::<bool>()
|
||||
.map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position()))
|
||||
.and_then(|guard_val| {
|
||||
if *guard_val {
|
||||
self.eval_stmt(scope, if_body)
|
||||
self.eval_stmt(scope, if_body, level)
|
||||
} else if let Some(stmt) = else_body {
|
||||
self.eval_stmt(scope, stmt.as_ref())
|
||||
self.eval_stmt(scope, stmt.as_ref(), level)
|
||||
} else {
|
||||
Ok(().into_dynamic())
|
||||
}
|
||||
@ -1224,10 +1243,10 @@ impl Engine<'_> {
|
||||
|
||||
// While loop
|
||||
Stmt::While(guard, body) => loop {
|
||||
match self.eval_expr(scope, guard)?.downcast::<bool>() {
|
||||
match self.eval_expr(scope, guard, level)?.downcast::<bool>() {
|
||||
Ok(guard_val) => {
|
||||
if *guard_val {
|
||||
match self.eval_stmt(scope, body) {
|
||||
match self.eval_stmt(scope, body, level) {
|
||||
Ok(_) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(_)) => {
|
||||
return Ok(().into_dynamic())
|
||||
@ -1244,7 +1263,7 @@ impl Engine<'_> {
|
||||
|
||||
// Loop statement
|
||||
Stmt::Loop(body) => loop {
|
||||
match self.eval_stmt(scope, body) {
|
||||
match self.eval_stmt(scope, body, level) {
|
||||
Ok(_) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(_)) => return Ok(().into_dynamic()),
|
||||
Err(x) => return Err(x),
|
||||
@ -1253,7 +1272,7 @@ impl Engine<'_> {
|
||||
|
||||
// For loop
|
||||
Stmt::For(name, expr, body) => {
|
||||
let arr = self.eval_expr(scope, expr)?;
|
||||
let arr = self.eval_expr(scope, expr, level)?;
|
||||
let tid = Any::type_id(&*arr);
|
||||
|
||||
if let Some(iter_fn) = self.type_iterators.get(&tid) {
|
||||
@ -1268,7 +1287,7 @@ impl Engine<'_> {
|
||||
for a in iter_fn(&arr) {
|
||||
*scope.get_mut(entry) = a;
|
||||
|
||||
match self.eval_stmt(scope, body) {
|
||||
match self.eval_stmt(scope, body, level) {
|
||||
Ok(_) => (),
|
||||
Err(EvalAltResult::ErrorLoopBreak(_)) => break,
|
||||
Err(x) => return Err(x),
|
||||
@ -1291,9 +1310,10 @@ impl Engine<'_> {
|
||||
}
|
||||
|
||||
// Return value
|
||||
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => {
|
||||
Err(EvalAltResult::Return(self.eval_expr(scope, a)?, *pos))
|
||||
}
|
||||
Stmt::ReturnWithVal(Some(a), ReturnType::Return, pos) => Err(EvalAltResult::Return(
|
||||
self.eval_expr(scope, a, level)?,
|
||||
*pos,
|
||||
)),
|
||||
|
||||
// Empty throw
|
||||
Stmt::ReturnWithVal(None, ReturnType::Exception, pos) => {
|
||||
@ -1302,7 +1322,7 @@ impl Engine<'_> {
|
||||
|
||||
// Throw value
|
||||
Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => {
|
||||
let val = self.eval_expr(scope, a)?;
|
||||
let val = self.eval_expr(scope, a, level)?;
|
||||
Err(EvalAltResult::ErrorRuntime(
|
||||
val.downcast::<String>()
|
||||
.map(|s| *s)
|
||||
@ -1313,7 +1333,7 @@ impl Engine<'_> {
|
||||
|
||||
// Let statement
|
||||
Stmt::Let(name, Some(expr), _) => {
|
||||
let val = self.eval_expr(scope, expr)?;
|
||||
let val = self.eval_expr(scope, expr, level)?;
|
||||
scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false);
|
||||
Ok(().into_dynamic())
|
||||
}
|
||||
@ -1325,7 +1345,7 @@ impl Engine<'_> {
|
||||
|
||||
// Const statement
|
||||
Stmt::Const(name, expr, _) if expr.is_constant() => {
|
||||
let val = self.eval_expr(scope, expr)?;
|
||||
let val = self.eval_expr(scope, expr, level)?;
|
||||
scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true);
|
||||
Ok(().into_dynamic())
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ pub enum EvalAltResult {
|
||||
ErrorDotExpr(String, Position),
|
||||
/// Arithmetic error encountered. Wrapped value is the error message.
|
||||
ErrorArithmetic(String, Position),
|
||||
/// Call stack over maximum limit.
|
||||
ErrorStackOverflow(Position),
|
||||
/// Run-time error encountered. Wrapped value is the error message.
|
||||
ErrorRuntime(String, Position),
|
||||
/// Breaking out of loops - not an error if within a loop.
|
||||
@ -105,6 +107,7 @@ impl EvalAltResult {
|
||||
Self::ErrorReadingScriptFile(_, _) => "Cannot read from script file",
|
||||
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||
Self::ErrorLoopBreak(_) => "Break statement not inside a loop",
|
||||
Self::Return(_, _) => "[Not Error] Function returns value",
|
||||
@ -131,6 +134,7 @@ impl fmt::Display for EvalAltResult {
|
||||
Self::ErrorDotExpr(s, pos) if !s.is_empty() => write!(f, "{} {} ({})", desc, s, pos),
|
||||
Self::ErrorDotExpr(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorArithmetic(s, pos) => write!(f, "{} ({})", s, pos),
|
||||
Self::ErrorStackOverflow(pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorRuntime(s, pos) => {
|
||||
write!(f, "{} ({})", if s.is_empty() { desc } else { s }, pos)
|
||||
}
|
||||
@ -230,6 +234,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorMismatchOutputType(_, pos)
|
||||
| Self::ErrorDotExpr(_, pos)
|
||||
| Self::ErrorArithmetic(_, pos)
|
||||
| Self::ErrorStackOverflow(pos)
|
||||
| Self::ErrorRuntime(_, pos)
|
||||
| Self::ErrorLoopBreak(pos)
|
||||
| Self::Return(_, pos) => *pos,
|
||||
@ -260,6 +265,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorMismatchOutputType(_, ref mut pos)
|
||||
| Self::ErrorDotExpr(_, ref mut pos)
|
||||
| Self::ErrorArithmetic(_, ref mut pos)
|
||||
| Self::ErrorStackOverflow(ref mut pos)
|
||||
| Self::ErrorRuntime(_, ref mut pos)
|
||||
| Self::ErrorLoopBreak(ref mut pos)
|
||||
| Self::Return(_, ref mut pos) => *pos = new_position,
|
||||
|
Loading…
Reference in New Issue
Block a user