Fix bug with wrong method call hash.

This commit is contained in:
Stephen Chung 2020-05-12 10:20:29 +08:00
parent 33c9be7efc
commit 2e28967565
3 changed files with 47 additions and 17 deletions

View File

@ -767,6 +767,7 @@ fn parse_call_expr<'a>(
// id(...args) // id(...args)
(Token::RightParen, _) => { (Token::RightParen, _) => {
eat_token(input, Token::RightParen); eat_token(input, Token::RightParen);
let args_iter = repeat(EMPTY_TYPE_ID()).take(args.len());
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let hash_fn_def = { 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 // 2) Calculate a second hash with no qualifiers, empty function name, and
// the actual list of parameter `TypeId`'.s // the actual list of parameter `TypeId`'.s
// 3) The final hash is the XOR of the two hashes. // 3) The final hash is the XOR of the two hashes.
calc_fn_hash( calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, args_iter)
modules.iter().map(|(m, _)| m.as_str()),
&id,
repeat(EMPTY_TYPE_ID()).take(args.len()),
)
} else { } 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). // Qualifiers (none) + function name + dummy parameter types (one for each parameter).
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
let hash_fn_def = let hash_fn_def = calc_fn_hash(empty(), &id, args_iter);
calc_fn_hash(empty(), &id, repeat(EMPTY_TYPE_ID()).take(args.len()));
return Ok(Expr::FnCall(Box::new(( return Ok(Expr::FnCall(Box::new((
(id.into(), begin), (id.into(), begin),
@ -1204,10 +1200,7 @@ fn parse_primary<'a>(
let ((name, pos), modules, _, _) = *x; let ((name, pos), modules, _, _) = *x;
parse_call_expr(input, stack, name, modules, pos, allow_stmt_expr)? parse_call_expr(input, stack, name, modules, pos, allow_stmt_expr)?
} }
(Expr::Property(x), Token::LeftParen) => { (Expr::Property(_), _) => unreachable!(),
let (name, pos) = *x;
parse_call_expr(input, stack, name, None, pos, allow_stmt_expr)?
}
// module access // module access
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() { (Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() {
@ -1742,8 +1735,20 @@ fn parse_binary_op<'a>(
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Token::Period => { Token::Period => {
let rhs = args.pop().unwrap(); let mut rhs = args.pop().unwrap();
let current_lhs = 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)? make_dot_expr(current_lhs, rhs, pos, false)?
} }

25
tests/functions.rs Normal file
View File

@ -0,0 +1,25 @@
#![cfg(not(feature = "no_function"))]
use rhai::{Engine, EvalAltResult, Func, ParseErrorType, Scope, INT};
#[test]
fn test_functions() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new();
assert_eq!(engine.eval::<i64>("fn add(x, n) { x + n } add(40, 2)")?, 42);
#[cfg(not(feature = "no_object"))]
assert_eq!(
engine.eval::<i64>("fn add(x, n) { x + n } let x = 40; x.add(2)")?,
42
);
assert_eq!(engine.eval::<i64>("fn mul2(x) { x * 2 } mul2(21)")?, 42);
#[cfg(not(feature = "no_object"))]
assert_eq!(
engine.eval::<i64>("fn mul2(x) { x * 2 } let x = 21; x.mul2()")?,
42
);
Ok(())
}

View File

@ -10,8 +10,8 @@ fn test_method_call() -> Result<(), Box<EvalAltResult>> {
} }
impl TestStruct { impl TestStruct {
fn update(&mut self) { fn update(&mut self, n: INT) {
self.x += 1000; self.x += n;
} }
fn new() -> Self { fn new() -> Self {
@ -27,12 +27,12 @@ fn test_method_call() -> Result<(), Box<EvalAltResult>> {
engine.register_fn("new_ts", TestStruct::new); engine.register_fn("new_ts", TestStruct::new);
assert_eq!( assert_eq!(
engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?, engine.eval::<TestStruct>("let x = new_ts(); x.update(1000); x")?,
TestStruct { x: 1001 } TestStruct { x: 1001 }
); );
assert_eq!( assert_eq!(
engine.eval::<TestStruct>("let x = new_ts(); update(x); x")?, engine.eval::<TestStruct>("let x = new_ts(); update(x, 1000); x")?,
TestStruct { x: 1 } TestStruct { x: 1 }
); );