From 6f3ce96d9d0eea8d37f9e988102460a5a0f4d4ca Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 2 Nov 2020 11:04:45 +0800 Subject: [PATCH] Enable termination token. --- RELEASES.md | 2 + doc/src/safety/progress.md | 7 +-- src/engine.rs | 23 +++------- src/engine_api.rs | 8 ++-- src/fn_native.rs | 7 ++- src/module/mod.rs | 5 +-- src/packages/array_basic.rs | 90 +++++++++++++++---------------------- src/packages/string_more.rs | 26 +++-------- src/result.rs | 36 +++++++-------- 9 files changed, 82 insertions(+), 122 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index d70fcdfd..06d3f9f8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -20,6 +20,8 @@ Breaking changes * Custom syntax can no longer start with a keyword (even a _reserved_ one), even if it has been disabled. That is to avoid breaking scripts later when the keyword is no longer disabled. * `EvalAltResult::ErrorAssignmentToUnknownLHS` is moved to `ParseError::AssignmentToInvalidLHS`. `ParseError::AssignmentToCopy` is removed. +* `EvalAltResult::ErrorDataTooLarge` is simplified. +* `Engine::on_progress` closure signature now returns `Option` with the termination value passed on to `EvalAltResult::ErrorTerminated`. New features ------------ diff --git a/doc/src/safety/progress.md b/doc/src/safety/progress.md index c34eed7c..99158470 100644 --- a/doc/src/safety/progress.md +++ b/doc/src/safety/progress.md @@ -17,13 +17,14 @@ engine.on_progress(|&count| { // parameter is '&u64' - number of operations al if count % 1000 == 0 { println!("{}", count); // print out a progress log every 1,000 operations } - true // return 'true' to continue running the script - // return 'false' to immediately terminate the script + None // return 'None' to continue running the script + // return 'Some(token)' to immediately terminate the script }); ``` The closure passed to `Engine::on_progress` will be called once for every operation. -Return `false` to terminate the script immediately. +Return `Some(token)` to terminate the script immediately, with the provided value +(any [`Dynamic`] value) passed to `EvalAltResult::ErrorTerminated` as a termination token. Operations Count vs. Progress Percentage diff --git a/src/engine.rs b/src/engine.rs index 8be258f6..494d2e3a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -576,7 +576,7 @@ pub struct Engine { /// Callback closure for implementing the `debug` command. pub(crate) debug: Callback, /// Callback closure for progress reporting. - pub(crate) progress: Option>, + pub(crate) progress: Option>>, /// Optimize the AST after compilation. pub(crate) optimization_level: OptimizationLevel, @@ -2300,8 +2300,6 @@ impl Engine { if s > self.max_string_size() { return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - self.max_string_size(), - s, Position::none(), ) .into(); @@ -2309,21 +2307,14 @@ impl Engine { #[cfg(not(feature = "no_index"))] if _arr > self.max_array_size() { - return EvalAltResult::ErrorDataTooLarge( - "Size of array".to_string(), - self.max_array_size(), - _arr, - Position::none(), - ) - .into(); + return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::none()) + .into(); } #[cfg(not(feature = "no_object"))] if _map > self.max_map_size() { return EvalAltResult::ErrorDataTooLarge( - "Number of properties in object map".to_string(), - self.max_map_size(), - _map, + "Size of object map".to_string(), Position::none(), ) .into(); @@ -2345,9 +2336,9 @@ impl Engine { // Report progress - only in steps if let Some(progress) = &self.progress { - if !progress(&state.operations) { - // Terminate script if progress returns false - return EvalAltResult::ErrorTerminated(Position::none()).into(); + if let Some(token) = progress(&state.operations) { + // Terminate script if progress returns a termination token + return EvalAltResult::ErrorTerminated(token, Position::none()).into(); } } diff --git a/src/engine_api.rs b/src/engine_api.rs index 36e664dc..26fd57ee 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1732,12 +1732,12 @@ impl Engine { /// /// engine.on_progress(move |&ops| { /// if ops > 10000 { - /// false + /// Some("Over 10,000 operations!".into()) /// } else if ops % 800 == 0 { /// *logger.write().unwrap() = ops; - /// true + /// None /// } else { - /// true + /// None /// } /// }); /// @@ -1752,7 +1752,7 @@ impl Engine { #[inline(always)] pub fn on_progress( &mut self, - callback: impl Fn(&u64) -> bool + SendSync + 'static, + callback: impl Fn(&u64) -> Option + SendSync + 'static, ) -> &mut Self { self.progress = Some(Box::new(callback)); self diff --git a/src/fn_native.rs b/src/fn_native.rs index db95c6b3..312af42b 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -160,7 +160,7 @@ impl FnPtr { /// clone them _before_ calling this function. pub fn call_dynamic( &self, - context: NativeCallContext, + ctx: NativeCallContext, this_ptr: Option<&mut Dynamic>, mut arg_values: impl AsMut<[Dynamic]>, ) -> Result> { @@ -182,11 +182,10 @@ impl FnPtr { args.insert(0, obj); } - context - .engine() + ctx.engine() .exec_fn_call( &mut Default::default(), - context.lib, + ctx.lib, fn_name, hash_script, args.as_mut(), diff --git a/src/module/mod.rs b/src/module/mod.rs index 9b6f135e..a8714a4d 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -529,9 +529,8 @@ impl Module { + SendSync + 'static, ) -> u64 { - let f = move |context: NativeCallContext, args: &mut FnCallArgs| { - func(context, args).map(Dynamic::from) - }; + let f = + move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); self.set_fn( name, FnAccess::Public, diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 9f3179a5..fa831e6f 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -41,12 +41,12 @@ macro_rules! gen_array_functions { } #[rhai_fn(return_raw)] - pub fn pad(_context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result> { + pub fn pad(_ctx: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result> { // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] - if _context.engine().max_array_size() > 0 && len > 0 && (len as usize) > _context.engine().max_array_size() { + if _ctx.engine().max_array_size() > 0 && len > 0 && (len as usize) > _ctx.engine().max_array_size() { return EvalAltResult::ErrorDataTooLarge( - "Size of array".to_string(), _context.engine().max_array_size(), len as usize, Position::none(), + "Size of array".to_string(), Position::none() ).into(); } @@ -197,7 +197,7 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn map( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, mapper: FnPtr, ) -> Result> { @@ -206,12 +206,12 @@ mod array_functions { for (i, item) in list.iter().enumerate() { array.push( mapper - .call_dynamic(context, None, [item.clone()]) + .call_dynamic(ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(mapper.fn_name()) => { - mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()]) + mapper.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -229,7 +229,7 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn filter( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, filter: FnPtr, ) -> Result> { @@ -237,12 +237,12 @@ mod array_functions { for (i, item) in list.iter().enumerate() { if filter - .call_dynamic(context, None, [item.clone()]) + .call_dynamic(ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -264,18 +264,18 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn some( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, filter: FnPtr, ) -> Result> { for (i, item) in list.iter().enumerate() { if filter - .call_dynamic(context, None, [item.clone()]) + .call_dynamic(ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -297,18 +297,18 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn all( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, filter: FnPtr, ) -> Result> { for (i, item) in list.iter().enumerate() { if !filter - .call_dynamic(context, None, [item.clone()]) + .call_dynamic(ctx, None, [item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(context, None, [item.clone(), (i as INT).into()]) + filter.call_dynamic(ctx, None, [item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -330,7 +330,7 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn reduce( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, reducer: FnPtr, ) -> Result> { @@ -338,16 +338,12 @@ mod array_functions { for (i, item) in list.iter().enumerate() { result = reducer - .call_dynamic(context, None, [result.clone(), item.clone()]) + .call_dynamic(ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic( - context, - None, - [result, item.clone(), (i as INT).into()], - ) + reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -364,12 +360,12 @@ mod array_functions { } #[rhai_fn(name = "reduce", return_raw)] pub fn reduce_with_initial( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, reducer: FnPtr, initial: FnPtr, ) -> Result> { - let mut result = initial.call_dynamic(context, None, []).map_err(|err| { + let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), err, @@ -379,16 +375,12 @@ mod array_functions { for (i, item) in list.iter().enumerate() { result = reducer - .call_dynamic(context, None, [result.clone(), item.clone()]) + .call_dynamic(ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic( - context, - None, - [result, item.clone(), (i as INT).into()], - ) + reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -405,7 +397,7 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn reduce_rev( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, reducer: FnPtr, ) -> Result> { @@ -413,16 +405,12 @@ mod array_functions { for (i, item) in list.iter().enumerate().rev() { result = reducer - .call_dynamic(context, None, [result.clone(), item.clone()]) + .call_dynamic(ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic( - context, - None, - [result, item.clone(), (i as INT).into()], - ) + reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -439,12 +427,12 @@ mod array_functions { } #[rhai_fn(name = "reduce_rev", return_raw)] pub fn reduce_rev_with_initial( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, reducer: FnPtr, initial: FnPtr, ) -> Result> { - let mut result = initial.call_dynamic(context, None, []).map_err(|err| { + let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| { Box::new(EvalAltResult::ErrorInFunctionCall( "reduce".to_string(), err, @@ -454,16 +442,12 @@ mod array_functions { for (i, item) in list.iter().enumerate().rev() { result = reducer - .call_dynamic(context, None, [result.clone(), item.clone()]) + .call_dynamic(ctx, None, [result.clone(), item.clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(reducer.fn_name()) => { - reducer.call_dynamic( - context, - None, - [result, item.clone(), (i as INT).into()], - ) + reducer.call_dynamic(ctx, None, [result, item.clone(), (i as INT).into()]) } _ => Err(err), }) @@ -480,13 +464,13 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn sort( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, comparer: FnPtr, ) -> Result> { list.sort_by(|x, y| { comparer - .call_dynamic(context, None, [x.clone(), y.clone()]) + .call_dynamic(ctx, None, [x.clone(), y.clone()]) .ok() .and_then(|v| v.as_int().ok()) .map(|v| { @@ -516,7 +500,7 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn drain( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, filter: FnPtr, ) -> Result> { @@ -528,12 +512,12 @@ mod array_functions { i -= 1; if filter - .call_dynamic(context, None, [list[i].clone()]) + .call_dynamic(ctx, None, [list[i].clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()]) + filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()]) } _ => Err(err), }) @@ -575,7 +559,7 @@ mod array_functions { } #[rhai_fn(return_raw)] pub fn retain( - context: NativeCallContext, + ctx: NativeCallContext, list: &mut Array, filter: FnPtr, ) -> Result> { @@ -587,12 +571,12 @@ mod array_functions { i -= 1; if !filter - .call_dynamic(context, None, [list[i].clone()]) + .call_dynamic(ctx, None, [list[i].clone()]) .or_else(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.starts_with(filter.fn_name()) => { - filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()]) + filter.call_dynamic(ctx, None, [list[i].clone(), (i as INT).into()]) } _ => Err(err), }) diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 7a76d91e..7ce4fb06 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -251,20 +251,16 @@ mod string_functions { #[rhai_fn(return_raw)] pub fn pad( - _context: NativeCallContext, + _ctx: NativeCallContext, s: &mut ImmutableString, len: INT, ch: char, ) -> Result> { // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] - if _context.engine().max_string_size() > 0 - && len as usize > _context.engine().max_string_size() - { + if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - _context.engine().max_string_size(), - len as usize, Position::none(), ) .into(); @@ -281,13 +277,10 @@ mod string_functions { } #[cfg(not(feature = "unchecked"))] - if _context.engine().max_string_size() > 0 - && s.len() > _context.engine().max_string_size() + if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size() { return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - _context.engine().max_string_size(), - s.len(), Position::none(), ) .into(); @@ -299,20 +292,16 @@ mod string_functions { } #[rhai_fn(name = "pad", return_raw)] pub fn pad_with_string( - _context: NativeCallContext, + _ctx: NativeCallContext, s: &mut ImmutableString, len: INT, padding: &str, ) -> Result> { // Check if string will be over max size limit #[cfg(not(feature = "unchecked"))] - if _context.engine().max_string_size() > 0 - && len as usize > _context.engine().max_string_size() - { + if _ctx.engine().max_string_size() > 0 && len as usize > _ctx.engine().max_string_size() { return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - _context.engine().max_string_size(), - len as usize, Position::none(), ) .into(); @@ -336,13 +325,10 @@ mod string_functions { } #[cfg(not(feature = "unchecked"))] - if _context.engine().max_string_size() > 0 - && s.len() > _context.engine().max_string_size() + if _ctx.engine().max_string_size() > 0 && s.len() > _ctx.engine().max_string_size() { return EvalAltResult::ErrorDataTooLarge( "Length of string".to_string(), - _context.engine().max_string_size(), - s.len(), Position::none(), ) .into(); diff --git a/src/result.rs b/src/result.rs index 1a42c118..5370850e 100644 --- a/src/result.rs +++ b/src/result.rs @@ -83,11 +83,11 @@ pub enum EvalAltResult { ErrorTooManyModules(Position), /// Call stack over maximum limit. ErrorStackOverflow(Position), - /// Data value over maximum size limit. Wrapped values are the type name, maximum size and current size. - ErrorDataTooLarge(String, usize, usize, Position), - /// The script is prematurely terminated. - ErrorTerminated(Position), - /// Run-time error encountered. Wrapped value is the error. + /// Data value over maximum size limit. Wrapped value is the type name. + ErrorDataTooLarge(String, Position), + /// The script is prematurely terminated. Wrapped value is the termination token. + ErrorTerminated(Dynamic, Position), + /// Run-time error encountered. Wrapped value is the error token. ErrorRuntime(Dynamic, Position), /// Breaking out of loops - not an error if within a loop. @@ -135,8 +135,8 @@ impl EvalAltResult { Self::ErrorTooManyOperations(_) => "Too many operations", Self::ErrorTooManyModules(_) => "Too many modules imported", Self::ErrorStackOverflow(_) => "Stack overflow", - Self::ErrorDataTooLarge(_, _, _, _) => "Data size exceeds maximum limit", - Self::ErrorTerminated(_) => "Script terminated.", + Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit", + Self::ErrorTerminated(_,_) => "Script terminated.", Self::ErrorRuntime(_, _) => "Runtime error", Self::LoopBreak(true, _) => "Break statement not inside a loop", Self::LoopBreak(false, _) => "Continue statement not inside a loop", @@ -185,7 +185,7 @@ impl fmt::Display for EvalAltResult { | Self::ErrorTooManyOperations(_) | Self::ErrorTooManyModules(_) | Self::ErrorStackOverflow(_) - | Self::ErrorTerminated(_) => f.write_str(desc)?, + | Self::ErrorTerminated(_, _) => f.write_str(desc)?, Self::ErrorRuntime(d, _) if d.is::() => { let s = d.as_str().unwrap(); @@ -237,9 +237,7 @@ impl fmt::Display for EvalAltResult { "String index {} is out of bounds: only {} characters in the string", index, max )?, - Self::ErrorDataTooLarge(typ, max, size, _) => { - write!(f, "{} ({}) exceeds the maximum limit ({})", typ, size, max)? - } + Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?, } // Do not write any position if None @@ -297,8 +295,8 @@ impl EvalAltResult { Self::ErrorTooManyOperations(_) | Self::ErrorTooManyModules(_) | Self::ErrorStackOverflow(_) - | Self::ErrorDataTooLarge(_, _, _, _) - | Self::ErrorTerminated(_) => false, + | Self::ErrorDataTooLarge(_, _) + | Self::ErrorTerminated(_, _) => false, Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(), } @@ -313,9 +311,9 @@ impl EvalAltResult { Self::ErrorTooManyOperations(_) | Self::ErrorTooManyModules(_) | Self::ErrorStackOverflow(_) - | Self::ErrorDataTooLarge(_, _, _, _) => true, + | Self::ErrorDataTooLarge(_, _) => true, - Self::ErrorTerminated(_) => true, + Self::ErrorTerminated(_, _) => true, Self::LoopBreak(_, _) | Self::Return(_, _) => unreachable!(), @@ -349,8 +347,8 @@ impl EvalAltResult { | Self::ErrorTooManyOperations(pos) | Self::ErrorTooManyModules(pos) | Self::ErrorStackOverflow(pos) - | Self::ErrorDataTooLarge(_, _, _, pos) - | Self::ErrorTerminated(pos) + | Self::ErrorDataTooLarge(_, pos) + | Self::ErrorTerminated(_, pos) | Self::ErrorRuntime(_, pos) | Self::LoopBreak(_, pos) | Self::Return(_, pos) => *pos, @@ -383,8 +381,8 @@ impl EvalAltResult { | Self::ErrorTooManyOperations(pos) | Self::ErrorTooManyModules(pos) | Self::ErrorStackOverflow(pos) - | Self::ErrorDataTooLarge(_, _, _, pos) - | Self::ErrorTerminated(pos) + | Self::ErrorDataTooLarge(_, pos) + | Self::ErrorTerminated(_, pos) | Self::ErrorRuntime(_, pos) | Self::LoopBreak(_, pos) | Self::Return(_, pos) => *pos = new_position,