From 337a96394fa691b715a78f0910d10f4f5ad61473 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 27 Mar 2020 14:34:01 +0800 Subject: [PATCH] Limit function calls depth. --- README.md | 12 +++ src/api.rs | 12 ++- src/engine.rs | 234 +++++++++++++++++++++++++++----------------------- src/result.rs | 6 ++ 4 files changed, 150 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 5c9f4ad2..51c33149 100644 --- a/README.md +++ b/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 =================== diff --git a/src/api.rs b/src/api.rs index d1fea857..dd006161 100644 --- a/src/api.rs +++ b/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, ) -> Result { 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| { diff --git a/src/engine.rs b/src/engine.rs index 0fe872b0..aea9f29e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -32,6 +32,7 @@ pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result Box>; +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, Box>, - /// 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>, - /// A hashmap mapping type names to pretty-print names + /// A hashmap mapping type names to pretty-print names. pub(crate) type_names: HashMap, - /// Closure for implementing the print commands + /// Closure for implementing the print commands. pub(crate) on_print: Box, - /// Closure for implementing the debug commands + /// Closure for implementing the debug commands. pub(crate) on_debug: Box, + + /// 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 { + 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, target: Option<&mut Variant>, dot_rhs: &Expr, + level: usize, ) -> Result { // 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::, _>>()?; 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 { 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 { - self.eval_expr(scope, idx_expr)? + self.eval_expr(scope, idx_expr, level)? .downcast::() .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>, 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 { 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 { 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 { + fn eval_expr( + &mut self, + scope: &mut Scope, + expr: &Expr, + level: usize, + ) -> Result { 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::>(); + .collect::>() + .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::() @@ -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::, _>>()?; 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::() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position()) })? && // Short-circuit using && *self - .eval_expr(scope, &*rhs)? + .eval_expr(scope, &*rhs, level)? .downcast::() .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::() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position()) })? || // Short-circuit using || *self - .eval_expr(scope, &*rhs)? + .eval_expr(scope, &*rhs, level)? .downcast::() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) @@ -1172,6 +1195,7 @@ impl Engine<'_> { &mut self, scope: &mut Scope, stmt: &Stmt, + level: usize, ) -> Result { 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 = 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::() .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::() { + match self.eval_expr(scope, guard, level)?.downcast::() { 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::() .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()) } diff --git a/src/result.rs b/src/result.rs index 793f99d5..e96a29c7 100644 --- a/src/result.rs +++ b/src/result.rs @@ -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,