commit
77630942e7
10
CHANGELOG.md
10
CHANGELOG.md
@ -9,7 +9,13 @@ New features
|
|||||||
|
|
||||||
### Stable hashing
|
### Stable hashing
|
||||||
|
|
||||||
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher in order to force stable (i.e. deterministic) hashes for function signatures.
|
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. This is necessary when using Rhai across shared-library boundaries.
|
||||||
|
* A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation.
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
@ -18,6 +24,8 @@ Enhancements
|
|||||||
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
||||||
* `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.
|
||||||
|
* 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
|
||||||
|
@ -19,7 +19,7 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smallvec = { version = "1.7", default-features = false, features = ["union", "const_new", "const_generics"] }
|
smallvec = { version = "1.7", default-features = false, features = ["union", "const_new", "const_generics"] }
|
||||||
ahash = { version = "0.8", default-features = false }
|
ahash = { version = "0.8", default-features = false, features = ["compile-time-rng"] }
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
bitflags = { version = "1", default-features = false }
|
bitflags = { version = "1", default-features = false }
|
||||||
smartstring = { version = "1", default-features = false }
|
smartstring = { version = "1", default-features = false }
|
||||||
@ -40,8 +40,8 @@ serde_bytes = "0.11"
|
|||||||
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std", "ahash/runtime-rng"] # ahash/runtime-rng trumps ahash/compile-time-rng
|
||||||
std = ["ahash/std", "ahash/runtime-rng", "num-traits/std", "smartstring/std"]
|
std = ["ahash/std", "num-traits/std", "smartstring/std"]
|
||||||
unchecked = [] # unchecked arithmetic
|
unchecked = [] # unchecked arithmetic
|
||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
no_position = [] # do not track position in the parser
|
no_position = [] # do not track position in the parser
|
||||||
@ -64,7 +64,7 @@ debugging = ["internals"] # enable debugging
|
|||||||
serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types
|
serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types
|
||||||
|
|
||||||
# compiling for no-std
|
# compiling for no-std
|
||||||
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng", "hashbrown/ahash-compile-time-rng"]
|
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "hashbrown"]
|
||||||
|
|
||||||
# compiling for WASM
|
# compiling for WASM
|
||||||
wasm-bindgen = ["instant/wasm-bindgen"]
|
wasm-bindgen = ["instant/wasm-bindgen"]
|
||||||
|
1
build.rs
1
build.rs
@ -10,7 +10,6 @@ fn main() {
|
|||||||
|
|
||||||
f.write_fmt(format_args!(
|
f.write_fmt(format_args!(
|
||||||
"//! Configuration settings for this Rhai build
|
"//! Configuration settings for this Rhai build
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
"
|
"
|
||||||
))
|
))
|
||||||
|
@ -36,6 +36,8 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
|||||||
type Item = i64;
|
type Item = i64;
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
vec![self.x - 1, self.x, self.x + 1].into_iter()
|
vec![self.x - 1, self.x, self.x + 1].into_iter()
|
||||||
}
|
}
|
||||||
|
@ -295,6 +295,6 @@ impl Engine {
|
|||||||
|
|
||||||
let mut peekable = stream.peekable();
|
let mut peekable = stream.peekable();
|
||||||
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
|
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
|
||||||
self.parse_global_expr(&mut peekable, &mut state, self.optimization_level)
|
self.parse_global_expr(&mut peekable, &mut state, |_| {}, self.optimization_level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,6 @@ impl Deref for Expression<'_> {
|
|||||||
type Target = Expr;
|
type Target = Expr;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ pub struct DefinitionsConfig {
|
|||||||
|
|
||||||
impl Default for DefinitionsConfig {
|
impl Default for DefinitionsConfig {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
write_headers: false,
|
write_headers: false,
|
||||||
|
@ -122,6 +122,7 @@ impl Engine {
|
|||||||
let ast = self.parse_global_expr(
|
let ast = self.parse_global_expr(
|
||||||
&mut stream.peekable(),
|
&mut stream.peekable(),
|
||||||
&mut state,
|
&mut state,
|
||||||
|
|_| {},
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
OptimizationLevel::None,
|
OptimizationLevel::None,
|
||||||
#[cfg(feature = "no_optimize")]
|
#[cfg(feature = "no_optimize")]
|
||||||
|
@ -23,7 +23,7 @@ impl Engine {
|
|||||||
/// JSON sub-objects are handled transparently.
|
/// JSON sub-objects are handled transparently.
|
||||||
///
|
///
|
||||||
/// This function can be used together with [`format_map_as_json`] to work with JSON texts
|
/// This function can be used together with [`format_map_as_json`] to work with JSON texts
|
||||||
/// without using the [`serde`](https://crates.io/crates/serde) crate (which is heavy).
|
/// without using the [`serde_json`](https://crates.io/crates/serde_json) crate (which is heavy).
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -122,6 +122,7 @@ impl Engine {
|
|||||||
let ast = self.parse_global_expr(
|
let ast = self.parse_global_expr(
|
||||||
&mut stream.peekable(),
|
&mut stream.peekable(),
|
||||||
&mut state,
|
&mut state,
|
||||||
|
|s| s.allow_unquoted_map_properties = false,
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
OptimizationLevel::None,
|
OptimizationLevel::None,
|
||||||
#[cfg(feature = "no_optimize")]
|
#[cfg(feature = "no_optimize")]
|
||||||
@ -137,7 +138,7 @@ impl Engine {
|
|||||||
/// Not available under `no_std`.
|
/// Not available under `no_std`.
|
||||||
///
|
///
|
||||||
/// This function can be used together with [`Engine::parse_json`] to work with JSON texts
|
/// This function can be used together with [`Engine::parse_json`] to work with JSON texts
|
||||||
/// without using the [`serde`](https://crates.io/crates/serde) crate (which is heavy).
|
/// without using the [`serde_json`](https://crates.io/crates/serde_json) crate (which is heavy).
|
||||||
///
|
///
|
||||||
/// # Data types
|
/// # Data types
|
||||||
///
|
///
|
||||||
|
@ -1,12 +1,31 @@
|
|||||||
//! Settings for [`Engine`]'s limitations.
|
//! Settings for [`Engine`]'s limitations.
|
||||||
#![cfg(not(feature = "unchecked"))]
|
#![cfg(not(feature = "unchecked"))]
|
||||||
|
|
||||||
use super::default_limits;
|
|
||||||
use crate::Engine;
|
use crate::Engine;
|
||||||
use std::num::{NonZeroU64, NonZeroUsize};
|
use std::num::{NonZeroU64, NonZeroUsize};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
pub mod default_limits {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub const MAX_CALL_STACK_DEPTH: usize = 8;
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub const MAX_EXPR_DEPTH: usize = 32;
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub const MAX_CALL_STACK_DEPTH: usize = 64;
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
pub const MAX_EXPR_DEPTH: usize = 64;
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
||||||
|
}
|
||||||
|
|
||||||
/// A type containing all the limits imposed by the [`Engine`].
|
/// A type containing all the limits imposed by the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under `unchecked`.
|
/// Not available under `unchecked`.
|
||||||
@ -75,12 +94,34 @@ impl Limits {
|
|||||||
|
|
||||||
impl Default for Limits {
|
impl Default for Limits {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
||||||
///
|
///
|
||||||
@ -93,12 +134,14 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// The maximum levels of function calls allowed for a script.
|
/// The maximum levels of function calls allowed for a script.
|
||||||
///
|
///
|
||||||
/// Not available under `unchecked` or `no_function`.
|
/// Zero under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn max_call_levels(&self) -> usize {
|
pub const fn max_call_levels(&self) -> usize {
|
||||||
self.limits.max_call_stack_depth
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
return self.limits.max_call_stack_depth;
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
/// Set the maximum number of operations allowed for a script to run to avoid
|
/// Set the maximum number of operations allowed for a script to run to avoid
|
||||||
/// consuming too much resources (0 for unlimited).
|
/// consuming too much resources (0 for unlimited).
|
||||||
@ -115,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.
|
||||||
@ -132,12 +174,14 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
||||||
///
|
///
|
||||||
/// Not available under `unchecked` or `no_module`.
|
/// Zero under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn max_modules(&self) -> usize {
|
pub const fn max_modules(&self) -> usize {
|
||||||
self.limits.max_modules
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
return self.limits.max_modules;
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
/// Set the depth limits for expressions (0 for unlimited).
|
/// Set the depth limits for expressions (0 for unlimited).
|
||||||
///
|
///
|
||||||
@ -156,29 +200,27 @@ impl Engine {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// The depth limit for expressions (0 for unlimited).
|
/// The depth limit for expressions (0 for unlimited).
|
||||||
///
|
|
||||||
/// Not available under `unchecked`.
|
|
||||||
#[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).
|
||||||
///
|
///
|
||||||
/// Not available under `unchecked` or `no_function`.
|
/// Zero under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn max_function_expr_depth(&self) -> usize {
|
pub const fn max_function_expr_depth(&self) -> usize {
|
||||||
if let Some(n) = self.limits.max_function_expr_depth {
|
#[cfg(not(feature = "no_function"))]
|
||||||
n.get()
|
return match self.limits.max_function_expr_depth {
|
||||||
} else {
|
Some(n) => n.get(),
|
||||||
0
|
None => 0,
|
||||||
}
|
};
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
/// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
/// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||||
///
|
///
|
||||||
@ -189,15 +231,12 @@ impl Engine {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||||
///
|
|
||||||
/// Not available under `unchecked`.
|
|
||||||
#[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).
|
||||||
@ -211,16 +250,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||||
///
|
///
|
||||||
/// Not available under `unchecked` or `no_index`.
|
/// Zero under `no_index`.
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn max_array_size(&self) -> usize {
|
pub const fn max_array_size(&self) -> usize {
|
||||||
if let Some(n) = self.limits.max_array_size {
|
#[cfg(not(feature = "no_index"))]
|
||||||
n.get()
|
return match self.limits.max_array_size {
|
||||||
} else {
|
Some(n) => n.get(),
|
||||||
0
|
None => 0,
|
||||||
}
|
};
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
/// Set the maximum size of [object maps][crate::Map] (0 for unlimited).
|
/// Set the maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||||
///
|
///
|
||||||
@ -233,15 +273,16 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||||
///
|
///
|
||||||
/// Not available under `unchecked` or `no_object`.
|
/// Zero under `no_object`.
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn max_map_size(&self) -> usize {
|
pub const fn max_map_size(&self) -> usize {
|
||||||
if let Some(n) = self.limits.max_map_size {
|
#[cfg(not(feature = "no_object"))]
|
||||||
n.get()
|
return match self.limits.max_map_size {
|
||||||
} else {
|
Some(n) => n.get(),
|
||||||
0
|
None => 0,
|
||||||
}
|
};
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,28 +43,7 @@ use std::prelude::v1::*;
|
|||||||
|
|
||||||
pub mod default_limits {
|
pub mod default_limits {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(debug_assertions)]
|
pub use super::limits::default_limits::*;
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
pub const MAX_CALL_STACK_DEPTH: usize = 8;
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub const MAX_EXPR_DEPTH: usize = 32;
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
pub const MAX_CALL_STACK_DEPTH: usize = 64;
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
pub const MAX_EXPR_DEPTH: usize = 64;
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(debug_assertions))]
|
|
||||||
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
|
|
||||||
|
|
||||||
pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
|
pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
|
||||||
}
|
}
|
||||||
|
@ -226,12 +226,12 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_type_with_name_raw(
|
pub fn register_type_with_name_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
fully_qualified_type_path: impl Into<Identifier>,
|
type_path: impl Into<Identifier>,
|
||||||
name: impl Into<Identifier>,
|
name: impl Into<Identifier>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
// Add the pretty-print type name into the map
|
// Add the pretty-print type name into the map
|
||||||
self.global_namespace_mut()
|
self.global_namespace_mut()
|
||||||
.set_custom_type_raw(fully_qualified_type_path, name);
|
.set_custom_type_raw(type_path, name);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Register a type iterator for an iterable type with the [`Engine`].
|
/// Register a type iterator for an iterable type with the [`Engine`].
|
||||||
|
@ -36,6 +36,7 @@ pub struct AST {
|
|||||||
|
|
||||||
impl Default for AST {
|
impl Default for AST {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::empty()
|
Self::empty()
|
||||||
}
|
}
|
||||||
@ -69,7 +70,7 @@ impl fmt::Debug for AST {
|
|||||||
impl AST {
|
impl AST {
|
||||||
/// Create a new [`AST`].
|
/// Create a new [`AST`].
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
statements: impl IntoIterator<Item = Stmt>,
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
@ -89,7 +90,7 @@ impl AST {
|
|||||||
/// _(internals)_ Create a new [`AST`].
|
/// _(internals)_ Create a new [`AST`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
statements: impl IntoIterator<Item = Stmt>,
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
@ -108,7 +109,7 @@ impl AST {
|
|||||||
}
|
}
|
||||||
/// Create a new [`AST`] with a source name.
|
/// Create a new [`AST`] with a source name.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn new_with_source(
|
pub(crate) fn new_with_source(
|
||||||
statements: impl IntoIterator<Item = Stmt>,
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
@ -126,7 +127,7 @@ impl AST {
|
|||||||
/// _(internals)_ Create a new [`AST`] with a source name.
|
/// _(internals)_ Create a new [`AST`] with a source name.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_with_source(
|
pub fn new_with_source(
|
||||||
statements: impl IntoIterator<Item = Stmt>,
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
@ -157,7 +158,7 @@ impl AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the source, if any.
|
/// Get the source, if any.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn source(&self) -> Option<&str> {
|
pub fn source(&self) -> Option<&str> {
|
||||||
if self.source.is_empty() {
|
if self.source.is_empty() {
|
||||||
@ -664,7 +665,6 @@ impl AST {
|
|||||||
self.combine_filtered_impl(other, filter)
|
self.combine_filtered_impl(other, filter)
|
||||||
}
|
}
|
||||||
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
||||||
#[inline]
|
|
||||||
fn combine_filtered_impl(
|
fn combine_filtered_impl(
|
||||||
&mut self,
|
&mut self,
|
||||||
other: Self,
|
other: Self,
|
||||||
@ -957,19 +957,21 @@ pub enum ASTNode<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Stmt> for ASTNode<'a> {
|
impl<'a> From<&'a Stmt> for ASTNode<'a> {
|
||||||
|
#[inline(always)]
|
||||||
fn from(stmt: &'a Stmt) -> Self {
|
fn from(stmt: &'a Stmt) -> Self {
|
||||||
Self::Stmt(stmt)
|
Self::Stmt(stmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Expr> for ASTNode<'a> {
|
impl<'a> From<&'a Expr> for ASTNode<'a> {
|
||||||
|
#[inline(always)]
|
||||||
fn from(expr: &'a Expr) -> Self {
|
fn from(expr: &'a Expr) -> Self {
|
||||||
Self::Expr(expr)
|
Self::Expr(expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for ASTNode<'_> {
|
impl PartialEq for ASTNode<'_> {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Stmt(x), Self::Stmt(y)) => ptr::eq(*x, *y),
|
(Self::Stmt(x), Self::Stmt(y)) => ptr::eq(*x, *y),
|
||||||
@ -986,8 +988,8 @@ impl ASTNode<'_> {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
ASTNode::Stmt(stmt) => stmt.position(),
|
Self::Stmt(stmt) => stmt.position(),
|
||||||
ASTNode::Expr(expr) => expr.position(),
|
Self::Expr(expr) => expr.position(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,8 @@ impl CustomExpr {
|
|||||||
/// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)?
|
/// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)?
|
||||||
///
|
///
|
||||||
/// A self-terminated custom syntax always ends in `$block$`, `}` or `;`
|
/// A self-terminated custom syntax always ends in `$block$`, `}` or `;`
|
||||||
#[must_use]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub const fn is_self_terminated(&self) -> bool {
|
pub const fn is_self_terminated(&self) -> bool {
|
||||||
self.self_terminated
|
self.self_terminated
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ impl fmt::Debug for FnCallHashes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<u64> for FnCallHashes {
|
impl From<u64> for FnCallHashes {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn from(hash: u64) -> Self {
|
fn from(hash: u64) -> Self {
|
||||||
let hash = if hash == 0 { ALT_ZERO_HASH } else { hash };
|
let hash = if hash == 0 { ALT_ZERO_HASH } else { hash };
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ impl From<u64> for FnCallHashes {
|
|||||||
|
|
||||||
impl FnCallHashes {
|
impl FnCallHashes {
|
||||||
/// Create a [`FnCallHashes`] with only the native Rust hash.
|
/// Create a [`FnCallHashes`] with only the native Rust hash.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn from_native(hash: u64) -> Self {
|
pub const fn from_native(hash: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -158,7 +158,7 @@ impl FnCallHashes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a [`FnCallHashes`] with both native Rust and script function hashes.
|
/// Create a [`FnCallHashes`] with both native Rust and script function hashes.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
|
pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -252,7 +252,7 @@ pub struct FloatWrapper<F>(F);
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
impl Hash for FloatWrapper<crate::FLOAT> {
|
impl Hash for FloatWrapper<crate::FLOAT> {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.0.to_ne_bytes().hash(state);
|
self.0.to_ne_bytes().hash(state);
|
||||||
}
|
}
|
||||||
@ -281,7 +281,6 @@ impl<F: Float> Deref for FloatWrapper<F> {
|
|||||||
type Target = F;
|
type Target = F;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
@ -290,7 +289,6 @@ impl<F: Float> Deref for FloatWrapper<F> {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
impl<F: Float> DerefMut for FloatWrapper<F> {
|
impl<F: Float> DerefMut for FloatWrapper<F> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.0
|
&mut self.0
|
||||||
}
|
}
|
||||||
@ -447,6 +445,7 @@ pub enum Expr {
|
|||||||
|
|
||||||
impl Default for Expr {
|
impl Default for Expr {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Unit(Position::NONE)
|
Self::Unit(Position::NONE)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ pub enum FnAccess {
|
|||||||
|
|
||||||
impl FnAccess {
|
impl FnAccess {
|
||||||
/// Is this function private?
|
/// Is this function private?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_private(self) -> bool {
|
pub const fn is_private(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
@ -27,7 +27,7 @@ impl FnAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this function public?
|
/// Is this function public?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_public(self) -> bool {
|
pub const fn is_public(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -40,7 +40,6 @@ impl Deref for Ident {
|
|||||||
type Target = ImmutableString;
|
type Target = ImmutableString;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
@ -48,7 +47,6 @@ impl Deref for Ident {
|
|||||||
|
|
||||||
impl DerefMut for Ident {
|
impl DerefMut for Ident {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.name
|
&mut self.name
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,6 @@ impl Deref for Namespace {
|
|||||||
type Target = StaticVec<Ident>;
|
type Target = StaticVec<Ident>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
@ -80,14 +79,13 @@ impl Deref for Namespace {
|
|||||||
|
|
||||||
impl DerefMut for Namespace {
|
impl DerefMut for Namespace {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.path
|
&mut self.path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<Ident>> for Namespace {
|
impl From<Vec<Ident>> for Namespace {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn from(mut path: Vec<Ident>) -> Self {
|
fn from(mut path: Vec<Ident>) -> Self {
|
||||||
path.shrink_to_fit();
|
path.shrink_to_fit();
|
||||||
Self {
|
Self {
|
||||||
@ -98,7 +96,7 @@ impl From<Vec<Ident>> for Namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<StaticVec<Ident>> for Namespace {
|
impl From<StaticVec<Ident>> for Namespace {
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
fn from(mut path: StaticVec<Ident>) -> Self {
|
fn from(mut path: StaticVec<Ident>) -> Self {
|
||||||
path.shrink_to_fit();
|
path.shrink_to_fit();
|
||||||
Self { index: None, path }
|
Self { index: None, path }
|
||||||
|
@ -209,6 +209,7 @@ impl IntoIterator for RangeCase {
|
|||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
match self {
|
match self {
|
||||||
Self::ExclusiveInt(r, ..) => Box::new(r),
|
Self::ExclusiveInt(r, ..) => Box::new(r),
|
||||||
@ -426,7 +427,6 @@ impl Deref for StmtBlock {
|
|||||||
type Target = StmtBlockContainer;
|
type Target = StmtBlockContainer;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.block
|
&self.block
|
||||||
}
|
}
|
||||||
@ -434,7 +434,6 @@ impl Deref for StmtBlock {
|
|||||||
|
|
||||||
impl DerefMut for StmtBlock {
|
impl DerefMut for StmtBlock {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.block
|
&mut self.block
|
||||||
}
|
}
|
||||||
@ -607,6 +606,7 @@ pub enum Stmt {
|
|||||||
|
|
||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Noop(Position::NONE)
|
Self::Noop(Position::NONE)
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
//! Configuration settings for this Rhai build
|
//! Configuration settings for this Rhai build
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
pub const AHASH_SEED: Option<[u64; 4]> = None;
|
pub const AHASH_SEED: Option<[u64; 4]> = None;
|
||||||
|
@ -190,6 +190,7 @@ impl fmt::Debug for Engine {
|
|||||||
|
|
||||||
impl Default for Engine {
|
impl Default for Engine {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -346,9 +347,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that it is valid.
|
/// Check a result to ensure that it is valid.
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult {
|
pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if let Ok(ref r) = result {
|
if let Ok(ref r) = result {
|
||||||
self.check_data_size(r, _pos)?;
|
self.check_data_size(r, _pos)?;
|
||||||
}
|
}
|
||||||
@ -356,3 +357,81 @@ impl Engine {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
impl Engine {
|
||||||
|
/// The maximum levels of function calls allowed for a script.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_call_levels(&self) -> usize {
|
||||||
|
usize::MAX
|
||||||
|
}
|
||||||
|
/// The maximum number of operations allowed for a script to run (0 for unlimited).
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_operations(&self) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_modules(&self) -> usize {
|
||||||
|
usize::MAX
|
||||||
|
}
|
||||||
|
/// The depth limit for expressions (0 for unlimited).
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_expr_depth(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
/// The depth limit for expressions in functions (0 for unlimited).
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_function_expr_depth(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_string_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_array_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn max_map_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the number of operations stay within limit.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) const fn track_operation(
|
||||||
|
&self,
|
||||||
|
_: &crate::eval::GlobalRuntimeState,
|
||||||
|
_: Position,
|
||||||
|
) -> crate::RhaiResultOf<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether the size of a [`Dynamic`] is within limits.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) const fn check_data_size(
|
||||||
|
&self,
|
||||||
|
_: &Dynamic,
|
||||||
|
_: Position,
|
||||||
|
) -> crate::RhaiResultOf<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check a result to ensure that it is valid.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) const fn check_return_value(&self, result: RhaiResult, _: Position) -> RhaiResult {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -50,7 +50,7 @@ impl Engine {
|
|||||||
idx_values: &mut FnArgsVec<Dynamic>,
|
idx_values: &mut FnArgsVec<Dynamic>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<(Dynamic, &OpAssignment)>,
|
new_val: &mut Option<(Dynamic, &OpAssignment)>,
|
||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
let is_ref_mut = target.is_ref();
|
let is_ref_mut = target.is_ref();
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||||
|
|
||||||
let idx_val = idx_values.pop().unwrap();
|
let idx_val = &mut idx_values.pop().unwrap();
|
||||||
let mut idx_val_for_setter = idx_val.clone();
|
let mut idx_val_for_setter = idx_val.clone();
|
||||||
let idx_pos = x.lhs.start_position();
|
let idx_pos = x.lhs.start_position();
|
||||||
let rhs_chain = rhs.into();
|
let rhs_chain = rhs.into();
|
||||||
@ -119,19 +119,18 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||||
|
|
||||||
let (new_val, op_info) = new_val.expect("`Some`");
|
let (new_val, op_info) = new_val.take().expect("`Some`");
|
||||||
let idx_val = idx_values.pop().unwrap();
|
let idx_val = &mut idx_values.pop().unwrap();
|
||||||
let mut idx_val2 = idx_val.clone();
|
let idx = &mut idx_val.clone();
|
||||||
|
|
||||||
let try_setter = match self.get_indexed_mut(
|
let try_setter = match self.get_indexed_mut(
|
||||||
global, caches, lib, target, idx_val, pos, true, false, level,
|
global, caches, lib, target, idx, pos, true, false, level,
|
||||||
) {
|
) {
|
||||||
// Indexed value is not a temp value - update directly
|
// Indexed value is not a temp value - update directly
|
||||||
Ok(ref mut obj_ptr) => {
|
Ok(ref mut obj_ptr) => {
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
global, caches, lib, op_info, obj_ptr, root, new_val, level,
|
global, caches, lib, op_info, obj_ptr, root, new_val, level,
|
||||||
)?;
|
)?;
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
self.check_data_size(obj_ptr, op_info.pos)?;
|
self.check_data_size(obj_ptr, op_info.pos)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -143,11 +142,10 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(mut new_val) = try_setter {
|
if let Some(mut new_val) = try_setter {
|
||||||
let idx = &mut idx_val2;
|
|
||||||
|
|
||||||
// Is this an op-assignment?
|
// Is this an op-assignment?
|
||||||
if op_info.is_op_assignment() {
|
if op_info.is_op_assignment() {
|
||||||
let idx = &mut idx.clone();
|
let idx = &mut idx_val.clone();
|
||||||
|
|
||||||
// Call the index getter to get the current value
|
// Call the index getter to get the current value
|
||||||
if let Ok(val) =
|
if let Ok(val) =
|
||||||
self.call_indexer_get(global, caches, lib, target, idx, level)
|
self.call_indexer_get(global, caches, lib, target, idx, level)
|
||||||
@ -160,15 +158,15 @@ impl Engine {
|
|||||||
)?;
|
)?;
|
||||||
// Replace new value
|
// Replace new value
|
||||||
new_val = val.take_or_clone();
|
new_val = val.take_or_clone();
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
self.check_data_size(&new_val, op_info.pos)?;
|
self.check_data_size(&new_val, op_info.pos)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to call index setter
|
// Try to call index setter
|
||||||
let new_val = &mut new_val;
|
let new_val = &mut new_val;
|
||||||
|
|
||||||
self.call_indexer_set(
|
self.call_indexer_set(
|
||||||
global, caches, lib, target, idx, new_val, is_ref_mut, level,
|
global, caches, lib, target, idx_val, new_val, is_ref_mut, level,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +177,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||||
|
|
||||||
let idx_val = idx_values.pop().unwrap();
|
let idx_val = &mut idx_values.pop().unwrap();
|
||||||
|
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
global, caches, lib, target, idx_val, pos, false, true, level,
|
global, caches, lib, target, idx_val, pos, false, true, level,
|
||||||
@ -236,8 +234,8 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||||
|
|
||||||
let index = x.2.clone().into();
|
let index = &mut x.2.clone().into();
|
||||||
let (new_val, op_info) = new_val.expect("`Some`");
|
let (new_val, op_info) = new_val.take().expect("`Some`");
|
||||||
{
|
{
|
||||||
let val_target = &mut self.get_indexed_mut(
|
let val_target = &mut self.get_indexed_mut(
|
||||||
global, caches, lib, target, index, *pos, true, false, level,
|
global, caches, lib, target, index, *pos, true, false, level,
|
||||||
@ -246,7 +244,6 @@ impl Engine {
|
|||||||
global, caches, lib, op_info, val_target, root, new_val, level,
|
global, caches, lib, op_info, val_target, root, new_val, level,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
self.check_data_size(target.source(), op_info.pos)?;
|
self.check_data_size(target.source(), op_info.pos)?;
|
||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
@ -255,7 +252,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||||
|
|
||||||
let index = x.2.clone().into();
|
let index = &mut x.2.clone().into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
global, caches, lib, target, index, *pos, false, false, level,
|
global, caches, lib, target, index, *pos, false, false, level,
|
||||||
)?;
|
)?;
|
||||||
@ -267,7 +264,7 @@ impl Engine {
|
|||||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||||
|
|
||||||
let ((getter, hash_get), (setter, hash_set), name) = &**x;
|
let ((getter, hash_get), (setter, hash_set), name) = &**x;
|
||||||
let (mut new_val, op_info) = new_val.expect("`Some`");
|
let (mut new_val, op_info) = new_val.take().expect("`Some`");
|
||||||
|
|
||||||
if op_info.is_op_assignment() {
|
if op_info.is_op_assignment() {
|
||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
@ -368,7 +365,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, _node, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, _node, level)?;
|
||||||
|
|
||||||
let index = p.2.clone().into();
|
let index = &mut p.2.clone().into();
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
global, caches, lib, target, index, pos, false, true, level,
|
global, caches, lib, target, index, pos, false, true, level,
|
||||||
)?
|
)?
|
||||||
@ -558,7 +555,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<(Dynamic, &OpAssignment)>,
|
new_val: &mut Option<(Dynamic, &OpAssignment)>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let chain_type = ChainType::from(expr);
|
let chain_type = ChainType::from(expr);
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr {
|
||||||
@ -595,7 +592,7 @@ impl Engine {
|
|||||||
// All other patterns - evaluate the arguments chain
|
// All other patterns - evaluate the arguments chain
|
||||||
_ => {
|
_ => {
|
||||||
self.eval_dot_index_chain_arguments(
|
self.eval_dot_index_chain_arguments(
|
||||||
scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0,
|
scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values,
|
||||||
level,
|
level,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@ -606,9 +603,7 @@ impl Engine {
|
|||||||
Expr::Variable(x, .., var_pos) => {
|
Expr::Variable(x, .., var_pos) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, lhs, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, lhs, level)?;
|
||||||
|
self.track_operation(global, *var_pos)?;
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
self.inc_operations(&mut global.num_operations, *var_pos)?;
|
|
||||||
|
|
||||||
let (mut target, ..) =
|
let (mut target, ..) =
|
||||||
self.search_namespace(scope, global, lib, this_ptr, lhs, level)?;
|
self.search_namespace(scope, global, lib, this_ptr, lhs, level)?;
|
||||||
@ -653,11 +648,9 @@ impl Engine {
|
|||||||
parent_options: ASTFlags,
|
parent_options: ASTFlags,
|
||||||
_parent_chain_type: ChainType,
|
_parent_chain_type: ChainType,
|
||||||
idx_values: &mut FnArgsVec<Dynamic>,
|
idx_values: &mut FnArgsVec<Dynamic>,
|
||||||
size: usize,
|
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, expr.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -731,7 +724,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.eval_dot_index_chain_arguments(
|
self.eval_dot_index_chain_arguments(
|
||||||
scope, global, caches, lib, this_ptr, rhs, *options, chain_type, idx_values,
|
scope, global, caches, lib, this_ptr, rhs, *options, chain_type, idx_values,
|
||||||
size, level,
|
level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if !_arg_values.is_empty() {
|
if !_arg_values.is_empty() {
|
||||||
@ -809,14 +802,13 @@ impl Engine {
|
|||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
target: &'t mut Dynamic,
|
target: &'t mut Dynamic,
|
||||||
mut idx: Dynamic,
|
idx: &mut Dynamic,
|
||||||
idx_pos: Position,
|
idx_pos: Position,
|
||||||
_add_if_not_found: bool,
|
_add_if_not_found: bool,
|
||||||
use_indexers: bool,
|
use_indexers: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResultOf<Target<'t>> {
|
) -> RhaiResultOf<Target<'t>> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, Position::NONE)?;
|
||||||
self.inc_operations(&mut global.num_operations, Position::NONE)?;
|
|
||||||
|
|
||||||
match target {
|
match target {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1018,7 +1010,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ if use_indexers => self
|
_ if use_indexers => self
|
||||||
.call_indexer_get(global, caches, lib, target, &mut idx, level)
|
.call_indexer_get(global, caches, lib, target, idx, level)
|
||||||
.map(Into::into),
|
.map(Into::into),
|
||||||
|
|
||||||
_ => Err(ERR::ErrorIndexingType(
|
_ => Err(ERR::ErrorIndexingType(
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! Data size checks during evaluation.
|
//! Data size checks during evaluation.
|
||||||
#![cfg(not(feature = "unchecked"))]
|
#![cfg(not(feature = "unchecked"))]
|
||||||
|
|
||||||
|
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::*;
|
||||||
|
|
||||||
@ -15,46 +15,45 @@ impl Engine {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if any interior data is shared (should never happen).
|
/// Panics if any interior data is shared (should never happen).
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
pub(crate) fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) {
|
pub(crate) fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) {
|
||||||
match value.0 {
|
match value.0 {
|
||||||
#[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,56 +69,34 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is there a data size limit set?
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
pub(crate) const fn has_data_size_limit(&self) -> bool {
|
|
||||||
let mut _limited = self.limits.max_string_size.is_some();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
{
|
|
||||||
_limited = _limited || self.limits.max_array_size.is_some();
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
{
|
|
||||||
_limited = _limited || self.limits.max_map_size.is_some();
|
|
||||||
}
|
|
||||||
|
|
||||||
_limited
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Raise an error if any data size exceeds limit.
|
/// Raise an error if any data size exceeds limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
@ -128,7 +105,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether the size of a [`Dynamic`] is within limits.
|
/// Check whether the size of a [`Dynamic`] is within limits.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> {
|
pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> {
|
||||||
// If no data size limits, just return
|
// If no data size limits, just return
|
||||||
if !self.has_data_size_limit() {
|
if !self.has_data_size_limit() {
|
||||||
@ -143,29 +119,30 @@ impl Engine {
|
|||||||
/// Raise an error if the size of a [`Dynamic`] is out of limits (if any).
|
/// Raise an error if the size of a [`Dynamic`] is out of limits (if any).
|
||||||
///
|
///
|
||||||
/// Not available under `unchecked`.
|
/// Not available under `unchecked`.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> {
|
pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> {
|
||||||
self.check_data_size(value, Position::NONE)
|
self.check_data_size(value, Position::NONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
pub(crate) fn track_operation(
|
||||||
pub(crate) fn inc_operations(
|
|
||||||
&self,
|
&self,
|
||||||
num_operations: &mut u64,
|
global: &mut GlobalRuntimeState,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
*num_operations += 1;
|
global.num_operations += 1;
|
||||||
|
|
||||||
// Guard against too many operations
|
// Guard against too many operations
|
||||||
if self.max_operations() > 0 && *num_operations > self.max_operations() {
|
let max = self.max_operations();
|
||||||
|
let num_operations = global.num_operations;
|
||||||
|
|
||||||
|
if max > 0 && num_operations > max {
|
||||||
return Err(ERR::ErrorTooManyOperations(pos).into());
|
return Err(ERR::ErrorTooManyOperations(pos).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report progress - only in steps
|
// Report progress - only in steps
|
||||||
if let Some(ref progress) = self.progress {
|
if let Some(ref progress) = self.progress {
|
||||||
if let Some(token) = progress(*num_operations) {
|
if let Some(token) = progress(num_operations) {
|
||||||
// Terminate script if progress returns a termination token
|
// Terminate script if progress returns a termination token
|
||||||
return Err(ERR::ErrorTerminated(token, pos).into());
|
return Err(ERR::ErrorTerminated(token, pos).into());
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ pub enum DebuggerCommand {
|
|||||||
|
|
||||||
impl Default for DebuggerCommand {
|
impl Default for DebuggerCommand {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Continue
|
Self::Continue
|
||||||
}
|
}
|
||||||
@ -495,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)
|
||||||
|
@ -318,8 +318,7 @@ impl Engine {
|
|||||||
let reset_debugger =
|
let reset_debugger =
|
||||||
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
|
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, expr.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
||||||
|
|
||||||
let result =
|
let result =
|
||||||
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
|
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
|
||||||
@ -337,8 +336,7 @@ impl Engine {
|
|||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, expr, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, expr.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
||||||
|
|
||||||
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
|
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
|
||||||
this_ptr
|
this_ptr
|
||||||
@ -355,8 +353,7 @@ impl Engine {
|
|||||||
let reset_debugger =
|
let reset_debugger =
|
||||||
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
|
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, expr.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
|
||||||
|
|
||||||
let result = match expr {
|
let result = match expr {
|
||||||
// Constants
|
// Constants
|
||||||
@ -410,7 +407,7 @@ impl Engine {
|
|||||||
let mut result = Ok(Dynamic::UNIT);
|
let mut result = Ok(Dynamic::UNIT);
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let mut sizes = (0, 0, 0);
|
let mut total_data_sizes = (0, 0, 0);
|
||||||
|
|
||||||
for item_expr in &**x {
|
for item_expr in &**x {
|
||||||
let value = match self
|
let value = match self
|
||||||
@ -424,19 +421,21 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if self.has_data_size_limit() {
|
||||||
let val_sizes = Self::calc_data_sizes(&value, true);
|
let val_sizes = Self::calc_data_sizes(&value, true);
|
||||||
|
|
||||||
array.push(value);
|
total_data_sizes = (
|
||||||
|
total_data_sizes.0 + val_sizes.0,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
total_data_sizes.1 + val_sizes.1,
|
||||||
if self.has_data_size_limit() {
|
total_data_sizes.2 + val_sizes.2,
|
||||||
sizes = (
|
|
||||||
sizes.0 + val_sizes.0,
|
|
||||||
sizes.1 + val_sizes.1,
|
|
||||||
sizes.2 + val_sizes.2,
|
|
||||||
);
|
);
|
||||||
self.raise_err_if_over_data_size_limit(sizes, item_expr.position())?;
|
self.raise_err_if_over_data_size_limit(
|
||||||
|
total_data_sizes,
|
||||||
|
item_expr.position(),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.map(|_| array.into())
|
result.map(|_| array.into())
|
||||||
@ -448,7 +447,7 @@ impl Engine {
|
|||||||
let mut result = Ok(Dynamic::UNIT);
|
let mut result = Ok(Dynamic::UNIT);
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let mut sizes = (0, 0, 0);
|
let mut total_data_sizes = (0, 0, 0);
|
||||||
|
|
||||||
for (key, value_expr) in &x.0 {
|
for (key, value_expr) in &x.0 {
|
||||||
let value = match self
|
let value = match self
|
||||||
@ -462,15 +461,20 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if self.has_data_size_limit() {
|
||||||
let delta = Self::calc_data_sizes(&value, true);
|
let delta = Self::calc_data_sizes(&value, true);
|
||||||
|
total_data_sizes = (
|
||||||
|
total_data_sizes.0 + delta.0,
|
||||||
|
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(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
*map.get_mut(key.as_str()).unwrap() = value;
|
*map.get_mut(key.as_str()).unwrap() = value;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if self.has_data_size_limit() {
|
|
||||||
sizes = (sizes.0 + delta.0, sizes.1 + delta.1, sizes.2 + delta.2);
|
|
||||||
self.raise_err_if_over_data_size_limit(sizes, value_expr.position())?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.map(|_| map.into())
|
result.map(|_| map.into())
|
||||||
@ -561,14 +565,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(..) => {
|
Expr::Index(..) => self
|
||||||
self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None)
|
.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, &mut None),
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(..) => {
|
Expr::Dot(..) => self
|
||||||
self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None)
|
.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, &mut None),
|
||||||
}
|
|
||||||
|
|
||||||
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
||||||
};
|
};
|
||||||
|
@ -36,6 +36,7 @@ pub struct GlobalRuntimeState<'a> {
|
|||||||
/// Number of operations performed.
|
/// Number of operations performed.
|
||||||
pub num_operations: u64,
|
pub num_operations: u64,
|
||||||
/// Number of modules loaded.
|
/// Number of modules loaded.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub num_modules_loaded: usize,
|
pub num_modules_loaded: usize,
|
||||||
/// Level of the current scope.
|
/// Level of the current scope.
|
||||||
///
|
///
|
||||||
@ -85,6 +86,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
modules: crate::StaticVec::new_const(),
|
modules: crate::StaticVec::new_const(),
|
||||||
source: Identifier::new_const(),
|
source: Identifier::new_const(),
|
||||||
num_operations: 0,
|
num_operations: 0,
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
num_modules_loaded: 0,
|
num_modules_loaded: 0,
|
||||||
scope_level: 0,
|
scope_level: 0,
|
||||||
always_search_scope: false,
|
always_search_scope: false,
|
||||||
@ -105,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
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -316,7 +317,6 @@ impl IntoIterator for GlobalRuntimeState<'_> {
|
|||||||
std::iter::Rev<smallvec::IntoIter<[crate::Shared<crate::Module>; 3]>>,
|
std::iter::Rev<smallvec::IntoIter<[crate::Shared<crate::Module>; 3]>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.keys
|
self.keys
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -333,10 +333,8 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
|||||||
std::iter::Rev<std::slice::Iter<'a, crate::Shared<crate::Module>>>,
|
std::iter::Rev<std::slice::Iter<'a, crate::Shared<crate::Module>>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
let x = self.keys.iter().rev().zip(self.modules.iter().rev());
|
self.keys.iter().rev().zip(self.modules.iter().rev())
|
||||||
x
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,14 +361,14 @@ impl fmt::Debug for GlobalRuntimeState<'_> {
|
|||||||
f.field("imports", &self.keys.iter().zip(self.modules.iter()));
|
f.field("imports", &self.keys.iter().zip(self.modules.iter()));
|
||||||
|
|
||||||
f.field("source", &self.source)
|
f.field("source", &self.source)
|
||||||
.field("num_operations", &self.num_operations)
|
.field("num_operations", &self.num_operations);
|
||||||
.field("num_modules_loaded", &self.num_modules_loaded);
|
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
f.field("fn_hash_indexing", &self.fn_hash_indexing);
|
f.field("fn_hash_indexing", &self.fn_hash_indexing);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
f.field("embedded_module_resolver", &self.embedded_module_resolver);
|
f.field("num_modules_loaded", &self.num_modules_loaded)
|
||||||
|
.field("embedded_module_resolver", &self.embedded_module_resolver);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
@ -152,7 +152,6 @@ impl Engine {
|
|||||||
let context = (self, op, None, &*global, lib, *op_pos, level).into();
|
let context = (self, op, None, &*global, lib, *op_pos, level).into();
|
||||||
let result = func(context, args).map(|_| ());
|
let result = func(context, args).map(|_| ());
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
self.check_data_size(args[0], root.1)?;
|
self.check_data_size(args[0], root.1)?;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -165,10 +164,7 @@ impl Engine {
|
|||||||
match self.call_native_fn(
|
match self.call_native_fn(
|
||||||
global, caches, lib, op_assign, hash, args, true, true, *op_pos, level,
|
global, caches, lib, op_assign, hash, args, true, true, *op_pos, level,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => self.check_data_size(args[0], root.1)?,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
self.check_data_size(args[0], root.1)?;
|
|
||||||
}
|
|
||||||
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
@ -187,15 +183,6 @@ impl Engine {
|
|||||||
*target.write_lock::<Dynamic>().unwrap() = new_val;
|
*target.write_lock::<Dynamic>().unwrap() = new_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
if let Some(mut guard) = target.write_lock::<Dynamic>() {
|
|
||||||
if guard.is::<ImmutableString>() {
|
|
||||||
let s = std::mem::take(&mut *guard).cast::<ImmutableString>();
|
|
||||||
*guard = self.get_interned_string(s).into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
target.propagate_changed_value(op_info.pos)
|
target.propagate_changed_value(op_info.pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,8 +214,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Function calls should account for a relatively larger portion of statements.
|
// Function calls should account for a relatively larger portion of statements.
|
||||||
if let Stmt::FnCall(x, ..) = stmt {
|
if let Stmt::FnCall(x, ..) = stmt {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, stmt.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, stmt.position())?;
|
|
||||||
|
|
||||||
let result =
|
let result =
|
||||||
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
|
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
|
||||||
@ -245,8 +231,7 @@ impl Engine {
|
|||||||
if let Stmt::Assignment(x, ..) = stmt {
|
if let Stmt::Assignment(x, ..) = stmt {
|
||||||
let (op_info, BinaryExpr { lhs, rhs }) = &**x;
|
let (op_info, BinaryExpr { lhs, rhs }) = &**x;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, stmt.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, stmt.position())?;
|
|
||||||
|
|
||||||
let result = if let Expr::Variable(x, ..) = lhs {
|
let result = if let Expr::Variable(x, ..) = lhs {
|
||||||
let rhs_result = self
|
let rhs_result = self
|
||||||
@ -276,8 +261,7 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, pos)?;
|
|
||||||
|
|
||||||
let root = (var_name, pos);
|
let root = (var_name, pos);
|
||||||
let lhs_ptr = &mut lhs_ptr;
|
let lhs_ptr = &mut lhs_ptr;
|
||||||
@ -295,19 +279,25 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
let (op_info, BinaryExpr { lhs, rhs }) = &**x;
|
let (op_info, BinaryExpr { lhs, rhs }) = &**x;
|
||||||
|
|
||||||
let rhs_result = self
|
let rhs_result = self.eval_expr(scope, global, caches, lib, this_ptr, rhs, level);
|
||||||
.eval_expr(scope, global, caches, lib, this_ptr, rhs, level)
|
|
||||||
.map(Dynamic::flatten);
|
|
||||||
|
|
||||||
if let Ok(rhs_val) = rhs_result {
|
if let Ok(rhs_val) = rhs_result {
|
||||||
let rhs_val = if rhs_val.is::<ImmutableString>() {
|
// Check if the result is a string. If so, intern it.
|
||||||
self.get_interned_string(rhs_val.cast::<ImmutableString>())
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
let is_string = !rhs_val.is_shared() && rhs_val.is::<ImmutableString>();
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
|
let is_string = rhs_val.is::<ImmutableString>();
|
||||||
|
|
||||||
|
let rhs_val = if is_string {
|
||||||
|
self.get_interned_string(
|
||||||
|
rhs_val.into_immutable_string().expect("`ImmutableString`"),
|
||||||
|
)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
rhs_val
|
rhs_val.flatten()
|
||||||
};
|
};
|
||||||
|
|
||||||
let _new_val = Some((rhs_val, op_info));
|
let _new_val = &mut Some((rhs_val, op_info));
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||||
match lhs {
|
match lhs {
|
||||||
@ -342,8 +332,7 @@ impl Engine {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, stmt.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, stmt.position())?;
|
|
||||||
|
|
||||||
let result = match stmt {
|
let result = match stmt {
|
||||||
// No-op
|
// No-op
|
||||||
@ -515,8 +504,7 @@ impl Engine {
|
|||||||
let (.., body) = &**x;
|
let (.., body) = &**x;
|
||||||
|
|
||||||
if body.is_empty() {
|
if body.is_empty() {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, body.position())?;
|
||||||
self.inc_operations(&mut global.num_operations, body.position())?;
|
|
||||||
} else {
|
} else {
|
||||||
match self
|
match self
|
||||||
.eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level)
|
.eval_stmt_block(scope, global, caches, lib, this_ptr, body, true, level)
|
||||||
@ -671,10 +659,7 @@ impl Engine {
|
|||||||
|
|
||||||
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
|
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
if let Err(err) = self.track_operation(global, statements.position()) {
|
||||||
if let Err(err) = self
|
|
||||||
.inc_operations(&mut global.num_operations, statements.position())
|
|
||||||
{
|
|
||||||
loop_result = Err(err);
|
loop_result = Err(err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -741,7 +726,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(feature = "no_object")]
|
#[cfg(feature = "no_object")]
|
||||||
_ => {
|
_ => {
|
||||||
err.take_position();
|
let _ = err.take_position();
|
||||||
err.to_string().into()
|
err.to_string().into()
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -927,7 +912,6 @@ impl Engine {
|
|||||||
let (expr, export) = &**x;
|
let (expr, export) = &**x;
|
||||||
|
|
||||||
// Guard against too many modules
|
// Guard against too many modules
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if global.num_modules_loaded >= self.max_modules() {
|
if global.num_modules_loaded >= self.max_modules() {
|
||||||
return Err(ERR::ErrorTooManyModules(*_pos).into());
|
return Err(ERR::ErrorTooManyModules(*_pos).into());
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,6 @@ impl Deref for Target<'_> {
|
|||||||
type Target = Dynamic;
|
type Target = Dynamic;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Dynamic {
|
fn deref(&self) -> &Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
@ -425,7 +424,6 @@ impl AsRef<Dynamic> for Target<'_> {
|
|||||||
|
|
||||||
impl DerefMut for Target<'_> {
|
impl DerefMut for Target<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn deref_mut(&mut self) -> &mut Dynamic {
|
fn deref_mut(&mut self) -> &mut Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
|
@ -298,7 +298,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
use crate::packages::arithmetic::decimal_functions::builtin::*;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
match op {
|
match op {
|
||||||
@ -697,7 +697,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
|
|||||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
use crate::packages::arithmetic::decimal_functions::*;
|
use crate::packages::arithmetic::decimal_functions::builtin::*;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
return match op {
|
return match op {
|
||||||
|
@ -358,8 +358,7 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, pos)?;
|
|
||||||
|
|
||||||
let parent_source = global.source.clone();
|
let parent_source = global.source.clone();
|
||||||
let op_assign = if is_op_assign {
|
let op_assign = if is_op_assign {
|
||||||
@ -1232,8 +1231,7 @@ impl Engine {
|
|||||||
target = target.into_owned();
|
target = target.into_owned();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, _pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, _pos)?;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1312,8 +1310,7 @@ impl Engine {
|
|||||||
let (target, _pos) =
|
let (target, _pos) =
|
||||||
self.search_scope_only(scope, global, lib, this_ptr, first_arg, level)?;
|
self.search_scope_only(scope, global, lib, this_ptr, first_arg, level)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, _pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, _pos)?;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1350,9 +1347,7 @@ impl Engine {
|
|||||||
let mut func = match module.get_qualified_fn(hash) {
|
let mut func = match module.get_qualified_fn(hash) {
|
||||||
// Then search native Rust functions
|
// Then search native Rust functions
|
||||||
None => {
|
None => {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, pos)?;
|
|
||||||
|
|
||||||
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
||||||
let hash_qualified_fn = combine_hashes(hash, hash_params);
|
let hash_qualified_fn = combine_hashes(hash, hash_params);
|
||||||
|
|
||||||
@ -1384,8 +1379,7 @@ impl Engine {
|
|||||||
}));
|
}));
|
||||||
let hash_qualified_fn = combine_hashes(hash, hash_params);
|
let hash_qualified_fn = combine_hashes(hash, hash_params);
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, pos)?;
|
|
||||||
|
|
||||||
if let Some(f) = module.get_qualified_fn(hash_qualified_fn) {
|
if let Some(f) = module.get_qualified_fn(hash_qualified_fn) {
|
||||||
func = Some(f);
|
func = Some(f);
|
||||||
@ -1461,8 +1455,7 @@ impl Engine {
|
|||||||
_pos: Position,
|
_pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, _pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, _pos)?;
|
|
||||||
|
|
||||||
let script = script.trim();
|
let script = script.trim();
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ pub trait Func<ARGS, RET> {
|
|||||||
/// func(123, "hello")? == false; // call the anonymous function
|
/// func(123, "hello")? == false; // call the anonymous function
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
#[must_use]
|
||||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output;
|
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output;
|
||||||
|
|
||||||
/// Create a Rust closure from a script.
|
/// Create a Rust closure from a script.
|
||||||
@ -79,6 +80,7 @@ pub trait Func<ARGS, RET> {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[must_use]
|
||||||
fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output>;
|
fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ pub struct StraightHasher(u64);
|
|||||||
|
|
||||||
impl Hasher for StraightHasher {
|
impl Hasher for StraightHasher {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn finish(&self) -> u64 {
|
fn finish(&self) -> u64 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
@ -66,6 +67,7 @@ impl BuildHasher for StraightHasherBuilder {
|
|||||||
type Hasher = StraightHasher;
|
type Hasher = StraightHasher;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn build_hasher(&self) -> Self::Hasher {
|
fn build_hasher(&self) -> Self::Hasher {
|
||||||
StraightHasher(ALT_ZERO_HASH)
|
StraightHasher(ALT_ZERO_HASH)
|
||||||
}
|
}
|
||||||
@ -75,10 +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 {
|
||||||
|
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()
|
_ => ahash::AHasher::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,8 +86,11 @@ pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
|
|||||||
/// _(metadata)_ Get the type name of this function's return value.
|
/// _(metadata)_ Get the type name of this function's return value.
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn return_type_name() -> &'static str;
|
fn return_type_name() -> &'static str {
|
||||||
|
std::any::type_name::<RET>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EXPECT_ARGS: &str = "arguments";
|
const EXPECT_ARGS: &str = "arguments";
|
||||||
@ -138,7 +141,6 @@ macro_rules! def_register {
|
|||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
|
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
@ -164,7 +166,6 @@ macro_rules! def_register {
|
|||||||
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
#[inline(always)] fn param_types() -> Box<[TypeId]> { vec![$(TypeId::of::<$par>()),*].into_boxed_slice() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> Box<[&'static str]> { vec![$(std::any::type_name::<$param>()),*].into_boxed_slice() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
|
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
|
@ -63,11 +63,9 @@ impl Engine {
|
|||||||
|
|
||||||
assert!(fn_def.params.len() == args.len());
|
assert!(fn_def.params.len() == args.len());
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
self.track_operation(global, pos)?;
|
||||||
self.inc_operations(&mut global.num_operations, pos)?;
|
|
||||||
|
|
||||||
// Check for stack overflow
|
// Check for stack overflow
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if level > self.max_call_levels() {
|
if level > self.max_call_levels() {
|
||||||
return Err(ERR::ErrorStackOverflow(pos).into());
|
return Err(ERR::ErrorStackOverflow(pos).into());
|
||||||
}
|
}
|
||||||
@ -251,13 +249,10 @@ impl Engine {
|
|||||||
// Then check sub-modules
|
// Then check sub-modules
|
||||||
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|
||||||
|
|
||||||
if !result {
|
if !result && !cache.filter.is_absent_and_set(hash_script) {
|
||||||
if cache.filter.is_absent(hash_script) {
|
// Do not cache "one-hit wonders"
|
||||||
cache.filter.mark(hash_script);
|
|
||||||
} else {
|
|
||||||
cache.map.insert(hash_script, None);
|
cache.map.insert(hash_script, None);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -196,6 +196,7 @@ pub struct Module {
|
|||||||
|
|
||||||
impl Default for Module {
|
impl Default for Module {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -493,12 +494,12 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_custom_type_raw(
|
pub fn set_custom_type_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
type_name: impl Into<Identifier>,
|
type_path: impl Into<Identifier>,
|
||||||
name: impl Into<Identifier>,
|
name: impl Into<Identifier>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.custom_types
|
self.custom_types
|
||||||
.get_or_insert_with(CustomTypesCollection::new)
|
.get_or_insert_with(CustomTypesCollection::new)
|
||||||
.add(type_name, name);
|
.add(type_path, name);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Get the display name of a registered custom type.
|
/// Get the display name of a registered custom type.
|
||||||
@ -1741,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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1766,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;
|
||||||
|
@ -42,7 +42,7 @@ impl ModuleResolversCollection {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self(Vec::new())
|
Self(Vec::new())
|
||||||
}
|
}
|
||||||
/// Append a [module resolver][ModuleResolver] to the end.
|
/// Append a [module resolver][ModuleResolver] to the end.
|
||||||
@ -112,6 +112,7 @@ impl IntoIterator for ModuleResolversCollection {
|
|||||||
type IntoIter = IntoIter<Box<dyn ModuleResolver>>;
|
type IntoIter = IntoIter<Box<dyn ModuleResolver>>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ pub struct FileModuleResolver {
|
|||||||
|
|
||||||
impl Default for FileModuleResolver {
|
impl Default for FileModuleResolver {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -321,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();
|
||||||
|
@ -134,6 +134,7 @@ impl IntoIterator for StaticModuleResolver {
|
|||||||
type IntoIter = IntoIter<SmartString, Shared<Module>>;
|
type IntoIter = IntoIter<SmartString, Shared<Module>>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ pub enum OptimizationLevel {
|
|||||||
|
|
||||||
impl Default for OptimizationLevel {
|
impl Default for OptimizationLevel {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Simple
|
Self::Simple
|
||||||
}
|
}
|
||||||
@ -550,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(
|
||||||
@ -615,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 =
|
||||||
@ -664,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
|
||||||
|
@ -488,39 +488,29 @@ mod f64_functions {
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
#[export_module]
|
#[export_module]
|
||||||
pub mod decimal_functions {
|
pub mod decimal_functions {
|
||||||
use num_traits::Pow;
|
use rust_decimal::{prelude::Zero, Decimal};
|
||||||
use rust_decimal::{prelude::Zero, Decimal, MathematicalOps};
|
|
||||||
|
|
||||||
#[rhai_fn(skip, return_raw)]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
pub mod builtin {
|
||||||
|
use rust_decimal::MathematicalOps;
|
||||||
|
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
x.checked_add(y)
|
x.checked_add(y)
|
||||||
.ok_or_else(|| make_err(format!("Addition overflow: {x} + {y}")))
|
.ok_or_else(|| make_err(format!("Addition overflow: {x} + {y}")))
|
||||||
} else {
|
|
||||||
Ok(x + y)
|
|
||||||
}
|
}
|
||||||
}
|
#[rhai_fn(return_raw)]
|
||||||
#[rhai_fn(skip, return_raw)]
|
|
||||||
pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
x.checked_sub(y)
|
x.checked_sub(y)
|
||||||
.ok_or_else(|| make_err(format!("Subtraction overflow: {x} - {y}")))
|
.ok_or_else(|| make_err(format!("Subtraction overflow: {x} - {y}")))
|
||||||
} else {
|
|
||||||
Ok(x - y)
|
|
||||||
}
|
}
|
||||||
}
|
#[rhai_fn(return_raw)]
|
||||||
#[rhai_fn(skip, return_raw)]
|
|
||||||
pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
x.checked_mul(y)
|
x.checked_mul(y)
|
||||||
.ok_or_else(|| make_err(format!("Multiplication overflow: {x} * {y}")))
|
.ok_or_else(|| make_err(format!("Multiplication overflow: {x} * {y}")))
|
||||||
} else {
|
|
||||||
Ok(x * y)
|
|
||||||
}
|
}
|
||||||
}
|
#[rhai_fn(return_raw)]
|
||||||
#[rhai_fn(skip, return_raw)]
|
|
||||||
pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
// Detect division by zero
|
// Detect division by zero
|
||||||
if y == Decimal::zero() {
|
if y == Decimal::zero() {
|
||||||
Err(make_err(format!("Division by zero: {x} / {y}")))
|
Err(make_err(format!("Division by zero: {x} / {y}")))
|
||||||
@ -528,26 +518,16 @@ pub mod decimal_functions {
|
|||||||
x.checked_div(y)
|
x.checked_div(y)
|
||||||
.ok_or_else(|| make_err(format!("Division overflow: {x} / {y}")))
|
.ok_or_else(|| make_err(format!("Division overflow: {x} / {y}")))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Ok(x / y)
|
|
||||||
}
|
}
|
||||||
}
|
#[rhai_fn(return_raw)]
|
||||||
#[rhai_fn(skip, return_raw)]
|
|
||||||
pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
x.checked_rem(y)
|
x.checked_rem(y)
|
||||||
.ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {x} % {y}")))
|
.ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {x} % {y}")))
|
||||||
} else {
|
|
||||||
Ok(x % y)
|
|
||||||
}
|
}
|
||||||
}
|
#[rhai_fn(return_raw)]
|
||||||
#[rhai_fn(skip, return_raw)]
|
|
||||||
pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||||
if cfg!(not(feature = "unchecked")) {
|
|
||||||
x.checked_powd(y)
|
x.checked_powd(y)
|
||||||
.ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}")))
|
.ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}")))
|
||||||
} else {
|
|
||||||
Ok(x.pow(y))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "-")]
|
#[rhai_fn(name = "-")]
|
||||||
|
@ -236,9 +236,8 @@ pub mod array_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let check_sizes = match item.0 {
|
let check_sizes = match item.0 {
|
||||||
crate::types::dynamic::Union::Array(..) | crate::types::dynamic::Union::Str(..) => {
|
crate::types::dynamic::Union::Str(..) => true,
|
||||||
true
|
crate::types::dynamic::Union::Array(..) => true,
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
crate::types::dynamic::Union::Map(..) => true,
|
crate::types::dynamic::Union::Map(..) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -79,7 +79,6 @@ pub mod blob_functions {
|
|||||||
let _ctx = ctx;
|
let _ctx = ctx;
|
||||||
|
|
||||||
// Check if blob will be over max size limit
|
// Check if blob will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
|
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
|
||||||
return Err(
|
return Err(
|
||||||
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
|
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
|
||||||
@ -364,7 +363,6 @@ pub mod blob_functions {
|
|||||||
let _ctx = ctx;
|
let _ctx = ctx;
|
||||||
|
|
||||||
// Check if blob will be over max size limit
|
// Check if blob will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
|
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
|
||||||
return Err(
|
return Err(
|
||||||
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
|
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
|
||||||
|
@ -88,7 +88,6 @@ mod core_functions {
|
|||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
|
std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block the current thread for a particular number of `seconds`.
|
/// Block the current thread for a particular number of `seconds`.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
pub fn sleep(seconds: INT) {
|
pub fn sleep(seconds: INT) {
|
||||||
@ -97,6 +96,23 @@ mod core_functions {
|
|||||||
}
|
}
|
||||||
std::thread::sleep(std::time::Duration::from_secs(seconds as u64));
|
std::thread::sleep(std::time::Duration::from_secs(seconds as u64));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a JSON string into a value.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let m = parse_json(`{"a":1, "b":2, "c":3}`);
|
||||||
|
///
|
||||||
|
/// print(m); // prints #{"a":1, "b":2, "c":3}
|
||||||
|
/// ```
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn parse_json(_ctx: NativeCallContext, json: &str) -> RhaiResultOf<Dynamic> {
|
||||||
|
serde_json::from_str(json).map_err(|err| err.to_string().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::engine::OP_EQUALS;
|
use crate::engine::OP_EQUALS;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, format_map_as_json, Dynamic, ImmutableString, Map, RhaiResultOf, INT};
|
use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -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() {
|
||||||
@ -304,6 +305,9 @@ mod map_functions {
|
|||||||
/// print(m.to_json()); // prints {"a":1, "b":2, "c":3}
|
/// print(m.to_json()); // prints {"a":1, "b":2, "c":3}
|
||||||
/// ```
|
/// ```
|
||||||
pub fn to_json(map: &mut Map) -> String {
|
pub fn to_json(map: &mut Map) -> String {
|
||||||
format_map_as_json(map)
|
#[cfg(feature = "metadata")]
|
||||||
|
return serde_json::to_string(map).unwrap_or_else(|_| "ERROR".into());
|
||||||
|
#[cfg(not(feature = "metadata"))]
|
||||||
|
return crate::format_map_as_json(map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,14 @@ pub use time_basic::BasicTimePackage;
|
|||||||
pub trait Package {
|
pub trait Package {
|
||||||
/// Initialize the package.
|
/// Initialize the package.
|
||||||
/// Functions should be registered into `module` here.
|
/// Functions should be registered into `module` here.
|
||||||
|
#[cold]
|
||||||
fn init(module: &mut Module);
|
fn init(module: &mut Module);
|
||||||
|
|
||||||
/// Initialize the package with an [`Engine`].
|
/// Initialize the package with an [`Engine`].
|
||||||
///
|
///
|
||||||
/// Perform tasks such as registering custom operators/syntax.
|
/// Perform tasks such as registering custom operators/syntax.
|
||||||
|
#[cold]
|
||||||
|
#[inline]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn init_engine(engine: &mut Engine) {}
|
fn init_engine(engine: &mut Engine) {}
|
||||||
|
|
||||||
@ -65,6 +68,8 @@ pub trait Package {
|
|||||||
///
|
///
|
||||||
/// package.register_into_engine(&mut engine);
|
/// package.register_into_engine(&mut engine);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cold]
|
||||||
|
#[inline]
|
||||||
fn register_into_engine(&self, engine: &mut Engine) -> &Self {
|
fn register_into_engine(&self, engine: &mut Engine) -> &Self {
|
||||||
Self::init_engine(engine);
|
Self::init_engine(engine);
|
||||||
engine.register_global_module(self.as_shared_module());
|
engine.register_global_module(self.as_shared_module());
|
||||||
@ -84,6 +89,8 @@ pub trait Package {
|
|||||||
/// package.register_into_engine_as(&mut engine, "core");
|
/// package.register_into_engine_as(&mut engine, "core");
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[cold]
|
||||||
|
#[inline]
|
||||||
fn register_into_engine_as(&self, engine: &mut Engine, name: &str) -> &Self {
|
fn register_into_engine_as(&self, engine: &mut Engine, name: &str) -> &Self {
|
||||||
Self::init_engine(engine);
|
Self::init_engine(engine);
|
||||||
engine.register_static_module(name, self.as_shared_module());
|
engine.register_static_module(name, self.as_shared_module());
|
||||||
@ -133,7 +140,6 @@ macro_rules! def_package {
|
|||||||
fn as_shared_module(&self) -> $crate::Shared<$crate::Module> {
|
fn as_shared_module(&self) -> $crate::Shared<$crate::Module> {
|
||||||
self.0.clone()
|
self.0.clone()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn init($lib: &mut $crate::Module) {
|
fn init($lib: &mut $crate::Module) {
|
||||||
$($(
|
$($(
|
||||||
$(#[$base_meta])* { <$base_pkg>::init($lib); }
|
$(#[$base_meta])* { <$base_pkg>::init($lib); }
|
||||||
@ -141,7 +147,6 @@ macro_rules! def_package {
|
|||||||
|
|
||||||
$block
|
$block
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
fn init_engine(_engine: &mut $crate::Engine) {
|
fn init_engine(_engine: &mut $crate::Engine) {
|
||||||
$($(
|
$($(
|
||||||
$(#[$base_meta])* { <$base_pkg>::init_engine(_engine); }
|
$(#[$base_meta])* { <$base_pkg>::init_engine(_engine); }
|
||||||
@ -156,6 +161,7 @@ macro_rules! def_package {
|
|||||||
|
|
||||||
impl Default for $package {
|
impl Default for $package {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -193,12 +199,16 @@ macro_rules! def_package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for $package {
|
impl Default for $package {
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $package {
|
impl $package {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut module = $root::Module::new();
|
let mut module = $root::Module::new();
|
||||||
<Self as $root::packages::Package>::init(&mut module);
|
<Self as $root::packages::Package>::init(&mut module);
|
||||||
@ -229,12 +239,16 @@ macro_rules! def_package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for $package {
|
impl Default for $package {
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $package {
|
impl $package {
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut module = $root::Module::new();
|
let mut module = $root::Module::new();
|
||||||
<Self as $root::packages::Package>::init(&mut module);
|
<Self as $root::packages::Package>::init(&mut module);
|
||||||
|
@ -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.
|
||||||
@ -1216,7 +1218,6 @@ mod string_functions {
|
|||||||
let _ctx = ctx;
|
let _ctx = ctx;
|
||||||
|
|
||||||
// Check if string will be over max size limit
|
// Check if string will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
|
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
|
||||||
return Err(crate::ERR::ErrorDataTooLarge(
|
return Err(crate::ERR::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
@ -1234,7 +1235,6 @@ mod string_functions {
|
|||||||
p.push(character);
|
p.push(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
||||||
{
|
{
|
||||||
return Err(crate::ERR::ErrorDataTooLarge(
|
return Err(crate::ERR::ErrorDataTooLarge(
|
||||||
@ -1278,7 +1278,6 @@ mod string_functions {
|
|||||||
let _ctx = ctx;
|
let _ctx = ctx;
|
||||||
|
|
||||||
// Check if string will be over max size limit
|
// Check if string will be over max size limit
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
|
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
|
||||||
return Err(crate::ERR::ErrorDataTooLarge(
|
return Err(crate::ERR::ErrorDataTooLarge(
|
||||||
"Length of string".to_string(),
|
"Length of string".to_string(),
|
||||||
@ -1303,7 +1302,6 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
||||||
{
|
{
|
||||||
return Err(crate::ERR::ErrorDataTooLarge(
|
return Err(crate::ERR::ErrorDataTooLarge(
|
||||||
|
@ -34,10 +34,6 @@ pub type ParseResult<T> = Result<T, ParseError>;
|
|||||||
|
|
||||||
type FnLib = StraightHashMap<Shared<ScriptFnDef>>;
|
type FnLib = StraightHashMap<Shared<ScriptFnDef>>;
|
||||||
|
|
||||||
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
|
|
||||||
|
|
||||||
const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax();
|
|
||||||
|
|
||||||
/// Invalid variable name that acts as a search barrier in a [`Scope`].
|
/// Invalid variable name that acts as a search barrier in a [`Scope`].
|
||||||
const SCOPE_SEARCH_BARRIER_MARKER: &str = "$ BARRIER $";
|
const SCOPE_SEARCH_BARRIER_MARKER: &str = "$ BARRIER $";
|
||||||
|
|
||||||
@ -110,7 +106,7 @@ impl fmt::Debug for ParseState<'_> {
|
|||||||
|
|
||||||
impl<'e> ParseState<'e> {
|
impl<'e> ParseState<'e> {
|
||||||
/// Create a new [`ParseState`].
|
/// Create a new [`ParseState`].
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
@ -183,7 +179,6 @@ impl<'e> ParseState<'e> {
|
|||||||
///
|
///
|
||||||
/// * `is_func_name`: `true` if the variable is actually the name of a function
|
/// * `is_func_name`: `true` if the variable is actually the name of a function
|
||||||
/// (in which case it will be converted into a function pointer).
|
/// (in which case it will be converted into a function pointer).
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn access_var(
|
pub fn access_var(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -235,7 +230,6 @@ impl<'e> ParseState<'e> {
|
|||||||
///
|
///
|
||||||
/// Panics when called under `no_module`.
|
/// Panics when called under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
|
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
|
||||||
self.imports
|
self.imports
|
||||||
@ -258,7 +252,7 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
/// Get an interned property getter, creating one if it is not yet interned.
|
/// Get an interned property getter, creating one if it is not yet interned.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_getter(
|
pub fn get_interned_getter(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -273,7 +267,7 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
/// Get an interned property setter, creating one if it is not yet interned.
|
/// Get an interned property setter, creating one if it is not yet interned.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_setter(
|
pub fn get_interned_setter(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -289,31 +283,33 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
/// A type that encapsulates all the settings for a particular parsing function.
|
/// A type that encapsulates all the settings for a particular parsing function.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
struct ParseSettings {
|
pub(crate) struct ParseSettings {
|
||||||
/// Is the construct being parsed located at global level?
|
/// Is the construct being parsed located at global level?
|
||||||
at_global_level: bool,
|
pub at_global_level: bool,
|
||||||
/// Is the construct being parsed located inside a function definition?
|
/// Is the construct being parsed located inside a function definition?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
in_fn_scope: bool,
|
pub in_fn_scope: bool,
|
||||||
/// Is the construct being parsed located inside a closure definition?
|
/// Is the construct being parsed located inside a closure definition?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
in_closure: bool,
|
pub in_closure: bool,
|
||||||
/// Is the construct being parsed located inside a breakable loop?
|
/// Is the construct being parsed located inside a breakable loop?
|
||||||
is_breakable: bool,
|
pub is_breakable: bool,
|
||||||
/// Allow statements in blocks?
|
/// Allow statements in blocks?
|
||||||
allow_statements: bool,
|
pub allow_statements: bool,
|
||||||
|
/// Allow unquoted map properties?
|
||||||
|
pub allow_unquoted_map_properties: bool,
|
||||||
/// Language options in effect (overrides Engine options).
|
/// Language options in effect (overrides Engine options).
|
||||||
options: LangOptions,
|
pub options: LangOptions,
|
||||||
/// Current expression nesting level.
|
/// Current expression nesting level.
|
||||||
level: usize,
|
pub level: usize,
|
||||||
/// Current position.
|
/// Current position.
|
||||||
pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParseSettings {
|
impl ParseSettings {
|
||||||
/// Create a new `ParseSettings` with one higher expression level.
|
/// Create a new `ParseSettings` with one higher expression level.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn level_up(&self) -> Self {
|
pub const fn level_up(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -417,7 +413,6 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Make sure that the next expression is not a statement expression (i.e. wrapped in `{}`).
|
/// Make sure that the next expression is not a statement expression (i.e. wrapped in `{}`).
|
||||||
#[inline]
|
|
||||||
fn ensure_not_statement_expr(
|
fn ensure_not_statement_expr(
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
type_name: &(impl ToString + ?Sized),
|
type_name: &(impl ToString + ?Sized),
|
||||||
@ -429,7 +424,6 @@ fn ensure_not_statement_expr(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Make sure that the next expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`).
|
/// Make sure that the next expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`).
|
||||||
#[inline]
|
|
||||||
fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> {
|
fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> {
|
||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
(Token::Equals, pos) => Err(LexError::ImproperSymbol(
|
(Token::Equals, pos) => Err(LexError::ImproperSymbol(
|
||||||
@ -446,7 +440,6 @@ fn ensure_not_assignment(input: &mut TokenStream) -> ParseResult<()> {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if the next token is not the expected one.
|
/// Panics if the next token is not the expected one.
|
||||||
#[inline]
|
|
||||||
fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position {
|
fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position {
|
||||||
let (t, pos) = input.next().expect(NEVER_ENDS);
|
let (t, pos) = input.next().expect(NEVER_ENDS);
|
||||||
|
|
||||||
@ -462,7 +455,6 @@ fn eat_token(input: &mut TokenStream, expected_token: Token) -> Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Match a particular [token][Token], consuming it if matched.
|
/// Match a particular [token][Token], consuming it if matched.
|
||||||
#[inline]
|
|
||||||
fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) {
|
fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) {
|
||||||
let (t, pos) = input.peek().expect(NEVER_ENDS);
|
let (t, pos) = input.peek().expect(NEVER_ENDS);
|
||||||
if *t == token {
|
if *t == token {
|
||||||
@ -473,7 +465,6 @@ fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a variable name.
|
/// Parse a variable name.
|
||||||
#[inline]
|
|
||||||
fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> {
|
fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> {
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
// Variable name
|
// Variable name
|
||||||
@ -491,7 +482,6 @@ fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position
|
|||||||
|
|
||||||
/// Parse a symbol.
|
/// Parse a symbol.
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
#[inline]
|
|
||||||
fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> {
|
fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> {
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
// Symbol
|
// Symbol
|
||||||
@ -912,7 +902,6 @@ impl Engine {
|
|||||||
loop {
|
loop {
|
||||||
const MISSING_RBRACKET: &str = "to end this array literal";
|
const MISSING_RBRACKET: &str = "to end this array literal";
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if self.max_array_size() > 0 && array.len() >= self.max_array_size() {
|
if self.max_array_size() > 0 && array.len() >= self.max_array_size() {
|
||||||
return Err(PERR::LiteralTooLarge(
|
return Err(PERR::LiteralTooLarge(
|
||||||
"Size of array literal".to_string(),
|
"Size of array literal".to_string(),
|
||||||
@ -1004,6 +993,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (name, pos) = match input.next().expect(NEVER_ENDS) {
|
let (name, pos) = match input.next().expect(NEVER_ENDS) {
|
||||||
|
(Token::Identifier(..), pos) if !settings.allow_unquoted_map_properties => {
|
||||||
|
return Err(PERR::PropertyExpected.into_err(pos))
|
||||||
|
}
|
||||||
(Token::Identifier(s) | Token::StringConstant(s), pos) => {
|
(Token::Identifier(s) | Token::StringConstant(s), pos) => {
|
||||||
if map.iter().any(|(p, ..)| **p == *s) {
|
if map.iter().any(|(p, ..)| **p == *s) {
|
||||||
return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos));
|
return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos));
|
||||||
@ -1046,7 +1038,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if self.max_map_size() > 0 && map.len() >= self.max_map_size() {
|
if self.max_map_size() > 0 && map.len() >= self.max_map_size() {
|
||||||
return Err(PERR::LiteralTooLarge(
|
return Err(PERR::LiteralTooLarge(
|
||||||
"Number of properties in object map literal".to_string(),
|
"Number of properties in object map literal".to_string(),
|
||||||
@ -2473,6 +2464,8 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
) -> ParseResult<Expr> {
|
) -> ParseResult<Expr> {
|
||||||
use crate::api::custom_syntax::markers::*;
|
use crate::api::custom_syntax::markers::*;
|
||||||
|
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
|
||||||
|
const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax();
|
||||||
|
|
||||||
let mut settings = settings;
|
let mut settings = settings;
|
||||||
let mut inputs = StaticVec::new_const();
|
let mut inputs = StaticVec::new_const();
|
||||||
@ -3334,6 +3327,7 @@ impl Engine {
|
|||||||
in_closure: false,
|
in_closure: false,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
allow_statements: true,
|
allow_statements: true,
|
||||||
|
allow_unquoted_map_properties: settings.allow_unquoted_map_properties,
|
||||||
level: 0,
|
level: 0,
|
||||||
options,
|
options,
|
||||||
pos,
|
pos,
|
||||||
@ -3801,6 +3795,7 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
|
process_settings: impl Fn(&mut ParseSettings),
|
||||||
_optimization_level: OptimizationLevel,
|
_optimization_level: OptimizationLevel,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let mut functions = StraightHashMap::default();
|
let mut functions = StraightHashMap::default();
|
||||||
@ -3810,7 +3805,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
options.remove(LangOptions::ANON_FN);
|
options.remove(LangOptions::ANON_FN);
|
||||||
|
|
||||||
let settings = ParseSettings {
|
let mut settings = ParseSettings {
|
||||||
at_global_level: true,
|
at_global_level: true,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
in_fn_scope: false,
|
in_fn_scope: false,
|
||||||
@ -3819,10 +3814,13 @@ impl Engine {
|
|||||||
in_closure: false,
|
in_closure: false,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
allow_statements: false,
|
allow_statements: false,
|
||||||
|
allow_unquoted_map_properties: true,
|
||||||
level: 0,
|
level: 0,
|
||||||
options,
|
options,
|
||||||
pos: Position::NONE,
|
pos: Position::START,
|
||||||
};
|
};
|
||||||
|
process_settings(&mut settings);
|
||||||
|
|
||||||
let expr = self.parse_expr(input, state, &mut functions, settings)?;
|
let expr = self.parse_expr(input, state, &mut functions, settings)?;
|
||||||
|
|
||||||
assert!(functions.is_empty());
|
assert!(functions.is_empty());
|
||||||
@ -3861,12 +3859,11 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
|
process_settings: impl Fn(&mut ParseSettings),
|
||||||
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
|
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
|
||||||
let mut statements = StmtBlockContainer::new_const();
|
let mut statements = StmtBlockContainer::new_const();
|
||||||
let mut functions = StraightHashMap::default();
|
let mut functions = StraightHashMap::default();
|
||||||
|
let mut settings = ParseSettings {
|
||||||
while !input.peek().expect(NEVER_ENDS).0.is_eof() {
|
|
||||||
let settings = ParseSettings {
|
|
||||||
at_global_level: true,
|
at_global_level: true,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
in_fn_scope: false,
|
in_fn_scope: false,
|
||||||
@ -3875,11 +3872,14 @@ impl Engine {
|
|||||||
in_closure: false,
|
in_closure: false,
|
||||||
is_breakable: false,
|
is_breakable: false,
|
||||||
allow_statements: true,
|
allow_statements: true,
|
||||||
|
allow_unquoted_map_properties: true,
|
||||||
options: self.options,
|
options: self.options,
|
||||||
level: 0,
|
level: 0,
|
||||||
pos: Position::NONE,
|
pos: Position::START,
|
||||||
};
|
};
|
||||||
|
process_settings(&mut settings);
|
||||||
|
|
||||||
|
while !input.peek().expect(NEVER_ENDS).0.is_eof() {
|
||||||
let stmt = self.parse_stmt(input, state, &mut functions, settings)?;
|
let stmt = self.parse_stmt(input, state, &mut functions, settings)?;
|
||||||
|
|
||||||
if stmt.is_noop() {
|
if stmt.is_noop() {
|
||||||
@ -3926,7 +3926,7 @@ impl Engine {
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
_optimization_level: OptimizationLevel,
|
_optimization_level: OptimizationLevel,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let (statements, _lib) = self.parse_global_level(input, state)?;
|
let (statements, _lib) = self.parse_global_level(input, state, |_| {})?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
return Ok(crate::optimizer::optimize_into_ast(
|
return Ok(crate::optimizer::optimize_into_ast(
|
||||||
|
@ -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 {
|
||||||
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ pub struct TokenizerControlBlock {
|
|||||||
|
|
||||||
impl TokenizerControlBlock {
|
impl TokenizerControlBlock {
|
||||||
/// Create a new `TokenizerControlBlock`.
|
/// Create a new `TokenizerControlBlock`.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -97,7 +97,7 @@ impl Position {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if `line` is zero.
|
/// Panics if `line` is zero.
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new(line: u16, position: u16) -> Self {
|
pub const fn new(line: u16, position: u16) -> Self {
|
||||||
assert!(line != 0, "line cannot be zero");
|
assert!(line != 0, "line cannot be zero");
|
||||||
@ -220,6 +220,7 @@ impl Position {
|
|||||||
|
|
||||||
impl Default for Position {
|
impl Default for Position {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::START
|
Self::START
|
||||||
}
|
}
|
||||||
@ -299,6 +300,8 @@ pub struct Span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Span {
|
impl Default for Span {
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::NONE
|
Self::NONE
|
||||||
}
|
}
|
||||||
@ -315,7 +318,7 @@ impl Span {
|
|||||||
Self { start, end }
|
Self { start, end }
|
||||||
}
|
}
|
||||||
/// Is this [`Span`] non-existent?
|
/// Is this [`Span`] non-existent?
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_none(&self) -> bool {
|
pub const fn is_none(&self) -> bool {
|
||||||
self.start.is_none() && self.end.is_none()
|
self.start.is_none() && self.end.is_none()
|
||||||
|
@ -153,7 +153,6 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
|
|||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
DynamicWriteLockInner::Reference(ref reference) => *reference,
|
DynamicWriteLockInner::Reference(ref reference) => *reference,
|
||||||
@ -165,7 +164,6 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
|
|||||||
|
|
||||||
impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
DynamicWriteLockInner::Reference(ref mut reference) => *reference,
|
DynamicWriteLockInner::Reference(ref mut reference) => *reference,
|
||||||
|
@ -251,9 +251,9 @@ impl<T: AsRef<str>> From<T> for EvalAltResult {
|
|||||||
|
|
||||||
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(always)]
|
||||||
fn from(err: T) -> Self {
|
fn from(err: T) -> Self {
|
||||||
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
|
Into::<EvalAltResult>::into(err).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ impl Deref for ImmutableString {
|
|||||||
type Target = SmartString;
|
type Target = SmartString;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
@ -148,6 +147,7 @@ impl FromStr for ImmutableString {
|
|||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let s: SmartString = s.into();
|
let s: SmartString = s.into();
|
||||||
Ok(Self(s.into()))
|
Ok(Self(s.into()))
|
||||||
@ -156,6 +156,7 @@ impl FromStr for ImmutableString {
|
|||||||
|
|
||||||
impl FromIterator<char> for ImmutableString {
|
impl FromIterator<char> for ImmutableString {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<SmartString>().into())
|
Self(iter.into_iter().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
@ -163,6 +164,7 @@ impl FromIterator<char> for ImmutableString {
|
|||||||
|
|
||||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().copied().collect::<SmartString>().into())
|
Self(iter.into_iter().copied().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
@ -170,6 +172,7 @@ impl<'a> FromIterator<&'a char> for ImmutableString {
|
|||||||
|
|
||||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<SmartString>().into())
|
Self(iter.into_iter().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
@ -177,6 +180,7 @@ impl<'a> FromIterator<&'a str> for ImmutableString {
|
|||||||
|
|
||||||
impl FromIterator<String> for ImmutableString {
|
impl FromIterator<String> for ImmutableString {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<SmartString>().into())
|
Self(iter.into_iter().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
@ -184,6 +188,7 @@ impl FromIterator<String> for ImmutableString {
|
|||||||
|
|
||||||
impl FromIterator<SmartString> for ImmutableString {
|
impl FromIterator<SmartString> for ImmutableString {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn from_iter<T: IntoIterator<Item = SmartString>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = SmartString>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<SmartString>().into())
|
Self(iter.into_iter().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ pub struct StringsInterner<'a> {
|
|||||||
|
|
||||||
impl Default for StringsInterner<'_> {
|
impl Default for StringsInterner<'_> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ impl IntoIterator for Scope<'_> {
|
|||||||
type Item = (String, Dynamic, Vec<Identifier>);
|
type Item = (String, Dynamic, Vec<Identifier>);
|
||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
||||||
|
|
||||||
#[inline]
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Box::new(
|
Box::new(
|
||||||
self.values
|
self.values
|
||||||
@ -143,7 +143,7 @@ impl<'a> IntoIterator for &'a Scope<'_> {
|
|||||||
type Item = (&'a Identifier, &'a Dynamic, &'a Vec<Identifier>);
|
type Item = (&'a Identifier, &'a Dynamic, &'a Vec<Identifier>);
|
||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
||||||
|
|
||||||
#[inline]
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Box::new(
|
Box::new(
|
||||||
self.values
|
self.values
|
||||||
@ -719,7 +719,6 @@ impl Scope<'_> {
|
|||||||
scope
|
scope
|
||||||
}
|
}
|
||||||
/// Get an iterator to entries in the [`Scope`].
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
#[inline]
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Identifier, Dynamic, Vec<Identifier>)> {
|
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Identifier, Dynamic, Vec<Identifier>)> {
|
||||||
self.names
|
self.names
|
||||||
|
@ -49,6 +49,8 @@ fn build_type() -> Result<(), Box<EvalAltResult>> {
|
|||||||
type Item = INT;
|
type Item = INT;
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
vec![self.x, self.y, self.z].into_iter()
|
vec![self.x, self.y, self.z].into_iter()
|
||||||
}
|
}
|
||||||
|
@ -337,6 +337,8 @@ impl IntoIterator for MyIterableType {
|
|||||||
type Item = char;
|
type Item = char;
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.0.chars().collect::<Vec<_>>().into_iter()
|
self.0.chars().collect::<Vec<_>>().into_iter()
|
||||||
}
|
}
|
||||||
|
@ -276,6 +276,11 @@ fn test_map_json() -> Result<(), Box<EvalAltResult>> {
|
|||||||
EvalAltResult::ErrorMismatchOutputType(..)
|
EvalAltResult::ErrorMismatchOutputType(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.parse_json("{a:42}", true).expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorParsing(..)
|
||||||
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.parse_json("#{a:123}", true)
|
.parse_json("#{a:123}", true)
|
||||||
|
Loading…
Reference in New Issue
Block a user