Refactor.

This commit is contained in:
Stephen Chung 2022-10-10 16:46:35 +08:00
parent 796206f293
commit d6b0f99781
15 changed files with 161 additions and 166 deletions

View File

@ -25,7 +25,7 @@ Enhancements
* `Scope` is now serializable and deserializable via `serde`. * `Scope` is now serializable and deserializable via `serde`.
* `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline. * `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline.
* `parse_json` function is added to parse a JSON string into an object map. * `parse_json` function is added to parse a JSON string into an object map.
* Methods return maximum limits (e.g. `max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code. * Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code.
Version 1.10.1 Version 1.10.1

View File

@ -101,6 +101,27 @@ impl Default for Limits {
} }
impl Engine { impl Engine {
/// Is there a data size limit set?
#[inline]
pub(crate) const fn has_data_size_limit(&self) -> bool {
self.limits.max_string_size.is_some()
|| {
#[cfg(not(feature = "no_index"))]
{
self.limits.max_array_size.is_some()
}
#[cfg(feature = "no_index")]
false
}
|| {
#[cfg(not(feature = "no_object"))]
{
self.limits.max_map_size.is_some()
}
#[cfg(feature = "no_object")]
false
}
}
/// Set the maximum levels of function calls allowed for a script in order to avoid /// Set the maximum levels of function calls allowed for a script in order to avoid
/// infinite recursion and stack overflows. /// infinite recursion and stack overflows.
/// ///
@ -137,10 +158,9 @@ impl Engine {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn max_operations(&self) -> u64 { pub const fn max_operations(&self) -> u64 {
if let Some(n) = self.limits.max_operations { match self.limits.max_operations {
n.get() Some(n) => n.get(),
} else { None => 0,
0
} }
} }
/// Set the maximum number of imported [modules][crate::Module] allowed for a script. /// Set the maximum number of imported [modules][crate::Module] allowed for a script.
@ -183,10 +203,9 @@ impl Engine {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn max_expr_depth(&self) -> usize { pub const fn max_expr_depth(&self) -> usize {
if let Some(n) = self.limits.max_expr_depth { match self.limits.max_expr_depth {
n.get() Some(n) => n.get(),
} else { None => 0,
0
} }
} }
/// The depth limit for expressions in functions (0 for unlimited). /// The depth limit for expressions in functions (0 for unlimited).
@ -196,10 +215,9 @@ impl Engine {
#[must_use] #[must_use]
pub const fn max_function_expr_depth(&self) -> usize { pub const fn max_function_expr_depth(&self) -> usize {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
return if let Some(n) = self.limits.max_function_expr_depth { return match self.limits.max_function_expr_depth {
n.get() Some(n) => n.get(),
} else { None => 0,
0
}; };
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
return 0; return 0;
@ -216,10 +234,9 @@ impl Engine {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn max_string_size(&self) -> usize { pub const fn max_string_size(&self) -> usize {
if let Some(n) = self.limits.max_string_size { match self.limits.max_string_size {
n.get() Some(n) => n.get(),
} else { None => 0,
0
} }
} }
/// Set the maximum length of [arrays][crate::Array] (0 for unlimited). /// Set the maximum length of [arrays][crate::Array] (0 for unlimited).
@ -238,10 +255,9 @@ impl Engine {
#[must_use] #[must_use]
pub const fn max_array_size(&self) -> usize { pub const fn max_array_size(&self) -> usize {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return if let Some(n) = self.limits.max_array_size { return match self.limits.max_array_size {
n.get() Some(n) => n.get(),
} else { None => 0,
0
}; };
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return 0; return 0;
@ -262,10 +278,9 @@ impl Engine {
#[must_use] #[must_use]
pub const fn max_map_size(&self) -> usize { pub const fn max_map_size(&self) -> usize {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return if let Some(n) = self.limits.max_map_size { return match self.limits.max_map_size {
n.get() Some(n) => n.get(),
} else { None => 0,
0
}; };
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return 0; return 0;

View File

@ -45,7 +45,6 @@ fn print_source(lines: &[String], pos: Position, offset: usize, window: (usize,
if n == line { if n == line {
if let Some(pos) = pos.position() { if let Some(pos) = pos.position() {
let shift = offset + line_no_len + marker.len() + 2; let shift = offset + line_no_len + marker.len() + 2;
println!("{0:>1$}{2:>3$}", "", shift, "\x1b[36m^\x1b[39m", pos + 10); println!("{0:>1$}{2:>3$}", "", shift, "\x1b[36m^\x1b[39m", pos + 10);
} }
} }
@ -310,10 +309,11 @@ fn debug_callback(
["node"] => { ["node"] => {
if pos.is_none() { if pos.is_none() {
println!("{:?}", node); println!("{:?}", node);
} else if let Some(source) = source {
println!("{:?} {} @ {:?}", node, source, pos);
} else { } else {
println!("{:?} @ {:?}", node, pos); match source {
Some(source) => println!("{:?} {} @ {:?}", node, source, pos),
None => println!("{:?} @ {:?}", node, pos),
}
} }
println!(); println!();
} }
@ -339,20 +339,14 @@ fn debug_callback(
["over" | "o"] => break Ok(DebuggerCommand::StepOver), ["over" | "o"] => break Ok(DebuggerCommand::StepOver),
["next" | "n"] => break Ok(DebuggerCommand::Next), ["next" | "n"] => break Ok(DebuggerCommand::Next),
["scope"] => println!("{}", context.scope()), ["scope"] => println!("{}", context.scope()),
["print" | "p", "this"] => { ["print" | "p", "this"] => match context.this_ptr() {
if let Some(value) = context.this_ptr() { Some(value) => println!("=> {:?}", value),
println!("=> {:?}", value); None => println!("`this` pointer is unbound."),
} else { },
println!("`this` pointer is unbound."); ["print" | "p", var_name] => match context.scope().get_value::<Dynamic>(var_name) {
} Some(value) => println!("=> {:?}", value),
} None => eprintln!("Variable not found: {}", var_name),
["print" | "p", var_name] => { },
if let Some(value) = context.scope().get_value::<Dynamic>(var_name) {
println!("=> {:?}", value);
} else {
eprintln!("Variable not found: {}", var_name);
}
}
["print" | "p"] => { ["print" | "p"] => {
println!("{}", context.scope().clone_visible()); println!("{}", context.scope().clone_visible());
if let Some(value) = context.this_ptr() { if let Some(value) = context.this_ptr() {

View File

@ -482,27 +482,30 @@ fn main() {
continue; continue;
} }
"!!" => { "!!" => {
if let Some(line) = rl.history().last() { match rl.history().last() {
Some(line) => {
replacement = Some(line.clone()); replacement = Some(line.clone());
replacement_index = history_offset + rl.history().len() - 1; replacement_index = history_offset + rl.history().len() - 1;
} else { }
eprintln!("No lines history!"); None => eprintln!("No lines history!"),
} }
continue; continue;
} }
_ if cmd.starts_with("!?") => { _ if cmd.starts_with("!?") => {
let text = cmd[2..].trim(); let text = cmd[2..].trim();
if let Some((n, line)) = rl let history = rl
.history() .history()
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|&(.., h)| h.contains(text)) .find(|&(.., h)| h.contains(text));
{
match history {
Some((n, line)) => {
replacement = Some(line.clone()); replacement = Some(line.clone());
replacement_index = history_offset + (rl.history().len() - 1 - n); replacement_index = history_offset + (rl.history().len() - 1 - n);
} else { }
eprintln!("History line not found: {}", text); None => eprintln!("History line not found: {}", text),
} }
continue; continue;
} }

View File

@ -4,7 +4,6 @@
use super::GlobalRuntimeState; use super::GlobalRuntimeState;
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR}; use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR};
use std::num::NonZeroUsize;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -21,40 +20,40 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref arr, ..) => { Union::Array(ref arr, ..) => {
arr.iter() arr.iter()
.fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
Union::Array(..) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a + 1, maps + m, strings + s) (ax + a + 1, mx + m, sx + s)
} }
Union::Blob(ref a, ..) => (arrays + 1 + a.len(), maps, strings), Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(..) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a + 1, maps + m, strings + s) (ax + a + 1, mx + m, sx + s)
} }
Union::Str(ref s, ..) => (arrays + 1, maps, strings + s.len()), Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()),
_ => (arrays + 1, maps, strings), _ => (ax + 1, mx, sx),
}) })
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref arr, ..) => (arr.len(), 0, 0), Union::Blob(ref blob, ..) => (blob.len(), 0, 0),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref map, ..) => { Union::Map(ref map, ..) => {
map.values() map.values()
.fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(..) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a, maps + m + 1, strings + s) (ax + a, mx + m + 1, sx + s)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref a, ..) => (arrays + a.len(), maps, strings), Union::Blob(ref a, ..) => (ax + a.len(), mx, sx),
Union::Map(..) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a, maps + m + 1, strings + s) (ax + a, mx + m + 1, sx + s)
} }
Union::Str(ref s, ..) => (arrays, maps + 1, strings + s.len()), Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()),
_ => (arrays, maps + 1, strings), _ => (ax, mx + 1, sx),
}) })
} }
Union::Str(ref s, ..) => (0, 0, s.len()), Union::Str(ref s, ..) => (0, 0, s.len()),
@ -70,43 +69,34 @@ impl Engine {
} }
} }
/// Is there a data size limit set?
pub(crate) const fn has_data_size_limit(&self) -> bool {
self.max_string_size() > 0 || self.max_array_size() > 0 || self.max_map_size() > 0
}
/// Raise an error if any data size exceeds limit. /// Raise an error if any data size exceeds limit.
pub(crate) fn raise_err_if_over_data_size_limit( pub(crate) fn raise_err_if_over_data_size_limit(
&self, &self,
sizes: (usize, usize, usize), (_arr, _map, s): (usize, usize, usize),
pos: Position, pos: Position,
) -> RhaiResultOf<()> { ) -> RhaiResultOf<()> {
let (_arr, _map, s) = sizes; if self
if s > self
.limits .limits
.max_string_size .max_string_size
.map_or(usize::MAX, NonZeroUsize::get) .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(), pos).into());
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
if _arr if self
> self
.limits .limits
.max_array_size .max_array_size
.map_or(usize::MAX, NonZeroUsize::get) .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".to_string(), pos).into());
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if _map if self
> self
.limits .limits
.max_map_size .max_map_size
.map_or(usize::MAX, NonZeroUsize::get) .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(), pos).into());
} }

View File

@ -496,13 +496,10 @@ impl Engine {
let event = match event { let event = match event {
Some(e) => e, Some(e) => e,
None => { None => match global.debugger.is_break_point(&global.source, node) {
if let Some(bp) = global.debugger.is_break_point(&global.source, node) { Some(bp) => DebuggerEvent::BreakPoint(bp),
DebuggerEvent::BreakPoint(bp) None => return Ok(None),
} else { },
return Ok(None);
}
}
}; };
self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level) self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level)

View File

@ -107,10 +107,9 @@ impl GlobalRuntimeState<'_> {
} else { } else {
crate::eval::DebuggerStatus::CONTINUE crate::eval::DebuggerStatus::CONTINUE
}, },
if let Some((ref init, ..)) = engine.debugger { match engine.debugger {
init(engine) Some((ref init, ..)) => init(engine),
} else { None => Dynamic::UNIT,
Dynamic::UNIT
}, },
), ),

View File

@ -77,14 +77,11 @@ impl BuildHasher for StraightHasherBuilder {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn get_hasher() -> ahash::AHasher { pub fn get_hasher() -> ahash::AHasher {
if let Some([seed1, seed2, seed3, seed4]) = config::AHASH_SEED { match config::AHASH_SEED {
if seed1 | seed2 | seed3 | seed4 != 0 { Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => {
ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher()
} else {
ahash::AHasher::default()
} }
} else { _ => ahash::AHasher::default(),
ahash::AHasher::default()
} }
} }

View File

@ -1742,10 +1742,9 @@ impl Module {
} }
if let Some(ref variables) = other.variables { if let Some(ref variables) = other.variables {
if let Some(ref mut m) = self.variables { match self.variables {
m.extend(variables.iter().map(|(k, v)| (k.clone(), v.clone()))); Some(ref mut m) => m.extend(variables.iter().map(|(k, v)| (k.clone(), v.clone()))),
} else { None => self.variables = other.variables.clone(),
self.variables = other.variables.clone();
} }
} }
@ -1767,10 +1766,9 @@ impl Module {
self.dynamic_functions_filter += &other.dynamic_functions_filter; self.dynamic_functions_filter += &other.dynamic_functions_filter;
if let Some(ref type_iterators) = other.type_iterators { if let Some(ref type_iterators) = other.type_iterators {
if let Some(ref mut t) = self.type_iterators { match self.type_iterators {
t.extend(type_iterators.iter().map(|(&k, v)| (k, v.clone()))); Some(ref mut t) => t.extend(type_iterators.iter().map(|(&k, v)| (k, v.clone()))),
} else { None => self.type_iterators = other.type_iterators.clone(),
self.type_iterators = other.type_iterators.clone();
} }
} }
self.all_functions = None; self.all_functions = None;

View File

@ -322,10 +322,9 @@ impl FileModuleResolver {
let scope = Scope::new(); let scope = Scope::new();
let m: Shared<_> = if let Some(global) = global { let m: Shared<_> = match global {
Module::eval_ast_as_new_raw(engine, scope, global, &ast) Some(global) => Module::eval_ast_as_new_raw(engine, scope, global, &ast),
} else { None => Module::eval_ast_as_new(scope, &ast, engine),
Module::eval_ast_as_new(scope, &ast, engine)
} }
.map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))? .map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
.into(); .into();

View File

@ -551,13 +551,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def } // switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut b.condition, state, false); optimize_expr(&mut b.condition, state, false);
let else_stmt = if let Some(index) = def_case { let else_stmt = match def_case {
Some(index) => {
let mut def_stmt = let mut def_stmt =
Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
optimize_stmt(&mut def_stmt, state, true); optimize_stmt(&mut def_stmt, state, true);
def_stmt.into() def_stmt.into()
} else { }
StmtBlock::NONE _ => StmtBlock::NONE,
}; };
*stmt = Stmt::If( *stmt = Stmt::If(
@ -616,13 +617,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def } // switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
optimize_expr(&mut condition, state, false); optimize_expr(&mut condition, state, false);
let else_stmt = if let Some(index) = def_case { let else_stmt = match def_case {
Some(index) => {
let mut def_stmt = let mut def_stmt =
Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
optimize_stmt(&mut def_stmt, state, true); optimize_stmt(&mut def_stmt, state, true);
def_stmt.into() def_stmt.into()
} else { }
StmtBlock::NONE _ => StmtBlock::NONE,
}; };
let if_stmt = let if_stmt =
@ -665,12 +667,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
// Promote the default case // Promote the default case
state.set_dirty(); state.set_dirty();
if let Some(index) = def_case { match def_case {
Some(index) => {
let mut def_stmt = Stmt::Expr(mem::take(&mut expressions[*index].expr).into()); let mut def_stmt = Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
optimize_stmt(&mut def_stmt, state, true); optimize_stmt(&mut def_stmt, state, true);
*stmt = def_stmt; *stmt = def_stmt;
} else { }
*stmt = StmtBlock::empty(*pos).into(); _ => *stmt = StmtBlock::empty(*pos).into(),
} }
} }
// switch // switch

View File

@ -82,12 +82,13 @@ mod map_functions {
/// print(m); // prints "#{a: 1, b: 42, c: 3, x: 0}" /// print(m); // prints "#{a: 1, b: 42, c: 3, x: 0}"
/// ``` /// ```
pub fn set(map: &mut Map, property: &str, value: Dynamic) { pub fn set(map: &mut Map, property: &str, value: Dynamic) {
if let Some(value_ref) = map.get_mut(property) { match map.get_mut(property) {
*value_ref = value; Some(value_ref) => *value_ref = value,
} else { _ => {
map.insert(property.into(), value); map.insert(property.into(), value);
} }
} }
}
/// Clear the object map. /// Clear the object map.
pub fn clear(map: &mut Map) { pub fn clear(map: &mut Map) {
if !map.is_empty() { if !map.is_empty() {

View File

@ -239,10 +239,9 @@ mod string_functions {
/// Clear the string, making it empty. /// Clear the string, making it empty.
pub fn clear(string: &mut ImmutableString) { pub fn clear(string: &mut ImmutableString) {
if !string.is_empty() { if !string.is_empty() {
if let Some(s) = string.get_mut() { match string.get_mut() {
s.clear(); Some(s) => s.clear(),
} else { _ => *string = ImmutableString::new(),
*string = ImmutableString::new();
} }
} }
} }
@ -287,13 +286,15 @@ mod string_functions {
/// print(text); // prints "hello" /// print(text); // prints "hello"
/// ``` /// ```
pub fn trim(string: &mut ImmutableString) { pub fn trim(string: &mut ImmutableString) {
if let Some(s) = string.get_mut() { match string.get_mut() {
Some(s) => {
let trimmed = s.trim(); let trimmed = s.trim();
if trimmed != s { if trimmed != s {
*s = trimmed.into(); *s = trimmed.into();
} }
} else { }
None => {
let trimmed = string.trim(); let trimmed = string.trim();
if trimmed != string { if trimmed != string {
@ -301,6 +302,7 @@ mod string_functions {
} }
} }
} }
}
/// Remove the last character from the string and return it. /// Remove the last character from the string and return it.
/// ///
/// If the string is empty, `()` is returned. /// If the string is empty, `()` is returned.

View File

@ -215,10 +215,9 @@ impl<'de> Deserialize<'de> for Scope<'de> {
where where
A: SeqAccess<'de>, A: SeqAccess<'de>,
{ {
let mut scope = if let Some(size) = access.size_hint() { let mut scope = match access.size_hint() {
Scope::with_capacity(size) Some(size) => Scope::with_capacity(size),
} else { None => Scope::new(),
Scope::new()
}; };
while let Some(ScopeEntry { while let Some(ScopeEntry {

View File

@ -37,10 +37,9 @@ impl Serialize for Dynamic {
Union::Decimal(ref x, ..) => { Union::Decimal(ref x, ..) => {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
if let Some(v) = x.to_f64() { match x.to_f64() {
ser.serialize_f64(v) Some(v) => ser.serialize_f64(v),
} else { None => ser.serialize_str(&x.to_string()),
ser.serialize_str(&x.to_string())
} }
} }
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
@ -48,10 +47,9 @@ impl Serialize for Dynamic {
Union::Decimal(ref x, ..) => { Union::Decimal(ref x, ..) => {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
if let Some(v) = x.to_f32() { match x.to_f32() {
ser.serialize_f32(v) Some(v) => ser.serialize_f32(v),
} else { _ => ser.serialize_str(&x.to_string()),
ser.serialize_str(&x.to_string())
} }
} }