From 55922b5c20a8d44a6224d0585b3bb05f0e3d6135 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 3 Dec 2022 16:20:13 +0800 Subject: [PATCH] Hard code constant checking. --- src/api/register.rs | 13 +- src/ast/script_fn.rs | 2 +- src/eval/data_check.rs | 12 +- src/eval/expr.rs | 4 +- src/eval/stmt.rs | 8 +- src/func/builtin.rs | 545 +++++++++++++++++++--------------- src/func/call.rs | 69 +++-- src/func/callable_function.rs | 17 +- src/func/native.rs | 9 +- src/func/register.rs | 87 +++--- src/module/mod.rs | 53 ++-- src/optimizer.rs | 8 +- src/packages/array_basic.rs | 3 +- src/packages/blob_basic.rs | 3 +- src/packages/lang_core.rs | 2 +- src/parser.rs | 13 +- 16 files changed, 474 insertions(+), 374 deletions(-) diff --git a/src/api/register.rs b/src/api/register.rs index eeb50fda..259ca0d7 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -90,13 +90,24 @@ impl Engine { #[cfg(not(feature = "metadata"))] let param_type_names: Option<&[&str]> = None; + let fn_name = name.as_ref(); + let no_const = false; + + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET); + #[cfg(not(feature = "no_object"))] + let no_const = + no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET)); + + let func = func.into_callable_function(fn_name.into(), no_const); + self.global_namespace_mut().set_fn( name, FnNamespace::Global, FnAccess::Public, param_type_names, param_types, - func.into_callable_function(), + func, ); self } diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs index 7292563a..bf2398d0 100644 --- a/src/ast/script_fn.rs +++ b/src/ast/script_fn.rs @@ -56,7 +56,7 @@ pub struct ScriptFnDef { /// /// Each line in non-block doc-comments starts with `///`. #[cfg(feature = "metadata")] - pub comments: Box<[Box]>, + pub comments: Box<[crate::Identifier]>, } impl fmt::Display for ScriptFnDef { diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index fc1948fc..1e9e4a7f 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -74,10 +74,7 @@ impl Engine { /// /// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE] /// and should be set afterwards. - pub(crate) fn raise_err_if_over_data_size_limit( - &self, - (_arr, _map, s): (usize, usize, usize), - ) -> RhaiResultOf<()> { + pub(crate) fn throw_on_size(&self, (_arr, _map, s): (usize, usize, usize)) -> RhaiResultOf<()> { if self .limits .max_string_len @@ -127,9 +124,10 @@ impl Engine { let sizes = value.borrow().calc_data_sizes(true); - self.raise_err_if_over_data_size_limit(sizes) - .map(|_| value) - .map_err(|err| err.fill_position(pos)) + self.throw_on_size(sizes) + .map_err(|err| err.fill_position(pos))?; + + Ok(value) } /// Raise an error if the size of a [`Dynamic`] is out of limits (if any). diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 020bb9fb..6750c398 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -329,7 +329,7 @@ impl Engine { total_data_sizes.1 + val_sizes.1, total_data_sizes.2 + val_sizes.2, ); - self.raise_err_if_over_data_size_limit(total_data_sizes) + self.throw_on_size(total_data_sizes) .map_err(|err| err.fill_position(item_expr.position()))?; } @@ -360,7 +360,7 @@ impl Engine { total_data_sizes.1 + delta.1, total_data_sizes.2 + delta.2, ); - self.raise_err_if_over_data_size_limit(total_data_sizes) + self.throw_on_size(total_data_sizes) .map_err(|err| err.fill_position(value_expr.position()))?; } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 54d409df..867aff7d 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -142,7 +142,7 @@ impl Engine { let args = &mut [&mut *lock_guard, &mut new_val]; if self.fast_operators() { - if let Some(func) = + if let Some((func, ctx)) = get_builtin_op_assignment_fn(op_assign_token.clone(), args[0], args[1]) { // Built-in found @@ -152,7 +152,11 @@ impl Engine { global.level += 1; let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level); - let context = (self, op, None, global, *op_pos).into(); + let context = if ctx { + Some((self, op, None, global, *op_pos).into()) + } else { + None + }; return func(context, args).map(|_| ()); } } diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 5294882a..1608a3bb 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -71,12 +71,12 @@ fn is_numeric(type_id: TypeId) -> bool { /// A function that returns `true`. #[inline(always)] -fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { +fn const_true_fn(_: Option, _: &mut [&mut Dynamic]) -> RhaiResult { Ok(Dynamic::TRUE) } /// A function that returns `false`. #[inline(always)] -fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { +fn const_false_fn(_: Option, _: &mut [&mut Dynamic]) -> RhaiResult { Ok(Dynamic::FALSE) } @@ -89,55 +89,55 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option { |_, args| { + ($xx:ident $op:tt $yy:ident) => { (|_, args| { let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); Ok((x $op y).into()) - } }; - ($xx:ident . $func:ident ( $yy:ty )) => { |_, args| { + }, false) }; + ($xx:ident . $func:ident ( $yy:ty )) => { (|_, args| { let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); Ok(x.$func(y).into()) - } }; - ($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { |_, args| { + }, false) }; + ($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { (|_, args| { let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); Ok(x.$func(y.$yyy()).into()) - } }; - ($func:ident ( $op:tt )) => { |_, args| { + }, false) }; + ($func:ident ( $op:tt )) => { (|_, args| { let (x, y) = $func(args); Ok((x $op y).into()) - } }; - ($base:ty => $xx:ident $op:tt $yy:ident) => { |_, args| { + }, false) }; + ($base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| { let x = args[0].$xx().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base; Ok((x $op y).into()) - } }; - ($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { |_, args| { + }, false) }; + ($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { (|_, args| { let x = args[0].$xx().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base; Ok(x.$func(y as $yyy).into()) - } }; - ($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| { + }, false) }; + ($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { let x = args[0].$xx().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base; $func(x, y).map(Into::into) - } }; - (from $base:ty => $xx:ident $op:tt $yy:ident) => { |_, args| { + }, false) }; + (from $base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN)); Ok((x $op y).into()) - } }; - (from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { |_, args| { + }, false) }; + (from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN)); Ok(x.$func(y).into()) - } }; - (from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| { + }, false) }; + (from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN)); $func(x, y).map(Into::into) - } }; + }, false) }; } // Check for common patterns @@ -206,16 +206,20 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option() { return match op { - Plus => Some(|_ctx, args| { - let s1 = &*args[0].read_lock::().expect(BUILTIN); - let s2 = &*args[1].read_lock::().expect(BUILTIN); + Plus => Some(( + |_ctx, args| { + let s1 = &*args[0].read_lock::().expect(BUILTIN); + let s2 = &*args[1].read_lock::().expect(BUILTIN); - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((0, 0, s1.len() + s2.len()))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((0, 0, s1.len() + s2.len()))?; - Ok((s1 + s2).into()) - }), + Ok((s1 + s2).into()) + }, + cfg!(not(feature = "unchecked")), + )), Minus => Some(impl_op!(ImmutableString - ImmutableString)), EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), @@ -229,20 +233,22 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option() { return match op { - Plus => Some(|_ctx, args| { - let x = args[0].as_char().expect(BUILTIN); - let y = args[1].as_char().expect(BUILTIN); + Plus => Some(( + |_ctx, args| { + let x = args[0].as_char().expect(BUILTIN); + let y = args[1].as_char().expect(BUILTIN); - let mut result = SmartString::new_const(); - result.push(x); - result.push(y); + let mut result = SmartString::new_const(); + result.push(x); + result.push(y); - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?; - Ok(result.into()) - }), + Ok(result.into()) + }, + cfg!(not(feature = "unchecked")), + )), EqualsTo => Some(impl_op!(char => as_char == as_char)), NotEqualsTo => Some(impl_op!(char => as_char != as_char)), GreaterThan => Some(impl_op!(char => as_char > as_char)), @@ -258,24 +264,28 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(|_ctx, args| { - let b2 = &*args[1].read_lock::().expect(BUILTIN); - if b2.is_empty() { - return Ok(args[0].flatten_clone()); - } - let b1 = &*args[0].read_lock::().expect(BUILTIN); - if b1.is_empty() { - return Ok(args[1].flatten_clone()); - } + Plus => Some(( + |_ctx, args| { + let b2 = &*args[1].read_lock::().expect(BUILTIN); + if b2.is_empty() { + return Ok(args[0].flatten_clone()); + } + let b1 = &*args[0].read_lock::().expect(BUILTIN); + if b1.is_empty() { + return Ok(args[1].flatten_clone()); + } - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((b1.len() + b2.len(), 0, 0))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((b1.len() + b2.len(), 0, 0))?; - let mut blob = b1.clone(); - blob.extend(b2); - Ok(Dynamic::from_blob(blob)) - }), + let mut blob = b1.clone(); + blob.extend(b2); + Ok(Dynamic::from_blob(blob)) + }, + cfg!(not(feature = "unchecked")), + )), EqualsTo => Some(impl_op!(Blob == Blob)), NotEqualsTo => Some(impl_op!(Blob != Blob)), _ => None, @@ -284,9 +294,9 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option() { return match op { - EqualsTo => Some(const_true_fn), + EqualsTo => Some((const_true_fn, false)), NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { - Some(const_false_fn) + Some((const_false_fn, false)) } _ => None, }; @@ -388,20 +398,22 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(|_ctx, args| { - let x = args[0].as_char().expect(BUILTIN); - let y = &*args[1].read_lock::().expect(BUILTIN); + Plus => Some(( + |_ctx, args| { + let x = args[0].as_char().expect(BUILTIN); + let y = &*args[1].read_lock::().expect(BUILTIN); - let mut result = SmartString::new_const(); - result.push(x); - result.push_str(y); + let mut result = SmartString::new_const(); + result.push(x); + result.push_str(y); - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?; - Ok(result.into()) - }), + Ok(result.into()) + }, + cfg!(not(feature = "unchecked")), + )), EqualsTo => Some(impl_op!(get_s1s2(==))), NotEqualsTo => Some(impl_op!(get_s1s2(!=))), GreaterThan => Some(impl_op!(get_s1s2(>))), @@ -423,22 +435,27 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(|_ctx, args| { - let x = &*args[0].read_lock::().expect(BUILTIN); - let y = args[1].as_char().expect(BUILTIN); - let result = x + y; + Plus => Some(( + |_ctx, args| { + let x = &*args[0].read_lock::().expect(BUILTIN); + let y = args[1].as_char().expect(BUILTIN); + let result = x + y; - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap().engine().throw_on_size((0, 0, result.len()))?; - Ok(result.into()) - }), - Minus => Some(|_, args| { - let x = &*args[0].read_lock::().expect(BUILTIN); - let y = args[1].as_char().expect(BUILTIN); - Ok((x - y).into()) - }), + Ok(result.into()) + }, + cfg!(not(feature = "unchecked")), + )), + Minus => Some(( + |_, args| { + let x = &*args[0].read_lock::().expect(BUILTIN); + let y = args[1].as_char().expect(BUILTIN); + Ok((x - y).into()) + }, + false, + )), EqualsTo => Some(impl_op!(get_s1s2(==))), NotEqualsTo => Some(impl_op!(get_s1s2(!=))), GreaterThan => Some(impl_op!(get_s1s2(>))), @@ -451,22 +468,22 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option(), TypeId::of::()) { return match op { - Plus => Some(|_, args| Ok(args[1].clone())), + Plus => Some((|_, args| Ok(args[1].clone()), false)), EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { - Some(const_false_fn) + Some((const_false_fn, false)) } - NotEqualsTo => Some(const_true_fn), + NotEqualsTo => Some((const_true_fn, false)), _ => None, }; } // string op () if (type1, type2) == (TypeId::of::(), TypeId::of::<()>()) { return match op { - Plus => Some(|_, args| Ok(args[0].clone())), + Plus => Some((|_, args| Ok(args[0].clone()), false)), EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { - Some(const_false_fn) + Some((const_false_fn, false)) } - NotEqualsTo => Some(const_true_fn), + NotEqualsTo => Some((const_true_fn, false)), _ => None, }; } @@ -478,21 +495,22 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option() { return match op { - Plus => Some(|_ctx, args| { - let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); - let mut buf = [0_u8; 4]; - let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); + Plus => Some(( + |_ctx, args| { + let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); + let mut buf = [0_u8; 4]; + let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); - #[cfg(not(feature = "unchecked"))] - _ctx.engine().raise_err_if_over_data_size_limit(( - blob.len() + x.len(), - 0, - 0, - ))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((blob.len() + x.len(), 0, 0))?; - blob.extend(x.as_bytes()); - Ok(Dynamic::from_blob(blob)) - }), + blob.extend(x.as_bytes()); + Ok(Dynamic::from_blob(blob)) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } @@ -511,8 +529,8 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(const_true_fn), - Equals => Some(const_false_fn), + NotEqualsTo => Some((const_true_fn, false)), + Equals => Some((const_false_fn, false)), _ => None, }; } @@ -542,9 +560,9 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(const_true_fn), + NotEqualsTo => Some((const_true_fn, false)), EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { - Some(const_false_fn) + Some((const_false_fn, false)) } _ => None, } @@ -557,9 +575,9 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(const_true_fn), + NotEqualsTo => Some((const_true_fn, false)), EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => { - Some(const_false_fn) + Some((const_false_fn, false)) } _ => None, }; @@ -578,43 +596,43 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti let type2 = y.type_id(); macro_rules! impl_op { - ($x:ty = x $op:tt $yy:ident) => { |_, args| { + ($x:ty = x $op:tt $yy:ident) => { (|_, args| { let x = args[0].$yy().expect(BUILTIN); let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into()) - } }; - ($x:ident $op:tt $yy:ident) => { |_, args| { + }, false) }; + ($x:ident $op:tt $yy:ident) => { (|_, args| { let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) - } }; - ($x:ident $op:tt $yy:ident as $yyy:ty) => { |_, args| { + }, false) }; + ($x:ident $op:tt $yy:ident as $yyy:ty) => { (|_, args| { let y = args[1].$yy().expect(BUILTIN) as $yyy; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) - } }; - ($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { |_, args| { + }, false) }; + ($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { (|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into()) - } }; - ($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| { + }, false) }; + ($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = args[1].$yy().expect(BUILTIN) as $x; Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into()) - } }; - (from $x:ident $op:tt $yy:ident) => { |_, args| { + }, false) }; + (from $x:ident $op:tt $yy:ident) => { (|_, args| { let y = <$x>::from(args[1].$yy().expect(BUILTIN)); Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) - } }; - (from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { |_, args| { + }, false) }; + (from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = <$x>::from(args[1].$yy().expect(BUILTIN)); Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into()) - } }; - (from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { |_, args| { + }, false) }; + (from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { let x = args[0].$xx().expect(BUILTIN); let y = <$x>::from(args[1].$yy().expect(BUILTIN)); Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into()) - } }; + }, false) }; } // Check for common patterns @@ -668,42 +686,50 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti if type1 == TypeId::of::() { return match op { - PlusAssign => Some(|_, args| { - let y = args[1].as_char().expect(BUILTIN); - let x = &mut *args[0].write_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_, args| { + let y = args[1].as_char().expect(BUILTIN); + let x = &mut *args[0].write_lock::().expect(BUILTIN); - let mut buf = SmartString::new_const(); - write!(&mut buf, "{y}").unwrap(); - buf.push(y); + let mut buf = SmartString::new_const(); + write!(&mut buf, "{y}").unwrap(); + buf.push(y); - Ok((*x = buf.into()).into()) - }), + Ok((*x = buf.into()).into()) + }, + false, + )), _ => None, }; } if type1 == TypeId::of::() { return match op { - PlusAssign => Some(|_ctx, args| { - let (first, second) = args.split_first_mut().expect(BUILTIN); - let x = &mut *first.write_lock::().expect(BUILTIN); - let y = &*second[0].read_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let (first, second) = args.split_first_mut().expect(BUILTIN); + let x = &mut *first.write_lock::().expect(BUILTIN); + let y = &*second[0].read_lock::().expect(BUILTIN); - #[cfg(not(feature = "unchecked"))] - if !x.is_empty() && !y.is_empty() { - let total_len = x.len() + y.len(); - _ctx.engine() - .raise_err_if_over_data_size_limit((0, 0, total_len))?; - } + #[cfg(not(feature = "unchecked"))] + if !x.is_empty() && !y.is_empty() { + let total_len = x.len() + y.len(); + _ctx.unwrap().engine().throw_on_size((0, 0, total_len))?; + } - Ok((*x += y).into()) - }), - MinusAssign => Some(|_, args| { - let (first, second) = args.split_first_mut().expect(BUILTIN); - let x = &mut *first.write_lock::().expect(BUILTIN); - let y = &*second[0].read_lock::().expect(BUILTIN); - Ok((*x -= y).into()) - }), + Ok((*x += y).into()) + }, + cfg!(not(feature = "unchecked")), + )), + MinusAssign => Some(( + |_, args| { + let (first, second) = args.split_first_mut().expect(BUILTIN); + let x = &mut *first.write_lock::().expect(BUILTIN); + let y = &*second[0].read_lock::().expect(BUILTIN); + Ok((*x -= y).into()) + }, + false, + )), _ => None, }; } @@ -715,27 +741,31 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti use crate::Array; return match op { - PlusAssign => Some(|_ctx, args| { - let x = std::mem::take(args[1]).into_array().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let x = std::mem::take(args[1]).into_array().expect(BUILTIN); - if x.is_empty() { - return Ok(Dynamic::UNIT); - } + if x.is_empty() { + return Ok(Dynamic::UNIT); + } - let _array_is_empty = args[0].read_lock::().expect(BUILTIN).is_empty(); + let _array_is_empty = + args[0].read_lock::().expect(BUILTIN).is_empty(); - #[cfg(not(feature = "unchecked"))] - if !_array_is_empty { - _ctx.engine().check_data_size( - &*args[0].read_lock().expect(BUILTIN), - crate::Position::NONE, - )?; - } + #[cfg(not(feature = "unchecked"))] + if !_array_is_empty { + _ctx.unwrap().engine().check_data_size( + &*args[0].read_lock().expect(BUILTIN), + crate::Position::NONE, + )?; + } - let array = &mut *args[0].write_lock::().expect(BUILTIN); + let array = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(append(array, x).into()) - }), + Ok(append(array, x).into()) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } @@ -747,19 +777,20 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti use crate::Blob; return match op { - PlusAssign => Some(|_ctx, args| { - let blob2 = std::mem::take(args[1]).into_blob().expect(BUILTIN); - let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let blob2 = std::mem::take(args[1]).into_blob().expect(BUILTIN); + let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); - #[cfg(not(feature = "unchecked"))] - _ctx.engine().raise_err_if_over_data_size_limit(( - blob1.len() + blob2.len(), - 0, - 0, - ))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((blob1.len() + blob2.len(), 0, 0))?; - Ok(append(blob1, blob2).into()) - }), + Ok(append(blob1, blob2).into()) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } @@ -833,17 +864,21 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - PlusAssign => Some(|_ctx, args| { - let mut buf = [0_u8; 4]; - let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); - let mut x = args[0].write_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let mut buf = [0_u8; 4]; + let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf); + let mut x = args[0].write_lock::().expect(BUILTIN); - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((0, 0, x.len() + ch.len()))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((0, 0, x.len() + ch.len()))?; - Ok((*x += ch).into()) - }), + Ok((*x += ch).into()) + }, + cfg!(not(feature = "unchecked")), + )), MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; @@ -851,28 +886,32 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - PlusAssign => Some(|_ctx, args| { - let ch = { - let s = &*args[1].read_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let ch = { + let s = &*args[1].read_lock::().expect(BUILTIN); - if s.is_empty() { - return Ok(Dynamic::UNIT); - } + if s.is_empty() { + return Ok(Dynamic::UNIT); + } - let mut ch = args[0].as_char().expect(BUILTIN).to_string(); + let mut ch = args[0].as_char().expect(BUILTIN).to_string(); - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((0, 0, ch.len() + s.len()))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((0, 0, ch.len() + s.len()))?; - ch.push_str(s); - ch - }; + ch.push_str(s); + ch + }; - *args[0].write_lock::().expect(BUILTIN) = ch.into(); + *args[0].write_lock::().expect(BUILTIN) = ch.into(); - Ok(Dynamic::UNIT) - }), + Ok(Dynamic::UNIT) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } @@ -885,21 +924,24 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti use crate::Array; return match op { - PlusAssign => Some(|_ctx, args| { - { - let x = std::mem::take(args[1]); - let array = &mut *args[0].write_lock::().expect(BUILTIN); - push(array, x); - } + PlusAssign => Some(( + |_ctx, args| { + { + let x = std::mem::take(args[1]); + let array = &mut *args[0].write_lock::().expect(BUILTIN); + push(array, x); + } - #[cfg(not(feature = "unchecked"))] - _ctx.engine().check_data_size( - &*args[0].read_lock().expect(BUILTIN), - crate::Position::NONE, - )?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap().engine().check_data_size( + &*args[0].read_lock().expect(BUILTIN), + crate::Position::NONE, + )?; - Ok(Dynamic::UNIT) - }), + Ok(Dynamic::UNIT) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } @@ -914,16 +956,20 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti use crate::packages::blob_basic::blob_functions::*; return match op { - PlusAssign => Some(|_ctx, args| { - let x = args[1].as_int().expect(BUILTIN); - let blob = &mut *args[0].write_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let x = args[1].as_int().expect(BUILTIN); + let blob = &mut *args[0].write_lock::().expect(BUILTIN); - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((blob.len() + 1, 0, 0))?; - Ok(push(blob, x).into()) - }), + Ok(push(blob, x).into()) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } @@ -934,16 +980,20 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti use crate::packages::blob_basic::blob_functions::*; return match op { - PlusAssign => Some(|_ctx, args| { - let x = args[1].as_char().expect(BUILTIN); - let blob = &mut *args[0].write_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let x = args[1].as_char().expect(BUILTIN); + let blob = &mut *args[0].write_lock::().expect(BUILTIN); - #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((blob.len() + 1, 0, 0))?; - Ok(append_char(blob, x).into()) - }), + Ok(append_char(blob, x).into()) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } @@ -954,24 +1004,25 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti use crate::packages::blob_basic::blob_functions::*; return match op { - PlusAssign => Some(|_ctx, args| { - let (first, second) = args.split_first_mut().expect(BUILTIN); - let blob = &mut *first.write_lock::().expect(BUILTIN); - let s = &*second[0].read_lock::().expect(BUILTIN); + PlusAssign => Some(( + |_ctx, args| { + let (first, second) = args.split_first_mut().expect(BUILTIN); + let blob = &mut *first.write_lock::().expect(BUILTIN); + let s = &*second[0].read_lock::().expect(BUILTIN); - if s.is_empty() { - return Ok(Dynamic::UNIT); - } + if s.is_empty() { + return Ok(Dynamic::UNIT); + } - #[cfg(not(feature = "unchecked"))] - _ctx.engine().raise_err_if_over_data_size_limit(( - blob.len() + s.len(), - 0, - 0, - ))?; + #[cfg(not(feature = "unchecked"))] + _ctx.unwrap() + .engine() + .throw_on_size((blob.len() + s.len(), 0, 0))?; - Ok(append_str(blob, s).into()) - }), + Ok(append_str(blob, s).into()) + }, + cfg!(not(feature = "unchecked")), + )), _ => None, }; } diff --git a/src/func/call.rs b/src/func/call.rs index bc2e60ad..91522cad 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -271,25 +271,25 @@ impl Engine { } // Try to find a built-in version - let builtin = args.and_then(|args| match op_token { - Token::NONE => None, - token if token.is_op_assignment() => { - let (first_arg, rest_args) = args.split_first().unwrap(); + let builtin = + args.and_then(|args| match op_token { + Token::NONE => None, + token if token.is_op_assignment() => { + let (first_arg, rest_args) = args.split_first().unwrap(); - get_builtin_op_assignment_fn(token, first_arg, rest_args[0]).map( - |f| FnResolutionCacheEntry { - func: CallableFunction::Method(Shared::new(f)), + get_builtin_op_assignment_fn(token, first_arg, rest_args[0]) + .map(|(f, ctx)| FnResolutionCacheEntry { + func: CallableFunction::Method(Shared::new(f), ctx), + source: None, + }) + } + token => get_builtin_binary_op_fn(token, args[0], args[1]).map( + |(f, ctx)| FnResolutionCacheEntry { + func: CallableFunction::Method(Shared::new(f), ctx), source: None, }, - ) - } - token => get_builtin_binary_op_fn(token, args[0], args[1]).map(|f| { - FnResolutionCacheEntry { - func: CallableFunction::Method(Shared::new(f)), - source: None, - } - }), - }); + ), + }); return if cache.filter.is_absent_and_set(hash) { // Do not cache "one-hit wonders" @@ -400,22 +400,26 @@ impl Engine { // Run external function let is_method = func.is_method(); let src = source.as_ref().map(|s| s.as_str()); - let context = (self, name, src, &*global, pos).into(); - let mut _result = if func.is_plugin_fn() { - let f = func.get_plugin_fn().unwrap(); + let mut _result = if let Some(f) = func.get_plugin_fn() { if !f.is_pure() && !args.is_empty() && args[0].is_read_only() { Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into()) } else { + let context = (self, name, src, &*global, pos).into(); f.call(context, args) - .and_then(|r| self.check_data_size(r, pos)) - .map_err(|err| err.fill_position(pos)) } + } else if let Some(f) = func.get_native_fn() { + let context = if func.has_context() { + Some((self, name, src, &*global, pos).into()) + } else { + None + }; + f(context, args) } else { - func.get_native_fn().unwrap()(context, args) - .and_then(|r| self.check_data_size(r, pos)) - .map_err(|err| err.fill_position(pos)) - }; + unreachable!(); + } + .and_then(|r| self.check_data_size(r, pos)) + .map_err(|err| err.fill_position(pos)); #[cfg(feature = "debugging")] if self.is_debugger_registered() { @@ -1398,7 +1402,11 @@ 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, pos).into(); + let context = if f.has_context() { + Some((self, fn_name, module.id(), &*global, pos).into()) + } else { + None + }; func(context, &mut args).and_then(|r| self.check_data_size(r, pos)) } @@ -1518,14 +1526,19 @@ impl Engine { let operands = &mut [&mut lhs, &mut rhs]; - if let Some(func) = get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1]) + if let Some((func, ctx)) = + get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1]) { // Built-in found let orig_level = global.level; global.level += 1; let global = &*RestoreOnDrop::lock(global, move |g| g.level = orig_level); - let context = (self, name.as_str(), None, global, pos).into(); + let context = if ctx { + Some((self, name.as_str(), None, global, pos).into()) + } else { + None + }; return func(context, operands); } diff --git a/src/func/callable_function.rs b/src/func/callable_function.rs index aad705c9..cde5effa 100644 --- a/src/func/callable_function.rs +++ b/src/func/callable_function.rs @@ -14,10 +14,10 @@ use std::prelude::v1::*; #[non_exhaustive] pub enum CallableFunction { /// A pure native Rust function with all arguments passed by value. - Pure(Shared), + Pure(Shared, bool), /// A native Rust object method with the first argument passed by reference, /// and the rest passed by value. - Method(Shared), + Method(Shared, bool), /// An iterator function. Iterator(Shared), /// A plugin function, @@ -136,6 +136,17 @@ impl CallableFunction { Self::Script(..) => false, } } + /// Is there a [`NativeCallContext`] parameter? + #[inline] + #[must_use] + pub fn has_context(&self) -> bool { + match self { + Self::Pure(.., ctx) | Self::Method(.., ctx) => *ctx, + Self::Plugin(..) | Self::Iterator(..) => false, + #[cfg(not(feature = "no_function"))] + Self::Script(..) => false, + } + } /// Get the access mode. #[inline] #[must_use] @@ -156,7 +167,7 @@ impl CallableFunction { #[must_use] pub fn get_native_fn(&self) -> Option<&Shared> { match self { - Self::Pure(f) | Self::Method(f) => Some(f), + Self::Pure(f, ..) | Self::Method(f, ..) => Some(f), Self::Iterator(..) | Self::Plugin(..) => None, #[cfg(not(feature = "no_function"))] diff --git a/src/func/native.rs b/src/func/native.rs index 5d029fb6..103195b9 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -546,13 +546,16 @@ pub fn locked_write(value: &Locked) -> LockGuardMut { /// General Rust function trail object. #[cfg(not(feature = "sync"))] -pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult; +pub type FnAny = dyn Fn(Option, &mut FnCallArgs) -> RhaiResult; /// General Rust function trail object. #[cfg(feature = "sync")] -pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync; +pub type FnAny = dyn Fn(Option, &mut FnCallArgs) -> RhaiResult + Send + Sync; /// Built-in function trait object. -pub type FnBuiltin = fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult; +pub type FnBuiltin = ( + fn(Option, &mut FnCallArgs) -> RhaiResult, + bool, +); /// Function that gets an iterator from a type. #[cfg(not(feature = "sync"))] diff --git a/src/func/register.rs b/src/func/register.rs index f7c79f51..1459bf74 100644 --- a/src/func/register.rs +++ b/src/func/register.rs @@ -9,7 +9,7 @@ use super::call::FnCallArgs; use super::callable_function::CallableFunction; use super::native::{SendSync, Shared}; use crate::types::dynamic::{DynamicWriteLock, Variant}; -use crate::{reify, Dynamic, NativeCallContext, RhaiResultOf}; +use crate::{reify, Dynamic, Identifier, NativeCallContext, RhaiResultOf}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -78,13 +78,19 @@ pub fn by_value(data: &mut Dynamic) -> T { pub trait RegisterNativeFunction { /// Convert this function into a [`CallableFunction`]. #[must_use] - fn into_callable_function(self) -> CallableFunction; + fn into_callable_function(self, name: Identifier, no_const: bool) -> CallableFunction; /// Get the type ID's of this function's parameters. #[must_use] fn param_types() -> [TypeId; NUM]; /// Get the number of parameters for this function. + #[inline(always)] #[must_use] - fn num_params() -> usize; + fn num_params() -> usize { + NUM + } + /// Is there a [`NativeCallContext`] parameter for this function? + #[must_use] + fn has_context() -> bool; /// _(metadata)_ Get the type names of this function's parameters. /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] @@ -106,27 +112,14 @@ pub trait RegisterNativeFunction { + ($abi:ident, $n:expr, $fn_name:ident, $no_const:ident, $args:ident) => { #[cfg(any(not(feature = "no_object"), not(feature = "no_index")))] - if stringify!($abi) == "Method" { - let mut deny = false; - - #[cfg(not(feature = "no_index"))] - if $n == 3 && !deny { - deny = $ctx.fn_name() == crate::engine::FN_IDX_SET && $args[0].is_read_only(); - } - #[cfg(not(feature = "no_object"))] - if $n == 2 && !deny { - deny = $ctx.fn_name().starts_with(crate::engine::FN_SET) && $args[0].is_read_only(); - } - - if deny { - return Err(crate::ERR::ErrorNonPureMethodCallOnConstant( - $ctx.fn_name().to_string(), - crate::Position::NONE, - ) - .into()); - } + if stringify!($abi) == "Method" && $no_const && $args[0].is_read_only() { + return Err(crate::ERR::ErrorNonPureMethodCallOnConstant( + $fn_name.to_string(), + crate::Position::NONE, + ) + .into()); } }; } @@ -147,16 +140,16 @@ macro_rules! def_register { impl< FN: Fn($($param),*) -> RET + SendSync + 'static, $($par: Variant + Clone,)* - RET: Variant + Clone + RET: Variant + Clone, > RegisterNativeFunction<($($mark,)*), $n, false, RET, false> for FN { #[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] } - #[inline(always)] fn num_params() -> usize { $n } + #[inline(always)] fn has_context() -> bool { false } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } - #[inline(always)] fn into_callable_function(self) -> CallableFunction { - CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { + #[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction { + CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!($abi, $n, ctx, args); + check_constant!($abi, $n, fn_name, no_const, args); let mut drain = args.iter_mut(); $(let mut $par = $clone(drain.next().unwrap()); )* @@ -166,23 +159,25 @@ macro_rules! def_register { // Map the result Ok(Dynamic::from(r)) - })) + }), false) } } impl< FN: for<'a> Fn(NativeCallContext<'a>, $($param),*) -> RET + SendSync + 'static, $($par: Variant + Clone,)* - RET: Variant + Clone + RET: Variant + Clone, > RegisterNativeFunction<($($mark,)*), $n, true, RET, false> for FN { #[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] } - #[inline(always)] fn num_params() -> usize { $n } + #[inline(always)] fn has_context() -> bool { true } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::() } - #[inline(always)] fn into_callable_function(self) -> CallableFunction { - CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { + #[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction { + CallableFunction::$abi(Shared::new(move |ctx: Option, args: &mut FnCallArgs| { + let ctx = ctx.unwrap(); + // The arguments are assumed to be of the correct number and types! - check_constant!($abi, $n, ctx, args); + check_constant!($abi, $n, fn_name, no_const, args); let mut drain = args.iter_mut(); $(let mut $par = $clone(drain.next().unwrap()); )* @@ -192,7 +187,7 @@ macro_rules! def_register { // Map the result Ok(Dynamic::from(r)) - })) + }), true) } } @@ -202,21 +197,21 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<($($mark,)*), $n, false, RET, true> for FN { #[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] } - #[inline(always)] fn num_params() -> usize { $n } + #[inline(always)] fn has_context() -> bool { false } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::>() } - #[inline(always)] fn into_callable_function(self) -> CallableFunction { - CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { + #[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction { + CallableFunction::$abi(Shared::new(move |_, args: &mut FnCallArgs| { // The arguments are assumed to be of the correct number and types! - check_constant!($abi, $n, ctx, args); + check_constant!($abi, $n, fn_name, no_const, args); let mut drain = args.iter_mut(); $(let mut $par = $clone(drain.next().unwrap()); )* // Call the function with each argument value self($($arg),*).map(Dynamic::from) - })) + }), false) } } @@ -226,21 +221,23 @@ macro_rules! def_register { RET: Variant + Clone > RegisterNativeFunction<($($mark,)*), $n, true, RET, true> for FN { #[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] } - #[inline(always)] fn num_params() -> usize { $n } + #[inline(always)] fn has_context() -> bool { true } #[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] } #[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::>() } #[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::>() } - #[inline(always)] fn into_callable_function(self) -> CallableFunction { - CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| { + #[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction { + CallableFunction::$abi(Shared::new(move |ctx: Option, args: &mut FnCallArgs| { + let ctx = ctx.unwrap(); + // The arguments are assumed to be of the correct number and types! - check_constant!($abi, $n, ctx, args); + check_constant!($abi, $n, fn_name, no_const, args); let mut drain = args.iter_mut(); $(let mut $par = $clone(drain.next().unwrap()); )* // Call the function with each argument value self(ctx, $($arg),*).map(Dynamic::from) - })) + }), true) } } diff --git a/src/module/mod.rs b/src/module/mod.rs index 4d426979..76f4ac91 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -72,7 +72,7 @@ pub struct FuncInfoMetadata { /// Function access mode. pub access: FnAccess, /// Function name. - pub name: ImmutableString, + pub name: Identifier, /// Number of parameters. pub num_params: usize, /// Parameter types (if applicable). @@ -85,7 +85,7 @@ pub struct FuncInfoMetadata { pub return_type: Identifier, /// Comments. #[cfg(feature = "metadata")] - pub comments: Box<[Box]>, + pub comments: Box<[Identifier]>, } /// A type containing a single registered function. @@ -879,13 +879,12 @@ impl Module { /// In other words, the number of entries should be one larger than the number of parameters. #[cfg(feature = "metadata")] #[inline] - pub fn update_fn_metadata>( + pub fn update_fn_metadata>( &mut self, hash_fn: u64, arg_names: impl IntoIterator, ) -> &mut Self { - let mut param_names: FnArgsVec<_> = - arg_names.into_iter().map(|s| s.as_ref().into()).collect(); + let mut param_names: FnArgsVec<_> = arg_names.into_iter().map(Into::into).collect(); if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) { let (param_names, return_type_name) = if param_names.len() > f.metadata.num_params { @@ -927,7 +926,7 @@ impl Module { /// Each line in non-block doc-comments should start with `///`. #[cfg(feature = "metadata")] #[inline] - pub fn update_fn_metadata_with_comments, C: AsRef>( + pub fn update_fn_metadata_with_comments, C: Into>( &mut self, hash_fn: u64, arg_names: impl IntoIterator, @@ -940,7 +939,7 @@ impl Module { .and_then(|m| m.get_mut(&hash_fn)) .unwrap() .metadata - .comments = comments.into_iter().map(|s| s.as_ref().into()).collect(); + .comments = comments.into_iter().map(Into::into).collect(); self } @@ -1201,8 +1200,9 @@ impl Module { arg_types: impl AsRef<[TypeId]>, func: impl Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResultOf + SendSync + 'static, ) -> u64 { - let f = - move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); + let f = move |ctx: Option, args: &mut FnCallArgs| { + func(ctx.unwrap(), args).map(Dynamic::from) + }; self.set_fn( name, @@ -1210,7 +1210,7 @@ impl Module { access, None, arg_types, - CallableFunction::Method(Shared::new(f)), + CallableFunction::Method(Shared::new(f), true), ) } @@ -1246,13 +1246,24 @@ impl Module { T: Variant + Clone, F: RegisterNativeFunction, { + let fn_name = name.into(); + let no_const = false; + + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] + let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET); + #[cfg(not(feature = "no_object"))] + let no_const = + no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET)); + + let func = func.into_callable_function(fn_name.clone(), no_const); + self.set_fn( - name, + fn_name, FnNamespace::Internal, FnAccess::Public, None, F::param_types(), - func.into_callable_function(), + func, ) } @@ -1282,13 +1293,16 @@ impl Module { T: Variant + Clone, F: RegisterNativeFunction<(Mut,), 1, C, T, true> + SendSync + 'static, { + let fn_name = crate::engine::make_getter(name.as_ref()); + let func = func.into_callable_function(fn_name.clone(), false); + self.set_fn( - crate::engine::make_getter(name.as_ref()).as_str(), + fn_name, FnNamespace::Global, FnAccess::Public, None, F::param_types(), - func.into_callable_function(), + func, ) } @@ -1323,13 +1337,16 @@ impl Module { T: Variant + Clone, F: RegisterNativeFunction<(Mut, T), 2, C, (), true> + SendSync + 'static, { + let fn_name = crate::engine::make_setter(name.as_ref()); + let func = func.into_callable_function(fn_name.clone(), true); + self.set_fn( - crate::engine::make_setter(name.as_ref()).as_str(), + fn_name, FnNamespace::Global, FnAccess::Public, None, F::param_types(), - func.into_callable_function(), + func, ) } @@ -1437,7 +1454,7 @@ impl Module { FnAccess::Public, None, F::param_types(), - func.into_callable_function(), + func.into_callable_function(crate::engine::FN_IDX_GET.into(), false), ) } @@ -1498,7 +1515,7 @@ impl Module { FnAccess::Public, None, F::param_types(), - func.into_callable_function(), + func.into_callable_function(crate::engine::FN_IDX_SET.into(), true), ) } diff --git a/src/optimizer.rs b/src/optimizer.rs index 1224d6d5..a534e75a 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1137,8 +1137,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { // Overloaded operators can override built-in. _ if x.args.len() == 2 && x.op_token != Token::NONE && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => { if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1]) - .and_then(|f| { - let context = (state.engine, x.name.as_str(),None, &state.global, *pos).into(); + .and_then(|(f, ctx)| { + let context = if ctx { + Some((state.engine, x.name.as_str(),None, &state.global, *pos).into()) + } else { + None + }; let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 9309a2f6..e9cefb23 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -261,8 +261,7 @@ pub mod array_functions { m1 += m2; s1 += s2; - _ctx.engine() - .raise_err_if_over_data_size_limit((a1, m1, s1))?; + _ctx.engine().throw_on_size((a1, m1, s1))?; guard.push(item.clone()); arr_len += 1; diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index e8b0b9e8..057a00ef 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -81,8 +81,7 @@ pub mod blob_functions { // Check if blob will be over max size limit #[cfg(not(feature = "unchecked"))] - _ctx.engine() - .raise_err_if_over_data_size_limit((len, 0, 0))?; + _ctx.engine().throw_on_size((len, 0, 0))?; let mut blob = Blob::new(); blob.resize(len, (value & 0x0000_00ff) as u8); diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index e0e6c1b0..a19b0f72 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -220,7 +220,7 @@ fn collect_fn_metadata( "comments".into(), func.comments .iter() - .map(|s| engine.get_interned_string(s.as_ref()).into()) + .map(|s| engine.get_interned_string(s.as_str()).into()) .collect::() .into(), ); diff --git a/src/parser.rs b/src/parser.rs index 508b2852..5cb9ae15 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3567,16 +3567,14 @@ impl Engine { /// Parse a function definition. #[cfg(not(feature = "no_function"))] - fn parse_fn( + fn parse_fn>( &self, input: &mut TokenStream, state: &mut ParseState, lib: &mut FnLib, access: crate::FnAccess, settings: ParseSettings, - #[cfg(not(feature = "no_function"))] - #[cfg(feature = "metadata")] - comments: impl IntoIterator, + #[cfg(feature = "metadata")] comments: impl IntoIterator, ) -> ParseResult { let settings = settings; @@ -3660,13 +3658,8 @@ impl Engine { body, #[cfg(not(feature = "no_module"))] environ: None, - #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - comments: comments - .into_iter() - .map(|s| s.into_boxed_str()) - .collect::>() - .into_boxed_slice(), + comments: comments.into_iter().map(Into::into).collect(), }) }