From 2e2896756530b92092a8db9139dca5b0d1b3d3d2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 12 May 2020 10:20:29 +0800 Subject: [PATCH] Fix bug with wrong method call hash. --- src/parser.rs | 31 ++++++++++++++++++------------- tests/functions.rs | 25 +++++++++++++++++++++++++ tests/method_call.rs | 8 ++++---- 3 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 tests/functions.rs diff --git a/src/parser.rs b/src/parser.rs index 484616ad..da35d62d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -767,6 +767,7 @@ fn parse_call_expr<'a>( // id(...args) (Token::RightParen, _) => { eat_token(input, Token::RightParen); + let args_iter = repeat(EMPTY_TYPE_ID()).take(args.len()); #[cfg(not(feature = "no_module"))] let hash_fn_def = { @@ -779,19 +780,14 @@ fn parse_call_expr<'a>( // 2) Calculate a second hash with no qualifiers, empty function name, and // the actual list of parameter `TypeId`'.s // 3) The final hash is the XOR of the two hashes. - calc_fn_hash( - modules.iter().map(|(m, _)| m.as_str()), - &id, - repeat(EMPTY_TYPE_ID()).take(args.len()), - ) + calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, args_iter) } else { - calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len())) + calc_fn_hash(empty(), &id, args_iter) } }; // Qualifiers (none) + function name + dummy parameter types (one for each parameter). #[cfg(feature = "no_module")] - let hash_fn_def = - calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len())); + let hash_fn_def = calc_fn_hash(empty(), &id, args_iter); return Ok(Expr::FnCall(Box::new(( (id.into(), begin), @@ -1204,10 +1200,7 @@ fn parse_primary<'a>( let ((name, pos), modules, _, _) = *x; parse_call_expr(input, stack, name, modules, pos, allow_stmt_expr)? } - (Expr::Property(x), Token::LeftParen) => { - let (name, pos) = *x; - parse_call_expr(input, stack, name, None, pos, allow_stmt_expr)? - } + (Expr::Property(_), _) => unreachable!(), // module access #[cfg(not(feature = "no_module"))] (Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() { @@ -1742,8 +1735,20 @@ fn parse_binary_op<'a>( #[cfg(not(feature = "no_object"))] Token::Period => { - let rhs = args.pop().unwrap(); + let mut rhs = args.pop().unwrap(); let current_lhs = args.pop().unwrap(); + + match &mut rhs { + // current_lhs.rhs(...) - method call + Expr::FnCall(x) => { + let ((id, _), _, hash, args, _) = x.as_mut(); + // Recalculate function call hash because there is an additional argument + let args_iter = repeat(EMPTY_TYPE_ID()).take(args.len() + 1); + *hash = calc_fn_hash(empty(), id, args_iter); + } + _ => (), + } + make_dot_expr(current_lhs, rhs, pos, false)? } diff --git a/tests/functions.rs b/tests/functions.rs new file mode 100644 index 00000000..8200962b --- /dev/null +++ b/tests/functions.rs @@ -0,0 +1,25 @@ +#![cfg(not(feature = "no_function"))] +use rhai::{Engine, EvalAltResult, Func, ParseErrorType, Scope, INT}; + +#[test] +fn test_functions() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!(engine.eval::("fn add(x, n) { x + n } add(40, 2)")?, 42); + + #[cfg(not(feature = "no_object"))] + assert_eq!( + engine.eval::("fn add(x, n) { x + n } let x = 40; x.add(2)")?, + 42 + ); + + assert_eq!(engine.eval::("fn mul2(x) { x * 2 } mul2(21)")?, 42); + + #[cfg(not(feature = "no_object"))] + assert_eq!( + engine.eval::("fn mul2(x) { x * 2 } let x = 21; x.mul2()")?, + 42 + ); + + Ok(()) +} diff --git a/tests/method_call.rs b/tests/method_call.rs index 59d43b3d..59857102 100644 --- a/tests/method_call.rs +++ b/tests/method_call.rs @@ -10,8 +10,8 @@ fn test_method_call() -> Result<(), Box> { } impl TestStruct { - fn update(&mut self) { - self.x += 1000; + fn update(&mut self, n: INT) { + self.x += n; } fn new() -> Self { @@ -27,12 +27,12 @@ fn test_method_call() -> Result<(), Box> { engine.register_fn("new_ts", TestStruct::new); assert_eq!( - engine.eval::("let x = new_ts(); x.update(); x")?, + engine.eval::("let x = new_ts(); x.update(1000); x")?, TestStruct { x: 1001 } ); assert_eq!( - engine.eval::("let x = new_ts(); update(x); x")?, + engine.eval::("let x = new_ts(); update(x, 1000); x")?, TestStruct { x: 1 } );