Limit function calls depth.

This commit is contained in:
Stephen Chung 2020-03-27 14:34:01 +08:00
parent cc8554d095
commit 337a96394f
4 changed files with 150 additions and 114 deletions

View File

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

View File

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

View File

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

View File

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