diff --git a/CHANGELOG.md b/CHANGELOG.md index 136c1fd9..532c29e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ Version 1.14.0 The code hacks that attempt to optimize branch prediction performance are removed because benchmarks do not show any material speed improvements. +Buf fixes +---------- + +* `is_shared` is a reserved keyword and is now handled properly (e.g. it cannot be the target of a function pointer). + New features ------------ diff --git a/src/func/call.rs b/src/func/call.rs index 52400b3b..38eb7cca 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -573,58 +573,47 @@ impl Engine { _is_method_call: bool, pos: Position, ) -> RhaiResultOf<(Dynamic, bool)> { + // These may be redirected from method style calls. + if hashes.is_native_only() { + let error = match fn_name { + // Handle type_of() + KEYWORD_TYPE_OF => { + if args.len() == 1 { + let typ = self.get_interned_string(self.map_type_name(args[0].type_name())); + return Ok((typ.into(), false)); + } + true + } + + #[cfg(not(feature = "no_closure"))] + crate::engine::KEYWORD_IS_SHARED => { + if args.len() == 1 { + return Ok((args[0].is_shared().into(), false)); + } + true + } + + #[cfg(not(feature = "no_function"))] + crate::engine::KEYWORD_IS_DEF_FN => true, + + KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL + | KEYWORD_FN_PTR_CURRY => true, + + _ => false, + }; + + if error { + let sig = self.gen_fn_call_signature(fn_name, args); + return Err(ERR::ErrorFunctionNotFound(sig, pos).into()); + } + } + // Check for data race. #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, args, is_ref_mut)?; defer! { let orig_level = global.level; global.level += 1 } - // These may be redirected from method style calls. - if hashes.is_native_only() { - match fn_name { - // Handle type_of() - KEYWORD_TYPE_OF if args.len() == 1 => { - let typ = self.get_interned_string(self.map_type_name(args[0].type_name())); - return Ok((typ.into(), false)); - } - - // Handle is_def_fn() - #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN - if args.len() == 2 && args[0].is_fnptr() && args[1].is_int() => - { - let fn_name = args[0].read_lock::().expect("`FnPtr`"); - let num_params = args[1].as_int().expect("`INT`"); - - return Ok(( - if (0..=crate::MAX_USIZE_INT).contains(&num_params) { - #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] - let hash_script = - calc_fn_hash(None, fn_name.as_str(), num_params as usize); - self.has_script_fn(global, caches, hash_script) - } else { - false - } - .into(), - false, - )); - } - - // Handle is_shared() - #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED => { - unreachable!("{} called as method", fn_name) - } - - KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL - | KEYWORD_FN_PTR_CURRY => { - unreachable!("{} called as method", fn_name) - } - - _ => (), - } - } - // Script-defined function call? #[cfg(not(feature = "no_function"))] if !hashes.is_native_only() { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 60d13808..da4676fb 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -514,117 +514,117 @@ static KEYWORDS_LIST: [(&str, Token); 153] = [ const MIN_RESERVED_LEN: usize = 1; const MAX_RESERVED_LEN: usize = 10; const MIN_RESERVED_HASH_VALUE: usize = 1; -const MAX_RESERVED_HASH_VALUE: usize = 112; +const MAX_RESERVED_HASH_VALUE: usize = 149; static RESERVED_ASSOC_VALUES: [u8; 256] = [ - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 35, 113, 45, 25, 113, - 113, 113, 60, 55, 50, 50, 113, 15, 0, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 10, 85, 45, 5, 55, 50, 5, 113, 113, 113, 113, 113, 85, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 35, 113, 113, 113, 55, 113, 10, 40, - 5, 0, 5, 35, 10, 5, 0, 113, 113, 20, 25, 5, 45, 0, 113, 0, 0, 0, 15, 30, 20, 25, 20, 113, 113, - 20, 113, 0, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, - 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 10, 150, 5, 35, 150, 150, + 150, 45, 35, 30, 30, 150, 20, 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 35, + 30, 15, 5, 25, 0, 25, 150, 150, 150, 150, 150, 65, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 40, 150, 150, 150, 150, 150, 0, 150, 0, + 0, 0, 15, 45, 10, 15, 150, 150, 35, 25, 10, 50, 0, 150, 5, 0, 15, 0, 5, 25, 45, 15, 150, 150, + 25, 150, 20, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, ]; -static RESERVED_LIST: [(&str, bool, bool, bool); 113] = [ +static RESERVED_LIST: [(&str, bool, bool, bool); 150] = [ ("", false, false, false), - ("~", true, false, false), - ("is", true, false, false), - ("...", true, false, false), - ("", false, false, false), - ("print", true, true, false), - ("@", true, false, false), - ("private", cfg!(feature = "no_function"), false, false), - ("", false, false, false), - ("this", true, false, false), - ("", false, false, false), - ("thread", true, false, false), + ("?", true, false, false), ("as", cfg!(feature = "no_module"), false, false), - ("", false, false, false), - ("", false, false, false), - ("spawn", true, false, false), - ("static", true, false, false), - (":=", true, false, false), - ("===", true, false, false), - ("case", true, false, false), - ("super", true, false, false), - ("shared", true, false, false), - ("package", true, false, false), ("use", true, false, false), - ("with", true, false, false), - ("curry", true, true, true), - ("$", true, false, false), - ("type_of", true, true, true), - ("nil", true, false, false), - ("sync", true, false, false), - ("yield", true, false, false), - ("import", cfg!(feature = "no_module"), false, false), - ("--", true, false, false), - ("new", true, false, false), - ("exit", true, false, false), + ("case", true, false, false), ("async", true, false, false), - ("export", cfg!(feature = "no_module"), false, false), - ("!.", true, false, false), + ("public", true, false, false), + ("package", true, false, false), ("", false, false, false), - ("call", true, true, true), - ("match", true, false, false), ("", false, false, false), - ("fn", cfg!(feature = "no_function"), false, false), - ("var", true, false, false), - ("null", true, false, false), - ("await", true, false, false), + ("super", true, false, false), ("#", true, false, false), + ("private", cfg!(feature = "no_function"), false, false), + ("var", true, false, false), + ("protected", true, false, false), + ("spawn", true, false, false), + ("shared", true, false, false), + ("is", true, false, false), + ("===", true, false, false), + ("sync", true, false, false), + ("curry", true, true, true), + ("static", true, false, false), ("default", true, false, false), ("!==", true, false, false), - ("eval", true, true, false), - ("debug", true, true, false), - ("?", true, false, false), + ("is_shared", cfg!(not(feature = "no_closure")), true, true), + ("print", true, true, false), + ("", false, false, false), + ("#!", true, false, false), + ("", false, false, false), + ("this", true, false, false), + ("is_def_var", true, true, false), + ("thread", true, false, false), ("?.", cfg!(feature = "no_object"), false, false), ("", false, false, false), - ("protected", true, false, false), + ("is_def_fn", cfg!(not(feature = "no_function")), true, false), + ("yield", true, false, false), + ("", false, false, false), + ("fn", cfg!(feature = "no_function"), false, false), + ("new", true, false, false), + ("call", true, true, true), + ("match", true, false, false), + ("~", true, false, false), + ("!.", true, false, false), + ("", false, false, false), + ("eval", true, true, false), + ("await", true, false, false), + ("", false, false, false), + (":=", true, false, false), + ("...", true, false, false), + ("null", true, false, false), + ("debug", true, true, false), + ("@", true, false, false), + ("type_of", true, true, true), + ("", false, false, false), + ("with", true, false, false), ("", false, false, false), ("", false, false, false), - ("go", true, false, false), - ("", false, false, false), - ("goto", true, false, false), - ("", false, false, false), - ("public", true, false, false), ("<-", true, false, false), ("", false, false, false), - ("is_def_fn", cfg!(not(feature = "no_function")), true, false), - ("is_def_var", true, true, false), + ("void", true, false, false), ("", false, false, false), + ("import", cfg!(feature = "no_module"), false, false), + ("--", true, false, false), + ("nil", true, false, false), + ("exit", true, false, false), + ("", false, false, false), + ("export", cfg!(feature = "no_module"), false, false), ("<|", true, false, false), - ("::<", true, false, false), ("", false, false, false), ("", false, false, false), ("", false, false, false), + ("$", true, false, false), ("->", true, false, false), ("", false, false, false), ("", false, false, false), ("", false, false, false), - ("module", true, false, false), + ("", false, false, false), ("|>", true, false, false), ("", false, false, false), - ("void", true, false, false), - ("", false, false, false), - ("", false, false, false), - ("#!", true, false, false), - ("", false, false, false), - ("", false, false, false), ("", false, false, false), ("", false, false, false), + ("module", true, false, false), ("?[", cfg!(feature = "no_index"), false, false), ("", false, false, false), ("", false, false, false), ("", false, false, false), ("", false, false, false), ("Fn", true, true, false), + ("::<", true, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("++", true, false, false), ("", false, false, false), ("", false, false, false), ("", false, false, false), @@ -634,17 +634,54 @@ static RESERVED_LIST: [(&str, bool, bool, bool); 113] = [ ("", false, false, false), ("", false, false, false), ("", false, false, false), - ("++", true, false, false), - ("", false, false, false), - ("", false, false, false), - ("", false, false, false), - ("", false, false, false), ("*)", true, false, false), ("", false, false, false), ("", false, false, false), ("", false, false, false), ("", false, false, false), ("(*", true, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("", false, false, false), + ("go", true, false, false), + ("", false, false, false), + ("goto", true, false, false), ]; impl Token { @@ -875,12 +912,13 @@ impl Token { // by GNU `gperf` on the list of keywords. let utf8 = syntax.as_bytes(); let len = utf8.len(); - let mut hash_val = len; if !(MIN_KEYWORD_LEN..=MAX_KEYWORD_LEN).contains(&len) { return None; } + let mut hash_val = len; + match len { 1 => (), _ => hash_val += KEYWORD_ASSOC_VALUES[(utf8[1] as usize) + 1] as usize, @@ -2306,8 +2344,10 @@ pub fn is_id_continue(x: char) -> bool { /// The first `bool` indicates whether it is a reserved keyword or symbol. /// /// The second `bool` indicates whether the keyword can be called normally as a function. +/// `false` if it is not a reserved keyword. /// /// The third `bool` indicates whether the keyword can be called in method-call style. +/// `false` if it is not a reserved keyword or it cannot be called as a function. #[inline] #[must_use] pub fn is_reserved_keyword_or_symbol(syntax: &str) -> (bool, bool, bool) { @@ -2315,16 +2355,19 @@ pub fn is_reserved_keyword_or_symbol(syntax: &str) -> (bool, bool, bool) { // by GNU `gperf` on the list of keywords. let utf8 = syntax.as_bytes(); let len = utf8.len(); - let rounds = len.min(3); - let mut hash_val = len; if !(MIN_RESERVED_LEN..=MAX_RESERVED_LEN).contains(&len) { return (false, false, false); } - for x in 0..rounds { - hash_val += RESERVED_ASSOC_VALUES[utf8[rounds - 1 - x] as usize] as usize; + let mut hash_val = len; + + match len { + 1 => (), + _ => hash_val += RESERVED_ASSOC_VALUES[(utf8[1] as usize)] as usize, } + hash_val += RESERVED_ASSOC_VALUES[utf8[0] as usize] as usize; + hash_val += RESERVED_ASSOC_VALUES[utf8[len - 1] as usize] as usize; if !(MIN_RESERVED_HASH_VALUE..=MAX_RESERVED_HASH_VALUE).contains(&hash_val) { return (false, false, false); @@ -2332,13 +2375,12 @@ pub fn is_reserved_keyword_or_symbol(syntax: &str) -> (bool, bool, bool) { match RESERVED_LIST[hash_val] { ("", ..) => (false, false, false), - (s, true, a, b) => ( + (s, true, a, b) => { // Fail early to avoid calling memcmp(). // Since we are already working with bytes, mind as well check the first one. - s.len() == len && s.as_bytes()[0] == utf8[0] && s == syntax, - a, - b, - ), + let is_reserved = s.len() == len && s.as_bytes()[0] == utf8[0] && s == syntax; + (is_reserved, is_reserved && a, is_reserved && a && b) + } _ => (false, false, false), } } diff --git a/tools/reserved.txt b/tools/reserved.txt index 2dbe79cd..4db0859a 100644 --- a/tools/reserved.txt +++ b/tools/reserved.txt @@ -91,3 +91,4 @@ struct reserved; "this", true, false, false "is_def_var", true, true, false "is_def_fn", cfg!(not(feature = "no_function")), true, false +"is_shared", cfg!(not(feature = "no_closure")), true, true