diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index bc8953ca..9195f632 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -73,14 +73,15 @@ impl Engine { pub(crate) fn raise_err_if_over_data_size_limit( &self, (_arr, _map, s): (usize, usize, usize), - pos: Position, ) -> RhaiResultOf<()> { if self .limits .max_string_size .map_or(false, |max| s > max.get()) { - return Err(ERR::ErrorDataTooLarge("Length of string".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(), + ); } #[cfg(not(feature = "no_index"))] @@ -89,7 +90,9 @@ impl Engine { .max_array_size .map_or(false, |max| _arr > max.get()) { - return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Size of array/BLOB".to_string(), Position::NONE).into(), + ); } #[cfg(not(feature = "no_object"))] @@ -98,7 +101,9 @@ impl Engine { .max_map_size .map_or(false, |max| _map > max.get()) { - return Err(ERR::ErrorDataTooLarge("Size of object map".to_string(), pos).into()); + return Err( + ERR::ErrorDataTooLarge("Size of object map".to_string(), Position::NONE).into(), + ); } Ok(()) @@ -113,7 +118,8 @@ impl Engine { let sizes = Self::calc_data_sizes(value, true); - self.raise_err_if_over_data_size_limit(sizes, pos) + self.raise_err_if_over_data_size_limit(sizes) + .map_err(|err| err.fill_position(pos)) } /// 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 d86c3da6..285f5e23 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -252,8 +252,7 @@ impl Engine { { // Built-in found let context = (self, name, None, &*global, lib, pos, level + 1).into(); - let result = func(context, operands); - return self.check_return_value(result, pos); + return func(context, operands); } return self @@ -422,10 +421,8 @@ 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, - item_expr.position(), - )?; + self.raise_err_if_over_data_size_limit(total_data_sizes) + .map_err(|err| err.fill_position(item_expr.position()))?; } array.push(value); @@ -455,10 +452,8 @@ 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, - value_expr.position(), - )?; + self.raise_err_if_over_data_size_limit(total_data_sizes) + .map_err(|err| err.fill_position(value_expr.position()))?; } *map.get_mut(key.as_str()).unwrap() = value; diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index fac3c749..4e26d7d3 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -146,11 +146,7 @@ impl Engine { // Built-in found let op = op_assign.literal_syntax(); let context = (self, op, None, &*global, lib, *op_pos, level).into(); - let result = func(context, args).map(|_| ()); - - self.check_data_size(args[0], root.1)?; - - return result; + return func(context, args).map(|_| ()); } } diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 6b16366f..0803d35e 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -196,7 +196,19 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::Plus => Some(impl_op!(ImmutableString + ImmutableString)), + Token::Plus => Some(|_ctx, args| { + let s1 = &*args[0].read_lock::().expect(BUILTIN); + let s2 = &*args[1].read_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + if !s1.is_empty() && !s2.is_empty() { + let total_len = s1.len() + s2.len(); + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, total_len))?; + } + + Ok((s1 + s2).into()) + }), Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)), Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), @@ -210,10 +222,17 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type1 == TypeId::of::() { return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); - Ok(format!("{x}{y}").into()) + + let result = format!("{x}{y}"); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::EqualsTo => Some(impl_op!(char => as_char == as_char)), Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)), @@ -230,7 +249,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< use crate::Blob; return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let blob1 = &*args[0].read_lock::().expect(BUILTIN); let blob2 = &*args[1].read_lock::().expect(BUILTIN); @@ -239,6 +258,13 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } else if blob1.is_empty() { blob2.clone() } else { + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob1.len() + blob2.len(), + 0, + 0, + ))?; + let mut blob = blob1.clone(); blob.extend(blob2); blob @@ -357,10 +383,16 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); - Ok(format!("{x}{y}").into()) + let result = format!("{x}{y}"); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::EqualsTo => Some(impl_op!(get_s1s2(==))), Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), @@ -383,10 +415,16 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< } return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); - Ok((x + y).into()) + let result = x + y; + + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((0, 0, result.len()))?; + + Ok(result.into()) }), Token::Minus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); @@ -436,10 +474,18 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option< if type2 == TypeId::of::() { return match op { - Token::Plus => Some(|_, args| { + Token::Plus => Some(|_ctx, args| { let mut buf = [0_u8; 4]; let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); - let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf); + 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, + ))?; + blob.extend(x.as_bytes()); Ok(Dynamic::from_blob(blob)) }), @@ -638,10 +684,18 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt if type1 == TypeId::of::() { return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); + + #[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))?; + } + Ok((*x += y).into()) }), Token::MinusAssign => Some(|_, args| { @@ -654,14 +708,56 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt }; } + #[cfg(not(feature = "no_index"))] + if type1 == TypeId::of::() { + use crate::packages::array_basic::array_functions::*; + use crate::Array; + + return match op { + Token::PlusAssign => Some(|_ctx, args| { + let x = std::mem::take(args[1]).cast::(); + + if x.is_empty() { + return Ok(Dynamic::UNIT); + } + + let _array_was_empty = { + let array = &mut &mut *args[0].write_lock::().expect(BUILTIN); + let array_is_empty = array.is_empty(); + append(array, x); + array_is_empty + }; + + #[cfg(not(feature = "unchecked"))] + if !_array_was_empty { + _ctx.engine().check_data_size( + &*args[0].read_lock().expect(BUILTIN), + crate::Position::NONE, + )?; + } + + Ok(Dynamic::UNIT) + }), + _ => None, + }; + } + #[cfg(not(feature = "no_index"))] if type1 == TypeId::of::() { use crate::Blob; return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let blob2 = std::mem::take(args[1]).cast::(); 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, + ))?; + Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) }), _ => None, @@ -736,7 +832,17 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(impl_op!(ImmutableString += as_char as char)), + Token::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()))?; + + Ok((*x += ch).into()) + }), Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; @@ -744,17 +850,27 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let mut ch = args[0].as_char().expect(BUILTIN).to_string(); - ch.push_str( - args[1] - .read_lock::() - .expect(BUILTIN) - .as_str(), - ); + Token::PlusAssign => Some(|_ctx, args| { + let ch = { + let s = &*args[1].read_lock::().expect(BUILTIN); - let mut x = args[0].write_lock::().expect(BUILTIN); - Ok((*x = ch.into()).into()) + if s.is_empty() { + return Ok(Dynamic::UNIT); + } + + 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()))?; + + ch.push_str(s); + ch + }; + + *args[0].write_lock::().expect(BUILTIN) = ch.into(); + + Ok(Dynamic::UNIT) }), _ => None, }; @@ -766,21 +882,21 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt use crate::packages::array_basic::array_functions::*; use crate::Array; - if type2 == TypeId::of::() { - return match op { - Token::PlusAssign => Some(|_, args| { - let array2 = std::mem::take(args[1]).cast::(); - let array1 = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(append(array1, array2).into()) - }), - _ => None, - }; - } return match op { - Token::PlusAssign => Some(|_, args| { - let x = std::mem::take(args[1]); - let array = &mut *args[0].write_lock::().expect(BUILTIN); - Ok(push(array, x).into()) + Token::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, + )?; + + Ok(Dynamic::UNIT) }), _ => None, }; @@ -793,9 +909,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= int if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let x = args[1].as_int().expect("`INT`"); + Token::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))?; + Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) }), _ => None, @@ -805,9 +926,14 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { - let x = args[1].as_char().expect("`char`"); + Token::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))?; + Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) }), _ => None, @@ -817,9 +943,22 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt // blob op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - Token::PlusAssign => Some(|_, args| { + Token::PlusAssign => Some(|_ctx, args| { let s = std::mem::take(args[1]).cast::(); + + if s.is_empty() { + return Ok(Dynamic::UNIT); + } + let blob = &mut *args[0].write_lock::().expect(BUILTIN); + + #[cfg(not(feature = "unchecked"))] + _ctx.engine().raise_err_if_over_data_size_limit(( + blob.len() + s.len(), + 0, + 0, + ))?; + Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) }), _ => None, diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 2c5bd716..098b7f7d 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -259,7 +259,7 @@ pub mod array_functions { s1 += s2; _ctx.engine() - .raise_err_if_over_data_size_limit((a1, m1, s1), Position::NONE)?; + .raise_err_if_over_data_size_limit((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 4ddd68b6..2e51b108 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -79,11 +79,9 @@ pub mod blob_functions { let _ctx = ctx; // Check if blob will be over max size limit - if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { - return Err( - crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(), - ); - } + #[cfg(not(feature = "unchecked"))] + _ctx.engine() + .raise_err_if_over_data_size_limit((len, 0, 0))?; let mut blob = Blob::new(); blob.resize(len, (value & 0x0000_00ff) as u8);