diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a606c13..4f785f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.11.0 ============== +Speed Improvements +------------------ + +* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. + Bug fixes --------- @@ -12,10 +17,10 @@ Bug fixes * Functions marked `global` in `import`ed modules with no alias names now work properly. * Incorrect loop optimizations that are too aggressive (e.g. unrolling a `do { ... } until true` with a `break` statement inside) and cause crashes are removed. -Speed Improvements ------------------- +Breaking changes +---------------- -* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%. +* `NativeCallContext::new` is completely deprecated and unimplemented (always panics) in favor of new API's. New features ------------ diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 42f418e4..6ba9fcf3 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -267,7 +267,7 @@ impl Engine { }); let result = if eval_ast && !statements.is_empty() { - let r = self.eval_global_statements(global, caches, lib, 0, scope, statements); + let r = self.eval_global_statements(global, caches, lib, scope, statements); if rewind_scope { scope.rewind(orig_scope_len); @@ -289,7 +289,6 @@ impl Engine { global, caches, lib, - 0, scope, &mut this_ptr, fn_def, @@ -306,7 +305,7 @@ impl Engine { if self.debugger.is_some() { global.debugger.status = crate::eval::DebuggerStatus::Terminate; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this_ptr, node)?; + self.run_debugger(global, caches, lib, scope, &mut this_ptr, node)?; } Ok(result) diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 6b6ad977..cda495db 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -4,7 +4,7 @@ use crate::func::RegisterNativeFunction; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext, - Position, RhaiResult, RhaiResultOf, Scope, AST, + Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -354,6 +354,26 @@ impl Dynamic { } impl NativeCallContext<'_> { + /// Create a new [`NativeCallContext`]. + /// + /// # Unimplemented + /// + /// This method is deprecated. It is no longer implemented and always panics. + /// + /// Use [`FnPtr::call`] to call a function pointer directly. + /// + /// This method will be removed in the next major version. + #[deprecated( + since = "1.3.0", + note = "use `FnPtr::call` to call a function pointer directly." + )] + #[inline(always)] + #[must_use] + #[allow(unused_variables)] + pub fn new(engine: &Engine, fn_name: &str, lib: &[SharedModule]) -> Self { + unimplemented!("`NativeCallContext::new` is deprecated"); + } + /// Call a function inside the call context. /// /// # Deprecated diff --git a/src/api/eval.rs b/src/api/eval.rs index 8770996d..829c69b0 100644 --- a/src/api/eval.rs +++ b/src/api/eval.rs @@ -189,7 +189,7 @@ impl Engine { let global = &mut GlobalRuntimeState::new(self); let caches = &mut Caches::new(); - let result = self.eval_ast_with_scope_raw(global, caches, 0, scope, ast)?; + let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?; #[cfg(feature = "debugging")] if self.debugger.is_some() { @@ -201,7 +201,7 @@ impl Engine { let mut this = Dynamic::NULL; let node = &crate::ast::Stmt::Noop(Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; + self.run_debugger(global, caches, lib, scope, &mut this, node)?; } let typ = self.map_type_name(result.type_name()); @@ -217,7 +217,7 @@ impl Engine { &self, global: &mut GlobalRuntimeState, caches: &mut Caches, - level: usize, + scope: &mut Scope, ast: &'a AST, ) -> RhaiResult { @@ -244,7 +244,7 @@ impl Engine { AsRef::::as_ref(ast).clone(), ]; - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } /// _(internals)_ Evaluate a list of statements with no `this` pointer. /// Exported under the `internals` feature only. @@ -261,11 +261,11 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[crate::SharedModule], - level: usize, + scope: &mut Scope, statements: &[crate::ast::Stmt], ) -> RhaiResult { - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } } diff --git a/src/api/run.rs b/src/api/run.rs index f1c25bb4..551b5e98 100644 --- a/src/api/run.rs +++ b/src/api/run.rs @@ -131,7 +131,7 @@ impl Engine { } else { &lib }; - self.eval_global_statements(global, caches, lib, 0, scope, statements)?; + self.eval_global_statements(global, caches, lib, scope, statements)?; } #[cfg(feature = "debugging")] @@ -143,7 +143,7 @@ impl Engine { ]; let mut this = crate::Dynamic::NULL; let node = &crate::ast::Stmt::Noop(crate::Position::NONE); - self.run_debugger(global, caches, lib, 0, scope, &mut this, node)?; + self.run_debugger(global, caches, lib, scope, &mut this, node)?; } Ok(()) diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index a37fd16e..ca813d86 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -44,7 +44,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, this_ptr: &mut Dynamic, target: &mut Target, root: (&str, Position), @@ -76,7 +75,7 @@ impl Engine { if !parent_options.contains(ASTFlags::BREAK) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); let mut idx_val_for_setter = idx_val.clone(); @@ -85,13 +84,13 @@ impl Engine { let (try_setter, result) = { let mut obj = self.get_indexed_mut( - global, caches, lib, level, target, idx_val, idx_pos, false, true, + global, caches, lib, target, idx_val, idx_pos, false, true, )?; let is_obj_temp_val = obj.is_temp_value(); let obj_ptr = &mut obj; match self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, obj_ptr, root, rhs, *options, + global, caches, lib, this_ptr, obj_ptr, root, rhs, *options, &x.rhs, idx_values, rhs_chain, new_val, ) { Ok((result, true)) if is_obj_temp_val => { @@ -107,7 +106,7 @@ impl Engine { let idx = &mut idx_val_for_setter; let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx, new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, is_ref_mut, ) .or_else(|e| match *e { ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)), @@ -120,19 +119,19 @@ impl Engine { // xxx[rhs] op= new_val _ if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let (new_val, op_info) = new_val.take().expect("`Some`"); let idx_val = &mut idx_values.pop().unwrap(); let idx = &mut idx_val.clone(); - let try_setter = match self.get_indexed_mut( - global, caches, lib, level, target, idx, pos, true, false, - ) { + let try_setter = match self + .get_indexed_mut(global, caches, lib, target, idx, pos, true, false) + { // Indexed value is not a temp value - update directly Ok(ref mut obj_ptr) => { self.eval_op_assignment( - global, caches, lib, level, op_info, obj_ptr, root, new_val, + global, caches, lib, op_info, obj_ptr, root, new_val, )?; self.check_data_size(obj_ptr, op_info.pos)?; None @@ -151,13 +150,12 @@ impl Engine { // Call the index getter to get the current value if let Ok(val) = - self.call_indexer_get(global, caches, lib, level, target, idx) + self.call_indexer_get(global, caches, lib, target, idx) { let mut val = val.into(); // Run the op-assignment self.eval_op_assignment( - global, caches, lib, level, op_info, &mut val, root, - new_val, + global, caches, lib, op_info, &mut val, root, new_val, )?; // Replace new value new_val = val.take_or_clone(); @@ -169,7 +167,7 @@ impl Engine { let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx_val, new_val, is_ref_mut, + global, caches, lib, target, idx_val, new_val, is_ref_mut, )?; } @@ -178,14 +176,12 @@ impl Engine { // xxx[rhs] _ => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, _parent)?; + self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?; let idx_val = &mut idx_values.pop().unwrap(); - self.get_indexed_mut( - global, caches, lib, level, target, idx_val, pos, false, true, - ) - .map(|v| (v.take_or_clone(), false)) + self.get_indexed_mut(global, caches, lib, target, idx_val, pos, false, true) + .map(|v| (v.take_or_clone(), false)) } } } @@ -201,9 +197,8 @@ impl Engine { // xxx.fn_name(arg_expr_list) Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => { #[cfg(feature = "debugging")] - let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, rhs, - )?; + let reset = self + .run_debugger_with_reset(global, caches, lib, scope, this_ptr, rhs)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -222,8 +217,7 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, pos1, - *pos, + global, caches, lib, name, *hashes, target, call_args, pos1, *pos, ) } // xxx.fn_name(...) = ??? @@ -237,16 +231,16 @@ impl Engine { // {xxx:map}.id op= ??? Expr::Property(x, pos) if target.is::() && new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let (new_val, op_info) = new_val.take().expect("`Some`"); { let val_target = &mut self.get_indexed_mut( - global, caches, lib, level, target, index, *pos, true, false, + global, caches, lib, target, index, *pos, true, false, )?; self.eval_op_assignment( - global, caches, lib, level, op_info, val_target, root, new_val, + global, caches, lib, op_info, val_target, root, new_val, )?; } self.check_data_size(target.source(), op_info.pos)?; @@ -255,18 +249,18 @@ impl Engine { // {xxx:map}.id Expr::Property(x, pos) if target.is::() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let index = &mut x.2.clone().into(); let val = self.get_indexed_mut( - global, caches, lib, level, target, index, *pos, false, false, + global, caches, lib, target, index, *pos, false, false, )?; Ok((val.take_or_clone(), false)) } // xxx.id op= ??? Expr::Property(x, pos) if new_val.is_some() => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let ((getter, hash_get), (setter, hash_set), name) = &**x; let (mut new_val, op_info) = new_val.take().expect("`Some`"); @@ -275,15 +269,15 @@ impl Engine { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self .exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, - is_ref_mut, *pos, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, + *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, level, target, &mut prop, + global, caches, lib, target, &mut prop, ) .map(|r| (r, false)) .map_err(|e| { @@ -300,7 +294,7 @@ impl Engine { let orig_val = &mut (&mut orig_val).into(); self.eval_op_assignment( - global, caches, lib, level, op_info, orig_val, root, new_val, + global, caches, lib, op_info, orig_val, root, new_val, )?; } @@ -309,8 +303,7 @@ impl Engine { let args = &mut [target.as_mut(), &mut new_val]; self.exec_native_fn_call( - global, caches, lib, level, setter, None, *hash_set, args, is_ref_mut, - *pos, + global, caches, lib, setter, None, *hash_set, args, is_ref_mut, *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -318,7 +311,7 @@ impl Engine { let idx = &mut name.into(); let new_val = &mut new_val; self.call_indexer_set( - global, caches, lib, level, target, idx, new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, is_ref_mut, ) .map_err(|e| match *e { ERR::ErrorIndexingType(..) => err, @@ -331,27 +324,24 @@ impl Engine { // xxx.id Expr::Property(x, pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, rhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?; let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; self.exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, is_ref_mut, - *pos, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, *pos, ) .map_or_else( |err| match *err { // Try an indexer if property does not exist ERR::ErrorDotExpr(..) => { let mut prop = name.into(); - self.call_indexer_get( - global, caches, lib, level, target, &mut prop, - ) - .map(|r| (r, false)) - .map_err(|e| match *e { - ERR::ErrorIndexingType(..) => err, - _ => e, - }) + self.call_indexer_get(global, caches, lib, target, &mut prop) + .map(|r| (r, false)) + .map_err(|e| match *e { + ERR::ErrorIndexingType(..) => err, + _ => e, + }) } _ => Err(err), }, @@ -368,20 +358,18 @@ impl Engine { let val_target = &mut match x.lhs { Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger( - global, caches, lib, level, scope, this_ptr, _node, - )?; + self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; let index = &mut p.2.clone().into(); self.get_indexed_mut( - global, caches, lib, level, target, index, pos, false, true, + global, caches, lib, target, index, pos, false, true, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr Expr::MethodCall(ref x, pos) if !x.is_qualified() => { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, + global, caches, lib, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -402,8 +390,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, call_args, - pos1, pos, + global, caches, lib, name, *hashes, target, call_args, pos1, + pos, )? .0 .into() @@ -418,8 +406,8 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val_target, root, rhs, *options, - &x.rhs, idx_values, rhs_chain, new_val, + global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs, + idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos)) } @@ -431,9 +419,7 @@ impl Engine { // xxx.prop[expr] | xxx.prop.expr Expr::Property(ref p, pos) => { #[cfg(feature = "debugging")] - self.run_debugger( - global, caches, lib, level, scope, this_ptr, _node, - )?; + self.run_debugger(global, caches, lib, scope, this_ptr, _node)?; let ((getter, hash_get), (setter, hash_set), name) = &**p; let rhs_chain = rhs.into(); @@ -443,7 +429,7 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_native_fn_call( - global, caches, lib, level, getter, None, *hash_get, args, + global, caches, lib, getter, None, *hash_get, args, is_ref_mut, pos, ) .or_else(|err| match *err { @@ -451,7 +437,7 @@ impl Engine { ERR::ErrorDotExpr(..) => { let mut prop = name.into(); self.call_indexer_get( - global, caches, lib, level, target, &mut prop, + global, caches, lib, target, &mut prop, ) .map(|r| (r, false)) .map_err( @@ -468,8 +454,8 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val, root, rhs, - *options, &x.rhs, idx_values, rhs_chain, new_val, + global, caches, lib, this_ptr, val, root, rhs, *options, + &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(*x_pos))?; @@ -479,7 +465,7 @@ impl Engine { let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; self.exec_native_fn_call( - global, caches, lib, level, setter, None, *hash_set, args, + global, caches, lib, setter, None, *hash_set, args, is_ref_mut, pos, ) .or_else( @@ -489,8 +475,8 @@ impl Engine { let idx = &mut name.into(); let new_val = val; self.call_indexer_set( - global, caches, lib, level, target, idx, - new_val, is_ref_mut, + global, caches, lib, target, idx, new_val, + is_ref_mut, ) .or_else(|e| match *e { // If there is no setter, no need to feed it @@ -513,7 +499,7 @@ impl Engine { let val = { #[cfg(feature = "debugging")] let reset = self.run_debugger_with_reset( - global, caches, lib, level, scope, this_ptr, _node, + global, caches, lib, scope, this_ptr, _node, )?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| { @@ -535,8 +521,8 @@ impl Engine { let pos1 = args.get(0).map_or(Position::NONE, Expr::position); self.make_method_call( - global, caches, lib, level, name, *hashes, target, - call_args, pos1, pos, + global, caches, lib, name, *hashes, target, call_args, + pos1, pos, )? .0 }; @@ -545,7 +531,7 @@ impl Engine { let rhs_chain = rhs.into(); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, val, root, rhs, *options, + global, caches, lib, this_ptr, val, root, rhs, *options, &x.rhs, idx_values, rhs_chain, new_val, ) .map_err(|err| err.fill_position(pos)) @@ -571,7 +557,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -612,8 +597,7 @@ impl Engine { // All other patterns - evaluate the arguments chain _ => { self.eval_dot_index_chain_arguments( - global, caches, lib, level, scope, this_ptr, rhs, options, chain_type, - idx_values, + global, caches, lib, scope, this_ptr, rhs, options, chain_type, idx_values, )?; } } @@ -622,19 +606,19 @@ impl Engine { // id.??? or id[???] Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, lhs)?; + self.run_debugger(global, caches, lib, scope, this_ptr, lhs)?; self.track_operation(global, *var_pos)?; let (mut target, ..) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; let obj_ptr = &mut target; let root = (x.3.as_str(), *var_pos); let mut this = Dynamic::NULL; self.eval_dot_index_chain_helper( - global, caches, lib, level, &mut this, obj_ptr, root, expr, options, rhs, - idx_values, chain_type, new_val, + global, caches, lib, &mut this, obj_ptr, root, expr, options, rhs, idx_values, + chain_type, new_val, ) } // {expr}.??? = ??? or {expr}[???] = ??? @@ -642,14 +626,14 @@ impl Engine { // {expr}.??? or {expr}[???] expr => { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let obj_ptr = &mut value.into(); let root = ("", expr.start_position()); self.eval_dot_index_chain_helper( - global, caches, lib, level, this_ptr, obj_ptr, root, expr, options, rhs, - idx_values, chain_type, new_val, + global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values, + chain_type, new_val, ) } } @@ -663,7 +647,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -680,7 +663,7 @@ impl Engine { { for arg_expr in &x.args { idx_values.push( - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg_expr)? + self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? .0 .flatten(), ); @@ -714,11 +697,9 @@ impl Engine { { for arg_expr in &x.args { _arg_values.push( - self.get_arg_value( - global, caches, lib, level, scope, this_ptr, arg_expr, - )? - .0 - .flatten(), + self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)? + .0 + .flatten(), ); } } @@ -733,7 +714,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => { _arg_values.push( - self.eval_expr(global, caches, lib, level, scope, this_ptr, lhs)? + self.eval_expr(global, caches, lib, scope, this_ptr, lhs)? .flatten(), ); } @@ -744,8 +725,7 @@ impl Engine { let chain_type = expr.into(); self.eval_dot_index_chain_arguments( - global, caches, lib, level, scope, this_ptr, rhs, *options, chain_type, - idx_values, + global, caches, lib, scope, this_ptr, rhs, *options, chain_type, idx_values, )?; if !_arg_values.is_empty() { @@ -759,7 +739,7 @@ impl Engine { } #[cfg(not(feature = "no_index"))] _ if parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + self.eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(), ), _ => unreachable!("unknown chained expression: {:?}", expr), @@ -775,7 +755,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &mut Dynamic, idx: &mut Dynamic, ) -> RhaiResultOf { @@ -783,12 +762,12 @@ impl Engine { let hash = global.hash_idx_get(); let fn_name = crate::engine::FN_IDX_GET; let pos = Position::NONE; - let level = level + 1; - self.exec_native_fn_call( - global, caches, lib, level, fn_name, None, hash, args, true, pos, - ) - .map(|(r, ..)| r) + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + + self.exec_native_fn_call(global, caches, lib, fn_name, None, hash, args, true, pos) + .map(|(r, ..)| r) } /// Call a set indexer. @@ -798,7 +777,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &mut Dynamic, idx: &mut Dynamic, new_val: &mut Dynamic, @@ -808,10 +786,12 @@ impl Engine { let args = &mut [target, idx, new_val]; let fn_name = crate::engine::FN_IDX_SET; let pos = Position::NONE; - let level = level + 1; + + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); self.exec_native_fn_call( - global, caches, lib, level, fn_name, None, hash, args, is_ref_mut, pos, + global, caches, lib, fn_name, None, hash, args, is_ref_mut, pos, ) } @@ -822,7 +802,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, target: &'t mut Dynamic, idx: &mut Dynamic, idx_pos: Position, @@ -1031,7 +1010,7 @@ impl Engine { } _ if use_indexers => self - .call_indexer_get(global, caches, lib, level, target, idx) + .call_indexer_get(global, caches, lib, target, idx) .map(Into::into), _ => Err(ERR::ErrorIndexingType( diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index 124b3547..6eed7279 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -414,14 +414,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf<()> { if self.debugger.is_some() { if let Some(cmd) = - self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node)? + self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node)? { global.debugger.status = cmd; } @@ -441,13 +440,12 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, ) -> RhaiResultOf> { if self.debugger.is_some() { - self.run_debugger_with_reset_raw(global, caches, lib, level, scope, this_ptr, node) + self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node) } else { Ok(None) } @@ -464,7 +462,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: impl Into>, @@ -497,7 +494,7 @@ impl Engine { }, }; - self.run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) + self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) } /// Run the debugger callback unconditionally. /// @@ -511,7 +508,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, node: ASTNode<'a>, @@ -519,7 +515,7 @@ impl Engine { ) -> Result, Box> { let src = global.source_raw().cloned(); let src = src.as_ref().map(|s| s.as_str()); - let context = crate::EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = crate::EvalContext::new(self, global, caches, lib, scope, this_ptr); if let Some((.., ref on_debugger)) = self.debugger { let command = on_debugger(context, event, node, src, node.position())?; @@ -545,12 +541,12 @@ impl Engine { // Bump a level if it is a function call let level = match node { ASTNode::Expr(Expr::FnCall(..)) | ASTNode::Stmt(Stmt::FnCall(..)) => { - level + 1 + global.level + 1 } ASTNode::Stmt(Stmt::Expr(e)) if matches!(**e, Expr::FnCall(..)) => { - level + 1 + global.level + 1 } - _ => level, + _ => global.level, }; global.debugger.status = DebuggerStatus::FunctionExit(level); Ok(None) diff --git a/src/eval/eval_context.rs b/src/eval/eval_context.rs index ed5d6193..f8f2fd1a 100644 --- a/src/eval/eval_context.rs +++ b/src/eval/eval_context.rs @@ -21,8 +21,6 @@ pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> { lib: &'a [SharedModule], /// The current bound `this` pointer, if any. this_ptr: &'t mut Dynamic, - /// The current nesting level of function calls. - level: usize, } impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { @@ -34,7 +32,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { global: &'g mut GlobalRuntimeState, caches: &'c mut Caches, lib: &'a [SharedModule], - level: usize, scope: &'s mut Scope<'ps>, this_ptr: &'t mut Dynamic, ) -> Self { @@ -45,7 +42,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { caches, lib, this_ptr, - level, } } /// The current [`Engine`]. @@ -144,7 +140,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { #[inline(always)] #[must_use] pub const fn call_level(&self) -> usize { - self.level + self.global.level } /// Evaluate an [expression tree][crate::Expression] within this [evaluation context][`EvalContext`]. @@ -186,7 +182,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global, self.caches, self.lib, - self.level, self.scope, self.this_ptr, statements, @@ -196,7 +191,6 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> { self.global, self.caches, self.lib, - self.level, self.scope, self.this_ptr, expr, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 55467124..0c18772a 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -52,25 +52,23 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { Expr::Variable(_, Some(_), _) => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) + self.search_scope_only(global, caches, lib, scope, this_ptr, expr) } Expr::Variable(v, None, _var_pos) => match &**v { // Normal variable access #[cfg(not(feature = "no_module"))] (_, ns, ..) if ns.is_empty() => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) + self.search_scope_only(global, caches, lib, scope, this_ptr, expr) } #[cfg(feature = "no_module")] - (_, (), ..) => { - self.search_scope_only(global, caches, lib, level, scope, this_ptr, expr) - } + (_, (), ..) => self.search_scope_only(global, caches, lib, scope, this_ptr, expr), // Qualified variable access #[cfg(not(feature = "no_module"))] @@ -138,7 +136,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &'s mut Scope, this_ptr: &'s mut Dynamic, expr: &Expr, @@ -174,7 +172,7 @@ impl Engine { // Check the variable resolver, if any if let Some(ref resolve_var) = self.resolve_var { - let context = EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); let var_name = expr.get_variable_name(true).expect("`Expr::Variable`"); match resolve_var(var_name, index, context) { Ok(Some(mut result)) => { @@ -223,7 +221,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, + scope: &mut Scope, this_ptr: &mut Dynamic, expr: &Expr, @@ -235,8 +233,7 @@ impl Engine { // binary operators are also function calls. if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -244,7 +241,7 @@ impl Engine { self.track_operation(global, expr.position())?; - return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); } // Then variable access. @@ -252,7 +249,7 @@ impl Engine { // will cost more than the mis-predicted `match` branch. if let Expr::Variable(x, index, var_pos) = expr { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, expr)?; self.track_operation(global, expr.position())?; @@ -263,14 +260,13 @@ impl Engine { Ok(this_ptr.clone()) } } else { - self.search_namespace(global, caches, lib, level, scope, this_ptr, expr) + self.search_namespace(global, caches, lib, scope, this_ptr, expr) .map(|(val, ..)| val.take_or_clone()) }; } #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, expr)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?; #[cfg(feature = "debugging")] let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { g.debugger.reset_status(reset) @@ -300,14 +296,11 @@ impl Engine { let result = x .iter() .try_for_each(|expr| { - let item = - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let item = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; op_info.pos = expr.start_position(); - self.eval_op_assignment( - global, caches, lib, level, &op_info, target, root, item, - ) + self.eval_op_assignment(global, caches, lib, &op_info, target, root, item) }) .map(|_| concat.take_or_clone()); @@ -324,7 +317,7 @@ impl Engine { crate::Array::with_capacity(x.len()), |mut array, item_expr| { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, item_expr)? + .eval_expr(global, caches, lib, scope, this_ptr, item_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -356,7 +349,7 @@ impl Engine { x.0.iter() .try_fold(x.1.clone(), |mut map, (key, value_expr)| { let value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, value_expr)? + .eval_expr(global, caches, lib, scope, this_ptr, value_expr)? .flatten(); #[cfg(not(feature = "unchecked"))] @@ -379,30 +372,30 @@ impl Engine { } Expr::And(x, ..) => Ok((self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? && self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Or(x, ..) => Ok((self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.lhs.position()))? || self - .eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, x.rhs.position()))?) .into()), Expr::Coalesce(x, ..) => { - let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.lhs)?; + let value = self.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?; if value.is::<()>() { - self.eval_expr(global, caches, lib, level, scope, this_ptr, &x.rhs) + self.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs) } else { Ok(value) } @@ -422,8 +415,7 @@ impl Engine { *pos, )) })?; - let mut context = - EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let mut context = EvalContext::new(self, global, caches, lib, scope, this_ptr); let result = (custom_def.func)(&mut context, &expressions, &custom.state); @@ -431,17 +423,17 @@ impl Engine { } Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT), - Expr::Stmt(x) => { - self.eval_stmt_block(global, caches, lib, level, scope, this_ptr, x, true) - } + Expr::Stmt(x) => self.eval_stmt_block(global, caches, lib, scope, this_ptr, x, true), #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self - .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), + Expr::Index(..) => { + self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + } #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self - .eval_dot_index_chain(global, caches, lib, level, scope, this_ptr, expr, &mut None), + Expr::Dot(..) => { + self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None) + } _ => unreachable!("expression cannot be evaluated: {:?}", expr), } diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 413dee13..94dcb837 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -38,6 +38,8 @@ pub struct GlobalRuntimeState { /// Number of modules loaded. #[cfg(not(feature = "no_module"))] pub num_modules_loaded: usize, + /// The current nesting level of function calls. + pub level: usize, /// Level of the current scope. /// /// The global (root) level is zero, a new block (or function call) is one level higher, and so on. @@ -87,6 +89,7 @@ impl GlobalRuntimeState { #[cfg(not(feature = "no_module"))] num_modules_loaded: 0, scope_level: 0, + level: 0, always_search_scope: false, #[cfg(not(feature = "no_module"))] embedded_module_resolver: None, diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 0f064711..faea3923 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -27,7 +27,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, statements: &[Stmt], @@ -54,6 +53,7 @@ impl Engine { let global = &mut *RestoreOnDrop::lock_if(restore_orig_state, global, move |g| { g.scope_level -= 1; + #[cfg(not(feature = "no_module"))] g.truncate_imports(orig_imports_len); @@ -77,7 +77,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, stmt, @@ -119,7 +118,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, op_info: &OpAssignment, target: &mut Target, root: (&str, Position), @@ -143,14 +141,17 @@ impl Engine { let hash = *hash_op_assign; let args = &mut [&mut *lock_guard, &mut new_val]; - let level = level + 1; if self.fast_operators() { if let Some(func) = get_builtin_op_assignment_fn(op_assign_token, args[0], args[1]) { // Built-in found let op = op_assign_token.literal_syntax(); - let context = (self, op, None, &*global, lib, *op_pos, level).into(); + + global.level += 1; + let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let context = (self, op, None, global, lib, *op_pos).into(); return func(context, args).map(|_| ()); } } @@ -160,7 +161,7 @@ impl Engine { let token = Some(op_assign_token); match self.exec_native_fn_call( - global, caches, lib, level, op_assign, token, hash, args, true, *op_pos, + global, caches, lib, op_assign, token, hash, args, true, *op_pos, ) { Ok(_) => (), Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => @@ -170,7 +171,7 @@ impl Engine { *args[0] = self .exec_native_fn_call( - global, caches, lib, level, op, token, *hash_op, args, true, *op_pos, + global, caches, lib, op, token, *hash_op, args, true, *op_pos, ) .map_err(|err| err.fill_position(op_info.pos))? .0 @@ -201,15 +202,13 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, stmt: &Stmt, rewind_scope: bool, ) -> RhaiResult { #[cfg(feature = "debugging")] - let reset = - self.run_debugger_with_reset(global, caches, lib, level, scope, this_ptr, stmt)?; + let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, stmt)?; #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); @@ -220,7 +219,7 @@ impl Engine { if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; - return self.eval_fn_call_expr(global, caches, lib, level, scope, this_ptr, x, *pos); + return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos); } // Then assignments. @@ -233,11 +232,11 @@ impl Engine { if let Expr::Variable(x, ..) = lhs { let rhs_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, rhs)? + .eval_expr(global, caches, lib, scope, this_ptr, rhs)? .flatten(); let (mut lhs_ptr, pos) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, lhs)?; + self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?; let var_name = x.3.as_str(); @@ -259,13 +258,13 @@ impl Engine { let lhs_ptr = &mut lhs_ptr; return self - .eval_op_assignment(global, caches, lib, level, op_info, lhs_ptr, root, rhs_val) + .eval_op_assignment(global, caches, lib, op_info, lhs_ptr, root, rhs_val) .map(|_| Dynamic::UNIT); } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] { - let rhs_val = self.eval_expr(global, caches, lib, level, scope, this_ptr, rhs)?; + let rhs_val = self.eval_expr(global, caches, lib, scope, this_ptr, rhs)?; // Check if the result is a string. If so, intern it. #[cfg(not(feature = "no_closure"))] @@ -292,14 +291,12 @@ impl Engine { } // idx_lhs[idx_expr] op= rhs #[cfg(not(feature = "no_index"))] - Expr::Index(..) => self.eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ), + Expr::Index(..) => self + .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), // dot_lhs.dot_rhs op= rhs #[cfg(not(feature = "no_object"))] - Expr::Dot(..) => self.eval_dot_index_chain( - global, caches, lib, level, scope, this_ptr, lhs, _new_val, - ), + Expr::Dot(..) => self + .eval_dot_index_chain(global, caches, lib, scope, this_ptr, lhs, _new_val), _ => unreachable!("cannot assign to expression: {:?}", lhs), } .map(|_| Dynamic::UNIT); @@ -314,32 +311,28 @@ impl Engine { // Expression as statement Stmt::Expr(expr) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .map(Dynamic::flatten), // Block scope Stmt::Block(statements, ..) if statements.is_empty() => Ok(Dynamic::UNIT), - Stmt::Block(statements, ..) => self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ), + Stmt::Block(statements, ..) => { + self.eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) + } // If statement Stmt::If(x, ..) => { let (expr, if_block, else_block) = &**x; let guard_val = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; if guard_val && !if_block.is_empty() { - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, if_block, true, - ) + self.eval_stmt_block(global, caches, lib, scope, this_ptr, if_block, true) } else if !guard_val && !else_block.is_empty() { - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, else_block, true, - ) + self.eval_stmt_block(global, caches, lib, scope, this_ptr, else_block, true) } else { Ok(Dynamic::UNIT) } @@ -359,7 +352,7 @@ impl Engine { let mut result = None; - let value = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let value = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; if value.is_hashable() { let hasher = &mut get_hasher(); @@ -376,7 +369,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .eval_expr(global, caches, lib, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -398,7 +391,7 @@ impl Engine { let cond_result = match block.condition { Expr::BoolConstant(b, ..) => b, ref c => self - .eval_expr(global, caches, lib, level, scope, this_ptr, c)? + .eval_expr(global, caches, lib, scope, this_ptr, c)? .as_bool() .map_err(|typ| { self.make_type_mismatch_err::(typ, c.position()) @@ -416,7 +409,7 @@ impl Engine { result .or_else(|| def_case.as_ref().map(|&index| &expressions[index].expr)) .map_or(Ok(Dynamic::UNIT), |expr| { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr) + self.eval_expr(global, caches, lib, scope, this_ptr, expr) }) } @@ -431,8 +424,8 @@ impl Engine { } loop { - if let Err(err) = self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -449,7 +442,7 @@ impl Engine { loop { let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -461,8 +454,8 @@ impl Engine { continue; } - if let Err(err) = self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, body, true) + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) { match *err { ERR::LoopBreak(false, ..) => (), @@ -480,9 +473,9 @@ impl Engine { loop { if !body.is_empty() { - if let Err(err) = self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, body, true, - ) { + if let Err(err) = + self.eval_stmt_block(global, caches, lib, scope, this_ptr, body, true) + { match *err { ERR::LoopBreak(false, ..) => continue, ERR::LoopBreak(true, value, ..) => break Ok(value), @@ -492,7 +485,7 @@ impl Engine { } let condition = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .as_bool() .map_err(|typ| self.make_type_mismatch_err::(typ, expr.position()))?; @@ -507,7 +500,7 @@ impl Engine { let (var_name, counter, expr, statements) = &**x; let iter_obj = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let iter_type = iter_obj.type_id(); @@ -531,75 +524,77 @@ impl Engine { .find_map(|m| m.get_qualified_iter(iter_type)) }); - if let Some(func) = func { - // Restore scope at end of statement - let orig_scope_len = scope.len(); - let scope = &mut *RestoreOnDrop::lock(scope, move |s| { - s.rewind(orig_scope_len); - }); + let func = func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?; - // Add the loop variables - let counter_index = if counter.is_empty() { - usize::MAX - } else { - scope.push(counter.name.clone(), 0 as INT); - scope.len() - 1 - }; + // Restore scope at end of statement + let orig_scope_len = scope.len(); + let scope = &mut *RestoreOnDrop::lock(scope, move |s| { + s.rewind(orig_scope_len); + }); - scope.push(var_name.name.clone(), ()); - let index = scope.len() - 1; - - func(iter_obj) - .enumerate() - .try_fold(Dynamic::UNIT, |_, (x, iter_value)| { - // Increment counter - if counter_index < usize::MAX { - // As the variable increments from 0, this should always work - // since any overflow will first be caught below. - let index_value = x as INT; - - #[cfg(not(feature = "unchecked"))] - if index_value > crate::MAX_USIZE_INT { - return Err(ERR::ErrorArithmetic( - format!("for-loop counter overflow: {x}"), - counter.pos, - ) - .into()); - } - - *scope.get_mut_by_index(counter_index).write_lock().unwrap() = - Dynamic::from_int(index_value); - } - - let value = match iter_value { - Ok(v) => v.flatten(), - Err(err) => return Err(err.fill_position(expr.position())), - }; - - *scope.get_mut_by_index(index).write_lock().unwrap() = value; - - self.track_operation(global, statements.position())?; - - if statements.is_empty() { - return Ok(Dynamic::UNIT); - } - - self.eval_stmt_block( - global, caches, lib, level, scope, this_ptr, statements, true, - ) - .map(|_| Dynamic::UNIT) - .or_else(|err| match *err { - ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT), - _ => Err(err), - }) - }) - .or_else(|err| match *err { - ERR::LoopBreak(true, value, ..) => Ok(value), - _ => Err(err), - }) + // Add the loop variables + let counter_index = if counter.is_empty() { + usize::MAX } else { - Err(ERR::ErrorFor(expr.start_position()).into()) + scope.push(counter.name.clone(), 0 as INT); + scope.len() - 1 + }; + + scope.push(var_name.name.clone(), ()); + let index = scope.len() - 1; + + let mut result = Dynamic::UNIT; + + for (x, iter_value) in func(iter_obj).enumerate() { + // Increment counter + if counter_index < usize::MAX { + // As the variable increments from 0, this should always work + // since any overflow will first be caught below. + let index_value = x as INT; + + #[cfg(not(feature = "unchecked"))] + if index_value > crate::MAX_USIZE_INT { + return Err(ERR::ErrorArithmetic( + format!("for-loop counter overflow: {x}"), + counter.pos, + ) + .into()); + } + + *scope.get_mut_by_index(counter_index).write_lock().unwrap() = + Dynamic::from_int(index_value); + } + + // Set loop value + let value = iter_value + .map_err(|err| err.fill_position(expr.position()))? + .flatten(); + + *scope.get_mut_by_index(index).write_lock().unwrap() = value; + + // Run block + self.track_operation(global, statements.position())?; + + if statements.is_empty() { + continue; + } + + match self + .eval_stmt_block(global, caches, lib, scope, this_ptr, statements, true) + { + Ok(_) => (), + Err(err) => match *err { + ERR::LoopBreak(false, ..) => (), + ERR::LoopBreak(true, value, ..) => { + result = value; + break; + } + _ => return Err(err), + }, + } } + + Ok(result) } // Continue/Break statement @@ -607,7 +602,7 @@ impl Engine { let is_break = options.contains(ASTFlags::BREAK); let value = if let Some(ref expr) = expr { - self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + self.eval_expr(global, caches, lib, scope, this_ptr, expr)? } else { Dynamic::UNIT }; @@ -626,9 +621,7 @@ impl Engine { catch_block, } = &**x; - match self - .eval_stmt_block(global, caches, lib, level, scope, this_ptr, try_block, true) - { + match self.eval_stmt_block(global, caches, lib, scope, this_ptr, try_block, true) { r @ Ok(_) => r, Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), @@ -683,7 +676,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, catch_block, @@ -704,7 +696,7 @@ impl Engine { // Throw value Stmt::Return(Some(expr), options, pos) if options.contains(ASTFlags::BREAK) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .and_then(|v| Err(ERR::ErrorRuntime(v.flatten(), *pos).into())), // Empty throw @@ -714,7 +706,7 @@ impl Engine { // Return value Stmt::Return(Some(expr), .., pos) => self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr) + .eval_expr(global, caches, lib, scope, this_ptr, expr) .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())), // Empty return @@ -746,8 +738,7 @@ impl Engine { nesting_level, will_shadow, }; - let context = - EvalContext::new(self, global, caches, lib, level, scope, this_ptr); + let context = EvalContext::new(self, global, caches, lib, scope, this_ptr); if !filter(true, info, context)? { return Err(ERR::ErrorForbiddenVariable(var_name.to_string(), *pos).into()); @@ -756,7 +747,7 @@ impl Engine { // Evaluate initial value let mut value = self - .eval_expr(global, caches, lib, level, scope, this_ptr, expr)? + .eval_expr(global, caches, lib, scope, this_ptr, expr)? .flatten(); let _alias = if !rewind_scope { @@ -811,7 +802,7 @@ impl Engine { return Err(ERR::ErrorTooManyModules(*_pos).into()); } - let v = self.eval_expr(global, caches, lib, level, scope, this_ptr, expr)?; + let v = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?; let typ = v.type_name(); let path = v.try_cast::().ok_or_else(|| { self.make_type_mismatch_err::(typ, expr.position()) @@ -908,21 +899,18 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, statements: &[Stmt], ) -> RhaiResult { let mut this = Dynamic::NULL; - self.eval_stmt_block( - global, caches, lib, level, scope, &mut this, statements, false, - ) - .or_else(|err| match *err { - ERR::Return(out, ..) => Ok(out), - ERR::LoopBreak(..) => { - unreachable!("no outer loop scope to break out of") - } - _ => Err(err), - }) + self.eval_stmt_block(global, caches, lib, scope, &mut this, statements, false) + .or_else(|err| match *err { + ERR::Return(out, ..) => Ok(out), + ERR::LoopBreak(..) => { + unreachable!("no outer loop scope to break out of") + } + _ => Err(err), + }) } } diff --git a/src/func/call.rs b/src/func/call.rs index 8bc4cfe3..38020404 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -326,7 +326,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, name: &str, op_token: Option<&Token>, hash: u64, @@ -385,7 +384,7 @@ impl Engine { // Run external function let src = source.as_ref().map(|s| s.as_str()); - let context = (self, name, src, &*global, lib, pos, level).into(); + let context = (self, name, src, &*global, lib, pos).into(); let mut _result = if func.is_plugin_fn() { let f = func.get_plugin_fn().unwrap(); @@ -401,7 +400,7 @@ impl Engine { #[cfg(feature = "debugging")] { let trigger = match global.debugger.status { - crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, + crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level, crate::eval::DebuggerStatus::Next(.., true) => true, _ => false, }; @@ -415,8 +414,8 @@ impl Engine { Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - if let Err(err) = self - .run_debugger_raw(global, caches, lib, level, scope, &mut this, node, event) + if let Err(err) = + self.run_debugger_raw(global, caches, lib, scope, &mut this, node, event) { _result = Err(err); } @@ -539,7 +538,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, _scope: Option<&mut Scope>, fn_name: &str, op_token: Option<&Token>, @@ -561,7 +559,8 @@ impl Engine { #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, args, is_ref_mut)?; - let level = level + 1; + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); // These may be redirected from method style calls. if hashes.is_native_only() { @@ -647,7 +646,7 @@ impl Engine { let (first_arg, rest_args) = args.split_first_mut().unwrap(); self.call_script_fn( - global, caches, lib, level, scope, first_arg, func, rest_args, true, pos, + global, caches, lib, scope, first_arg, func, rest_args, true, pos, ) } else { // Normal call of script function @@ -667,7 +666,7 @@ impl Engine { let mut this = Dynamic::NULL; self.call_script_fn( - global, caches, lib, level, scope, &mut this, func, args, true, pos, + global, caches, lib, scope, &mut this, func, args, true, pos, ) } .map(|r| (r, false)); @@ -678,7 +677,7 @@ impl Engine { let hash = hashes.native(); self.exec_native_fn_call( - global, caches, lib, level, fn_name, op_token, hash, args, is_ref_mut, pos, + global, caches, lib, fn_name, op_token, hash, args, is_ref_mut, pos, ) } @@ -689,7 +688,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, arg_expr: &Expr, @@ -699,7 +697,7 @@ impl Engine { self.track_operation(global, arg_expr.start_position())?; #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, arg_expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, arg_expr)?; return Ok((value, arg_expr.start_position())); } @@ -712,7 +710,7 @@ impl Engine { #[cfg(feature = "debugging")] let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); - self.eval_expr(global, caches, lib, level, scope, this_ptr, arg_expr) + self.eval_expr(global, caches, lib, scope, this_ptr, arg_expr) .map(|r| (r, arg_expr.start_position())) } @@ -723,7 +721,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, fn_name: &str, mut hash: FnCallHashes, target: &mut crate::eval::Target, @@ -764,7 +761,6 @@ impl Engine { global, caches, lib, - level, None, fn_name, None, @@ -820,7 +816,6 @@ impl Engine { global, caches, lib, - level, None, &fn_name, None, @@ -921,7 +916,6 @@ impl Engine { global, caches, lib, - level, None, fn_name, None, @@ -948,7 +942,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, fn_name: &str, @@ -974,7 +967,7 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1015,7 +1008,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; // Fn - only in function call style return arg_value @@ -1030,7 +1023,7 @@ impl Engine { KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, first)?; if !arg_value.is::() { let typ = self.map_type_name(arg_value.type_name()); @@ -1042,7 +1035,7 @@ impl Engine { // Append the new curried arguments to the existing list. let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| { let (value, ..) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, expr)?; curried.push(value); Ok::<_, RhaiError>(curried) })?; @@ -1055,7 +1048,7 @@ impl Engine { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, ..) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; return Ok(arg_value.is_shared().into()); } @@ -1064,14 +1057,14 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let first = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, first)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, first)?; let fn_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, &a_expr[0])?; + self.get_arg_value(global, caches, lib, scope, this_ptr, &a_expr[0])?; let num_params = arg_value .as_int() @@ -1090,7 +1083,7 @@ impl Engine { KEYWORD_IS_DEF_VAR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1105,12 +1098,15 @@ impl Engine { let orig_imports_len = global.num_imports(); let arg = first_arg.unwrap(); let (arg_value, pos) = - self.get_arg_value(global, caches, lib, level, scope, this_ptr, arg)?; + self.get_arg_value(global, caches, lib, scope, this_ptr, arg)?; let s = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; - let result = - self.eval_script_expr_in_place(global, caches, lib, level + 1, scope, s, pos); + + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let result = self.eval_script_expr_in_place(global, caches, lib, scope, s, pos); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1152,7 +1148,7 @@ impl Engine { .copied() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1163,8 +1159,8 @@ impl Engine { return self .exec_fn_call( - global, caches, lib, level, scope, name, op_token, hashes, &mut args, - is_ref_mut, false, pos, + global, caches, lib, scope, name, op_token, hashes, &mut args, is_ref_mut, + false, pos, ) .map(|(v, ..)| v); } @@ -1180,16 +1176,16 @@ impl Engine { let first_expr = first_arg.unwrap(); #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, first_expr)?; + self.run_debugger(global, caches, lib, scope, this_ptr, first_expr)?; // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; let (mut target, _pos) = - self.search_namespace(global, caches, lib, level, scope, this_ptr, first_expr)?; + self.search_namespace(global, caches, lib, scope, this_ptr, first_expr)?; if target.is_read_only() { target = target.into_owned(); @@ -1216,7 +1212,7 @@ impl Engine { .into_iter() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1226,8 +1222,7 @@ impl Engine { } self.exec_fn_call( - global, caches, lib, level, None, name, op_token, hashes, &mut args, is_ref_mut, false, - pos, + global, caches, lib, None, name, op_token, hashes, &mut args, is_ref_mut, false, pos, ) .map(|(v, ..)| v) } @@ -1239,7 +1234,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, namespace: &crate::ast::Namespace, @@ -1260,20 +1254,20 @@ impl Engine { // and avoid cloning the value if !args_expr.is_empty() && args_expr[0].is_variable_access(true) { #[cfg(feature = "debugging")] - self.run_debugger(global, caches, lib, level, scope, this_ptr, &args_expr[0])?; + self.run_debugger(global, caches, lib, scope, this_ptr, &args_expr[0])?; // func(x, ...) -> x.func(...) arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; // Get target reference to first argument let first_arg = &args_expr[0]; let (target, _pos) = - self.search_scope_only(global, caches, lib, level, scope, this_ptr, first_arg)?; + self.search_scope_only(global, caches, lib, scope, this_ptr, first_arg)?; self.track_operation(global, _pos)?; @@ -1296,7 +1290,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(global, caches, lib, level, scope, this_ptr, expr) + self.get_arg_value(global, caches, lib, scope, this_ptr, expr) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); @@ -1363,7 +1357,8 @@ impl Engine { } } - let level = level + 1; + global.level += 1; + let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1); match func { #[cfg(not(feature = "no_function"))] @@ -1376,12 +1371,12 @@ impl Engine { let global = &mut *RestoreOnDrop::lock(global, move |g| g.source = orig_source); self.call_script_fn( - global, caches, lib, level, new_scope, &mut this, fn_def, &mut args, true, pos, + global, caches, lib, new_scope, &mut this, fn_def, &mut args, true, pos, ) } Some(f) if f.is_plugin_fn() => { - let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); let f = f.get_plugin_fn().expect("plugin function"); let result = if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into()) @@ -1393,7 +1388,7 @@ impl Engine { Some(f) if f.is_native() => { let func = f.get_native_fn().expect("native function"); - let context = (self, fn_name, module.id(), &*global, lib, pos, level).into(); + let context = (self, fn_name, module.id(), &*global, lib, pos).into(); let result = func(context, &mut args); self.check_return_value(result, pos) } @@ -1422,7 +1417,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, script: &str, _pos: Position, @@ -1458,7 +1452,7 @@ impl Engine { } // Evaluate the AST - self.eval_global_statements(global, caches, lib, level, scope, statements) + self.eval_global_statements(global, caches, lib, scope, statements) } /// Evaluate a function call expression. @@ -1467,7 +1461,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, expr: &FnCallExpr, @@ -1489,12 +1482,12 @@ impl Engine { // Short-circuit native binary operator call if under Fast Operators mode if op_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self - .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[0])? + .get_arg_value(global, caches, lib, scope, this_ptr, &args[0])? .0 .flatten(); let mut rhs = self - .get_arg_value(global, caches, lib, level, scope, this_ptr, &args[1])? + .get_arg_value(global, caches, lib, scope, this_ptr, &args[1])? .0 .flatten(); @@ -1504,14 +1497,16 @@ impl Engine { get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1]) { // Built-in found - let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); + global.level += 1; + let global = &*RestoreOnDrop::lock(global, move |g| g.level -= 1); + + let context = (self, name.as_str(), None, global, lib, pos).into(); return func(context, operands); } return self .exec_fn_call( - global, caches, lib, level, None, name, op_token, *hashes, operands, false, - false, pos, + global, caches, lib, None, name, op_token, *hashes, operands, false, false, pos, ) .map(|(v, ..)| v); } @@ -1522,7 +1517,7 @@ impl Engine { let hash = hashes.native(); return self.make_qualified_function_call( - global, caches, lib, level, scope, this_ptr, namespace, name, args, hash, pos, + global, caches, lib, scope, this_ptr, namespace, name, args, hash, pos, ); } @@ -1533,7 +1528,7 @@ impl Engine { ); self.make_function_call( - global, caches, lib, level, scope, this_ptr, name, op_token, first_arg, args, *hashes, + global, caches, lib, scope, this_ptr, name, op_token, first_arg, args, *hashes, *capture, pos, ) } diff --git a/src/func/native.rs b/src/func/native.rs index 65f5119d..de74f6c6 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -72,13 +72,11 @@ pub struct NativeCallContext<'a> { /// Function source, if any. source: Option<&'a str>, /// The current [`GlobalRuntimeState`], if any. - global: Option<&'a GlobalRuntimeState>, + global: &'a GlobalRuntimeState, /// The current stack of loaded [modules][Module]. lib: &'a [SharedModule], /// [Position] of the function call. pos: Position, - /// The current nesting level of function calls. - level: usize, } /// _(internals)_ Context of a native Rust function call. @@ -96,8 +94,6 @@ pub struct NativeCallContextStore { pub lib: StaticVec, /// [Position] of the function call. pub pos: Position, - /// The current nesting level of function calls. - pub level: usize, } #[cfg(feature = "internals")] @@ -118,7 +114,6 @@ impl<'a> &'a GlobalRuntimeState, &'a [SharedModule], Position, - usize, )> for NativeCallContext<'a> { #[inline(always)] @@ -130,56 +125,20 @@ impl<'a> &'a GlobalRuntimeState, &'a [SharedModule], Position, - usize, ), ) -> Self { Self { engine: value.0, fn_name: value.1, source: value.2, - global: Some(value.3), + global: value.3, lib: value.4, pos: value.5, - level: value.6, - } - } -} - -impl<'a> From<(&'a Engine, &'a str, &'a [SharedModule])> for NativeCallContext<'a> { - #[inline(always)] - fn from(value: (&'a Engine, &'a str, &'a [SharedModule])) -> Self { - Self { - engine: value.0, - fn_name: value.1, - source: None, - global: None, - lib: value.2, - pos: Position::NONE, - level: 0, } } } impl<'a> NativeCallContext<'a> { - /// _(internals)_ Create a new [`NativeCallContext`]. - /// Exported under the `internals` feature only. - #[deprecated( - since = "1.3.0", - note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly." - )] - #[inline(always)] - #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [SharedModule]) -> Self { - Self { - engine, - fn_name, - source: None, - global: None, - lib, - pos: Position::NONE, - level: 0, - } - } /// _(internals)_ Create a new [`NativeCallContext`]. /// Exported under the `internals` feature only. /// @@ -195,16 +154,14 @@ impl<'a> NativeCallContext<'a> { global: &'a GlobalRuntimeState, lib: &'a [SharedModule], pos: Position, - level: usize, ) -> Self { Self { engine, fn_name, source, - global: Some(global), + global, lib, pos, - level, } } @@ -218,10 +175,9 @@ impl<'a> NativeCallContext<'a> { engine, fn_name: &context.fn_name, source: context.source.as_ref().map(String::as_str), - global: Some(&context.global), + global: &context.global, lib: &context.lib, pos: context.pos, - level: context.level, } } /// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`]. @@ -233,10 +189,9 @@ impl<'a> NativeCallContext<'a> { NativeCallContextStore { fn_name: self.fn_name.to_string(), source: self.source.map(|s| s.to_string()), - global: self.global.unwrap().clone(), + global: self.global.clone(), lib: self.lib.iter().cloned().collect(), pos: self.pos, - level: self.level, } } @@ -262,7 +217,7 @@ impl<'a> NativeCallContext<'a> { #[inline(always)] #[must_use] pub const fn call_level(&self) -> usize { - self.level + self.global.level } /// The current source. #[inline(always)] @@ -274,7 +229,7 @@ impl<'a> NativeCallContext<'a> { #[inline(always)] #[must_use] pub fn tag(&self) -> Option<&Dynamic> { - self.global.as_ref().map(|g| &g.tag) + Some(&self.global.tag) } /// Get an iterator over the current set of modules imported via `import` statements /// in reverse order. @@ -283,7 +238,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[inline] pub fn iter_imports(&self) -> impl Iterator { - self.global.iter().flat_map(|&g| g.iter_imports()) + self.global.iter_imports() } /// Get an iterator over the current set of modules imported via `import` statements in reverse order. #[cfg(not(feature = "no_module"))] @@ -292,7 +247,7 @@ impl<'a> NativeCallContext<'a> { pub(crate) fn iter_imports_raw( &self, ) -> impl Iterator { - self.global.iter().flat_map(|&g| g.iter_imports_raw()) + self.global.iter_imports_raw() } /// _(internals)_ The current [`GlobalRuntimeState`], if any. /// Exported under the `internals` feature only. @@ -301,7 +256,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> { + pub const fn global_runtime_state(&self) -> &GlobalRuntimeState { self.global } /// Get an iterator over the namespaces containing definitions of all script-defined functions @@ -437,10 +392,7 @@ impl<'a> NativeCallContext<'a> { is_method_call: bool, args: &mut [&mut Dynamic], ) -> RhaiResult { - let global = &mut self - .global - .cloned() - .unwrap_or_else(|| GlobalRuntimeState::new(self.engine())); + let mut global = &mut self.global.clone(); let caches = &mut Caches::new(); let fn_name = fn_name.as_ref(); @@ -448,6 +400,8 @@ impl<'a> NativeCallContext<'a> { let op_token = op_token.as_ref(); let args_len = args.len(); + global.level += 1; + if native_only { return self .engine() @@ -455,7 +409,6 @@ impl<'a> NativeCallContext<'a> { global, caches, self.lib, - self.level + 1, fn_name, op_token, calc_fn_hash(None, fn_name, args_len), @@ -483,7 +436,6 @@ impl<'a> NativeCallContext<'a> { global, caches, self.lib, - self.level + 1, None, fn_name, op_token, diff --git a/src/func/script.rs b/src/func/script.rs index ae4a0f77..db3fa542 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -27,7 +27,6 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[SharedModule], - level: usize, scope: &mut Scope, this_ptr: &mut Dynamic, fn_def: &ScriptFnDef, @@ -66,7 +65,7 @@ impl Engine { self.track_operation(global, pos)?; // Check for stack overflow - if level > self.max_call_levels() { + if global.level > self.max_call_levels() { return Err(ERR::ErrorStackOverflow(pos).into()); } @@ -140,7 +139,7 @@ impl Engine { #[cfg(feature = "debugging")] { let node = crate::ast::Stmt::Noop(fn_def.body.position()); - self.run_debugger(global, caches, lib, level, scope, this_ptr, &node)?; + self.run_debugger(global, caches, lib, scope, this_ptr, &node)?; } // Evaluate the function @@ -149,7 +148,6 @@ impl Engine { global, caches, lib, - level, scope, this_ptr, &fn_def.body, @@ -179,7 +177,7 @@ impl Engine { #[cfg(feature = "debugging")] { let trigger = match global.debugger.status { - crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, + crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level, crate::eval::DebuggerStatus::Next(.., true) => true, _ => false, }; @@ -190,9 +188,7 @@ impl Engine { Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r), Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err), }; - match self - .run_debugger_raw(global, caches, lib, level, scope, this_ptr, node, event) - { + match self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) { Ok(_) => (), Err(err) => _result = Err(err), } diff --git a/src/module/mod.rs b/src/module/mod.rs index 0d78e0e4..a8676655 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1986,7 +1986,7 @@ impl Module { // Run the script let caches = &mut crate::eval::Caches::new(); - let result = engine.eval_ast_with_scope_raw(global, caches, 0, &mut scope, ast); + let result = engine.eval_ast_with_scope_raw(global, caches, &mut scope, ast); // Create new module let mut module = Module::new(); diff --git a/src/optimizer.rs b/src/optimizer.rs index 7ad765c1..9bf57005 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -148,7 +148,6 @@ impl<'a> OptimizerState<'a> { &mut self.global, &mut self.caches, lib, - 0, fn_name, op_token, calc_fn_hash(None, fn_name, arg_values.len()), @@ -1144,7 +1143,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { #[cfg(feature = "no_function")] let lib = &[][..]; - let context = (state.engine, x.name.as_str(), lib).into(); + let context = (state.engine, x.name.as_str(),None, &state.global, lib, *pos).into(); let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { diff --git a/src/packages/debugging.rs b/src/packages/debugging.rs index 8a800c1b..4e3cbc35 100644 --- a/src/packages/debugging.rs +++ b/src/packages/debugging.rs @@ -33,57 +33,47 @@ mod debugging_functions { #[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_index"))] pub fn back_trace(ctx: NativeCallContext) -> Array { - if let Some(global) = ctx.global_runtime_state() { - global - .debugger - .call_stack() - .iter() - .rev() - .filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| { - fn_name.as_str() != "back_trace" || !args.is_empty() - }) - .map( - |frame @ crate::debugger::CallStackFrame { - fn_name: _fn_name, - args: _args, - source: _source, - pos: _pos, - }| { - let display = frame.to_string(); + ctx.global_runtime_state() + .debugger + .call_stack() + .iter() + .rev() + .filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| { + fn_name.as_str() != "back_trace" || !args.is_empty() + }) + .map( + |frame @ crate::debugger::CallStackFrame { + fn_name: _fn_name, + args: _args, + source: _source, + pos: _pos, + }| { + let display = frame.to_string(); - #[cfg(not(feature = "no_object"))] - { - let mut map = Map::new(); - map.insert("display".into(), display.into()); - map.insert("fn_name".into(), _fn_name.into()); - if !_args.is_empty() { - map.insert( - "args".into(), - Dynamic::from_array(_args.clone().to_vec()), - ); - } - if let Some(source) = _source { - map.insert("source".into(), source.into()); - } - if !_pos.is_none() { - map.insert( - "line".into(), - (_pos.line().unwrap() as crate::INT).into(), - ); - map.insert( - "position".into(), - (_pos.position().unwrap_or(0) as crate::INT).into(), - ); - } - Dynamic::from_map(map) + #[cfg(not(feature = "no_object"))] + { + let mut map = Map::new(); + map.insert("display".into(), display.into()); + map.insert("fn_name".into(), _fn_name.into()); + if !_args.is_empty() { + map.insert("args".into(), Dynamic::from_array(_args.clone().to_vec())); } - #[cfg(feature = "no_object")] - display.into() - }, - ) - .collect() - } else { - Array::new() - } + if let Some(source) = _source { + map.insert("source".into(), source.into()); + } + if !_pos.is_none() { + map.insert("line".into(), (_pos.line().unwrap() as crate::INT).into()); + map.insert( + "position".into(), + (_pos.position().unwrap_or(0) as crate::INT).into(), + ); + } + Dynamic::from_map(map) + } + #[cfg(feature = "no_object")] + display.into() + }, + ) + .collect() } } diff --git a/src/parser.rs b/src/parser.rs index 03d5b16b..fed55ac3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2898,12 +2898,12 @@ impl Engine { if let Some(ref filter) = self.def_var_filter { let will_shadow = state.stack.iter().any(|(v, ..)| v == name); - let level = settings.level; + state.global.level = settings.level; let is_const = access == AccessMode::ReadOnly; let info = VarDefInfo { name: &name, is_const, - nesting_level: level, + nesting_level: state.global.level, will_shadow, }; let caches = &mut Caches::new(); @@ -2914,7 +2914,6 @@ impl Engine { &mut state.global, caches, &[], - level, &mut state.stack, &mut this, ); diff --git a/src/tests.rs b/src/tests.rs index 8bbef0d4..6e36c977 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -45,9 +45,9 @@ fn check_struct_sizes() { assert_eq!( size_of::(), if cfg!(feature = "no_position") { - 72 + 64 } else { - 80 + 72 } ); } diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 85355d4b..3348d6c2 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -1,5 +1,6 @@ //! The `FnPtr` type. +use crate::eval::GlobalRuntimeState; use crate::tokenizer::is_valid_function_name; use crate::types::dynamic::Variant; use crate::{ @@ -160,8 +161,16 @@ impl FnPtr { &lib }; - #[allow(deprecated)] - let ctx = NativeCallContext::new(engine, self.fn_name(), lib); + let global = &mut GlobalRuntimeState::new(engine); + + let ctx = NativeCallContext::new_with_all_fields( + engine, + self.fn_name(), + None, + global, + lib, + Position::NONE, + ); let result = self.call_raw(&ctx, None, arg_values)?; diff --git a/unreachable!() b/unreachable!() deleted file mode 100644 index e69de29b..00000000