Simplify code.

This commit is contained in:
Stephen Chung 2020-12-20 23:25:11 +08:00
parent f99703f951
commit cec27846ea
3 changed files with 56 additions and 72 deletions

View File

@ -376,29 +376,29 @@ impl<'a> Target<'a> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ch) => { Self::StringChar(_, _, ch) => {
let char_value = ch.clone(); let char_value = ch.clone();
self.set_value((char_value, Position::NONE)).unwrap(); self.set_value(char_value, Position::NONE).unwrap();
} }
} }
} }
/// Update the value of the `Target`. /// Update the value of the `Target`.
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))] #[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
pub fn set_value(&mut self, new_val: (Dynamic, Position)) -> Result<(), Box<EvalAltResult>> { pub fn set_value(&mut self, new_val: Dynamic, pos: Position) -> Result<(), Box<EvalAltResult>> {
match self { match self {
Self::Ref(r) => **r = new_val.0, Self::Ref(r) => **r = new_val,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Self::LockGuard((r, _)) => **r = new_val.0, Self::LockGuard((r, _)) => **r = new_val,
Self::Value(_) => unreachable!(), Self::Value(_) => unreachable!(),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::StringChar(string, index, _) if string.is::<ImmutableString>() => { Self::StringChar(string, index, _) if string.is::<ImmutableString>() => {
let mut s = string.write_lock::<ImmutableString>().unwrap(); let mut s = string.write_lock::<ImmutableString>().unwrap();
// Replace the character at the specified index position // Replace the character at the specified index position
let new_ch = new_val.0.as_char().map_err(|err| { let new_ch = new_val.as_char().map_err(|err| {
Box::new(EvalAltResult::ErrorMismatchDataType( Box::new(EvalAltResult::ErrorMismatchDataType(
err.to_string(), err.to_string(),
"char".to_string(), "char".to_string(),
new_val.1, pos,
)) ))
})?; })?;
@ -1031,7 +1031,8 @@ impl Engine {
) { ) {
// Indexed value is a reference - update directly // Indexed value is a reference - update directly
Ok(ref mut obj_ptr) => { Ok(ref mut obj_ptr) => {
obj_ptr.set_value(new_val.unwrap())?; let (new_val, new_val_pos) = new_val.unwrap();
obj_ptr.set_value(new_val, new_val_pos)?;
None None
} }
Err(err) => match *err { Err(err) => match *err {
@ -1107,7 +1108,9 @@ impl Engine {
mods, state, lib, target_val, index, *pos, true, is_ref, false, level, mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
)?; )?;
val.set_value(new_val.unwrap())?; let (new_val, new_val_pos) = new_val.unwrap();
val.set_value(new_val, new_val_pos)?;
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
// {xxx:map}.id // {xxx:map}.id
@ -1308,8 +1311,7 @@ impl Engine {
pos: var_pos, pos: var_pos,
} = &x.3; } = &x.3;
self.inc_operations(state) self.inc_operations(state, *var_pos)?;
.map_err(|err| err.fill_position(*var_pos))?;
let (target, _, pos) = let (target, _, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?; self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
@ -1360,8 +1362,7 @@ impl Engine {
size: usize, size: usize,
level: usize, level: usize,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
self.inc_operations(state) self.inc_operations(state, expr.position())?;
.map_err(|err| err.fill_position(expr.position()))?;
match expr { match expr {
Expr::FnCall(x, _) if x.namespace.is_none() => { Expr::FnCall(x, _) if x.namespace.is_none() => {
@ -1435,7 +1436,7 @@ impl Engine {
_indexers: bool, _indexers: bool,
_level: usize, _level: usize,
) -> Result<Target<'t>, Box<EvalAltResult>> { ) -> Result<Target<'t>, Box<EvalAltResult>> {
self.inc_operations(state)?; self.inc_operations(state, Position::NONE)?;
match target { match target {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -1538,8 +1539,7 @@ impl Engine {
rhs: &Expr, rhs: &Expr,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
self.inc_operations(state) self.inc_operations(state, rhs.position())?;
.map_err(|err| err.fill_position(rhs.position()))?;
let lhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?; let lhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)?;
let rhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, rhs, level)?; let rhs_value = self.eval_expr(scope, mods, state, lib, this_ptr, rhs, level)?;
@ -1712,8 +1712,7 @@ impl Engine {
expr: &Expr, expr: &Expr,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
self.inc_operations(state) self.inc_operations(state, expr.position())?;
.map_err(|err| err.fill_position(expr.position()))?;
let result = match expr { let result = match expr {
Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x, level), Expr::Expr(x) => self.eval_expr(scope, mods, state, lib, this_ptr, x, level),
@ -1868,8 +1867,7 @@ impl Engine {
_ => unreachable!(), _ => unreachable!(),
}; };
self.check_data_size(result) self.check_data_size(result, expr.position())
.map_err(|err| err.fill_position(expr.position()))
} }
/// Evaluate a statements block. /// Evaluate a statements block.
@ -1925,8 +1923,7 @@ impl Engine {
stmt: &Stmt, stmt: &Stmt,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
self.inc_operations(state) self.inc_operations(state, stmt.position())?;
.map_err(|err| err.fill_position(stmt.position()))?;
let result = match stmt { let result = match stmt {
// No-op // No-op
@ -1948,8 +1945,7 @@ impl Engine {
return EvalAltResult::ErrorAssignmentToConstant(name.to_string(), pos).into(); return EvalAltResult::ErrorAssignmentToConstant(name.to_string(), pos).into();
} }
self.inc_operations(state) self.inc_operations(state, pos)?;
.map_err(|err| err.fill_position(pos))?;
if lhs_ptr.as_ref().is_read_only() { if lhs_ptr.as_ref().is_read_only() {
// Assignment to constant variable // Assignment to constant variable
@ -2208,8 +2204,7 @@ impl Engine {
*loop_var = value; *loop_var = value;
} }
self.inc_operations(state) self.inc_operations(state, stmt.position())?;
.map_err(|err| err.fill_position(stmt.position()))?;
match self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) { match self.eval_stmt(scope, mods, state, lib, this_ptr, stmt, level) {
Ok(_) => (), Ok(_) => (),
@ -2427,8 +2422,7 @@ impl Engine {
} }
}; };
self.check_data_size(result) self.check_data_size(result, stmt.position())
.map_err(|err| err.fill_position(stmt.position()))
} }
/// Check a result to ensure that the data size is within allowable limit. /// Check a result to ensure that the data size is within allowable limit.
@ -2438,16 +2432,17 @@ impl Engine {
fn check_data_size( fn check_data_size(
&self, &self,
result: Result<Dynamic, Box<EvalAltResult>>, result: Result<Dynamic, Box<EvalAltResult>>,
_pos: Position,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
result result
} }
/// Check a result to ensure that the data size is within allowable limit. /// Check a result to ensure that the data size is within allowable limit.
/// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
fn check_data_size( fn check_data_size(
&self, &self,
result: Result<Dynamic, Box<EvalAltResult>>, result: Result<Dynamic, Box<EvalAltResult>>,
pos: Position,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
// If no data size limits, just return // If no data size limits, just return
let mut total = 0; let mut total = 0;
@ -2536,47 +2531,41 @@ impl Engine {
let (_arr, _map, s) = calc_size(result.as_ref().unwrap()); let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
if s > self.max_string_size() { if s > self.max_string_size() {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), pos).into();
"Length of string".to_string(),
Position::NONE,
)
.into();
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
if _arr > self.max_array_size() { if _arr > self.max_array_size() {
return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::NONE) return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), pos).into();
.into();
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if _map > self.max_map_size() { if _map > self.max_map_size() {
return EvalAltResult::ErrorDataTooLarge( return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), pos).into();
"Size of object map".to_string(),
Position::NONE,
)
.into();
} }
result result
} }
/// Check if the number of operations stay within limit. /// Check if the number of operations stay within limit.
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards. pub(crate) fn inc_operations(
pub(crate) fn inc_operations(&self, state: &mut State) -> Result<(), Box<EvalAltResult>> { &self,
state: &mut State,
pos: Position,
) -> Result<(), Box<EvalAltResult>> {
state.operations += 1; state.operations += 1;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
// Guard against too many operations // Guard against too many operations
if self.max_operations() > 0 && state.operations > self.max_operations() { if self.max_operations() > 0 && state.operations > self.max_operations() {
return EvalAltResult::ErrorTooManyOperations(Position::NONE).into(); return EvalAltResult::ErrorTooManyOperations(pos).into();
} }
// Report progress - only in steps // Report progress - only in steps
if let Some(progress) = &self.progress { if let Some(progress) = &self.progress {
if let Some(token) = progress(state.operations) { if let Some(token) = progress(state.operations) {
// Terminate script if progress returns a termination token // Terminate script if progress returns a termination token
return EvalAltResult::ErrorTerminated(token, Position::NONE).into(); return EvalAltResult::ErrorTerminated(token, pos).into();
} }
} }

View File

@ -1370,7 +1370,7 @@ impl Engine {
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
let mut mods = self.global_sub_modules.clone(); let mut mods = self.global_sub_modules.clone();
let (result, _) = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?; let result = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?;
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());
@ -1390,8 +1390,9 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
mods: &mut Imports, mods: &mut Imports,
ast: &'a AST, ast: &'a AST,
) -> Result<(Dynamic, u64), Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
self.eval_statements_raw(scope, mods, ast.statements(), &[ast.lib()]) let state = &mut Default::default();
self.eval_statements_raw(scope, mods, state, ast.statements(), &[ast.lib()])
} }
/// Evaluate a file, but throw away the result and only return error (if any). /// Evaluate a file, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
@ -1451,9 +1452,9 @@ impl Engine {
ast: &AST, ast: &AST,
) -> Result<(), Box<EvalAltResult>> { ) -> Result<(), Box<EvalAltResult>> {
let mut mods = self.global_sub_modules.clone(); let mut mods = self.global_sub_modules.clone();
let mut state = Default::default();
self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()]) self.eval_statements_raw(scope, &mut mods, &mut state, ast.statements(), &[ast.lib()])?;
.map(|_| ()) Ok(())
} }
/// Call a script function defined in an [`AST`] with multiple arguments. /// Call a script function defined in an [`AST`] with multiple arguments.
/// Arguments are passed as a tuple. /// Arguments are passed as a tuple.

View File

@ -168,7 +168,7 @@ impl Engine {
pos: Position, pos: Position,
def_val: Option<&Dynamic>, def_val: Option<&Dynamic>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
self.inc_operations(state)?; self.inc_operations(state, pos)?;
let func = state.functions_cache.get(&hash_fn).cloned(); let func = state.functions_cache.get(&hash_fn).cloned();
@ -348,7 +348,7 @@ impl Engine {
pos: Position, pos: Position,
level: usize, level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
self.inc_operations(state)?; self.inc_operations(state, pos)?;
// Check for stack overflow // Check for stack overflow
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -623,36 +623,34 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
mods: &mut Imports, mods: &mut Imports,
state: &mut State,
statements: impl IntoIterator<Item = &'a Stmt>, statements: impl IntoIterator<Item = &'a Stmt>,
lib: &[&Module], lib: &[&Module],
) -> Result<(Dynamic, u64), Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let mut state = Default::default();
statements statements
.into_iter() .into_iter()
.try_fold(().into(), |_, stmt| { .try_fold(().into(), |_, stmt| {
self.eval_stmt(scope, mods, &mut state, lib, &mut None, stmt, 0) self.eval_stmt(scope, mods, state, lib, &mut None, stmt, 0)
}) })
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::Return(out, _) => Ok(out), EvalAltResult::Return(out, _) => Ok(out),
EvalAltResult::LoopBreak(_, _) => unreachable!(), EvalAltResult::LoopBreak(_, _) => unreachable!(),
_ => Err(err), _ => Err(err),
}) })
.map(|v| (v, state.operations))
} }
/// Evaluate a text string as a script - used primarily for 'eval'. /// Evaluate a text script in place - used primarily for 'eval'.
fn eval_script_expr( fn eval_script_expr_in_place(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
mods: &mut Imports, mods: &mut Imports,
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
script: &str, script: &str,
_pos: Position, pos: Position,
_level: usize, _level: usize,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
self.inc_operations(state)?; self.inc_operations(state, pos)?;
let script = script.trim(); let script = script.trim();
if script.is_empty() { if script.is_empty() {
@ -663,7 +661,7 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if _level > self.max_call_levels() { if _level > self.max_call_levels() {
return Err(Box::new(EvalAltResult::ErrorStackOverflow(_pos))); return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos)));
} }
// Compile the script text // Compile the script text
@ -680,10 +678,7 @@ impl Engine {
} }
// Evaluate the AST // Evaluate the AST
let (result, operations) = self.eval_statements_raw(scope, mods, ast.statements(), lib)?; let result = self.eval_statements_raw(scope, mods, state, ast.statements(), lib)?;
state.operations += operations;
self.inc_operations(state)?;
return Ok(result); return Ok(result);
} }
@ -977,7 +972,8 @@ impl Engine {
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position()) self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
})?; })?;
let pos = args_expr[0].position(); let pos = args_expr[0].position();
let result = self.eval_script_expr(scope, mods, state, lib, script, pos, level + 1); let result =
self.eval_script_expr_in_place(scope, mods, state, lib, script, pos, level + 1);
// IMPORTANT! If the eval defines new variables in the current scope, // IMPORTANT! If the eval defines new variables in the current scope,
// all variable offsets from this point on will be mis-aligned. // all variable offsets from this point on will be mis-aligned.
@ -1020,8 +1016,7 @@ impl Engine {
target = target.into_owned(); target = target.into_owned();
} }
self.inc_operations(state) self.inc_operations(state, pos)?;
.map_err(|err| err.fill_position(pos))?;
args = if target.is_shared() || target.is_value() { args = if target.is_shared() || target.is_value() {
arg_values.insert(0, target.take_or_clone().flatten()); arg_values.insert(0, target.take_or_clone().flatten());
@ -1103,8 +1098,7 @@ impl Engine {
let (target, _, pos) = let (target, _, pos) =
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?; self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
self.inc_operations(state) self.inc_operations(state, pos)?;
.map_err(|err| err.fill_position(pos))?;
if target.is_shared() || target.is_value() { if target.is_shared() || target.is_value() {
arg_values[0] = target.take_or_clone().flatten(); arg_values[0] = target.take_or_clone().flatten();
@ -1133,7 +1127,7 @@ impl Engine {
let func = match module.get_qualified_fn(hash_script) { let func = match module.get_qualified_fn(hash_script) {
// Then search in Rust functions // Then search in Rust functions
None => { None => {
self.inc_operations(state)?; self.inc_operations(state, pos)?;
// Namespace-qualified Rust functions are indexed in two steps: // Namespace-qualified Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions, // 1) Calculate a hash in a similar manner to script-defined functions,