diff --git a/src/api/call_fn.rs b/src/api/call_fn.rs index 3b0a7615..8c0139e8 100644 --- a/src/api/call_fn.rs +++ b/src/api/call_fn.rs @@ -274,7 +274,7 @@ impl Engine { // Check for data race. #[cfg(not(feature = "no_closure"))] - crate::func::call::ensure_no_data_race(name, &args, false).map(|_| Dynamic::UNIT)?; + crate::func::ensure_no_data_race(name, &args, false).map(|_| Dynamic::UNIT)?; if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) { self.call_script_fn( diff --git a/src/ast/expr.rs b/src/ast/expr.rs index f42209b6..52205808 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -17,7 +17,7 @@ use std::{ fmt::Write, hash::Hash, iter::once, - num::{NonZeroU8, NonZeroUsize}, + num::{NonZeroU64, NonZeroU8, NonZeroUsize}, }; #[cfg(not(feature = "no_float"))] @@ -86,7 +86,7 @@ impl CustomExpr { /// /// Two separate hashes are pre-calculated because of the following patterns: /// -/// ```js +/// ```rhai /// func(a, b, c); // Native: func(a, b, c) - 3 parameters /// // Script: func(a, b, c) - 3 parameters /// @@ -100,22 +100,22 @@ impl CustomExpr { /// /// Function call hashes are used in the following manner: /// -/// * First, the script hash is tried, which contains only the called function's name plus the -/// number of parameters. +/// * First, the script hash (if any) is tried, which contains only the called function's name plus +/// the number of parameters. /// /// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is -/// then used to search for a native function. In other words, a complete native function call -/// hash always contains the called function's name plus the types of the arguments. This is due -/// to possible function overloading for different parameter types. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] +/// then used to search for a native function. +/// +/// In other words, a complete native function call hash always contains the called function's +/// name plus the types of the arguments. This is due to possible function overloading for +/// different parameter types. +#[derive(Clone, Copy, Eq, PartialEq, Hash)] pub struct FnCallHashes { - /// Pre-calculated hash for a script-defined function (zero if native functions only). + /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). #[cfg(not(feature = "no_function"))] - script: u64, + script: Option, /// Pre-calculated hash for a native Rust function with no parameter types. - /// - /// This hash can never be zero. - native: u64, + native: NonZeroU64, } impl fmt::Debug for FnCallHashes { @@ -123,11 +123,11 @@ impl fmt::Debug for FnCallHashes { #[inline(never)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_function"))] - if self.script != 0 { - return if self.script == self.native { + if let Some(script) = self.script { + return if script == self.native { fmt::Debug::fmt(&self.native, f) } else { - write!(f, "({}, {})", self.script, self.native) + write!(f, "({}, {})", script, self.native) }; } @@ -138,11 +138,11 @@ impl fmt::Debug for FnCallHashes { impl From for FnCallHashes { #[inline] fn from(hash: u64) -> Self { - let hash = if hash == 0 { ALT_ZERO_HASH } else { hash }; + let hash = NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap(); Self { #[cfg(not(feature = "no_function"))] - script: hash, + script: Some(hash), native: hash, } } @@ -150,23 +150,23 @@ impl From for FnCallHashes { impl FnCallHashes { /// Create a [`FnCallHashes`] with only the native Rust hash. - #[inline(always)] + #[inline] #[must_use] - pub const fn from_native(hash: u64) -> Self { + pub fn from_native(hash: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: 0, - native: if hash == 0 { ALT_ZERO_HASH } else { hash }, + script: None, + native: NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap(), } } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. - #[inline(always)] + #[inline] #[must_use] - pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { + pub fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: if script == 0 { ALT_ZERO_HASH } else { script }, - native: if native == 0 { ALT_ZERO_HASH } else { native }, + script: NonZeroU64::new(if script == 0 { ALT_ZERO_HASH } else { script }), + native: NonZeroU64::new(if native == 0 { ALT_ZERO_HASH } else { native }).unwrap(), } } /// Is this [`FnCallHashes`] native-only? @@ -174,23 +174,31 @@ impl FnCallHashes { #[must_use] pub const fn is_native_only(&self) -> bool { #[cfg(not(feature = "no_function"))] - return self.script == 0; + return self.script.is_none(); #[cfg(feature = "no_function")] return true; } /// Get the native hash. + /// + /// The hash returned is never zero. #[inline(always)] #[must_use] - pub const fn native(&self) -> u64 { - self.native + pub fn native(&self) -> u64 { + self.native.get() } /// Get the script hash. + /// + /// The hash returned is never zero. + /// + /// # Panics + /// + /// Panics if this [`FnCallHashes`] is native-only. #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub const fn script(&self) -> u64 { - assert!(self.script != 0); - self.script + pub fn script(&self) -> u64 { + assert!(self.script.is_some()); + self.script.as_ref().unwrap().get() } } @@ -210,9 +218,7 @@ pub struct FnCallExpr { /// Does this function call capture the parent scope? pub capture_parent_scope: bool, /// Is this function call a native operator? - pub operator_token: Option, - /// [Position] of the function name. - pub pos: Position, + pub op_token: Option, } impl fmt::Debug for FnCallExpr { @@ -227,13 +233,12 @@ impl fmt::Debug for FnCallExpr { ff.field("hash", &self.hashes) .field("name", &self.name) .field("args", &self.args); - if let Some(ref token) = self.operator_token { - ff.field("operator_token", token); + if let Some(ref token) = self.op_token { + ff.field("op_token", token); } if self.capture_parent_scope { ff.field("capture_parent_scope", &self.capture_parent_scope); } - ff.field("pos", &self.pos); ff.finish() } } @@ -698,8 +703,7 @@ impl Expr { hashes: calc_fn_hash(None, f.fn_name(), 1).into(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), capture_parent_scope: false, - operator_token: None, - pos, + op_token: None, } .into(), pos, @@ -754,6 +758,8 @@ impl Expr { | Self::And(.., pos) | Self::Or(.., pos) | Self::Coalesce(.., pos) + | Self::FnCall(.., pos) + | Self::MethodCall(.., pos) | Self::Index(.., pos) | Self::Dot(.., pos) | Self::InterpolatedString(.., pos) @@ -762,8 +768,6 @@ impl Expr { #[cfg(not(feature = "no_custom_syntax"))] Self::Custom(.., pos) => *pos, - Self::FnCall(x, ..) | Self::MethodCall(x, ..) => x.pos, - Self::Stmt(x) => x.position(), } } diff --git a/src/ast/flags.rs b/src/ast/flags.rs index e26fb55c..452a57e6 100644 --- a/src/ast/flags.rs +++ b/src/ast/flags.rs @@ -18,7 +18,7 @@ pub enum FnAccess { impl FnAccess { /// Is this function private? - #[inline] + #[inline(always)] #[must_use] pub const fn is_private(self) -> bool { match self { @@ -27,7 +27,7 @@ impl FnAccess { } } /// Is this function public? - #[inline] + #[inline(always)] #[must_use] pub const fn is_public(self) -> bool { match self { diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index dbf40e3a..07c1f676 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -269,7 +269,7 @@ impl Engine { if op_info.is_op_assignment() { let args = &mut [target.as_mut()]; let (mut orig_val, ..) = self - .call_native_fn( + .exec_native_fn_call( global, caches, lib, getter, *hash_get, args, is_ref_mut, false, *pos, level, ) @@ -303,7 +303,7 @@ impl Engine { } let args = &mut [target.as_mut(), &mut new_val]; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, setter, *hash_set, args, is_ref_mut, false, *pos, level, ) @@ -330,7 +330,7 @@ impl Engine { let ((getter, hash_get), _, name) = &**x; let args = &mut [target.as_mut()]; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, getter, *hash_get, args, is_ref_mut, false, *pos, level, ) @@ -429,7 +429,7 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self - .call_native_fn( + .exec_native_fn_call( global, caches, lib, getter, *hash_get, args, is_ref_mut, false, pos, level, ) @@ -465,7 +465,7 @@ impl Engine { // Re-use args because the first &mut parameter will not be consumed let mut arg_values = [target.as_mut(), val.as_mut()]; let args = &mut arg_values; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, setter, *hash_set, args, is_ref_mut, false, pos, level, ) @@ -764,7 +764,7 @@ impl Engine { let pos = Position::NONE; let level = level + 1; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, fn_name, hash, args, true, false, pos, level, ) .map(|(r, ..)| r) @@ -789,7 +789,7 @@ impl Engine { let pos = Position::NONE; let level = level + 1; - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, ) } diff --git a/src/eval/debugger.rs b/src/eval/debugger.rs index f87e4a81..2ddf0991 100644 --- a/src/eval/debugger.rs +++ b/src/eval/debugger.rs @@ -430,7 +430,7 @@ impl Engine { } /// Run the debugger callback if there is a debugging interface registered. /// - /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or + /// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or /// function call. /// /// It is up to the [`Engine`] to reactivate the debugger. @@ -452,7 +452,7 @@ impl Engine { } /// Run the debugger callback. /// - /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or + /// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or /// function call. /// /// It is up to the [`Engine`] to reactivate the debugger. @@ -498,7 +498,7 @@ impl Engine { } /// Run the debugger callback unconditionally. /// - /// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or + /// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or /// function call. /// /// It is up to the [`Engine`] to reactivate the debugger. diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 17c2d82d..521cb036 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -1,9 +1,8 @@ //! Module defining functions for evaluating an expression. use super::{Caches, EvalContext, GlobalRuntimeState, Target}; -use crate::ast::{Expr, FnCallExpr, OpAssignment}; +use crate::ast::{Expr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; -use crate::func::get_builtin_binary_op_fn; use crate::types::dynamic::AccessMode; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; use std::num::NonZeroUsize; @@ -206,89 +205,6 @@ impl Engine { Ok((val.into(), var_pos)) } - /// Evaluate a function call expression. - pub(crate) fn eval_fn_call_expr( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - caches: &mut Caches, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &FnCallExpr, - pos: Position, - level: usize, - ) -> RhaiResult { - let FnCallExpr { - name, - hashes, - args, - operator_token, - .. - } = expr; - - // Short-circuit native binary operator call if under Fast Operators mode - if operator_token.is_some() && self.fast_operators() && args.len() == 2 { - let mut lhs = self - .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? - .0 - .flatten(); - - let mut rhs = self - .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? - .0 - .flatten(); - - let operands = &mut [&mut lhs, &mut rhs]; - - if let Some(func) = - get_builtin_binary_op_fn(operator_token.as_ref().unwrap(), operands[0], operands[1]) - { - // Built-in found - let context = (self, name.as_str(), None, &*global, lib, pos, level + 1).into(); - return func(context, operands); - } - - return self - .exec_fn_call( - None, global, caches, lib, name, *hashes, operands, false, false, pos, level, - ) - .map(|(v, ..)| v); - } - - #[cfg(not(feature = "no_module"))] - if !expr.namespace.is_empty() { - // Qualified function call - let hash = hashes.native(); - let namespace = &expr.namespace; - - return self.make_qualified_function_call( - scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level, - ); - } - - // Normal function call - let (first_arg, args) = args.split_first().map_or_else( - || (None, args.as_ref()), - |(first, rest)| (Some(first), rest), - ); - - self.make_function_call( - scope, - global, - caches, - lib, - this_ptr, - name, - first_arg, - args, - *hashes, - expr.capture_parent_scope, - expr.operator_token.as_ref(), - pos, - level, - ) - } - /// Evaluate an expression. // // # Implementation Notes @@ -312,7 +228,7 @@ impl Engine { // Function calls should account for a relatively larger portion of expressions because // binary operators are also function calls. - if let Expr::FnCall(x, ..) = expr { + if let Expr::FnCall(x, pos) = expr { #[cfg(feature = "debugging")] let reset_debugger = self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?; @@ -320,7 +236,7 @@ impl Engine { self.track_operation(global, expr.position())?; let result = - self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); + self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, *pos, level); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 0a302ee6..835b7b91 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -153,7 +153,7 @@ impl Engine { let op_assign = op_assign.literal_syntax(); let op = op.literal_syntax(); - match self.call_native_fn( + match self.exec_native_fn_call( global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, ) { Ok(_) => (), @@ -161,7 +161,7 @@ impl Engine { { // Expand to `var = var op rhs` *args[0] = self - .call_native_fn( + .exec_native_fn_call( global, caches, lib, op, *hash_op, args, true, false, *op_pos, level, ) .map_err(|err| err.fill_position(op_info.pos))? @@ -207,11 +207,11 @@ impl Engine { // Popular branches are lifted out of the `match` statement into their own branches. // Function calls should account for a relatively larger portion of statements. - if let Stmt::FnCall(x, ..) = stmt { + if let Stmt::FnCall(x, pos) = stmt { self.track_operation(global, stmt.position())?; let result = - self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level); + self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, *pos, level); #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); @@ -1006,4 +1006,28 @@ impl Engine { result } + + /// Evaluate a list of statements with no `this` pointer. + /// This is commonly used to evaluate a list of statements in an [`AST`][crate::AST] or a script function body. + #[inline] + pub(crate) fn eval_global_statements( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + caches: &mut Caches, + statements: &[Stmt], + lib: &[&Module], + level: usize, + ) -> RhaiResult { + self.eval_stmt_block( + scope, global, caches, lib, &mut None, statements, false, level, + ) + .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 387fd694..b757dda3 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1,9 +1,8 @@ //! Implement function-calling mechanism for [`Engine`]. -use super::callable_function::CallableFunction; -use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; +use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn, CallableFunction}; use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS; -use crate::ast::{Expr, FnCallHashes, Stmt}; +use crate::ast::{Expr, FnCallExpr, FnCallHashes}; use crate::engine::{ KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, @@ -108,17 +107,14 @@ impl Drop for ArgBackup<'_> { } } +// Ensure no data races in function call arguments. #[cfg(not(feature = "no_closure"))] #[inline] -pub fn ensure_no_data_race( - fn_name: &str, - args: &FnCallArgs, - is_method_call: bool, -) -> RhaiResultOf<()> { +pub fn ensure_no_data_race(fn_name: &str, args: &FnCallArgs, is_ref_mut: bool) -> RhaiResultOf<()> { if let Some((n, ..)) = args .iter() .enumerate() - .skip(if is_method_call { 1 } else { 0 }) + .skip(if is_ref_mut { 1 } else { 0 }) .find(|(.., a)| a.is_locked()) { return Err(ERR::ErrorDataRace( @@ -134,7 +130,7 @@ pub fn ensure_no_data_race( /// Generate the signature for a function call. #[inline] #[must_use] -pub fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynamic]) -> String { +fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynamic]) -> String { format!( "{fn_name} ({})", args.iter() @@ -154,7 +150,7 @@ pub fn gen_fn_call_signature(engine: &Engine, fn_name: &str, args: &[&mut Dynami #[cfg(not(feature = "no_module"))] #[inline] #[must_use] -pub fn gen_qualified_fn_call_signature( +fn gen_qualified_fn_call_signature( engine: &Engine, namespace: &crate::ast::Namespace, fn_name: &str, @@ -343,7 +339,7 @@ impl Engine { /// /// **DO NOT** reuse the argument values unless for the first `&mut` argument - /// all others are silently replaced by `()`! - pub(crate) fn call_native_fn( + pub(crate) fn exec_native_fn_call( &self, global: &mut GlobalRuntimeState, caches: &mut Caches, @@ -716,41 +712,17 @@ impl Engine { // Restore the original source global.source = orig_source; - return Ok((result?, false)); + return result.map(|r| (r, false)); } } // Native function call let hash = hashes.native(); - self.call_native_fn( + self.exec_native_fn_call( global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, ) } - /// Evaluate a list of statements with no `this` pointer. - /// This is commonly used to evaluate a list of statements in an [`AST`][crate::AST] or a script function body. - #[inline] - pub(crate) fn eval_global_statements( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - caches: &mut Caches, - statements: &[Stmt], - lib: &[&Module], - level: usize, - ) -> RhaiResult { - self.eval_stmt_block( - scope, global, caches, lib, &mut None, statements, false, level, - ) - .or_else(|err| match *err { - ERR::Return(out, ..) => Ok(out), - ERR::LoopBreak(..) => { - unreachable!("no outer loop scope to break out of") - } - _ => Err(err), - }) - } - /// Evaluate an argument. #[inline] pub(crate) fn get_arg_value( @@ -763,13 +735,14 @@ impl Engine { arg_expr: &Expr, level: usize, ) -> RhaiResultOf<(Dynamic, Position)> { - #[cfg(feature = "debugging")] - if self.debugger.is_some() { - if let Some(value) = arg_expr.get_literal_value() { - #[cfg(feature = "debugging")] - self.run_debugger(scope, global, lib, this_ptr, arg_expr, level)?; - return Ok((value, arg_expr.start_position())); - } + // Literal values + if let Some(value) = arg_expr.get_literal_value() { + self.track_operation(global, arg_expr.start_position())?; + + #[cfg(feature = "debugging")] + self.run_debugger(scope, global, lib, this_ptr, arg_expr, level)?; + + return Ok((value, arg_expr.start_position())); } // Do not match function exit for arguments @@ -784,7 +757,7 @@ impl Engine { #[cfg(feature = "debugging")] global.debugger.reset_status(reset_debugger); - Ok((result?, arg_expr.start_position())) + result.map(|r| (r, arg_expr.start_position())) } /// Call a dot method. @@ -1022,8 +995,8 @@ impl Engine { first_arg: Option<&Expr>, args_expr: &[Expr], hashes: FnCallHashes, + is_operator_call: bool, capture_scope: bool, - operator_token: Option<&Token>, pos: Position, level: usize, ) -> RhaiResult { @@ -1036,7 +1009,7 @@ impl Engine { let redirected; // Handle call() - Redirect function call match name { - _ if operator_token.is_some() => (), + _ if is_operator_call => (), // Handle call() KEYWORD_FN_PTR_CALL if total_args >= 1 => { @@ -1520,4 +1493,87 @@ impl Engine { // Evaluate the AST self.eval_global_statements(scope, global, caches, statements, lib, level) } + + /// Evaluate a function call expression. + pub(crate) fn eval_fn_call_expr( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + caches: &mut Caches, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &FnCallExpr, + pos: Position, + level: usize, + ) -> RhaiResult { + let FnCallExpr { + name, + hashes, + args, + op_token, + .. + } = expr; + + // 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(scope, global, caches, lib, this_ptr, &args[0], level)? + .0 + .flatten(); + + let mut rhs = self + .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? + .0 + .flatten(); + + let operands = &mut [&mut lhs, &mut rhs]; + + if let Some(func) = + 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(); + return func(context, operands); + } + + return self + .exec_fn_call( + None, global, caches, lib, name, *hashes, operands, false, false, pos, level, + ) + .map(|(v, ..)| v); + } + + #[cfg(not(feature = "no_module"))] + if !expr.namespace.is_empty() { + // Qualified function call + let hash = hashes.native(); + let namespace = &expr.namespace; + + return self.make_qualified_function_call( + scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level, + ); + } + + // Normal function call + let (first_arg, args) = args.split_first().map_or_else( + || (None, args.as_ref()), + |(first, rest)| (Some(first), rest), + ); + + self.make_function_call( + scope, + global, + caches, + lib, + this_ptr, + name, + first_arg, + args, + *hashes, + expr.op_token.is_some(), + expr.capture_parent_scope, + pos, + level, + ) + } } diff --git a/src/func/mod.rs b/src/func/mod.rs index 8298a63e..42ccb91d 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -14,8 +14,7 @@ pub mod script; pub use args::FuncArgs; pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn}; #[cfg(not(feature = "no_module"))] -pub use call::gen_qualified_fn_call_signature; -pub use call::{gen_fn_call_signature, FnCallArgs}; +pub use call::{ensure_no_data_race, FnCallArgs}; pub use callable_function::CallableFunction; #[cfg(not(feature = "no_function"))] pub use func::Func; diff --git a/src/func/native.rs b/src/func/native.rs index a8eec976..8378b075 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -382,7 +382,7 @@ impl<'a> NativeCallContext<'a> { if native_only { return self .engine() - .call_native_fn( + .exec_native_fn_call( global, caches, self.lib, diff --git a/src/optimizer.rs b/src/optimizer.rs index 01717a0a..f0424ed0 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -147,7 +147,7 @@ impl<'a> OptimizerState<'a> { let lib = &[]; self.engine - .call_native_fn( + .exec_native_fn_call( &mut self.global, &mut self.caches, lib, @@ -444,9 +444,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b ) => { match x.1.rhs { - Expr::FnCall(ref mut x2, ..) => { + Expr::FnCall(ref mut x2, pos) => { state.set_dirty(); - x.0 = OpAssignment::new_op_assignment_from_base(&x2.name, x2.pos); + x.0 = OpAssignment::new_op_assignment_from_base(&x2.name, pos); x.1.rhs = mem::take(&mut x2.args[1]); } ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr), @@ -1142,8 +1142,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && x.operator_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => { - if let Some(result) = get_builtin_binary_op_fn(x.operator_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) + _ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => { + if let Some(result) = get_builtin_binary_op_fn(x.op_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] let lib = state.lib; diff --git a/src/parser.rs b/src/parser.rs index 0eb767dd..31391363 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -174,7 +174,7 @@ impl<'e> ParseState<'e> { /// /// # Return value: `(index, is_func_name)` /// - /// * `index`: `None` when the variable name is not found in the `stack`, + /// * `index`: [`None`] when the variable name is not found in the `stack`, /// otherwise the index value. /// /// * `is_func_name`: `true` if the variable is actually the name of a function @@ -223,7 +223,7 @@ impl<'e> ParseState<'e> { /// Returns the offset to be deducted from `Stack::len`, /// i.e. the top element of the [`ParseState`] is offset 1. /// - /// Returns `None` when the variable name is not found in the [`ParseState`]. + /// Returns [`None`] when the variable name is not found in the [`ParseState`]. /// /// # Panics /// @@ -610,12 +610,11 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - operator_token: None, + op_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, args, - pos: settings.pos, } .into_fn_call_expr(settings.pos)); } @@ -678,12 +677,11 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - operator_token: None, + op_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, args, - pos: settings.pos, } .into_fn_call_expr(settings.pos)); } @@ -1933,8 +1931,7 @@ impl Engine { name: state.get_interned_string("-"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), args, - pos, - operator_token: Some(token), + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -1963,8 +1960,7 @@ impl Engine { name: state.get_interned_string("+"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), args, - pos, - operator_token: Some(token), + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -1986,8 +1982,7 @@ impl Engine { name: state.get_interned_string("!"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), args, - pos, - operator_token: Some(token), + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -2376,25 +2371,24 @@ impl Engine { Some(op_token.clone()) }; - let op_base = FnCallExpr { - #[cfg(not(feature = "no_module"))] - namespace: Default::default(), - name: state.get_interned_string(op.as_ref()), - hashes: FnCallHashes::from_native(hash), - args: StaticVec::new_const(), - pos, - operator_token, - capture_parent_scope: false, - }; - let mut args = StaticVec::new_const(); args.push(root); args.push(rhs); args.shrink_to_fit(); + let mut op_base = FnCallExpr { + #[cfg(not(feature = "no_module"))] + namespace: Default::default(), + name: state.get_interned_string(op.as_ref()), + hashes: FnCallHashes::from_native(hash), + args, + op_token: operator_token, + capture_parent_scope: false, + }; + root = match op_token { // '!=' defaults to true when passed invalid operands - Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), + Token::NotEqualsTo => op_base.into_fn_call_expr(pos), // Comparison operators default to false when passed invalid operands Token::EqualsTo @@ -2402,61 +2396,36 @@ impl Engine { | Token::LessThanEqualsTo | Token::GreaterThan | Token::GreaterThanEqualsTo => { - let pos = args[0].start_position(); - FnCallExpr { args, ..op_base }.into_fn_call_expr(pos) + let pos = op_base.args[0].start_position(); + op_base.into_fn_call_expr(pos) } Token::Or => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); - Expr::Or( - BinaryExpr { - lhs: current_lhs.ensure_bool_expr()?, - rhs: rhs.ensure_bool_expr()?, - } - .into(), - pos, - ) + let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + Expr::Or(BinaryExpr { lhs: lhs, rhs: rhs }.into(), pos) } Token::And => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); - Expr::And( - BinaryExpr { - lhs: current_lhs.ensure_bool_expr()?, - rhs: rhs.ensure_bool_expr()?, - } - .into(), - pos, - ) + let rhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + let lhs = op_base.args.pop().unwrap().ensure_bool_expr()?; + Expr::And(BinaryExpr { lhs: lhs, rhs: rhs }.into(), pos) } Token::DoubleQuestion => { - let rhs = args.pop().unwrap(); - let current_lhs = args.pop().unwrap(); - Expr::Coalesce( - BinaryExpr { - lhs: current_lhs, - rhs, - } - .into(), - pos, - ) + let rhs = op_base.args.pop().unwrap(); + let lhs = op_base.args.pop().unwrap(); + Expr::Coalesce(BinaryExpr { lhs, rhs }.into(), pos) } Token::In => { // Swap the arguments - let current_lhs = args.remove(0); - let pos = current_lhs.start_position(); - args.push(current_lhs); - args.shrink_to_fit(); + let lhs = op_base.args.remove(0); + let pos = lhs.start_position(); + op_base.args.push(lhs); + op_base.args.shrink_to_fit(); // Convert into a call to `contains` - FnCallExpr { - hashes: calc_fn_hash(None, OP_CONTAINS, 2).into(), - args, - name: state.get_interned_string(OP_CONTAINS), - ..op_base - } - .into_fn_call_expr(pos) + op_base.hashes = calc_fn_hash(None, OP_CONTAINS, 2).into(); + op_base.name = state.get_interned_string(OP_CONTAINS); + op_base.into_fn_call_expr(pos) } #[cfg(not(feature = "no_custom_syntax"))] @@ -2466,24 +2435,17 @@ impl Engine { .get(s.as_str()) .map_or(false, Option::is_some) => { - let hash = calc_fn_hash(None, &s, 2); - let pos = args[0].start_position(); - - FnCallExpr { - hashes: if is_valid_script_function { - hash.into() - } else { - FnCallHashes::from_native(hash) - }, - args, - ..op_base - } - .into_fn_call_expr(pos) + op_base.hashes = if is_valid_script_function { + calc_fn_hash(None, &s, 2).into() + } else { + FnCallHashes::from_native(calc_fn_hash(None, &s, 2)) + }; + op_base.into_fn_call_expr(pos) } _ => { - let pos = args[0].start_position(); - FnCallExpr { args, ..op_base }.into_fn_call_expr(pos) + let pos = op_base.args[0].start_position(); + op_base.into_fn_call_expr(pos) } }; } @@ -3725,8 +3687,7 @@ impl Engine { num_externals + 1, )), args, - pos, - operator_token: None, + op_token: None, capture_parent_scope: false, } .into_fn_call_expr(pos);