chore: sync fork.
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST,
|
||||
ERR,
|
||||
reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, SharedModule,
|
||||
StaticVec, AST, ERR,
|
||||
};
|
||||
use std::any::{type_name, TypeId};
|
||||
#[cfg(feature = "no_std")]
|
||||
@@ -233,6 +233,7 @@ impl Engine {
|
||||
arg_values,
|
||||
)
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
fn _call_fn(
|
||||
&self,
|
||||
@@ -247,8 +248,10 @@ impl Engine {
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> RhaiResult {
|
||||
let statements = ast.statements();
|
||||
let lib = &[ast.as_ref()];
|
||||
let mut this_ptr = this_ptr;
|
||||
let lib = &[AsRef::<SharedModule>::as_ref(ast).clone()];
|
||||
|
||||
let mut no_this_ptr = Dynamic::NULL;
|
||||
let this_ptr = this_ptr.unwrap_or(&mut no_this_ptr);
|
||||
|
||||
let orig_scope_len = scope.len();
|
||||
|
||||
@@ -257,54 +260,51 @@ impl Engine {
|
||||
&mut global.embedded_module_resolver,
|
||||
ast.resolver().cloned(),
|
||||
);
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
|
||||
g.embedded_module_resolver = orig_embedded_module_resolver
|
||||
});
|
||||
|
||||
let mut result = Ok(Dynamic::UNIT);
|
||||
|
||||
if eval_ast && !statements.is_empty() {
|
||||
result = self.eval_global_statements(scope, global, caches, statements, lib, 0);
|
||||
let result = if eval_ast && !statements.is_empty() {
|
||||
let r = self.eval_global_statements(global, caches, lib, scope, statements);
|
||||
|
||||
if rewind_scope {
|
||||
scope.rewind(orig_scope_len);
|
||||
}
|
||||
}
|
||||
|
||||
result = result.and_then(|_| {
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
r
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
.and_then(|_| {
|
||||
let args = &mut arg_values.iter_mut().collect::<StaticVec<_>>();
|
||||
|
||||
// Check for data race.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::func::call::ensure_no_data_race(name, &args, false).map(|_| Dynamic::UNIT)?;
|
||||
crate::func::ensure_no_data_race(name, args, false).map(|_| Dynamic::UNIT)?;
|
||||
|
||||
if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) {
|
||||
self.call_script_fn(
|
||||
scope,
|
||||
global,
|
||||
caches,
|
||||
lib,
|
||||
&mut this_ptr,
|
||||
scope,
|
||||
this_ptr,
|
||||
fn_def,
|
||||
&mut args,
|
||||
args,
|
||||
rewind_scope,
|
||||
Position::NONE,
|
||||
0,
|
||||
)
|
||||
} else {
|
||||
Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into())
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
global.embedded_module_resolver = orig_embedded_module_resolver;
|
||||
}
|
||||
|
||||
let result = result?;
|
||||
})?;
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
self.run_debugger(scope, global, lib, &mut this_ptr, node, 0)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, node)?;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
|
||||
@@ -295,6 +295,6 @@ impl Engine {
|
||||
|
||||
let mut peekable = stream.peekable();
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ use crate::{
|
||||
reify, Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position,
|
||||
RhaiResult, StaticVec,
|
||||
};
|
||||
use std::ops::Deref;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{borrow::Borrow, ops::Deref};
|
||||
|
||||
/// Collection of special markers for custom syntax definition.
|
||||
pub mod markers {
|
||||
@@ -145,8 +145,17 @@ impl Expression<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<Expr> for Expression<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &Expr {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Expr> for Expression<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &Expr {
|
||||
self.0
|
||||
}
|
||||
@@ -219,7 +228,13 @@ impl Engine {
|
||||
continue;
|
||||
}
|
||||
|
||||
let token = Token::lookup_from_syntax(s);
|
||||
let token = Token::lookup_symbol_from_syntax(s).or_else(|| {
|
||||
if Token::is_reserved_keyword(s) {
|
||||
Some(Token::Reserved(Box::new(s.into())))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let seg = match s {
|
||||
// Markers not in first position
|
||||
@@ -264,7 +279,7 @@ impl Engine {
|
||||
.into_err(Position::NONE));
|
||||
}
|
||||
// Identifier in first position
|
||||
_ if segments.is_empty() && is_valid_identifier(s.chars()) => {
|
||||
_ if segments.is_empty() && is_valid_identifier(s) => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s))
|
||||
|| token.map_or(false, |v| v.is_reserved())
|
||||
|
||||
@@ -77,6 +77,7 @@ pub struct DefinitionsConfig {
|
||||
|
||||
impl Default for DefinitionsConfig {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
write_headers: false,
|
||||
@@ -439,18 +440,16 @@ impl Module {
|
||||
}
|
||||
first = false;
|
||||
|
||||
if f.access == FnAccess::Private {
|
||||
continue;
|
||||
if f.access != FnAccess::Private {
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
let operator = def.engine.custom_keywords.contains_key(f.name.as_str())
|
||||
|| (!f.name.contains('$') && !is_valid_function_name(f.name.as_str()));
|
||||
|
||||
#[cfg(feature = "no_custom_syntax")]
|
||||
let operator = !f.name.contains('$') && !is_valid_function_name(&f.name);
|
||||
|
||||
f.write_definition(writer, def, operator)?;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
let operator = def.engine.custom_keywords.contains_key(&f.name)
|
||||
|| (!f.name.contains('$') && !is_valid_function_name(&f.name));
|
||||
|
||||
#[cfg(feature = "no_custom_syntax")]
|
||||
let operator = !f.name.contains('$') && !is_valid_function_name(&f.name);
|
||||
|
||||
f.write_definition(writer, def, operator)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -554,7 +553,7 @@ fn def_type_name<'a>(ty: &'a str, engine: &'a Engine) -> Cow<'a, str> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
let ty = ty.replace(type_name::<crate::Map>(), "Map");
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
let ty = ty.replace(type_name::<crate::Instant>(), "Instant");
|
||||
|
||||
let ty = ty.replace(type_name::<FnPtr>(), "FnPtr");
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::func::RegisterNativeFunction;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext,
|
||||
Position, RhaiResult, RhaiResultOf, Scope, AST,
|
||||
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -354,6 +354,26 @@ impl Dynamic {
|
||||
}
|
||||
|
||||
impl NativeCallContext<'_> {
|
||||
/// Create a new [`NativeCallContext`].
|
||||
///
|
||||
/// # Unimplemented
|
||||
///
|
||||
/// This method is deprecated. It is no longer implemented and always panics.
|
||||
///
|
||||
/// Use [`FnPtr::call`] to call a function pointer directly.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(
|
||||
since = "1.3.0",
|
||||
note = "use `FnPtr::call` to call a function pointer directly."
|
||||
)]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[allow(unused_variables)]
|
||||
pub fn new(engine: &Engine, fn_name: &str, lib: &[SharedModule]) -> Self {
|
||||
unimplemented!("`NativeCallContext::new` is deprecated");
|
||||
}
|
||||
|
||||
/// Call a function inside the call context.
|
||||
///
|
||||
/// # Deprecated
|
||||
|
||||
@@ -122,6 +122,7 @@ impl Engine {
|
||||
let ast = self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
|_| {},
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
@@ -185,18 +186,21 @@ impl Engine {
|
||||
ast: &AST,
|
||||
) -> RhaiResultOf<T> {
|
||||
let global = &mut GlobalRuntimeState::new(self);
|
||||
let caches = &mut Caches::new();
|
||||
|
||||
let result = self.eval_ast_with_scope_raw(scope, global, ast, 0)?;
|
||||
let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?;
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
if self.debugger.is_some() {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
let lib = &[
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
ast.as_ref(),
|
||||
AsRef::<crate::SharedModule>::as_ref(ast).clone(),
|
||||
];
|
||||
let mut this = Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||
self.run_debugger(scope, global, lib, &mut None, node, 0)?;
|
||||
|
||||
self.run_debugger(global, caches, lib, scope, &mut this, node)?;
|
||||
}
|
||||
|
||||
let typ = self.map_type_name(result.type_name());
|
||||
@@ -210,19 +214,23 @@ impl Engine {
|
||||
#[inline]
|
||||
pub(crate) fn eval_ast_with_scope_raw<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
|
||||
scope: &mut Scope,
|
||||
ast: &'a AST,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
let mut caches = Caches::new();
|
||||
global.source = ast.source_raw().clone();
|
||||
global.source = ast.source_raw().cloned();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let orig_embedded_module_resolver = std::mem::replace(
|
||||
&mut global.embedded_module_resolver,
|
||||
ast.resolver().cloned(),
|
||||
);
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
|
||||
g.embedded_module_resolver = orig_embedded_module_resolver
|
||||
});
|
||||
|
||||
let statements = ast.statements();
|
||||
|
||||
@@ -230,24 +238,12 @@ impl Engine {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let mut _lib = &[
|
||||
let lib = &[
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
ast.as_ref(),
|
||||
][..];
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if !ast.has_functions() {
|
||||
_lib = &[];
|
||||
}
|
||||
AsRef::<crate::SharedModule>::as_ref(ast).clone(),
|
||||
];
|
||||
|
||||
let result =
|
||||
self.eval_global_statements(scope, global, &mut caches, statements, _lib, level);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
global.embedded_module_resolver = orig_embedded_module_resolver;
|
||||
}
|
||||
|
||||
result
|
||||
self.eval_global_statements(global, caches, lib, scope, statements)
|
||||
}
|
||||
/// _(internals)_ Evaluate a list of statements with no `this` pointer.
|
||||
/// Exported under the `internals` feature only.
|
||||
@@ -261,14 +257,14 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn eval_statements_raw(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[crate::SharedModule],
|
||||
|
||||
scope: &mut Scope,
|
||||
statements: &[crate::ast::Stmt],
|
||||
lib: &[&crate::Module],
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
self.eval_global_statements(scope, global, caches, statements, lib, level)
|
||||
self.eval_global_statements(global, caches, lib, scope, statements)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -169,10 +169,11 @@ impl Engine {
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// // Register a token mapper.
|
||||
/// # #[allow(deprecated)]
|
||||
/// engine.on_parse_token(|token, _, _| {
|
||||
/// match token {
|
||||
/// // Convert all integer literals to strings
|
||||
/// Token::IntegerConstant(n) => Token::StringConstant(n.to_string().into()),
|
||||
/// Token::IntegerConstant(n) => Token::StringConstant(Box::new(n.to_string().into())),
|
||||
/// // Convert 'begin' .. 'end' to '{' .. '}'
|
||||
/// Token::Identifier(s) if &*s == "begin" => Token::LeftBrace,
|
||||
/// Token::Identifier(s) if &*s == "end" => Token::RightBrace,
|
||||
|
||||
@@ -108,7 +108,7 @@ impl Engine {
|
||||
pub fn compile_file_with_scope(&self, scope: &Scope, path: PathBuf) -> RhaiResultOf<AST> {
|
||||
Self::read_file(&path).and_then(|contents| {
|
||||
let mut ast = self.compile_with_scope(scope, &contents)?;
|
||||
ast.set_source(path.to_string_lossy());
|
||||
ast.set_source(path.to_string_lossy().as_ref());
|
||||
Ok(ast)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Engine {
|
||||
/// JSON sub-objects are handled transparently.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
@@ -122,6 +122,7 @@ impl Engine {
|
||||
let ast = self.parse_global_expr(
|
||||
&mut stream.peekable(),
|
||||
&mut state,
|
||||
|s| s.allow_unquoted_map_properties = false,
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
OptimizationLevel::None,
|
||||
#[cfg(feature = "no_optimize")]
|
||||
@@ -137,7 +138,7 @@ impl Engine {
|
||||
/// Not available under `no_std`.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
@@ -165,10 +166,7 @@ pub fn format_map_as_json(map: &Map) -> String {
|
||||
|
||||
if let Some(val) = value.read_lock::<Map>() {
|
||||
result.push_str(&format_map_as_json(&*val));
|
||||
continue;
|
||||
}
|
||||
|
||||
if value.is::<()>() {
|
||||
} else if value.is::<()>() {
|
||||
result.push_str("null");
|
||||
} else {
|
||||
write!(result, "{:?}", value).unwrap();
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
//! Settings for [`Engine`]'s limitations.
|
||||
#![cfg(not(feature = "unchecked"))]
|
||||
|
||||
use super::default_limits;
|
||||
use crate::Engine;
|
||||
use std::num::{NonZeroU64, NonZeroUsize};
|
||||
#[cfg(feature = "no_std")]
|
||||
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`].
|
||||
///
|
||||
/// Not available under `unchecked`.
|
||||
@@ -75,12 +94,34 @@ impl Limits {
|
||||
|
||||
impl Default for Limits {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
/// infinite recursion and stack overflows.
|
||||
///
|
||||
@@ -93,12 +134,14 @@ impl Engine {
|
||||
}
|
||||
/// The maximum levels of function calls allowed for a script.
|
||||
///
|
||||
/// Not available under `unchecked` or `no_function`.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
/// Zero under `no_function`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
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
|
||||
/// consuming too much resources (0 for unlimited).
|
||||
@@ -115,10 +158,9 @@ impl Engine {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn max_operations(&self) -> u64 {
|
||||
if let Some(n) = self.limits.max_operations {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
match self.limits.max_operations {
|
||||
Some(n) => n.get(),
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
/// 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.
|
||||
///
|
||||
/// Not available under `unchecked` or `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
/// Zero under `no_module`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
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).
|
||||
///
|
||||
@@ -156,29 +200,27 @@ impl Engine {
|
||||
self
|
||||
}
|
||||
/// The depth limit for expressions (0 for unlimited).
|
||||
///
|
||||
/// Not available under `unchecked`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn max_expr_depth(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_expr_depth {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
match self.limits.max_expr_depth {
|
||||
Some(n) => n.get(),
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
/// The depth limit for expressions in functions (0 for unlimited).
|
||||
///
|
||||
/// Not available under `unchecked` or `no_function`.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
/// Zero under `no_function`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn max_function_expr_depth(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_function_expr_depth {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
return match self.limits.max_function_expr_depth {
|
||||
Some(n) => n.get(),
|
||||
None => 0,
|
||||
};
|
||||
#[cfg(feature = "no_function")]
|
||||
return 0;
|
||||
}
|
||||
/// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
@@ -189,15 +231,12 @@ impl Engine {
|
||||
self
|
||||
}
|
||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
/// Not available under `unchecked`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_string_size {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
match self.limits.max_string_size {
|
||||
Some(n) => n.get(),
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
/// 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).
|
||||
///
|
||||
/// Not available under `unchecked` or `no_index`.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
/// Zero under `no_index`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn max_array_size(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_array_size {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return match self.limits.max_array_size {
|
||||
Some(n) => n.get(),
|
||||
None => 0,
|
||||
};
|
||||
#[cfg(feature = "no_index")]
|
||||
return 0;
|
||||
}
|
||||
/// 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).
|
||||
///
|
||||
/// Not available under `unchecked` or `no_object`.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
/// Zero under `no_object`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn max_map_size(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_map_size {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
return match self.limits.max_map_size {
|
||||
Some(n) => n.get(),
|
||||
None => 0,
|
||||
};
|
||||
#[cfg(feature = "no_object")]
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
103
src/api/mod.rs
103
src/api/mod.rs
@@ -35,36 +35,12 @@ pub mod definitions;
|
||||
|
||||
use crate::{Dynamic, Engine, Identifier};
|
||||
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
use crate::{engine::Precedence, tokenizer::Token};
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
pub mod default_limits {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[cfg(debug_assertions)]
|
||||
#[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 use super::limits::default_limits::*;
|
||||
|
||||
pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
|
||||
}
|
||||
@@ -75,6 +51,7 @@ impl Engine {
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn module_resolver(&self) -> &dyn crate::ModuleResolver {
|
||||
&*self.module_resolver
|
||||
}
|
||||
@@ -170,12 +147,14 @@ impl Engine {
|
||||
keyword: impl AsRef<str>,
|
||||
precedence: u8,
|
||||
) -> Result<&mut Self, String> {
|
||||
let precedence =
|
||||
Precedence::new(precedence).ok_or_else(|| "precedence cannot be zero".to_string())?;
|
||||
use crate::tokenizer::Token;
|
||||
|
||||
let precedence = crate::engine::Precedence::new(precedence)
|
||||
.ok_or_else(|| "precedence cannot be zero".to_string())?;
|
||||
|
||||
let keyword = keyword.as_ref();
|
||||
|
||||
match Token::lookup_from_syntax(keyword) {
|
||||
match Token::lookup_symbol_from_syntax(keyword) {
|
||||
// Standard identifiers and reserved keywords are OK
|
||||
None | Some(Token::Reserved(..)) => (),
|
||||
// custom keywords are OK
|
||||
@@ -235,3 +214,71 @@ impl Engine {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
impl Engine {
|
||||
/// The maximum levels of function calls allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[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).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_operations(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
/// The maximum number of imported [modules][crate::Module] allowed for a script.
|
||||
///
|
||||
/// Always returns [`usize::MAX`] under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_modules(&self) -> usize {
|
||||
usize::MAX
|
||||
}
|
||||
/// The depth limit for expressions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The depth limit for expressions in functions (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_function_expr_depth(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_array_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
/// The maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||
///
|
||||
/// Always returns zero under `unchecked`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn max_map_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,23 +12,25 @@ bitflags! {
|
||||
const IF_EXPR = 0b_0000_0000_0001;
|
||||
/// Is `switch` expression allowed?
|
||||
const SWITCH_EXPR = 0b_0000_0000_0010;
|
||||
/// Are loop expressions allowed?
|
||||
const LOOP_EXPR = 0b_0000_0000_0100;
|
||||
/// Is statement-expression allowed?
|
||||
const STMT_EXPR = 0b_0000_0000_0100;
|
||||
const STMT_EXPR = 0b_0000_0000_1000;
|
||||
/// Is anonymous function allowed?
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
const ANON_FN = 0b_0000_0000_1000;
|
||||
const ANON_FN = 0b_0000_0001_0000;
|
||||
/// Is looping allowed?
|
||||
const LOOPING = 0b_0000_0001_0000;
|
||||
const LOOPING = 0b_0000_0010_0000;
|
||||
/// Is variables shadowing allowed?
|
||||
const SHADOW = 0b_0000_0010_0000;
|
||||
const SHADOW = 0b_0000_0100_0000;
|
||||
/// Strict variables mode?
|
||||
const STRICT_VAR = 0b_0000_0100_0000;
|
||||
const STRICT_VAR = 0b_0000_1000_0000;
|
||||
/// Raise error if an object map property does not exist?
|
||||
/// Returns `()` if `false`.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0000_1000_0000;
|
||||
const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0001_0000_0000;
|
||||
/// Fast operators mode?
|
||||
const FAST_OPS = 0b_0001_0000_0000;
|
||||
const FAST_OPS = 0b_0010_0000_0000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +83,18 @@ impl Engine {
|
||||
pub fn set_allow_switch_expression(&mut self, enable: bool) {
|
||||
self.options.set(LangOptions::SWITCH_EXPR, enable);
|
||||
}
|
||||
/// Are loop expressions allowed?
|
||||
/// Default is `true`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn allow_loop_expressions(&self) -> bool {
|
||||
self.options.contains(LangOptions::LOOP_EXPR)
|
||||
}
|
||||
/// Set whether loop expressions are allowed.
|
||||
#[inline(always)]
|
||||
pub fn set_allow_loop_expressions(&mut self, enable: bool) {
|
||||
self.options.set(LangOptions::LOOP_EXPR, enable);
|
||||
}
|
||||
/// Is statement-expression allowed?
|
||||
/// Default is `true`.
|
||||
#[inline(always)]
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Engine, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, RhaiResultOf, Shared,
|
||||
SharedModule,
|
||||
};
|
||||
use std::any::{type_name, TypeId};
|
||||
#[cfg(feature = "no_std")]
|
||||
@@ -16,12 +17,14 @@ impl Engine {
|
||||
/// Get the global namespace module (which is the fist module in `global_modules`).
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
#[must_use]
|
||||
pub(crate) fn global_namespace(&self) -> &Module {
|
||||
self.global_modules.first().unwrap()
|
||||
}
|
||||
/// Get a mutable reference to the global namespace module
|
||||
/// (which is the first module in `global_modules`).
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn global_namespace_mut(&mut self) -> &mut Module {
|
||||
let module = self.global_modules.first_mut().unwrap();
|
||||
Shared::get_mut(module).expect("not shared")
|
||||
@@ -224,12 +227,12 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub fn register_type_with_name_raw(
|
||||
&mut self,
|
||||
fully_qualified_type_path: impl Into<Identifier>,
|
||||
type_path: impl Into<Identifier>,
|
||||
name: impl Into<Identifier>,
|
||||
) -> &mut Self {
|
||||
// Add the pretty-print type name into the map
|
||||
self.global_namespace_mut()
|
||||
.set_custom_type_raw(fully_qualified_type_path, name);
|
||||
.set_custom_type_raw(type_path, name);
|
||||
self
|
||||
}
|
||||
/// Register a type iterator for an iterable type with the [`Engine`].
|
||||
@@ -634,7 +637,7 @@ impl Engine {
|
||||
/// When searching for functions, modules loaded later are preferred. In other words, loaded
|
||||
/// modules are searched in reverse order.
|
||||
#[inline(always)]
|
||||
pub fn register_global_module(&mut self, module: Shared<Module>) -> &mut Self {
|
||||
pub fn register_global_module(&mut self, module: SharedModule) -> &mut Self {
|
||||
// Insert the module into the front.
|
||||
// The first module is always the global namespace.
|
||||
self.global_modules.insert(1, module);
|
||||
@@ -678,12 +681,12 @@ impl Engine {
|
||||
pub fn register_static_module(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
module: Shared<Module>,
|
||||
module: SharedModule,
|
||||
) -> &mut Self {
|
||||
fn register_static_module_raw(
|
||||
root: &mut std::collections::BTreeMap<Identifier, Shared<Module>>,
|
||||
root: &mut std::collections::BTreeMap<Identifier, SharedModule>,
|
||||
name: &str,
|
||||
module: Shared<Module>,
|
||||
module: SharedModule,
|
||||
) {
|
||||
let separator = crate::tokenizer::Token::DoubleColon.syntax();
|
||||
let separator = separator.as_ref();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::parser::ParseState;
|
||||
use crate::{Engine, Module, RhaiResultOf, Scope, AST};
|
||||
use crate::{Engine, RhaiResultOf, Scope, SharedModule, AST};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -113,7 +113,7 @@ impl Engine {
|
||||
pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
|
||||
let caches = &mut Caches::new();
|
||||
let global = &mut GlobalRuntimeState::new(self);
|
||||
global.source = ast.source_raw().clone();
|
||||
global.source = ast.source_raw().cloned();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
@@ -122,16 +122,16 @@ impl Engine {
|
||||
|
||||
let statements = ast.statements();
|
||||
if !statements.is_empty() {
|
||||
let lib = [
|
||||
let lib: &[SharedModule] = &[
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
ast.as_ref(),
|
||||
AsRef::<SharedModule>::as_ref(ast).clone(),
|
||||
];
|
||||
let lib = if lib.first().map_or(true, |m: &&Module| m.is_empty()) {
|
||||
&lib[0..0]
|
||||
let lib = if lib.first().map_or(true, |m| m.is_empty()) {
|
||||
&[][..]
|
||||
} else {
|
||||
&lib
|
||||
};
|
||||
self.eval_global_statements(scope, global, caches, statements, lib, 0)?;
|
||||
self.eval_global_statements(global, caches, lib, scope, statements)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
@@ -139,10 +139,11 @@ impl Engine {
|
||||
global.debugger.status = crate::eval::DebuggerStatus::Terminate;
|
||||
let lib = &[
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
ast.as_ref(),
|
||||
AsRef::<crate::SharedModule>::as_ref(ast).clone(),
|
||||
];
|
||||
let mut this = crate::Dynamic::NULL;
|
||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||
self.run_debugger(scope, global, lib, &mut None, node, 0)?;
|
||||
self.run_debugger(global, caches, lib, scope, &mut this, node)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -44,7 +44,7 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str {
|
||||
if name == type_name::<crate::Map>() || name == "Map" {
|
||||
return if shorthands { "map" } else { "Map" };
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
if name == type_name::<crate::Instant>() || name == "Instant" {
|
||||
return if shorthands { "timestamp" } else { "Instant" };
|
||||
}
|
||||
@@ -108,11 +108,15 @@ fn map_std_type_name(name: &str, shorthands: bool) -> &str {
|
||||
|
||||
/// Format a Rust type to be display-friendly.
|
||||
///
|
||||
/// * `rhai::` prefix is cleared.
|
||||
/// * `()` is cleared.
|
||||
/// * `&mut` is cleared.
|
||||
/// * `INT` and `FLOAT` are expanded.
|
||||
/// * [`RhaiResult`][crate::RhaiResult] and [`RhaiResultOf<T>`][crate::RhaiResultOf] are expanded.
|
||||
#[cfg(feature = "metadata")]
|
||||
pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow<str> {
|
||||
const RESULT_TYPE: &str = "Result<";
|
||||
const ERROR_TYPE: &str = ",Box<EvalAltResult>>";
|
||||
const RHAI_RESULT_TYPE: &str = "RhaiResult";
|
||||
const RHAI_RESULT_TYPE_EXPAND: &str = "Result<Dynamic, Box<EvalAltResult>>";
|
||||
const RHAI_RESULT_OF_TYPE: &str = "RhaiResultOf<";
|
||||
@@ -135,6 +139,10 @@ pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow<str> {
|
||||
} else {
|
||||
format!("&mut {r}").into()
|
||||
};
|
||||
} else if typ.contains(" ") {
|
||||
let typ = typ.replace(" ", "");
|
||||
let r = format_type(&typ, is_return_type);
|
||||
return r.into_owned().into();
|
||||
}
|
||||
|
||||
match typ {
|
||||
@@ -167,6 +175,12 @@ pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow<str> {
|
||||
.replace("{}", format_type(inner, false).trim())
|
||||
.into()
|
||||
}
|
||||
ty if ty.starts_with(RESULT_TYPE) && ty.ends_with(ERROR_TYPE) => {
|
||||
let inner = &ty[RESULT_TYPE.len()..ty.len() - ERROR_TYPE.len()];
|
||||
RHAI_RESULT_OF_TYPE_EXPAND
|
||||
.replace("{}", format_type(inner, false).trim())
|
||||
.into()
|
||||
}
|
||||
ty => ty.into(),
|
||||
}
|
||||
}
|
||||
|
||||
122
src/ast/ast.rs
122
src/ast/ast.rs
@@ -1,10 +1,11 @@
|
||||
//! Module defining the AST (abstract syntax tree).
|
||||
|
||||
use super::{ASTFlags, Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer};
|
||||
use crate::{Dynamic, FnNamespace, Identifier, Position};
|
||||
use crate::{Dynamic, FnNamespace, ImmutableString, Position};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
fmt,
|
||||
hash::Hash,
|
||||
ops::{Add, AddAssign},
|
||||
@@ -19,8 +20,7 @@ use std::{
|
||||
#[derive(Clone)]
|
||||
pub struct AST {
|
||||
/// Source of the [`AST`].
|
||||
/// No source if string is empty.
|
||||
source: Identifier,
|
||||
source: Option<ImmutableString>,
|
||||
/// [`AST`] documentation.
|
||||
#[cfg(feature = "metadata")]
|
||||
doc: crate::SmartString,
|
||||
@@ -28,7 +28,7 @@ pub struct AST {
|
||||
body: StmtBlock,
|
||||
/// Script-defined functions.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib: crate::Shared<crate::Module>,
|
||||
lib: crate::SharedModule,
|
||||
/// Embedded module resolver, if any.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
resolver: Option<crate::Shared<crate::module::resolvers::StaticModuleResolver>>,
|
||||
@@ -36,12 +36,15 @@ pub struct AST {
|
||||
|
||||
impl Default for AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AST {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut fp = f.debug_struct("AST");
|
||||
|
||||
@@ -67,14 +70,14 @@ impl fmt::Debug for AST {
|
||||
impl AST {
|
||||
/// Create a new [`AST`].
|
||||
#[cfg(not(feature = "internals"))]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn new(
|
||||
statements: impl IntoIterator<Item = Stmt>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::SharedModule>,
|
||||
) -> Self {
|
||||
Self {
|
||||
source: Identifier::new_const(),
|
||||
source: None,
|
||||
#[cfg(feature = "metadata")]
|
||||
doc: crate::SmartString::new_const(),
|
||||
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
||||
@@ -87,14 +90,14 @@ impl AST {
|
||||
/// _(internals)_ Create a new [`AST`].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
statements: impl IntoIterator<Item = Stmt>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::SharedModule>,
|
||||
) -> Self {
|
||||
Self {
|
||||
source: Identifier::new_const(),
|
||||
source: None,
|
||||
#[cfg(feature = "metadata")]
|
||||
doc: crate::SmartString::new_const(),
|
||||
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
||||
@@ -106,12 +109,12 @@ impl AST {
|
||||
}
|
||||
/// Create a new [`AST`] with a source name.
|
||||
#[cfg(not(feature = "internals"))]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn new_with_source(
|
||||
statements: impl IntoIterator<Item = Stmt>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||
source: impl Into<Identifier>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::SharedModule>,
|
||||
source: impl Into<ImmutableString>,
|
||||
) -> Self {
|
||||
let mut ast = Self::new(
|
||||
statements,
|
||||
@@ -124,12 +127,12 @@ impl AST {
|
||||
/// _(internals)_ Create a new [`AST`] with a source name.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new_with_source(
|
||||
statements: impl IntoIterator<Item = Stmt>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||
source: impl Into<Identifier>,
|
||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::SharedModule>,
|
||||
source: impl Into<ImmutableString>,
|
||||
) -> Self {
|
||||
let mut ast = Self::new(
|
||||
statements,
|
||||
@@ -144,7 +147,7 @@ impl AST {
|
||||
#[must_use]
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
source: Identifier::new_const(),
|
||||
source: None,
|
||||
#[cfg(feature = "metadata")]
|
||||
doc: crate::SmartString::new_const(),
|
||||
body: StmtBlock::NONE,
|
||||
@@ -158,33 +161,36 @@ impl AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn source(&self) -> Option<&str> {
|
||||
if self.source.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.source.as_str())
|
||||
}
|
||||
self.source.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
/// Get a reference to the source.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn source_raw(&self) -> &Identifier {
|
||||
&self.source
|
||||
pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
|
||||
self.source.as_ref()
|
||||
}
|
||||
/// Set the source.
|
||||
#[inline]
|
||||
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
||||
pub fn set_source(&mut self, source: impl Into<ImmutableString>) -> &mut Self {
|
||||
let source = source.into();
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
crate::Shared::get_mut(&mut self.lib)
|
||||
.as_mut()
|
||||
.map(|m| m.set_id(source.clone()));
|
||||
self.source = source;
|
||||
|
||||
if source.is_empty() {
|
||||
self.source = None;
|
||||
} else {
|
||||
self.source = Some(source);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
/// Clear the source.
|
||||
#[inline(always)]
|
||||
pub fn clear_source(&mut self) -> &mut Self {
|
||||
self.source.clear();
|
||||
self.source = None;
|
||||
self
|
||||
}
|
||||
/// Get the documentation (if any).
|
||||
@@ -261,7 +267,7 @@ impl AST {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
||||
pub(crate) const fn shared_lib(&self) -> &crate::SharedModule {
|
||||
&self.lib
|
||||
}
|
||||
/// _(internals)_ Get the internal shared [`Module`][crate::Module] containing all script-defined functions.
|
||||
@@ -272,7 +278,7 @@ impl AST {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn shared_lib(&self) -> &crate::Shared<crate::Module> {
|
||||
pub const fn shared_lib(&self) -> &crate::SharedModule {
|
||||
&self.lib
|
||||
}
|
||||
/// Get the embedded [module resolver][crate::ModuleResolver].
|
||||
@@ -555,18 +561,18 @@ impl AST {
|
||||
lib
|
||||
};
|
||||
|
||||
let mut _ast = if other.source.is_empty() {
|
||||
Self::new(
|
||||
merged,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib,
|
||||
)
|
||||
} else {
|
||||
let mut _ast = if let Some(ref source) = other.source {
|
||||
Self::new_with_source(
|
||||
merged,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib,
|
||||
other.source.clone(),
|
||||
source.clone(),
|
||||
)
|
||||
} else {
|
||||
Self::new(
|
||||
merged,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -662,7 +668,6 @@ impl AST {
|
||||
self.combine_filtered_impl(other, filter)
|
||||
}
|
||||
/// Combine one [`AST`] with another. The second [`AST`] is consumed.
|
||||
#[inline]
|
||||
fn combine_filtered_impl(
|
||||
&mut self,
|
||||
other: Self,
|
||||
@@ -917,25 +922,54 @@ impl<A: Into<Self>> AddAssign<A> for AST {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[Stmt]> for AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &[Stmt] {
|
||||
self.statements()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[Stmt]> for AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &[Stmt] {
|
||||
self.statements()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl Borrow<crate::Module> for AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &crate::Module {
|
||||
&self.shared_lib()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl AsRef<crate::Module> for AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &crate::Module {
|
||||
self.shared_lib().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl AsRef<crate::Shared<crate::Module>> for AST {
|
||||
impl Borrow<crate::SharedModule> for AST {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &crate::Shared<crate::Module> {
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &crate::SharedModule {
|
||||
self.shared_lib()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
impl AsRef<crate::SharedModule> for AST {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &crate::SharedModule {
|
||||
self.shared_lib()
|
||||
}
|
||||
}
|
||||
@@ -952,19 +986,21 @@ pub enum ASTNode<'a> {
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Stmt> for ASTNode<'a> {
|
||||
#[inline(always)]
|
||||
fn from(stmt: &'a Stmt) -> Self {
|
||||
Self::Stmt(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Expr> for ASTNode<'a> {
|
||||
#[inline(always)]
|
||||
fn from(expr: &'a Expr) -> Self {
|
||||
Self::Expr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ASTNode<'_> {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Stmt(x), Self::Stmt(y)) => ptr::eq(*x, *y),
|
||||
@@ -981,8 +1017,8 @@ impl ASTNode<'_> {
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Position {
|
||||
match self {
|
||||
ASTNode::Stmt(stmt) => stmt.position(),
|
||||
ASTNode::Expr(expr) => expr.position(),
|
||||
Self::Stmt(stmt) => stmt.position(),
|
||||
Self::Expr(expr) => expr.position(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
153
src/ast/expr.rs
153
src/ast/expr.rs
@@ -17,7 +17,7 @@ use std::{
|
||||
fmt::Write,
|
||||
hash::Hash,
|
||||
iter::once,
|
||||
num::{NonZeroU8, NonZeroUsize},
|
||||
num::{NonZeroU64, NonZeroU8, NonZeroUsize},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@@ -75,8 +75,8 @@ impl CustomExpr {
|
||||
/// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)?
|
||||
///
|
||||
/// A self-terminated custom syntax always ends in `$block$`, `}` or `;`
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_self_terminated(&self) -> bool {
|
||||
self.self_terminated
|
||||
}
|
||||
@@ -86,7 +86,7 @@ impl CustomExpr {
|
||||
///
|
||||
/// Two separate hashes are pre-calculated because of the following patterns:
|
||||
///
|
||||
/// ```js
|
||||
/// ```rhai
|
||||
/// func(a, b, c); // Native: func(a, b, c) - 3 parameters
|
||||
/// // Script: func(a, b, c) - 3 parameters
|
||||
///
|
||||
@@ -100,30 +100,34 @@ impl CustomExpr {
|
||||
///
|
||||
/// Function call hashes are used in the following manner:
|
||||
///
|
||||
/// * First, the script hash is tried, which contains only the called function's name plus the
|
||||
/// number of parameters.
|
||||
/// * First, the script hash (if any) is tried, which contains only the called function's name plus
|
||||
/// the number of parameters.
|
||||
///
|
||||
/// * Next, the actual types of arguments are hashed and _combined_ with the native hash, which is
|
||||
/// then used to search for a native function. In other words, a complete native function call
|
||||
/// hash always contains the called function's name plus the types of the arguments. This is due
|
||||
/// to possible function overloading for different parameter types.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)]
|
||||
/// then used to search for a native function.
|
||||
///
|
||||
/// In other words, a complete native function call hash always contains the called function's
|
||||
/// name plus the types of the arguments. This is due to possible function overloading for
|
||||
/// different parameter types.
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct FnCallHashes {
|
||||
/// Pre-calculated hash for a script-defined function (zero if native functions only).
|
||||
/// Pre-calculated hash for a script-defined function ([`None`] if native functions only).
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub script: u64,
|
||||
script: Option<NonZeroU64>,
|
||||
/// Pre-calculated hash for a native Rust function with no parameter types.
|
||||
pub native: u64,
|
||||
native: NonZeroU64,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FnCallHashes {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if self.script != 0 {
|
||||
return if self.script == self.native {
|
||||
if let Some(script) = self.script {
|
||||
return if script == self.native {
|
||||
fmt::Debug::fmt(&self.native, f)
|
||||
} else {
|
||||
write!(f, "({}, {})", self.script, self.native)
|
||||
write!(f, "({}, {})", script, self.native)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -132,13 +136,13 @@ impl fmt::Debug for FnCallHashes {
|
||||
}
|
||||
|
||||
impl From<u64> for FnCallHashes {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
fn from(hash: u64) -> Self {
|
||||
let hash = if hash == 0 { ALT_ZERO_HASH } else { hash };
|
||||
let hash = NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap();
|
||||
|
||||
Self {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
script: hash,
|
||||
script: Some(hash),
|
||||
native: hash,
|
||||
}
|
||||
}
|
||||
@@ -146,40 +150,61 @@ impl From<u64> for FnCallHashes {
|
||||
|
||||
impl FnCallHashes {
|
||||
/// Create a [`FnCallHashes`] with only the native Rust hash.
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_native(hash: u64) -> Self {
|
||||
pub fn from_native(hash: u64) -> Self {
|
||||
Self {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
script: 0,
|
||||
native: if hash == 0 { ALT_ZERO_HASH } else { hash },
|
||||
script: None,
|
||||
native: NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap(),
|
||||
}
|
||||
}
|
||||
/// Create a [`FnCallHashes`] with both native Rust and script function hashes.
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
|
||||
pub fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
|
||||
Self {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
script: if script == 0 { ALT_ZERO_HASH } else { script },
|
||||
native: if native == 0 { ALT_ZERO_HASH } else { native },
|
||||
script: NonZeroU64::new(if script == 0 { ALT_ZERO_HASH } else { script }),
|
||||
native: NonZeroU64::new(if native == 0 { ALT_ZERO_HASH } else { native }).unwrap(),
|
||||
}
|
||||
}
|
||||
/// Is this [`FnCallHashes`] native Rust only?
|
||||
/// Is this [`FnCallHashes`] native-only?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_native_only(&self) -> bool {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
return self.script == 0;
|
||||
|
||||
return self.script.is_none();
|
||||
#[cfg(feature = "no_function")]
|
||||
return true;
|
||||
}
|
||||
/// Get the native hash.
|
||||
///
|
||||
/// The hash returned is never zero.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn native(&self) -> u64 {
|
||||
self.native.get()
|
||||
}
|
||||
/// Get the script hash.
|
||||
///
|
||||
/// The hash returned is never zero.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if this [`FnCallHashes`] is native-only.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn script(&self) -> u64 {
|
||||
assert!(self.script.is_some());
|
||||
self.script.as_ref().unwrap().get()
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A function call.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone, Default, Hash)]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct FnCallExpr {
|
||||
/// Namespace of the function, if any.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@@ -193,28 +218,27 @@ pub struct FnCallExpr {
|
||||
/// Does this function call capture the parent scope?
|
||||
pub capture_parent_scope: bool,
|
||||
/// Is this function call a native operator?
|
||||
pub is_native_operator: bool,
|
||||
/// [Position] of the function name.
|
||||
pub pos: Position,
|
||||
pub op_token: Option<Token>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FnCallExpr {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut ff = f.debug_struct("FnCallExpr");
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if !self.namespace.is_empty() {
|
||||
ff.field("namespace", &self.namespace);
|
||||
}
|
||||
if self.capture_parent_scope {
|
||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||
}
|
||||
if self.is_native_operator {
|
||||
ff.field("is_native_operator", &self.is_native_operator);
|
||||
}
|
||||
ff.field("hash", &self.hashes)
|
||||
.field("name", &self.name)
|
||||
.field("args", &self.args);
|
||||
ff.field("pos", &self.pos);
|
||||
if let Some(ref token) = self.op_token {
|
||||
ff.field("op_token", token);
|
||||
}
|
||||
if self.capture_parent_scope {
|
||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||
}
|
||||
ff.finish()
|
||||
}
|
||||
}
|
||||
@@ -237,6 +261,16 @@ impl FnCallExpr {
|
||||
pub fn into_fn_call_expr(self, pos: Position) -> Expr {
|
||||
Expr::FnCall(self.into(), pos)
|
||||
}
|
||||
/// Are all arguments constant?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn constant_args(&self) -> bool {
|
||||
if self.args.is_empty() {
|
||||
true
|
||||
} else {
|
||||
self.args.iter().all(Expr::is_constant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that wraps a floating-point number and implements [`Hash`].
|
||||
@@ -248,7 +282,7 @@ pub struct FloatWrapper<F>(F);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl Hash for FloatWrapper<crate::FLOAT> {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.to_ne_bytes().hash(state);
|
||||
}
|
||||
@@ -257,6 +291,7 @@ impl Hash for FloatWrapper<crate::FLOAT> {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl<F: Float> AsRef<F> for FloatWrapper<F> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &F {
|
||||
&self.0
|
||||
}
|
||||
@@ -265,6 +300,7 @@ impl<F: Float> AsRef<F> for FloatWrapper<F> {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl<F: Float> AsMut<F> for FloatWrapper<F> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_mut(&mut self) -> &mut F {
|
||||
&mut self.0
|
||||
}
|
||||
@@ -290,7 +326,8 @@ impl<F: Float> DerefMut for FloatWrapper<F> {
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl<F: Float + fmt::Debug> fmt::Debug for FloatWrapper<F> {
|
||||
#[inline(always)]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
@@ -438,23 +475,26 @@ pub enum Expr {
|
||||
|
||||
impl Default for Expr {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::Unit(Position::NONE)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Expr {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut display_pos = format!(" @ {:?}", self.start_position());
|
||||
|
||||
match self {
|
||||
Self::DynamicConstant(value, ..) => write!(f, "{:?}", value),
|
||||
Self::BoolConstant(value, ..) => write!(f, "{:?}", value),
|
||||
Self::IntegerConstant(value, ..) => write!(f, "{:?}", value),
|
||||
Self::DynamicConstant(value, ..) => write!(f, "{value:?}"),
|
||||
Self::BoolConstant(value, ..) => write!(f, "{value:?}"),
|
||||
Self::IntegerConstant(value, ..) => write!(f, "{value:?}"),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(value, ..) => write!(f, "{:?}", value),
|
||||
Self::CharConstant(value, ..) => write!(f, "{:?}", value),
|
||||
Self::StringConstant(value, ..) => write!(f, "{:?}", value),
|
||||
Self::FloatConstant(value, ..) => write!(f, "{value:?}"),
|
||||
Self::CharConstant(value, ..) => write!(f, "{value:?}"),
|
||||
Self::StringConstant(value, ..) => write!(f, "{value:?}"),
|
||||
Self::Unit(..) => f.write_str("()"),
|
||||
|
||||
Self::InterpolatedString(x, ..) => {
|
||||
@@ -483,8 +523,12 @@ impl fmt::Debug for Expr {
|
||||
}
|
||||
}
|
||||
f.write_str(&x.3)?;
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if let Some(n) = x.1.index() {
|
||||
write!(f, " #{n}")?;
|
||||
}
|
||||
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
|
||||
write!(f, " #{}", n)?;
|
||||
write!(f, " #{n}")?;
|
||||
}
|
||||
f.write_str(")")
|
||||
}
|
||||
@@ -588,7 +632,7 @@ impl Expr {
|
||||
let mut s = SmartString::new_const();
|
||||
for segment in x.iter() {
|
||||
let v = segment.get_literal_value().unwrap();
|
||||
write!(&mut s, "{}", v).unwrap();
|
||||
write!(&mut s, "{v}").unwrap();
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
@@ -598,7 +642,7 @@ impl Expr {
|
||||
if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR =>
|
||||
{
|
||||
if let Self::StringConstant(ref s, ..) = x.args[0] {
|
||||
FnPtr::new(s).ok()?.into()
|
||||
FnPtr::new(s.clone()).ok()?.into()
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
@@ -669,8 +713,7 @@ impl Expr {
|
||||
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||
capture_parent_scope: false,
|
||||
is_native_operator: false,
|
||||
pos,
|
||||
op_token: None,
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
@@ -725,6 +768,8 @@ impl Expr {
|
||||
| Self::And(.., pos)
|
||||
| Self::Or(.., pos)
|
||||
| Self::Coalesce(.., pos)
|
||||
| Self::FnCall(.., pos)
|
||||
| Self::MethodCall(.., pos)
|
||||
| Self::Index(.., pos)
|
||||
| Self::Dot(.., pos)
|
||||
| Self::InterpolatedString(.., pos)
|
||||
@@ -733,8 +778,6 @@ impl Expr {
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Self::Custom(.., pos) => *pos,
|
||||
|
||||
Self::FnCall(x, ..) | Self::MethodCall(x, ..) => x.pos,
|
||||
|
||||
Self::Stmt(x) => x.position(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::{ImmutableString, Position};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
fmt,
|
||||
hash::Hash,
|
||||
ops::{Deref, DerefMut},
|
||||
@@ -20,14 +21,25 @@ pub struct Ident {
|
||||
}
|
||||
|
||||
impl fmt::Debug for Ident {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self.name)?;
|
||||
self.pos.debug_print(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for Ident {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &str {
|
||||
self.name.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Ident {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.name.as_ref()
|
||||
}
|
||||
|
||||
@@ -29,13 +29,15 @@ pub struct Namespace {
|
||||
}
|
||||
|
||||
impl fmt::Debug for Namespace {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_empty() {
|
||||
return f.write_str("NONE");
|
||||
}
|
||||
|
||||
if let Some(index) = self.index {
|
||||
write!(f, "{} -> ", index)?;
|
||||
write!(f, "{index} -> ")?;
|
||||
}
|
||||
|
||||
f.write_str(
|
||||
@@ -83,7 +85,7 @@ impl DerefMut for Namespace {
|
||||
}
|
||||
|
||||
impl From<Vec<Ident>> for Namespace {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
fn from(mut path: Vec<Ident>) -> Self {
|
||||
path.shrink_to_fit();
|
||||
Self {
|
||||
@@ -94,7 +96,7 @@ impl From<Vec<Ident>> for Namespace {
|
||||
}
|
||||
|
||||
impl From<StaticVec<Ident>> for Namespace {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
fn from(mut path: StaticVec<Ident>) -> Self {
|
||||
path.shrink_to_fit();
|
||||
Self { index: None, path }
|
||||
|
||||
@@ -20,9 +20,9 @@ use std::{fmt, hash::Hash};
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncapsulatedEnviron {
|
||||
/// Functions defined within the same [`AST`][crate::AST].
|
||||
pub lib: crate::Shared<crate::Module>,
|
||||
pub lib: crate::SharedModule,
|
||||
/// Imported [modules][crate::Module].
|
||||
pub imports: Box<[(ImmutableString, crate::Shared<crate::Module>)]>,
|
||||
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
|
||||
/// Globally-defined constants.
|
||||
pub constants: Option<crate::eval::GlobalConstants>,
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::{calc_fn_hash, Position, StaticVec, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::BTreeMap,
|
||||
fmt,
|
||||
hash::Hash,
|
||||
@@ -19,16 +20,16 @@ use std::{
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This type may hold a straight assignment (i.e. not an op-assignment).
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct OpAssignment {
|
||||
/// Hash of the op-assignment call.
|
||||
pub hash_op_assign: u64,
|
||||
/// Hash of the underlying operator call (for fallback).
|
||||
pub hash_op: u64,
|
||||
/// Op-assignment operator.
|
||||
pub op_assign: &'static str,
|
||||
pub op_assign: Token,
|
||||
/// Underlying operator.
|
||||
pub op: &'static str,
|
||||
pub op: Token,
|
||||
/// [Position] of the op-assignment operator.
|
||||
pub pos: Position,
|
||||
}
|
||||
@@ -41,8 +42,8 @@ impl OpAssignment {
|
||||
Self {
|
||||
hash_op_assign: 0,
|
||||
hash_op: 0,
|
||||
op_assign: "=",
|
||||
op: "=",
|
||||
op_assign: Token::Equals,
|
||||
op: Token::Equals,
|
||||
pos,
|
||||
}
|
||||
}
|
||||
@@ -60,7 +61,10 @@ impl OpAssignment {
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub fn new_op_assignment(name: &str, pos: Position) -> Self {
|
||||
Self::new_op_assignment_from_token(&Token::lookup_from_syntax(name).expect("operator"), pos)
|
||||
Self::new_op_assignment_from_token(
|
||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
/// Create a new [`OpAssignment`] from a [`Token`].
|
||||
///
|
||||
@@ -71,12 +75,11 @@ impl OpAssignment {
|
||||
pub fn new_op_assignment_from_token(op: &Token, pos: Position) -> Self {
|
||||
let op_raw = op
|
||||
.get_base_op_from_assignment()
|
||||
.expect("op-assignment operator")
|
||||
.literal_syntax();
|
||||
.expect("op-assignment operator");
|
||||
Self {
|
||||
hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2),
|
||||
hash_op: calc_fn_hash(None, op_raw, 2),
|
||||
op_assign: op.literal_syntax(),
|
||||
hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2),
|
||||
op_assign: op.clone(),
|
||||
op: op_raw,
|
||||
pos,
|
||||
}
|
||||
@@ -90,7 +93,7 @@ impl OpAssignment {
|
||||
#[inline(always)]
|
||||
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
||||
Self::new_op_assignment_from_base_token(
|
||||
&Token::lookup_from_syntax(name).expect("operator"),
|
||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
@@ -107,6 +110,8 @@ impl OpAssignment {
|
||||
}
|
||||
|
||||
impl fmt::Debug for OpAssignment {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_op_assignment() {
|
||||
f.debug_struct("OpAssignment")
|
||||
@@ -179,11 +184,12 @@ pub enum RangeCase {
|
||||
}
|
||||
|
||||
impl fmt::Debug for RangeCase {
|
||||
#[inline]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {}", r.start, r.end, n),
|
||||
Self::InclusiveInt(r, n) => write!(f, "{}..={} => {}", *r.start(), *r.end(), n),
|
||||
Self::ExclusiveInt(r, n) => write!(f, "{}..{} => {n}", r.start, r.end),
|
||||
Self::InclusiveInt(r, n) => write!(f, "{}..={} => {n}", *r.start(), *r.end()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,6 +213,7 @@ impl IntoIterator for RangeCase {
|
||||
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
Self::ExclusiveInt(r, ..) => Box::new(r),
|
||||
@@ -299,6 +306,10 @@ pub struct TryCatchBlock {
|
||||
pub catch_block: StmtBlock,
|
||||
}
|
||||
|
||||
/// Number of items to keep inline for [`StmtBlockContainer`].
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
const STMT_BLOCK_INLINE_SIZE: usize = 8;
|
||||
|
||||
/// _(internals)_ The underlying container type for [`StmtBlock`].
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
@@ -306,7 +317,7 @@ pub struct TryCatchBlock {
|
||||
/// hold a statements block, with the assumption that most program blocks would container fewer than
|
||||
/// 8 statements, and those that do have a lot more statements.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; 8]>;
|
||||
pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
|
||||
|
||||
/// _(internals)_ The underlying container type for [`StmtBlock`].
|
||||
/// Exported under the `internals` feature only.
|
||||
@@ -436,8 +447,17 @@ impl DerefMut for StmtBlock {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[Stmt]> for StmtBlock {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &[Stmt] {
|
||||
&self.block
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[Stmt]> for StmtBlock {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &[Stmt] {
|
||||
&self.block
|
||||
}
|
||||
@@ -445,12 +465,15 @@ impl AsRef<[Stmt]> for StmtBlock {
|
||||
|
||||
impl AsMut<[Stmt]> for StmtBlock {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_mut(&mut self) -> &mut [Stmt] {
|
||||
&mut self.block
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StmtBlock {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("Block")?;
|
||||
fmt::Debug::fmt(&self.block, f)?;
|
||||
@@ -484,9 +507,9 @@ impl From<Stmt> for StmtBlock {
|
||||
impl IntoIterator for StmtBlock {
|
||||
type Item = Stmt;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
type IntoIter = smallvec::IntoIter<[Stmt; 8]>;
|
||||
type IntoIter = smallvec::IntoIter<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
|
||||
#[cfg(feature = "no_std")]
|
||||
type IntoIter = smallvec::IntoIter<[Stmt; 3]>;
|
||||
type IntoIter = smallvec::IntoIter<[Stmt; crate::STATIC_VEC_INLINE_SIZE]>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
@@ -561,14 +584,14 @@ pub enum Stmt {
|
||||
TryCatch(Box<TryCatchBlock>, Position),
|
||||
/// [expression][Expr]
|
||||
Expr(Box<Expr>),
|
||||
/// `continue`/`break`
|
||||
/// `continue`/`break` expr
|
||||
///
|
||||
/// ### Flags
|
||||
///
|
||||
/// * [`NONE`][ASTFlags::NONE] = `continue`
|
||||
/// * [`BREAK`][ASTFlags::BREAK] = `break`
|
||||
BreakLoop(ASTFlags, Position),
|
||||
/// `return`/`throw`
|
||||
BreakLoop(Option<Box<Expr>>, ASTFlags, Position),
|
||||
/// `return`/`throw` expr
|
||||
///
|
||||
/// ### Flags
|
||||
///
|
||||
@@ -585,20 +608,21 @@ pub enum Stmt {
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
Export(Box<(Ident, Ident)>, Position),
|
||||
/// Convert a variable to shared.
|
||||
/// Convert a list of variables to shared.
|
||||
///
|
||||
/// Not available under `no_closure`.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// This variant does not map to any language structure. It is currently only used only to
|
||||
/// convert a normal variable into a shared variable when the variable is _captured_ by a closure.
|
||||
/// convert normal variables into shared variables when they are _captured_ by a closure.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Share(crate::ImmutableString, Position),
|
||||
Share(Box<crate::FnArgsVec<(crate::ImmutableString, Option<NonZeroUsize>, Position)>>),
|
||||
}
|
||||
|
||||
impl Default for Stmt {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::Noop(Position::NONE)
|
||||
}
|
||||
@@ -660,7 +684,7 @@ impl Stmt {
|
||||
Self::Export(.., pos) => *pos,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(.., pos) => *pos,
|
||||
Self::Share(x) => x[0].2,
|
||||
}
|
||||
}
|
||||
/// Override the [position][Position] of this statement.
|
||||
@@ -692,7 +716,7 @@ impl Stmt {
|
||||
Self::Export(.., pos) => *pos = new_pos,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(.., pos) => *pos = new_pos,
|
||||
Self::Share(x) => x.iter_mut().for_each(|(_, _, pos)| *pos = new_pos),
|
||||
}
|
||||
|
||||
self
|
||||
|
||||
@@ -45,7 +45,6 @@ fn print_source(lines: &[String], pos: Position, offset: usize, window: (usize,
|
||||
if n == line {
|
||||
if let Some(pos) = pos.position() {
|
||||
let shift = offset + line_no_len + marker.len() + 2;
|
||||
|
||||
println!("{0:>1$}{2:>3$}", "│ ", shift, "\x1b[36m^\x1b[39m", pos + 10);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +75,7 @@ fn print_current_source(
|
||||
}
|
||||
if !src.is_empty() {
|
||||
// Print just a line number for imported modules
|
||||
println!("{} @ {:?}", src, pos);
|
||||
println!("{src} @ {pos:?}");
|
||||
} else {
|
||||
// Print the current source line
|
||||
print_source(lines, pos, 0, window);
|
||||
@@ -101,17 +100,16 @@ fn print_error(input: &str, mut err: EvalAltResult) {
|
||||
// Print error position
|
||||
if pos.is_none() {
|
||||
// No position
|
||||
println!("{}", err);
|
||||
println!("{err}");
|
||||
} else {
|
||||
// Specific position - print line text
|
||||
println!("{}{}", line_no, lines[pos.line().unwrap() - 1]);
|
||||
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
||||
|
||||
// Display position marker
|
||||
println!(
|
||||
"{0:>1$} {2}",
|
||||
"{0:>1$} {err}",
|
||||
"^",
|
||||
line_no.len() + pos.position().unwrap(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -248,11 +246,11 @@ fn debug_callback(
|
||||
BreakPoint::AtPosition { .. } => (),
|
||||
BreakPoint::AtFunctionName { ref name, .. }
|
||||
| BreakPoint::AtFunctionCall { ref name, .. } => {
|
||||
println!("! Call to function {}.", name)
|
||||
println!("! Call to function {name}.")
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
BreakPoint::AtProperty { ref name, .. } => {
|
||||
println!("! Property {} accessed.", name)
|
||||
println!("! Property {name} accessed.")
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -310,10 +308,11 @@ fn debug_callback(
|
||||
["node"] => {
|
||||
if pos.is_none() {
|
||||
println!("{:?}", node);
|
||||
} else if let Some(source) = source {
|
||||
println!("{:?} {} @ {:?}", node, source, pos);
|
||||
} else {
|
||||
println!("{:?} @ {:?}", node, pos);
|
||||
match source {
|
||||
Some(source) => println!("{node:?} {source} @ {pos:?}"),
|
||||
None => println!("{node:?} @ {pos:?}"),
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
@@ -327,7 +326,7 @@ fn debug_callback(
|
||||
["list" | "l", n] if n.parse::<usize>().is_ok() => {
|
||||
let num = n.parse::<usize>().unwrap();
|
||||
if num == 0 || num > lines.len() {
|
||||
eprintln!("\x1b[31mInvalid line: {}\x1b[39m", num);
|
||||
eprintln!("\x1b[31mInvalid line: {num}\x1b[39m");
|
||||
} else {
|
||||
let pos = Position::new(num as u16, 0);
|
||||
print_current_source(&mut context, source, pos, lines, (3, 6));
|
||||
@@ -339,24 +338,18 @@ fn debug_callback(
|
||||
["over" | "o"] => break Ok(DebuggerCommand::StepOver),
|
||||
["next" | "n"] => break Ok(DebuggerCommand::Next),
|
||||
["scope"] => println!("{}", context.scope()),
|
||||
["print" | "p", "this"] => {
|
||||
if let Some(value) = context.this_ptr() {
|
||||
println!("=> {:?}", value);
|
||||
} else {
|
||||
println!("`this` pointer is unbound.");
|
||||
}
|
||||
}
|
||||
["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", "this"] => match context.this_ptr() {
|
||||
Some(value) => println!("=> {value:?}"),
|
||||
None => 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"] => {
|
||||
println!("{}", context.scope().clone_visible());
|
||||
if let Some(value) = context.this_ptr() {
|
||||
println!("this = {:?}", value);
|
||||
println!("this = {value:?}");
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@@ -385,7 +378,7 @@ fn debug_callback(
|
||||
.iter()
|
||||
.rev()
|
||||
{
|
||||
println!("{}", frame)
|
||||
println!("{frame}")
|
||||
}
|
||||
}
|
||||
["info" | "i", "break" | "b"] => Iterator::for_each(
|
||||
@@ -402,7 +395,7 @@ fn debug_callback(
|
||||
print!("{}", line_num);
|
||||
print_source(lines, *pos, line_num.len(), (0, 0));
|
||||
}
|
||||
_ => println!("[{}] {}", i + 1, bp),
|
||||
_ => println!("[{}] {bp}", i + 1),
|
||||
},
|
||||
),
|
||||
["enable" | "en", n] => {
|
||||
@@ -420,12 +413,12 @@ fn debug_callback(
|
||||
.get_mut(n - 1)
|
||||
.unwrap()
|
||||
.enable(true);
|
||||
println!("Break-point #{} enabled.", n)
|
||||
println!("Break-point #{n} enabled.")
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n);
|
||||
eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m");
|
||||
}
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n);
|
||||
eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m");
|
||||
}
|
||||
}
|
||||
["disable" | "dis", n] => {
|
||||
@@ -443,12 +436,12 @@ fn debug_callback(
|
||||
.get_mut(n - 1)
|
||||
.unwrap()
|
||||
.enable(false);
|
||||
println!("Break-point #{} disabled.", n)
|
||||
println!("Break-point #{n} disabled.")
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n);
|
||||
eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m");
|
||||
}
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n);
|
||||
eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m");
|
||||
}
|
||||
}
|
||||
["delete" | "d", n] => {
|
||||
@@ -464,12 +457,12 @@ fn debug_callback(
|
||||
.debugger
|
||||
.break_points_mut()
|
||||
.remove(n - 1);
|
||||
println!("Break-point #{} deleted.", n)
|
||||
println!("Break-point #{n} deleted.")
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid break-point: {}\x1b[39m", n);
|
||||
eprintln!("\x1b[31mInvalid break-point: {n}\x1b[39m");
|
||||
}
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid break-point: '{}'\x1b[39m", n);
|
||||
eprintln!("\x1b[31mInvalid break-point: '{n}'\x1b[39m");
|
||||
}
|
||||
}
|
||||
["delete" | "d"] => {
|
||||
@@ -487,14 +480,14 @@ fn debug_callback(
|
||||
args,
|
||||
enabled: true,
|
||||
};
|
||||
println!("Break-point added for {}", bp);
|
||||
println!("Break-point added for {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid number of arguments: '{}'\x1b[39m", args);
|
||||
eprintln!("\x1b[31mInvalid number of arguments: '{args}'\x1b[39m");
|
||||
}
|
||||
}
|
||||
// Property name
|
||||
@@ -504,7 +497,7 @@ fn debug_callback(
|
||||
name: param[1..].into(),
|
||||
enabled: true,
|
||||
};
|
||||
println!("Break-point added for {}", bp);
|
||||
println!("Break-point added for {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
@@ -523,18 +516,18 @@ fn debug_callback(
|
||||
|
||||
if range.contains(&n) {
|
||||
let bp = rhai::debugger::BreakPoint::AtPosition {
|
||||
source: source.unwrap_or("").into(),
|
||||
source: source.map(|s| s.into()),
|
||||
pos: Position::new(n as u16, 0),
|
||||
enabled: true,
|
||||
};
|
||||
println!("Break-point added {}", bp);
|
||||
println!("Break-point added {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
.break_points_mut()
|
||||
.push(bp);
|
||||
} else {
|
||||
eprintln!("\x1b[31mInvalid line number: '{}'\x1b[39m", n);
|
||||
eprintln!("\x1b[31mInvalid line number: '{n}'\x1b[39m");
|
||||
}
|
||||
}
|
||||
// Function name parameter
|
||||
@@ -543,7 +536,7 @@ fn debug_callback(
|
||||
name: param.trim().into(),
|
||||
enabled: true,
|
||||
};
|
||||
println!("Break-point added for {}", bp);
|
||||
println!("Break-point added for {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
@@ -553,11 +546,11 @@ fn debug_callback(
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
["break" | "b"] => {
|
||||
let bp = rhai::debugger::BreakPoint::AtPosition {
|
||||
source: source.unwrap_or("").into(),
|
||||
source: source.map(|s| s.into()),
|
||||
pos,
|
||||
enabled: true,
|
||||
};
|
||||
println!("Break-point added {}", bp);
|
||||
println!("Break-point added {bp}");
|
||||
context
|
||||
.global_runtime_state_mut()
|
||||
.debugger
|
||||
@@ -594,7 +587,7 @@ fn debug_callback(
|
||||
|
||||
fn main() {
|
||||
let title = format!("Rhai Debugger (version {})", env!("CARGO_PKG_VERSION"));
|
||||
println!("{}", title);
|
||||
println!("{title}");
|
||||
println!("{0:=<1$}", "", title.len());
|
||||
|
||||
// Initialize scripting engine
|
||||
|
||||
@@ -26,17 +26,16 @@ fn print_error(input: &str, mut err: EvalAltResult) {
|
||||
// Print error position
|
||||
if pos.is_none() {
|
||||
// No position
|
||||
println!("{}", err);
|
||||
println!("{err}");
|
||||
} else {
|
||||
// Specific position - print line text
|
||||
println!("{}{}", line_no, lines[pos.line().unwrap() - 1]);
|
||||
println!("{line_no}{}", lines[pos.line().unwrap() - 1]);
|
||||
|
||||
// Display position marker
|
||||
println!(
|
||||
"{0:>1$} {2}",
|
||||
"{0:>1$} {err}",
|
||||
"^",
|
||||
line_no.len() + pos.position().unwrap(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +118,7 @@ fn load_script_files(engine: &mut Engine) {
|
||||
for filename in env::args().skip(1) {
|
||||
let filename = match Path::new(&filename).canonicalize() {
|
||||
Err(err) => {
|
||||
eprintln!("Error script file path: {}\n{}", filename, err);
|
||||
eprintln!("Error script file path: {filename}\n{err}");
|
||||
exit(1);
|
||||
}
|
||||
Ok(f) => {
|
||||
@@ -164,7 +163,7 @@ fn load_script_files(engine: &mut Engine) {
|
||||
let filename = filename.to_string_lossy();
|
||||
|
||||
eprintln!("{:=<1$}", "", filename.len());
|
||||
eprintln!("{}", filename);
|
||||
eprintln!("{filename}");
|
||||
eprintln!("{:=<1$}", "", filename.len());
|
||||
eprintln!();
|
||||
|
||||
@@ -277,13 +276,13 @@ mod sample_functions {
|
||||
#[rhai_fn(name = "test")]
|
||||
pub fn test2(x: &mut INT, y: INT, z: &str) {
|
||||
*x += y + (z.len() as INT);
|
||||
println!("{} {} {}", x, y, z);
|
||||
println!("{x} {y} {z}");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION"));
|
||||
println!("{}", title);
|
||||
println!("{title}");
|
||||
println!("{0:=<1$}", "", title.len());
|
||||
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
@@ -338,11 +337,11 @@ fn main() {
|
||||
history_offset += 1;
|
||||
}
|
||||
if input.contains('\n') {
|
||||
println!("[{}] ~~~~", replacement_index);
|
||||
println!("{}", input);
|
||||
println!("[{replacement_index}] ~~~~");
|
||||
println!("{input}");
|
||||
println!("~~~~");
|
||||
} else {
|
||||
println!("[{}] {}", replacement_index, input);
|
||||
println!("[{replacement_index}] {input}");
|
||||
}
|
||||
replacement_index = 0;
|
||||
} else {
|
||||
@@ -374,7 +373,7 @@ fn main() {
|
||||
Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break 'main_loop,
|
||||
|
||||
Err(err) => {
|
||||
eprintln!("Error: {:?}", err);
|
||||
eprintln!("Error: {err:?}");
|
||||
break 'main_loop;
|
||||
}
|
||||
}
|
||||
@@ -401,12 +400,12 @@ fn main() {
|
||||
"history" => {
|
||||
for (i, h) in rl.history().iter().enumerate() {
|
||||
match &h.split('\n').collect::<Vec<_>>()[..] {
|
||||
[line] => println!("[{}] {}", history_offset + i, line),
|
||||
[line] => println!("[{}] {line}", history_offset + i),
|
||||
lines => {
|
||||
for (x, line) in lines.iter().enumerate() {
|
||||
let number = format!("[{}]", history_offset + i);
|
||||
if x == 0 {
|
||||
println!("{} {}", number, line.trim_end());
|
||||
println!("{number} {}", line.trim_end());
|
||||
} else {
|
||||
println!("{0:>1$} {2}", "", number.len(), line.trim_end());
|
||||
}
|
||||
@@ -439,30 +438,30 @@ fn main() {
|
||||
continue;
|
||||
}
|
||||
"scope" => {
|
||||
println!("{}", scope);
|
||||
println!("{scope}");
|
||||
continue;
|
||||
}
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
"astu" => {
|
||||
// print the last un-optimized AST
|
||||
println!("{:#?}\n", ast_u);
|
||||
println!("{ast_u:#?}\n");
|
||||
continue;
|
||||
}
|
||||
"ast" => {
|
||||
// print the last AST
|
||||
println!("{:#?}\n", ast);
|
||||
println!("{ast:#?}\n");
|
||||
continue;
|
||||
}
|
||||
#[cfg(feature = "metadata")]
|
||||
"functions" => {
|
||||
// print a list of all registered functions
|
||||
for f in engine.gen_fn_signatures(false) {
|
||||
println!("{}", f)
|
||||
println!("{f}")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
for f in main_ast.iter_functions() {
|
||||
println!("{}", f)
|
||||
println!("{f}")
|
||||
}
|
||||
|
||||
println!();
|
||||
@@ -482,27 +481,30 @@ fn main() {
|
||||
continue;
|
||||
}
|
||||
"!!" => {
|
||||
if let Some(line) = rl.history().last() {
|
||||
replacement = Some(line.clone());
|
||||
replacement_index = history_offset + rl.history().len() - 1;
|
||||
} else {
|
||||
eprintln!("No lines history!");
|
||||
match rl.history().last() {
|
||||
Some(line) => {
|
||||
replacement = Some(line.clone());
|
||||
replacement_index = history_offset + rl.history().len() - 1;
|
||||
}
|
||||
None => eprintln!("No lines history!"),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ if cmd.starts_with("!?") => {
|
||||
let text = cmd[2..].trim();
|
||||
if let Some((n, line)) = rl
|
||||
let history = rl
|
||||
.history()
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find(|&(.., h)| h.contains(text))
|
||||
{
|
||||
replacement = Some(line.clone());
|
||||
replacement_index = history_offset + (rl.history().len() - 1 - n);
|
||||
} else {
|
||||
eprintln!("History line not found: {}", text);
|
||||
.find(|&(.., h)| h.contains(text));
|
||||
|
||||
match history {
|
||||
Some((n, line)) => {
|
||||
replacement = Some(line.clone());
|
||||
replacement_index = history_offset + (rl.history().len() - 1 - n);
|
||||
}
|
||||
None => eprintln!("History line not found: {text}"),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -558,7 +560,7 @@ fn main() {
|
||||
engine.eval_ast_with_scope::<Dynamic>(&mut scope, &main_ast)
|
||||
}) {
|
||||
Ok(result) if !result.is::<()>() => {
|
||||
println!("=> {:?}", result);
|
||||
println!("=> {result:?}");
|
||||
println!();
|
||||
}
|
||||
Ok(_) => (),
|
||||
|
||||
@@ -7,12 +7,11 @@ fn eprint_error(input: &str, mut err: EvalAltResult) {
|
||||
let line = pos.line().unwrap();
|
||||
let line_no = format!("{line}: ");
|
||||
|
||||
eprintln!("{}{}", line_no, lines[line - 1]);
|
||||
eprintln!("{line_no}{}", lines[line - 1]);
|
||||
eprintln!(
|
||||
"{:>1$} {2}",
|
||||
"{:>1$} {err_msg}",
|
||||
"^",
|
||||
line_no.len() + pos.position().unwrap(),
|
||||
err_msg
|
||||
);
|
||||
eprintln!();
|
||||
}
|
||||
@@ -24,7 +23,7 @@ fn eprint_error(input: &str, mut err: EvalAltResult) {
|
||||
|
||||
if pos.is_none() {
|
||||
// No position
|
||||
eprintln!("{}", err);
|
||||
eprintln!("{err}");
|
||||
} else {
|
||||
// Specific position
|
||||
eprint_line(&lines, pos, &err.to_string())
|
||||
@@ -37,7 +36,7 @@ fn main() {
|
||||
for filename in env::args().skip(1) {
|
||||
let filename = match Path::new(&filename).canonicalize() {
|
||||
Err(err) => {
|
||||
eprintln!("Error script file path: {}\n{}", filename, err);
|
||||
eprintln!("Error script file path: {filename}\n{err}");
|
||||
exit(1);
|
||||
}
|
||||
Ok(f) => match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap())
|
||||
@@ -94,7 +93,7 @@ fn main() {
|
||||
let filename = filename.to_string_lossy();
|
||||
|
||||
eprintln!("{:=<1$}", "", filename.len());
|
||||
eprintln!("{}", filename);
|
||||
eprintln!("{filename}");
|
||||
eprintln!("{:=<1$}", "", filename.len());
|
||||
eprintln!();
|
||||
|
||||
|
||||
226
src/config/hashing.rs
Normal file
226
src/config/hashing.rs
Normal file
@@ -0,0 +1,226 @@
|
||||
//! Fixed hashing seeds for stable hashing.
|
||||
//!
|
||||
//! Set to [`None`] to disable stable hashing.
|
||||
//!
|
||||
//! See [`set_rhai_ahash_seed`].
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! // Set the hashing seed to [1, 2, 3, 4]
|
||||
//! rhai::config::hashing::set_ahash_seed(Some([1, 2, 3, 4])).unwrap();
|
||||
//! ```
|
||||
//! Alternatively, set this at compile time via the `RHAI_AHASH_SEED` environment variable.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```sh
|
||||
//! env RHAI_AHASH_SEED ="[236,800,954,213]"
|
||||
//! ```
|
||||
// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100
|
||||
|
||||
use crate::config::hashing_env;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
cell::UnsafeCell,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
mem::MaybeUninit,
|
||||
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
// omg its hokma from record team here to record our locks
|
||||
// what does this do?
|
||||
// so what this does is keep track of a global address in memory that acts as a global lock
|
||||
// i stole this from crossbeam so read their docs for more
|
||||
#[must_use]
|
||||
struct HokmaLock {
|
||||
lock: AtomicUsize,
|
||||
}
|
||||
|
||||
impl HokmaLock {
|
||||
#[inline(always)]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
lock: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&'static self) -> WhenTheHokmaSuppression {
|
||||
loop {
|
||||
let previous = self.lock.swap(1, Ordering::SeqCst);
|
||||
|
||||
if previous != 1 {
|
||||
return WhenTheHokmaSuppression {
|
||||
hokma: self,
|
||||
state: previous,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WhenTheHokmaSuppression {
|
||||
hokma: &'static HokmaLock,
|
||||
state: usize,
|
||||
}
|
||||
|
||||
impl WhenTheHokmaSuppression {
|
||||
#[inline]
|
||||
pub fn the_price_of_silence(self) {
|
||||
self.hokma.lock.store(self.state, Ordering::SeqCst);
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WhenTheHokmaSuppression {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.hokma
|
||||
.lock
|
||||
.store(self.state.wrapping_add(2), Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn hokmalock(address: usize) -> &'static HokmaLock {
|
||||
const LEN: usize = 787;
|
||||
const LCK: HokmaLock = HokmaLock::new();
|
||||
static RECORDS: [HokmaLock; LEN] = [LCK; LEN];
|
||||
|
||||
&RECORDS[address % LEN]
|
||||
}
|
||||
|
||||
// Safety: lol, there is a reason its called "SusLock<T>"
|
||||
#[must_use]
|
||||
struct SusLock<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
initialized: AtomicBool,
|
||||
data: UnsafeCell<MaybeUninit<T>>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> SusLock<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
#[inline]
|
||||
pub const fn new() -> SusLock<T> {
|
||||
SusLock {
|
||||
initialized: AtomicBool::new(false),
|
||||
data: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get(&self) -> Option<&'static T> {
|
||||
if self.initialized.load(Ordering::SeqCst) {
|
||||
let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) });
|
||||
// we forgo the optimistic read, because we don't really care
|
||||
let guard = hokma.write();
|
||||
let val = {
|
||||
let cast: *const T = self.data.get().cast();
|
||||
unsafe { mem::transmute::<*const T, &'static T>(cast) }
|
||||
};
|
||||
guard.the_price_of_silence();
|
||||
Some(val)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option<&'static T> {
|
||||
if !self.initialized.load(Ordering::SeqCst) {
|
||||
let value = f();
|
||||
self.initialized.store(true, Ordering::SeqCst);
|
||||
let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) });
|
||||
hokma.write();
|
||||
unsafe {
|
||||
self.data.get().write(MaybeUninit::new(value));
|
||||
}
|
||||
}
|
||||
|
||||
self.get()
|
||||
}
|
||||
|
||||
pub fn set(&self, value: T) -> Result<(), T> {
|
||||
if self.initialized.load(Ordering::SeqCst) {
|
||||
Err(value)
|
||||
} else {
|
||||
let _ = self.get_or_init(|| value);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Sync + Send> Sync for SusLock<T> where T: 'static {}
|
||||
unsafe impl<T: Send> Send for SusLock<T> where T: 'static {}
|
||||
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> where T: 'static {}
|
||||
|
||||
impl<T> Drop for SusLock<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.initialized.load(Ordering::SeqCst) {
|
||||
unsafe { (&mut *self.data.get()).assume_init_drop() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static AHASH_SEED: SusLock<Option<[u64; 4]>> = SusLock::new();
|
||||
|
||||
/// Set the hashing seed. This is used to hash functions etc.
|
||||
///
|
||||
/// This is a static global value and affects every Rhai instance.
|
||||
/// This should not be used _unless_ you know you need it.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// * You can only call this function **ONCE** for the entire duration of program execution.
|
||||
/// * You **MUST** call this before performing **ANY** Rhai operation (e.g. creating an [`Engine`]).
|
||||
///
|
||||
/// # Error
|
||||
///
|
||||
/// Returns an error containing the existing hashing seed if already set.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rhai::Engine;
|
||||
/// // Set the hashing seed to [1, 2, 3, 4]
|
||||
/// rhai::config::hashing::set_ahash_seed(Some([1, 2, 3, 4])).unwrap();
|
||||
///
|
||||
/// // Use Rhai AFTER setting the hashing seed
|
||||
/// let engine = Engine::new();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> {
|
||||
AHASH_SEED.set(new_seed)
|
||||
}
|
||||
|
||||
/// Get the current hashing Seed.
|
||||
///
|
||||
/// If the seed is not yet defined, the `RHAI_AHASH_SEED` environment variable (if any) is used.
|
||||
///
|
||||
/// Otherwise, the hashing seed is randomized to protect against DOS attacks.
|
||||
///
|
||||
/// See [`set_rhai_ahash_seed`] for more.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_ahash_seed() -> &'static Option<[u64; 4]> {
|
||||
const NONE: &'static Option<[u64; 4]> = &None;
|
||||
|
||||
match AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED) {
|
||||
Some(ash) => ash,
|
||||
None => NONE,
|
||||
}
|
||||
}
|
||||
3
src/config/hashing_env.rs
Normal file
3
src/config/hashing_env.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
//! This file is automatically recreated during build time by `build.rs` from `build.template`.
|
||||
|
||||
pub(crate) const AHASH_SEED: Option<[u64; 4]> = None;
|
||||
4
src/config/mod.rs
Normal file
4
src/config/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
//! Configuration for Rhai.
|
||||
|
||||
pub mod hashing;
|
||||
mod hashing_env;
|
||||
@@ -9,8 +9,8 @@ use crate::packages::{Package, StandardPackage};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::StringsInterner;
|
||||
use crate::{
|
||||
Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, Position, RhaiResult,
|
||||
Shared, StaticVec,
|
||||
Dynamic, Identifier, ImmutableString, Locked, Module, OptimizationLevel, SharedModule,
|
||||
StaticVec,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -86,16 +86,16 @@ pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax();
|
||||
///
|
||||
/// let result = engine.eval::<i64>("40 + 2")?;
|
||||
///
|
||||
/// println!("Answer: {}", result); // prints 42
|
||||
/// println!("Answer: {result}"); // prints 42
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct Engine {
|
||||
/// A collection of all modules loaded into the global namespace of the Engine.
|
||||
pub(crate) global_modules: StaticVec<Shared<Module>>,
|
||||
pub(crate) global_modules: StaticVec<SharedModule>,
|
||||
/// A collection of all sub-modules directly loaded into the Engine.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, Shared<Module>>,
|
||||
pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, SharedModule>,
|
||||
|
||||
/// A module resolution service.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@@ -150,7 +150,8 @@ pub struct Engine {
|
||||
}
|
||||
|
||||
impl fmt::Debug for Engine {
|
||||
#[inline]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut f = f.debug_struct("Engine");
|
||||
|
||||
@@ -189,6 +190,7 @@ impl fmt::Debug for Engine {
|
||||
|
||||
impl Default for Engine {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
@@ -235,18 +237,14 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
{
|
||||
engine.print = Box::new(|s| println!("{}", s));
|
||||
engine.debug = Box::new(|s, source, pos| {
|
||||
source.map_or_else(
|
||||
|| {
|
||||
if pos.is_none() {
|
||||
println!("{}", s);
|
||||
} else {
|
||||
println!("{:?} | {}", pos, s);
|
||||
}
|
||||
},
|
||||
|source| println!("{} @ {:?} | {}", source, pos, s),
|
||||
)
|
||||
engine.print = Box::new(|s| println!("{s}"));
|
||||
engine.debug = Box::new(|s, source, pos| match (source, pos) {
|
||||
(Some(source), crate::Position::NONE) => println!("{source} | {s}"),
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
(Some(source), pos) => println!("{source} @ {pos:?} | {s}"),
|
||||
(None, crate::Position::NONE) => println!("{s}"),
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
(None, pos) => println!("{pos:?} | {s}"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -343,15 +341,4 @@ impl Engine {
|
||||
pub fn const_empty_string(&self) -> ImmutableString {
|
||||
self.get_interned_string("")
|
||||
}
|
||||
|
||||
/// Check a result to ensure that it is valid.
|
||||
#[inline]
|
||||
pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if let Ok(ref r) = result {
|
||||
self.check_data_size(r, _pos)?;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
use crate::func::{CallableFunction, StraightHashMap};
|
||||
use crate::types::BloomFilterU64;
|
||||
use crate::{Identifier, StaticVec};
|
||||
use std::marker::PhantomData;
|
||||
use crate::{ImmutableString, StaticVec};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -14,7 +13,7 @@ pub struct FnResolutionCacheEntry {
|
||||
/// Function.
|
||||
pub func: CallableFunction,
|
||||
/// Optional source.
|
||||
pub source: Option<Box<Identifier>>,
|
||||
pub source: Option<ImmutableString>,
|
||||
}
|
||||
|
||||
/// _(internals)_ A function resolution cache with a bloom filter.
|
||||
@@ -45,21 +44,18 @@ impl FnResolutionCache {
|
||||
/// The following caches are contained inside this type:
|
||||
/// * A stack of [function resolution caches][FnResolutionCache]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Caches<'a> {
|
||||
pub struct Caches {
|
||||
/// Stack of [function resolution caches][FnResolutionCache].
|
||||
stack: StaticVec<FnResolutionCache>,
|
||||
/// Take care of the lifetime parameter.
|
||||
dummy: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl Caches<'_> {
|
||||
impl Caches {
|
||||
/// Create an empty [`Caches`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
stack: StaticVec::new_const(),
|
||||
dummy: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Get the number of function resolution cache(s) in the stack.
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
use super::{Caches, GlobalRuntimeState, Target};
|
||||
use crate::ast::{ASTFlags, Expr, OpAssignment};
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::{Dynamic, Engine, FnArgsVec, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
||||
use crate::types::RestoreOnDrop;
|
||||
use crate::{
|
||||
Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -40,17 +43,16 @@ impl Engine {
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
lib: &[SharedModule],
|
||||
this_ptr: &mut Dynamic,
|
||||
target: &mut Target,
|
||||
root: (&str, Position),
|
||||
_parent: &Expr,
|
||||
parent_options: ASTFlags,
|
||||
rhs: &Expr,
|
||||
_parent_options: ASTFlags,
|
||||
idx_values: &mut FnArgsVec<Dynamic>,
|
||||
chain_type: ChainType,
|
||||
level: usize,
|
||||
new_val: Option<(Dynamic, OpAssignment)>,
|
||||
new_val: &mut Option<(Dynamic, &OpAssignment)>,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
let is_ref_mut = target.is_ref();
|
||||
|
||||
@@ -61,7 +63,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
ChainType::Indexing => {
|
||||
// Check for existence with the null conditional operator
|
||||
if _parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
||||
if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
||||
return Ok((Dynamic::UNIT, false));
|
||||
}
|
||||
|
||||
@@ -70,26 +72,26 @@ impl Engine {
|
||||
match rhs {
|
||||
// xxx[idx].expr... | xxx[idx][expr]...
|
||||
Expr::Dot(x, options, x_pos) | Expr::Index(x, options, x_pos)
|
||||
if !_parent_options.contains(ASTFlags::BREAK) =>
|
||||
if !parent_options.contains(ASTFlags::BREAK) =>
|
||||
{
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?;
|
||||
|
||||
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 idx_pos = x.lhs.start_position();
|
||||
let rhs_chain = rhs.into();
|
||||
|
||||
let (try_setter, result) = {
|
||||
let mut obj = self.get_indexed_mut(
|
||||
global, caches, lib, target, idx_val, idx_pos, false, true, level,
|
||||
global, caches, lib, target, idx_val, idx_pos, false, true,
|
||||
)?;
|
||||
let is_obj_temp_val = obj.is_temp_value();
|
||||
let obj_ptr = &mut obj;
|
||||
|
||||
match self.eval_dot_index_chain_helper(
|
||||
global, caches, lib, this_ptr, obj_ptr, root, rhs, &x.rhs,
|
||||
*options, idx_values, rhs_chain, level, new_val,
|
||||
global, caches, lib, this_ptr, obj_ptr, root, rhs, *options,
|
||||
&x.rhs, idx_values, rhs_chain, new_val,
|
||||
) {
|
||||
Ok((result, true)) if is_obj_temp_val => {
|
||||
(Some(obj.take_or_clone()), (result, true))
|
||||
@@ -104,7 +106,7 @@ impl Engine {
|
||||
let idx = &mut idx_val_for_setter;
|
||||
let new_val = &mut new_val;
|
||||
self.call_indexer_set(
|
||||
global, caches, lib, target, idx, new_val, is_ref_mut, level,
|
||||
global, caches, lib, target, idx, new_val, is_ref_mut,
|
||||
)
|
||||
.or_else(|e| match *e {
|
||||
ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)),
|
||||
@@ -117,21 +119,20 @@ impl Engine {
|
||||
// xxx[rhs] op= new_val
|
||||
_ if new_val.is_some() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?;
|
||||
|
||||
let (new_val, op_info) = new_val.expect("`Some`");
|
||||
let idx_val = idx_values.pop().unwrap();
|
||||
let mut idx_val2 = idx_val.clone();
|
||||
let (new_val, op_info) = new_val.take().expect("`Some`");
|
||||
let idx_val = &mut idx_values.pop().unwrap();
|
||||
let idx = &mut idx_val.clone();
|
||||
|
||||
let try_setter = match self.get_indexed_mut(
|
||||
global, caches, lib, target, idx_val, pos, true, false, level,
|
||||
) {
|
||||
let try_setter = match self
|
||||
.get_indexed_mut(global, caches, lib, target, idx, pos, true, false)
|
||||
{
|
||||
// Indexed value is not a temp value - update directly
|
||||
Ok(ref mut obj_ptr) => {
|
||||
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,
|
||||
)?;
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.check_data_size(obj_ptr, op_info.pos)?;
|
||||
None
|
||||
}
|
||||
@@ -143,32 +144,30 @@ impl Engine {
|
||||
};
|
||||
|
||||
if let Some(mut new_val) = try_setter {
|
||||
let idx = &mut idx_val2;
|
||||
|
||||
// Is this an 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
|
||||
if let Ok(val) =
|
||||
self.call_indexer_get(global, caches, lib, target, idx, level)
|
||||
self.call_indexer_get(global, caches, lib, target, idx)
|
||||
{
|
||||
let mut val = val.into();
|
||||
// Run the op-assignment
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, op_info, &mut val, root, new_val,
|
||||
level,
|
||||
)?;
|
||||
// Replace new value
|
||||
new_val = val.take_or_clone();
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.check_data_size(&new_val, op_info.pos)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to call index setter
|
||||
let new_val = &mut new_val;
|
||||
|
||||
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,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -177,14 +176,12 @@ impl Engine {
|
||||
// xxx[rhs]
|
||||
_ => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, _parent)?;
|
||||
|
||||
let idx_val = idx_values.pop().unwrap();
|
||||
let idx_val = &mut idx_values.pop().unwrap();
|
||||
|
||||
self.get_indexed_mut(
|
||||
global, caches, lib, target, idx_val, pos, false, true, level,
|
||||
)
|
||||
.map(|v| (v.take_or_clone(), false))
|
||||
self.get_indexed_mut(global, caches, lib, target, idx_val, pos, false, true)
|
||||
.map(|v| (v.take_or_clone(), false))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,7 +189,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
ChainType::Dotting => {
|
||||
// Check for existence with the Elvis operator
|
||||
if _parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
||||
if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
||||
return Ok((Dynamic::UNIT, false));
|
||||
}
|
||||
|
||||
@@ -200,28 +197,28 @@ impl Engine {
|
||||
// xxx.fn_name(arg_expr_list)
|
||||
Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset_debugger =
|
||||
self.run_debugger_with_reset(scope, global, lib, this_ptr, rhs, level)?;
|
||||
let reset = self
|
||||
.run_debugger_with_reset(global, caches, lib, scope, this_ptr, rhs)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = &**x;
|
||||
|
||||
// Truncate the index values upon exit
|
||||
let offset = idx_values.len() - args.len();
|
||||
let idx_values =
|
||||
&mut *RestoreOnDrop::lock(idx_values, move |v| v.truncate(offset));
|
||||
|
||||
let call_args = &mut idx_values[offset..];
|
||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||
|
||||
let result = self.make_method_call(
|
||||
self.make_method_call(
|
||||
global, caches, lib, name, *hashes, target, call_args, pos1, *pos,
|
||||
level,
|
||||
);
|
||||
|
||||
idx_values.truncate(offset);
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.reset_status(reset_debugger);
|
||||
|
||||
result
|
||||
)
|
||||
}
|
||||
// xxx.fn_name(...) = ???
|
||||
Expr::MethodCall(..) if new_val.is_some() => {
|
||||
@@ -234,54 +231,53 @@ impl Engine {
|
||||
// {xxx:map}.id op= ???
|
||||
Expr::Property(x, pos) if target.is::<crate::Map>() && new_val.is_some() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?;
|
||||
|
||||
let index = x.2.clone().into();
|
||||
let (new_val, op_info) = new_val.expect("`Some`");
|
||||
let index = &mut x.2.clone().into();
|
||||
let (new_val, op_info) = new_val.take().expect("`Some`");
|
||||
{
|
||||
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,
|
||||
)?;
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, op_info, val_target, root, new_val, level,
|
||||
global, caches, lib, op_info, val_target, root, new_val,
|
||||
)?;
|
||||
}
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.check_data_size(target.source(), op_info.pos)?;
|
||||
Ok((Dynamic::UNIT, true))
|
||||
}
|
||||
// {xxx:map}.id
|
||||
Expr::Property(x, pos) if target.is::<crate::Map>() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?;
|
||||
|
||||
let index = x.2.clone().into();
|
||||
let index = &mut x.2.clone().into();
|
||||
let val = self.get_indexed_mut(
|
||||
global, caches, lib, target, index, *pos, false, false, level,
|
||||
global, caches, lib, target, index, *pos, false, false,
|
||||
)?;
|
||||
Ok((val.take_or_clone(), false))
|
||||
}
|
||||
// xxx.id op= ???
|
||||
Expr::Property(x, pos) if new_val.is_some() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?;
|
||||
|
||||
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() {
|
||||
let args = &mut [target.as_mut()];
|
||||
let (mut orig_val, ..) = self
|
||||
.call_native_fn(
|
||||
global, caches, lib, getter, *hash_get, args, is_ref_mut,
|
||||
false, *pos, level,
|
||||
.exec_native_fn_call(
|
||||
global, caches, lib, getter, None, *hash_get, args, is_ref_mut,
|
||||
*pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
ERR::ErrorDotExpr(..) => {
|
||||
let mut prop = name.into();
|
||||
self.call_indexer_get(
|
||||
global, caches, lib, target, &mut prop, level,
|
||||
global, caches, lib, target, &mut prop,
|
||||
)
|
||||
.map(|r| (r, false))
|
||||
.map_err(|e| {
|
||||
@@ -298,7 +294,7 @@ impl Engine {
|
||||
let orig_val = &mut (&mut orig_val).into();
|
||||
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, op_info, orig_val, root, new_val, level,
|
||||
global, caches, lib, op_info, orig_val, root, new_val,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -306,9 +302,8 @@ impl Engine {
|
||||
}
|
||||
|
||||
let args = &mut [target.as_mut(), &mut new_val];
|
||||
self.call_native_fn(
|
||||
global, caches, lib, setter, *hash_set, args, is_ref_mut, false, *pos,
|
||||
level,
|
||||
self.exec_native_fn_call(
|
||||
global, caches, lib, setter, None, *hash_set, args, is_ref_mut, *pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
@@ -316,7 +311,7 @@ impl Engine {
|
||||
let idx = &mut name.into();
|
||||
let new_val = &mut new_val;
|
||||
self.call_indexer_set(
|
||||
global, caches, lib, target, idx, new_val, is_ref_mut, level,
|
||||
global, caches, lib, target, idx, new_val, is_ref_mut,
|
||||
)
|
||||
.map_err(|e| match *e {
|
||||
ERR::ErrorIndexingType(..) => err,
|
||||
@@ -329,27 +324,24 @@ impl Engine {
|
||||
// xxx.id
|
||||
Expr::Property(x, pos) => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, rhs)?;
|
||||
|
||||
let ((getter, hash_get), _, name) = &**x;
|
||||
let args = &mut [target.as_mut()];
|
||||
self.call_native_fn(
|
||||
global, caches, lib, getter, *hash_get, args, is_ref_mut, false, *pos,
|
||||
level,
|
||||
self.exec_native_fn_call(
|
||||
global, caches, lib, getter, None, *hash_get, args, is_ref_mut, *pos,
|
||||
)
|
||||
.map_or_else(
|
||||
|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
ERR::ErrorDotExpr(..) => {
|
||||
let mut prop = name.into();
|
||||
self.call_indexer_get(
|
||||
global, caches, lib, target, &mut prop, level,
|
||||
)
|
||||
.map(|r| (r, false))
|
||||
.map_err(|e| match *e {
|
||||
ERR::ErrorIndexingType(..) => err,
|
||||
_ => e,
|
||||
})
|
||||
self.call_indexer_get(global, caches, lib, target, &mut prop)
|
||||
.map(|r| (r, false))
|
||||
.map_err(|e| match *e {
|
||||
ERR::ErrorIndexingType(..) => err,
|
||||
_ => e,
|
||||
})
|
||||
}
|
||||
_ => Err(err),
|
||||
},
|
||||
@@ -366,39 +358,43 @@ impl Engine {
|
||||
let val_target = &mut match x.lhs {
|
||||
Expr::Property(ref p, pos) => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, _node, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, _node)?;
|
||||
|
||||
let index = p.2.clone().into();
|
||||
let index = &mut p.2.clone().into();
|
||||
self.get_indexed_mut(
|
||||
global, caches, lib, target, index, pos, false, true, level,
|
||||
global, caches, lib, target, index, pos, false, true,
|
||||
)?
|
||||
}
|
||||
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
||||
Expr::MethodCall(ref x, pos) if !x.is_qualified() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset_debugger = self.run_debugger_with_reset(
|
||||
scope, global, lib, this_ptr, _node, level,
|
||||
let reset = self.run_debugger_with_reset(
|
||||
global, caches, lib, scope, this_ptr, _node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = &**x;
|
||||
|
||||
// Truncate the index values upon exit
|
||||
let offset = idx_values.len() - args.len();
|
||||
let idx_values = &mut *RestoreOnDrop::lock(idx_values, move |v| {
|
||||
v.truncate(offset)
|
||||
});
|
||||
|
||||
let call_args = &mut idx_values[offset..];
|
||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||
|
||||
let result = self.make_method_call(
|
||||
self.make_method_call(
|
||||
global, caches, lib, name, *hashes, target, call_args, pos1,
|
||||
pos, level,
|
||||
);
|
||||
|
||||
idx_values.truncate(offset);
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.reset_status(reset_debugger);
|
||||
|
||||
result?.0.into()
|
||||
pos,
|
||||
)?
|
||||
.0
|
||||
.into()
|
||||
}
|
||||
// {xxx:map}.module::fn_name(...) - syntax error
|
||||
Expr::MethodCall(..) => unreachable!(
|
||||
@@ -410,8 +406,8 @@ impl Engine {
|
||||
let rhs_chain = rhs.into();
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, caches, lib, this_ptr, val_target, root, rhs, &x.rhs, *options,
|
||||
idx_values, rhs_chain, level, new_val,
|
||||
global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs,
|
||||
idx_values, rhs_chain, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*x_pos))
|
||||
}
|
||||
@@ -423,7 +419,7 @@ impl Engine {
|
||||
// xxx.prop[expr] | xxx.prop.expr
|
||||
Expr::Property(ref p, pos) => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, _node, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, _node)?;
|
||||
|
||||
let ((getter, hash_get), (setter, hash_set), name) = &**p;
|
||||
let rhs_chain = rhs.into();
|
||||
@@ -432,16 +428,16 @@ impl Engine {
|
||||
|
||||
// Assume getters are always pure
|
||||
let (mut val, ..) = self
|
||||
.call_native_fn(
|
||||
global, caches, lib, getter, *hash_get, args, is_ref_mut,
|
||||
false, pos, level,
|
||||
.exec_native_fn_call(
|
||||
global, caches, lib, getter, None, *hash_get, args,
|
||||
is_ref_mut, pos,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Try an indexer if property does not exist
|
||||
ERR::ErrorDotExpr(..) => {
|
||||
let mut prop = name.into();
|
||||
self.call_indexer_get(
|
||||
global, caches, lib, target, &mut prop, level,
|
||||
global, caches, lib, target, &mut prop,
|
||||
)
|
||||
.map(|r| (r, false))
|
||||
.map_err(
|
||||
@@ -458,8 +454,8 @@ impl Engine {
|
||||
|
||||
let (result, may_be_changed) = self
|
||||
.eval_dot_index_chain_helper(
|
||||
global, caches, lib, this_ptr, val, root, rhs, &x.rhs,
|
||||
*options, idx_values, rhs_chain, level, new_val,
|
||||
global, caches, lib, this_ptr, val, root, rhs, *options,
|
||||
&x.rhs, idx_values, rhs_chain, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*x_pos))?;
|
||||
|
||||
@@ -468,9 +464,9 @@ impl Engine {
|
||||
// Re-use args because the first &mut parameter will not be consumed
|
||||
let mut arg_values = [target.as_mut(), val.as_mut()];
|
||||
let args = &mut arg_values;
|
||||
self.call_native_fn(
|
||||
global, caches, lib, setter, *hash_set, args, is_ref_mut,
|
||||
false, pos, level,
|
||||
self.exec_native_fn_call(
|
||||
global, caches, lib, setter, None, *hash_set, args,
|
||||
is_ref_mut, pos,
|
||||
)
|
||||
.or_else(
|
||||
|err| match *err {
|
||||
@@ -480,7 +476,7 @@ impl Engine {
|
||||
let new_val = val;
|
||||
self.call_indexer_set(
|
||||
global, caches, lib, target, idx, new_val,
|
||||
is_ref_mut, level,
|
||||
is_ref_mut,
|
||||
)
|
||||
.or_else(|e| match *e {
|
||||
// If there is no setter, no need to feed it
|
||||
@@ -500,36 +496,43 @@ impl Engine {
|
||||
}
|
||||
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
||||
Expr::MethodCall(ref f, pos) if !f.is_qualified() => {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset_debugger = self.run_debugger_with_reset(
|
||||
scope, global, lib, this_ptr, _node, level,
|
||||
)?;
|
||||
let val = {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset = self.run_debugger_with_reset(
|
||||
global, caches, lib, scope, this_ptr, _node,
|
||||
)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = &**f;
|
||||
let crate::ast::FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = &**f;
|
||||
|
||||
// Truncate the index values upon exit
|
||||
let offset = idx_values.len() - args.len();
|
||||
let idx_values =
|
||||
&mut *RestoreOnDrop::lock(idx_values, move |v| {
|
||||
v.truncate(offset)
|
||||
});
|
||||
|
||||
let call_args = &mut idx_values[offset..];
|
||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||
|
||||
self.make_method_call(
|
||||
global, caches, lib, name, *hashes, target, call_args,
|
||||
pos1, pos,
|
||||
)?
|
||||
.0
|
||||
};
|
||||
|
||||
let val = &mut val.into();
|
||||
let rhs_chain = rhs.into();
|
||||
|
||||
let offset = idx_values.len() - args.len();
|
||||
let call_args = &mut idx_values[offset..];
|
||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||
|
||||
let result = self.make_method_call(
|
||||
global, caches, lib, name, *hashes, target, call_args, pos1,
|
||||
pos, level,
|
||||
);
|
||||
|
||||
idx_values.truncate(offset);
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.reset_status(reset_debugger);
|
||||
|
||||
let (val, _) = &mut result?;
|
||||
let val = &mut val.into();
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, caches, lib, this_ptr, val, root, rhs, &x.rhs,
|
||||
*options, idx_values, rhs_chain, level, new_val,
|
||||
global, caches, lib, this_ptr, val, root, rhs, *options,
|
||||
&x.rhs, idx_values, rhs_chain, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
}
|
||||
@@ -551,14 +554,13 @@ impl Engine {
|
||||
/// Evaluate a dot/index chain.
|
||||
pub(crate) fn eval_dot_index_chain(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
lib: &[SharedModule],
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
expr: &Expr,
|
||||
level: usize,
|
||||
new_val: Option<(Dynamic, OpAssignment)>,
|
||||
new_val: &mut Option<(Dynamic, &OpAssignment)>,
|
||||
) -> RhaiResult {
|
||||
let chain_type = ChainType::from(expr);
|
||||
let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr {
|
||||
@@ -595,8 +597,7 @@ impl Engine {
|
||||
// All other patterns - evaluate the arguments chain
|
||||
_ => {
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, global, caches, lib, this_ptr, rhs, options, chain_type, idx_values, 0,
|
||||
level,
|
||||
global, caches, lib, scope, this_ptr, rhs, options, chain_type, idx_values,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -605,20 +606,19 @@ impl Engine {
|
||||
// id.??? or id[???]
|
||||
Expr::Variable(x, .., var_pos) => {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, lhs, level)?;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, *var_pos)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, lhs)?;
|
||||
self.track_operation(global, *var_pos)?;
|
||||
|
||||
let (mut target, ..) =
|
||||
self.search_namespace(scope, global, lib, this_ptr, lhs, level)?;
|
||||
self.search_namespace(global, caches, lib, scope, this_ptr, lhs)?;
|
||||
|
||||
let obj_ptr = &mut target;
|
||||
let root = (x.3.as_str(), *var_pos);
|
||||
let mut this = Dynamic::NULL;
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, caches, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values,
|
||||
chain_type, level, new_val,
|
||||
global, caches, lib, &mut this, obj_ptr, root, expr, options, rhs, idx_values,
|
||||
chain_type, new_val,
|
||||
)
|
||||
}
|
||||
// {expr}.??? = ??? or {expr}[???] = ???
|
||||
@@ -626,14 +626,14 @@ impl Engine {
|
||||
// {expr}.??? or {expr}[???]
|
||||
expr => {
|
||||
let value = self
|
||||
.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?
|
||||
.eval_expr(global, caches, lib, scope, this_ptr, expr)?
|
||||
.flatten();
|
||||
let obj_ptr = &mut value.into();
|
||||
let root = ("", expr.start_position());
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
global, caches, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values,
|
||||
chain_type, level, new_val,
|
||||
global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values,
|
||||
chain_type, new_val,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -644,41 +644,38 @@ impl Engine {
|
||||
/// Evaluate a chain of indexes and store the results in a [`FnArgsVec`].
|
||||
fn eval_dot_index_chain_arguments(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
lib: &[SharedModule],
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
expr: &Expr,
|
||||
parent_options: ASTFlags,
|
||||
_parent_chain_type: ChainType,
|
||||
parent_chain_type: ChainType,
|
||||
idx_values: &mut FnArgsVec<Dynamic>,
|
||||
size: usize,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<()> {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
match expr {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::MethodCall(x, ..)
|
||||
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||
if parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||
{
|
||||
for arg_expr in &x.args {
|
||||
idx_values.push(
|
||||
self.get_arg_value(scope, global, caches, lib, this_ptr, arg_expr, level)?
|
||||
self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)?
|
||||
.0
|
||||
.flatten(),
|
||||
);
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => {
|
||||
Expr::MethodCall(..) if parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Property(..) if _parent_chain_type == ChainType::Dotting => (),
|
||||
Expr::Property(..) if parent_chain_type == ChainType::Dotting => (),
|
||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
Expr::Index(x, options, ..) | Expr::Dot(x, options, ..)
|
||||
@@ -691,35 +688,33 @@ impl Engine {
|
||||
// Evaluate in left-to-right order
|
||||
match lhs {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Property(..) if _parent_chain_type == ChainType::Dotting => (),
|
||||
Expr::Property(..) if parent_chain_type == ChainType::Dotting => (),
|
||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::MethodCall(x, ..)
|
||||
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||
if parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||
{
|
||||
for arg_expr in &x.args {
|
||||
_arg_values.push(
|
||||
self.get_arg_value(
|
||||
scope, global, caches, lib, this_ptr, arg_expr, level,
|
||||
)?
|
||||
.0
|
||||
.flatten(),
|
||||
self.get_arg_value(global, caches, lib, scope, this_ptr, arg_expr)?
|
||||
.0
|
||||
.flatten(),
|
||||
);
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => {
|
||||
Expr::MethodCall(..) if parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
expr if _parent_chain_type == ChainType::Dotting => {
|
||||
expr if parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("invalid dot expression: {:?}", expr);
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
_ if _parent_chain_type == ChainType::Indexing => {
|
||||
_ if parent_chain_type == ChainType::Indexing => {
|
||||
_arg_values.push(
|
||||
self.eval_expr(scope, global, caches, lib, this_ptr, lhs, level)?
|
||||
self.eval_expr(global, caches, lib, scope, this_ptr, lhs)?
|
||||
.flatten(),
|
||||
);
|
||||
}
|
||||
@@ -730,8 +725,7 @@ impl Engine {
|
||||
let chain_type = expr.into();
|
||||
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, global, caches, lib, this_ptr, rhs, *options, chain_type, idx_values,
|
||||
size, level,
|
||||
global, caches, lib, scope, this_ptr, rhs, *options, chain_type, idx_values,
|
||||
)?;
|
||||
|
||||
if !_arg_values.is_empty() {
|
||||
@@ -740,12 +734,12 @@ impl Engine {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
_ if _parent_chain_type == ChainType::Dotting => {
|
||||
_ if parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("invalid dot expression: {:?}", expr);
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
_ if _parent_chain_type == ChainType::Indexing => idx_values.push(
|
||||
self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?
|
||||
_ if parent_chain_type == ChainType::Indexing => idx_values.push(
|
||||
self.eval_expr(global, caches, lib, scope, this_ptr, expr)?
|
||||
.flatten(),
|
||||
),
|
||||
_ => unreachable!("unknown chained expression: {:?}", expr),
|
||||
@@ -760,21 +754,20 @@ impl Engine {
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
lib: &[SharedModule],
|
||||
target: &mut Dynamic,
|
||||
idx: &mut Dynamic,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<Dynamic> {
|
||||
let args = &mut [target, idx];
|
||||
let hash = global.hash_idx_get();
|
||||
let fn_name = crate::engine::FN_IDX_GET;
|
||||
let pos = Position::NONE;
|
||||
let level = level + 1;
|
||||
|
||||
self.call_native_fn(
|
||||
global, caches, lib, fn_name, hash, args, true, false, pos, level,
|
||||
)
|
||||
.map(|(r, ..)| r)
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1);
|
||||
|
||||
self.exec_native_fn_call(global, caches, lib, fn_name, None, hash, args, true, pos)
|
||||
.map(|(r, ..)| r)
|
||||
}
|
||||
|
||||
/// Call a set indexer.
|
||||
@@ -783,21 +776,22 @@ impl Engine {
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
lib: &[SharedModule],
|
||||
target: &mut Dynamic,
|
||||
idx: &mut Dynamic,
|
||||
new_val: &mut Dynamic,
|
||||
is_ref_mut: bool,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
let hash = global.hash_idx_set();
|
||||
let args = &mut [target, idx, new_val];
|
||||
let fn_name = crate::engine::FN_IDX_SET;
|
||||
let pos = Position::NONE;
|
||||
let level = level + 1;
|
||||
|
||||
self.call_native_fn(
|
||||
global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level,
|
||||
global.level += 1;
|
||||
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level -= 1);
|
||||
|
||||
self.exec_native_fn_call(
|
||||
global, caches, lib, fn_name, None, hash, args, is_ref_mut, pos,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -807,16 +801,14 @@ impl Engine {
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
lib: &[SharedModule],
|
||||
target: &'t mut Dynamic,
|
||||
mut idx: Dynamic,
|
||||
idx: &mut Dynamic,
|
||||
idx_pos: Position,
|
||||
_add_if_not_found: bool,
|
||||
use_indexers: bool,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<Target<'t>> {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, Position::NONE)?;
|
||||
self.track_operation(global, Position::NONE)?;
|
||||
|
||||
match target {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@@ -1018,7 +1010,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
_ if use_indexers => self
|
||||
.call_indexer_get(global, caches, lib, target, &mut idx, level)
|
||||
.call_indexer_get(global, caches, lib, target, idx)
|
||||
.map(Into::into),
|
||||
|
||||
_ => Err(ERR::ErrorIndexingType(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Data size checks during evaluation.
|
||||
#![cfg(not(feature = "unchecked"))]
|
||||
|
||||
use super::GlobalRuntimeState;
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR};
|
||||
use std::num::NonZeroUsize;
|
||||
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, ERR};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -15,46 +15,45 @@ impl Engine {
|
||||
/// # Panics
|
||||
///
|
||||
/// 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) {
|
||||
match value.0 {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref arr, ..) => {
|
||||
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(..) => {
|
||||
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"))]
|
||||
Union::Map(..) => {
|
||||
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()),
|
||||
_ => (arrays + 1, maps, strings),
|
||||
Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()),
|
||||
_ => (ax + 1, mx, sx),
|
||||
})
|
||||
}
|
||||
#[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"))]
|
||||
Union::Map(ref map, ..) => {
|
||||
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"))]
|
||||
Union::Array(..) => {
|
||||
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"))]
|
||||
Union::Blob(ref a, ..) => (arrays + a.len(), maps, strings),
|
||||
Union::Blob(ref a, ..) => (ax + a.len(), mx, sx),
|
||||
Union::Map(..) => {
|
||||
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()),
|
||||
_ => (arrays, maps + 1, strings),
|
||||
Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()),
|
||||
_ => (ax, mx + 1, sx),
|
||||
})
|
||||
}
|
||||
Union::Str(ref s, ..) => (0, 0, s.len()),
|
||||
@@ -70,65 +69,48 @@ 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.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub(crate) fn raise_err_if_over_data_size_limit(
|
||||
&self,
|
||||
sizes: (usize, usize, usize),
|
||||
pos: Position,
|
||||
(_arr, _map, s): (usize, usize, usize),
|
||||
) -> RhaiResultOf<()> {
|
||||
let (_arr, _map, s) = sizes;
|
||||
|
||||
if s > self
|
||||
if self
|
||||
.limits
|
||||
.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(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if _arr
|
||||
> self
|
||||
.limits
|
||||
.max_array_size
|
||||
.map_or(usize::MAX, NonZeroUsize::get)
|
||||
if self
|
||||
.limits
|
||||
.max_array_size
|
||||
.map_or(false, |max| _arr > max.get())
|
||||
{
|
||||
return Err(ERR::ErrorDataTooLarge("Size of array".to_string(), pos).into());
|
||||
return Err(
|
||||
ERR::ErrorDataTooLarge("Size of array/BLOB".to_string(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if _map
|
||||
> self
|
||||
.limits
|
||||
.max_map_size
|
||||
.map_or(usize::MAX, NonZeroUsize::get)
|
||||
if self
|
||||
.limits
|
||||
.max_map_size
|
||||
.map_or(false, |max| _map > max.get())
|
||||
{
|
||||
return Err(ERR::ErrorDataTooLarge("Size of object map".to_string(), pos).into());
|
||||
return Err(
|
||||
ERR::ErrorDataTooLarge("Size of object map".to_string(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check whether the size of a [`Dynamic`] is within limits.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[inline]
|
||||
pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> {
|
||||
// If no data size limits, just return
|
||||
if !self.has_data_size_limit() {
|
||||
@@ -137,35 +119,37 @@ impl Engine {
|
||||
|
||||
let sizes = Self::calc_data_sizes(value, true);
|
||||
|
||||
self.raise_err_if_over_data_size_limit(sizes, pos)
|
||||
self.raise_err_if_over_data_size_limit(sizes)
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
}
|
||||
|
||||
/// Raise an error if the size of a [`Dynamic`] is out of limits (if any).
|
||||
///
|
||||
/// Not available under `unchecked`.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[inline(always)]
|
||||
pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> {
|
||||
self.check_data_size(value, Position::NONE)
|
||||
}
|
||||
|
||||
/// Check if the number of operations stay within limit.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub(crate) fn inc_operations(
|
||||
pub(crate) fn track_operation(
|
||||
&self,
|
||||
num_operations: &mut u64,
|
||||
global: &mut GlobalRuntimeState,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<()> {
|
||||
*num_operations += 1;
|
||||
global.num_operations += 1;
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// Report progress - only in steps
|
||||
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
|
||||
return Err(ERR::ErrorTerminated(token, pos).into());
|
||||
}
|
||||
@@ -173,4 +157,14 @@ impl Engine {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check a result to ensure that it is valid.
|
||||
#[inline]
|
||||
pub(crate) fn check_return_value(&self, result: RhaiResult, pos: Position) -> RhaiResult {
|
||||
if let Ok(ref r) = result {
|
||||
self.check_data_size(r, pos)?;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
//! Module defining the debugging interface.
|
||||
#![cfg(feature = "debugging")]
|
||||
|
||||
use super::{EvalContext, GlobalRuntimeState};
|
||||
use super::{Caches, EvalContext, GlobalRuntimeState};
|
||||
use crate::ast::{ASTNode, Expr, Stmt};
|
||||
use crate::{Dynamic, Engine, EvalAltResult, Identifier, Module, Position, RhaiResultOf, Scope};
|
||||
use crate::{
|
||||
Dynamic, Engine, EvalAltResult, ImmutableString, Position, RhaiResultOf, Scope, SharedModule,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{fmt, iter::repeat, mem};
|
||||
@@ -48,6 +50,7 @@ pub enum DebuggerCommand {
|
||||
|
||||
impl Default for DebuggerCommand {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::Continue
|
||||
}
|
||||
@@ -99,12 +102,10 @@ pub enum BreakPoint {
|
||||
/// Break at a particular position under a particular source.
|
||||
///
|
||||
/// Not available under `no_position`.
|
||||
///
|
||||
/// Source is empty if not available.
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
AtPosition {
|
||||
/// Source (empty if not available) of the break-point.
|
||||
source: Identifier,
|
||||
source: Option<ImmutableString>,
|
||||
/// [Position] of the break-point.
|
||||
pos: Position,
|
||||
/// Is the break-point enabled?
|
||||
@@ -113,14 +114,14 @@ pub enum BreakPoint {
|
||||
/// Break at a particular function call.
|
||||
AtFunctionName {
|
||||
/// Function name.
|
||||
name: Identifier,
|
||||
name: ImmutableString,
|
||||
/// Is the break-point enabled?
|
||||
enabled: bool,
|
||||
},
|
||||
/// Break at a particular function call with a particular number of arguments.
|
||||
AtFunctionCall {
|
||||
/// Function name.
|
||||
name: Identifier,
|
||||
name: ImmutableString,
|
||||
/// Number of arguments.
|
||||
args: usize,
|
||||
/// Is the break-point enabled?
|
||||
@@ -132,7 +133,7 @@ pub enum BreakPoint {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
AtProperty {
|
||||
/// Property name.
|
||||
name: Identifier,
|
||||
name: ImmutableString,
|
||||
/// Is the break-point enabled?
|
||||
enabled: bool,
|
||||
},
|
||||
@@ -147,35 +148,30 @@ impl fmt::Display for BreakPoint {
|
||||
pos,
|
||||
enabled,
|
||||
} => {
|
||||
if source.is_empty() {
|
||||
write!(f, "@ {:?}", pos)?;
|
||||
} else {
|
||||
write!(f, "{} @ {:?}", source, pos)?;
|
||||
if let Some(ref source) = source {
|
||||
write!(f, "{source} ")?;
|
||||
}
|
||||
write!(f, "@ {pos:?}")?;
|
||||
if !*enabled {
|
||||
f.write_str(" (disabled)")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::AtFunctionName {
|
||||
name: fn_name,
|
||||
enabled,
|
||||
} => {
|
||||
write!(f, "{} (...)", fn_name)?;
|
||||
Self::AtFunctionName { name, enabled } => {
|
||||
write!(f, "{name} (...)")?;
|
||||
if !*enabled {
|
||||
f.write_str(" (disabled)")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Self::AtFunctionCall {
|
||||
name: fn_name,
|
||||
name,
|
||||
args,
|
||||
enabled,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{} ({})",
|
||||
fn_name,
|
||||
"{name} ({})",
|
||||
repeat("_").take(*args).collect::<Vec<_>>().join(", ")
|
||||
)?;
|
||||
if !*enabled {
|
||||
@@ -184,11 +180,8 @@ impl fmt::Display for BreakPoint {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Self::AtProperty {
|
||||
name: prop,
|
||||
enabled,
|
||||
} => {
|
||||
write!(f, ".{}", prop)?;
|
||||
Self::AtProperty { name, enabled } => {
|
||||
write!(f, ".{name}")?;
|
||||
if !*enabled {
|
||||
f.write_str(" (disabled)")?;
|
||||
}
|
||||
@@ -230,11 +223,11 @@ impl BreakPoint {
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct CallStackFrame {
|
||||
/// Function name.
|
||||
pub fn_name: Identifier,
|
||||
pub fn_name: ImmutableString,
|
||||
/// Copies of function call arguments, if any.
|
||||
pub args: crate::StaticVec<Dynamic>,
|
||||
/// Source of the function, empty if none.
|
||||
pub source: Identifier,
|
||||
/// Source of the function.
|
||||
pub source: Option<ImmutableString>,
|
||||
/// [Position][`Position`] of the function call.
|
||||
pub pos: Position,
|
||||
}
|
||||
@@ -250,11 +243,10 @@ impl fmt::Display for CallStackFrame {
|
||||
fp.finish()?;
|
||||
|
||||
if !self.pos.is_none() {
|
||||
if self.source.is_empty() {
|
||||
write!(f, " @ {:?}", self.pos)?;
|
||||
} else {
|
||||
write!(f, ": {} @ {:?}", self.source, self.pos)?;
|
||||
if let Some(ref source) = self.source {
|
||||
write!(f, ": {source}")?;
|
||||
}
|
||||
write!(f, " @ {:?}", self.pos)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -301,15 +293,15 @@ impl Debugger {
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_call_stack_frame(
|
||||
&mut self,
|
||||
fn_name: impl Into<Identifier>,
|
||||
fn_name: ImmutableString,
|
||||
args: crate::StaticVec<Dynamic>,
|
||||
source: impl Into<Identifier>,
|
||||
source: Option<ImmutableString>,
|
||||
pos: Position,
|
||||
) {
|
||||
self.call_stack.push(CallStackFrame {
|
||||
fn_name: fn_name.into(),
|
||||
args,
|
||||
source: source.into(),
|
||||
source,
|
||||
pos,
|
||||
});
|
||||
}
|
||||
@@ -336,7 +328,7 @@ impl Debugger {
|
||||
}
|
||||
/// Returns the first break-point triggered by a particular [`AST` Node][ASTNode].
|
||||
#[must_use]
|
||||
pub fn is_break_point(&self, src: &str, node: ASTNode) -> Option<usize> {
|
||||
pub fn is_break_point(&self, src: Option<&str>, node: ASTNode) -> Option<usize> {
|
||||
let _src = src;
|
||||
|
||||
self.break_points()
|
||||
@@ -348,11 +340,12 @@ impl Debugger {
|
||||
BreakPoint::AtPosition { pos, .. } if pos.is_none() => false,
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
BreakPoint::AtPosition { source, pos, .. } if pos.is_beginning_of_line() => {
|
||||
node.position().line().unwrap_or(0) == pos.line().unwrap() && _src == source
|
||||
node.position().line().unwrap_or(0) == pos.line().unwrap()
|
||||
&& _src == source.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
BreakPoint::AtPosition { source, pos, .. } => {
|
||||
node.position() == *pos && _src == source
|
||||
node.position() == *pos && _src == source.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
BreakPoint::AtFunctionName { name, .. } => match node {
|
||||
ASTNode::Expr(Expr::FnCall(x, ..)) | ASTNode::Stmt(Stmt::FnCall(x, ..)) => {
|
||||
@@ -418,16 +411,16 @@ impl Engine {
|
||||
#[inline(always)]
|
||||
pub(crate) fn run_debugger<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
caches: &mut Caches,
|
||||
lib: &[SharedModule],
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<()> {
|
||||
if self.debugger.is_some() {
|
||||
if let Some(cmd) =
|
||||
self.run_debugger_with_reset_raw(scope, global, lib, this_ptr, node, level)?
|
||||
self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node)?
|
||||
{
|
||||
global.debugger.status = cmd;
|
||||
}
|
||||
@@ -437,41 +430,41 @@ impl Engine {
|
||||
}
|
||||
/// Run the debugger callback if there is a debugging interface registered.
|
||||
///
|
||||
/// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or
|
||||
/// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or
|
||||
/// function call.
|
||||
///
|
||||
/// It is up to the [`Engine`] to reactivate the debugger.
|
||||
#[inline(always)]
|
||||
pub(crate) fn run_debugger_with_reset<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
caches: &mut Caches,
|
||||
lib: &[SharedModule],
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
||||
if self.debugger.is_some() {
|
||||
self.run_debugger_with_reset_raw(scope, global, lib, this_ptr, node, level)
|
||||
self.run_debugger_with_reset_raw(global, caches, lib, scope, this_ptr, node)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
/// Run the debugger callback.
|
||||
///
|
||||
/// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or
|
||||
/// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or
|
||||
/// function call.
|
||||
///
|
||||
/// It is up to the [`Engine`] to reactivate the debugger.
|
||||
#[inline]
|
||||
pub(crate) fn run_debugger_with_reset_raw<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
caches: &mut Caches,
|
||||
lib: &[SharedModule],
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
node: impl Into<ASTNode<'a>>,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
||||
let node = node.into();
|
||||
|
||||
@@ -495,45 +488,37 @@ impl Engine {
|
||||
|
||||
let event = match event {
|
||||
Some(e) => e,
|
||||
None => {
|
||||
if let Some(bp) = global.debugger.is_break_point(&global.source, node) {
|
||||
DebuggerEvent::BreakPoint(bp)
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
None => match global.debugger.is_break_point(global.source(), node) {
|
||||
Some(bp) => DebuggerEvent::BreakPoint(bp),
|
||||
None => return Ok(None),
|
||||
},
|
||||
};
|
||||
|
||||
self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level)
|
||||
self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event)
|
||||
}
|
||||
/// Run the debugger callback unconditionally.
|
||||
///
|
||||
/// Returns `Some` if the debugger needs to be reactivated at the end of the block, statement or
|
||||
/// Returns [`Some`] if the debugger needs to be reactivated at the end of the block, statement or
|
||||
/// function call.
|
||||
///
|
||||
/// It is up to the [`Engine`] to reactivate the debugger.
|
||||
#[inline]
|
||||
pub(crate) fn run_debugger_raw<'a>(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
caches: &mut Caches,
|
||||
lib: &[SharedModule],
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
node: ASTNode<'a>,
|
||||
event: DebuggerEvent,
|
||||
level: usize,
|
||||
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
||||
let source = global.source.clone();
|
||||
let source = if source.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(source.as_str())
|
||||
};
|
||||
|
||||
let context = crate::EvalContext::new(self, scope, global, None, lib, this_ptr, level);
|
||||
let src = global.source_raw().cloned();
|
||||
let src = src.as_ref().map(|s| s.as_str());
|
||||
let context = crate::EvalContext::new(self, global, caches, lib, scope, this_ptr);
|
||||
|
||||
if let Some((.., ref on_debugger)) = self.debugger {
|
||||
let command = on_debugger(context, event, node, source, node.position())?;
|
||||
let command = on_debugger(context, event, node, src, node.position())?;
|
||||
|
||||
match command {
|
||||
DebuggerCommand::Continue => {
|
||||
@@ -556,12 +541,12 @@ impl Engine {
|
||||
// Bump a level if it is a function call
|
||||
let level = match node {
|
||||
ASTNode::Expr(Expr::FnCall(..)) | ASTNode::Stmt(Stmt::FnCall(..)) => {
|
||||
level + 1
|
||||
global.level + 1
|
||||
}
|
||||
ASTNode::Stmt(Stmt::Expr(e)) if matches!(**e, Expr::FnCall(..)) => {
|
||||
level + 1
|
||||
global.level + 1
|
||||
}
|
||||
_ => level,
|
||||
_ => global.level,
|
||||
};
|
||||
global.debugger.status = DebuggerStatus::FunctionExit(level);
|
||||
Ok(None)
|
||||
|
||||
@@ -1,42 +1,39 @@
|
||||
//! Evaluation context.
|
||||
|
||||
use super::{Caches, GlobalRuntimeState};
|
||||
use crate::{Dynamic, Engine, Module, Scope};
|
||||
use crate::{Dynamic, Engine, Module, Scope, SharedModule};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Context of a script evaluation process.
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> {
|
||||
pub struct EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
/// The current [`Engine`].
|
||||
engine: &'a Engine,
|
||||
/// The current [`Scope`].
|
||||
scope: &'s mut Scope<'ps>,
|
||||
/// The current [`GlobalRuntimeState`].
|
||||
global: &'g mut GlobalRuntimeState<'pg>,
|
||||
global: &'g mut GlobalRuntimeState,
|
||||
/// The current [caches][Caches], if available.
|
||||
caches: Option<&'c mut Caches<'pc>>,
|
||||
caches: &'c mut Caches,
|
||||
/// The current stack of imported [modules][Module].
|
||||
lib: &'a [&'a Module],
|
||||
lib: &'a [SharedModule],
|
||||
/// The current bound `this` pointer, if any.
|
||||
this_ptr: &'t mut Option<&'pt mut Dynamic>,
|
||||
/// The current nesting level of function calls.
|
||||
level: usize,
|
||||
this_ptr: &'t mut Dynamic,
|
||||
}
|
||||
|
||||
impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> {
|
||||
impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
||||
/// Create a new [`EvalContext`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
engine: &'a Engine,
|
||||
global: &'g mut GlobalRuntimeState,
|
||||
caches: &'c mut Caches,
|
||||
lib: &'a [SharedModule],
|
||||
scope: &'s mut Scope<'ps>,
|
||||
global: &'g mut GlobalRuntimeState<'pg>,
|
||||
caches: Option<&'c mut Caches<'pc>>,
|
||||
lib: &'a [&'a Module],
|
||||
this_ptr: &'t mut Option<&'pt mut Dynamic>,
|
||||
level: usize,
|
||||
this_ptr: &'t mut Dynamic,
|
||||
) -> Self {
|
||||
Self {
|
||||
engine,
|
||||
@@ -45,7 +42,6 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, '
|
||||
caches,
|
||||
lib,
|
||||
this_ptr,
|
||||
level,
|
||||
}
|
||||
}
|
||||
/// The current [`Engine`].
|
||||
@@ -58,11 +54,7 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, '
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn source(&self) -> Option<&str> {
|
||||
if self.global.source.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.global.source.as_str())
|
||||
}
|
||||
self.global.source()
|
||||
}
|
||||
/// The current [`Scope`].
|
||||
#[inline(always)]
|
||||
@@ -108,39 +100,47 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, '
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn global_runtime_state_mut(&mut self) -> &mut &'g mut GlobalRuntimeState<'pg> {
|
||||
&mut self.global
|
||||
pub fn global_runtime_state_mut(&mut self) -> &mut GlobalRuntimeState {
|
||||
self.global
|
||||
}
|
||||
/// Get an iterator over the namespaces containing definition of all script-defined functions.
|
||||
#[inline]
|
||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
||||
self.lib.iter().copied()
|
||||
self.lib.iter().map(|m| m.as_ref())
|
||||
}
|
||||
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn namespaces(&self) -> &[&Module] {
|
||||
pub const fn namespaces(&self) -> &[SharedModule] {
|
||||
self.lib
|
||||
}
|
||||
/// The current bound `this` pointer, if any.
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn this_ptr(&self) -> Option<&Dynamic> {
|
||||
self.this_ptr.as_ref().map(|v| &**v)
|
||||
if self.this_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(self.this_ptr)
|
||||
}
|
||||
}
|
||||
/// Mutable reference to the current bound `this` pointer, if any.
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn this_ptr_mut(&mut self) -> &mut Option<&'pt mut Dynamic> {
|
||||
self.this_ptr
|
||||
pub fn this_ptr_mut(&mut self) -> Option<&mut Dynamic> {
|
||||
if self.this_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(self.this_ptr)
|
||||
}
|
||||
}
|
||||
/// The current nesting level of function calls.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn call_level(&self) -> usize {
|
||||
self.level
|
||||
self.global.level
|
||||
}
|
||||
|
||||
/// Evaluate an [expression tree][crate::Expression] within this [evaluation context][`EvalContext`].
|
||||
@@ -177,32 +177,23 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, '
|
||||
) -> crate::RhaiResult {
|
||||
let expr: &crate::ast::Expr = expr;
|
||||
|
||||
let mut new_caches = Caches::new();
|
||||
|
||||
let caches = match self.caches.as_mut() {
|
||||
Some(c) => c,
|
||||
None => &mut new_caches,
|
||||
};
|
||||
|
||||
match expr {
|
||||
crate::ast::Expr::Stmt(statements) => self.engine.eval_stmt_block(
|
||||
self.scope,
|
||||
self.global,
|
||||
caches,
|
||||
self.caches,
|
||||
self.lib,
|
||||
self.scope,
|
||||
self.this_ptr,
|
||||
statements,
|
||||
rewind_scope,
|
||||
self.level,
|
||||
),
|
||||
_ => self.engine.eval_expr(
|
||||
self.scope,
|
||||
self.global,
|
||||
caches,
|
||||
self.caches,
|
||||
self.lib,
|
||||
self.scope,
|
||||
self.this_ptr,
|
||||
expr,
|
||||
self.level,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
464
src/eval/expr.rs
464
src/eval/expr.rs
@@ -1,19 +1,10 @@
|
||||
//! Module defining functions for evaluating an expression.
|
||||
|
||||
use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
||||
use crate::ast::{Expr, FnCallExpr, OpAssignment};
|
||||
use crate::ast::{Expr, OpAssignment};
|
||||
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
||||
use crate::eval::FnResolutionCacheEntry;
|
||||
use crate::func::{
|
||||
calc_fn_params_hash, combine_hashes, gen_fn_call_signature, get_builtin_binary_op_fn,
|
||||
CallableFunction,
|
||||
};
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
||||
#[cfg(feature = "no_std")]
|
||||
use hashbrown::hash_map::Entry;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
use std::collections::hash_map::Entry;
|
||||
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR};
|
||||
use std::num::NonZeroUsize;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -27,18 +18,18 @@ impl Engine {
|
||||
&self,
|
||||
global: &GlobalRuntimeState,
|
||||
namespace: &crate::ast::Namespace,
|
||||
) -> Option<crate::Shared<Module>> {
|
||||
) -> Option<SharedModule> {
|
||||
assert!(!namespace.is_empty());
|
||||
|
||||
let root = namespace.root();
|
||||
|
||||
// Qualified - check if the root module is directly indexed
|
||||
let index = if global.always_search_scope {
|
||||
None
|
||||
} else {
|
||||
namespace.index()
|
||||
};
|
||||
|
||||
// Qualified - check if the root module is directly indexed
|
||||
if let Some(index) = index {
|
||||
let offset = global.num_imports() - index.get();
|
||||
|
||||
@@ -58,25 +49,26 @@ impl Engine {
|
||||
/// depending on whether the variable name is namespace-qualified.
|
||||
pub(crate) fn search_namespace<'s>(
|
||||
&self,
|
||||
scope: &'s mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
lib: &[&Module],
|
||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||
caches: &mut Caches,
|
||||
lib: &[SharedModule],
|
||||
|
||||
scope: &'s mut Scope,
|
||||
this_ptr: &'s mut Dynamic,
|
||||
expr: &Expr,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<(Target<'s>, Position)> {
|
||||
match expr {
|
||||
Expr::Variable(_, Some(_), _) => {
|
||||
self.search_scope_only(scope, global, lib, this_ptr, expr, level)
|
||||
self.search_scope_only(global, caches, lib, scope, this_ptr, expr)
|
||||
}
|
||||
Expr::Variable(v, None, _var_pos) => match &**v {
|
||||
// Normal variable access
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
(_, ns, ..) if ns.is_empty() => {
|
||||
self.search_scope_only(scope, global, lib, this_ptr, expr, level)
|
||||
self.search_scope_only(global, caches, lib, scope, this_ptr, expr)
|
||||
}
|
||||
#[cfg(feature = "no_module")]
|
||||
(_, (), ..) => self.search_scope_only(scope, global, lib, this_ptr, expr, level),
|
||||
(_, (), ..) => self.search_scope_only(global, caches, lib, scope, this_ptr, expr),
|
||||
|
||||
// Qualified variable access
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@@ -141,22 +133,24 @@ impl Engine {
|
||||
/// Panics if `expr` is not [`Expr::Variable`].
|
||||
pub(crate) fn search_scope_only<'s>(
|
||||
&self,
|
||||
scope: &'s mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
lib: &[&Module],
|
||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||
caches: &mut Caches,
|
||||
lib: &[SharedModule],
|
||||
|
||||
scope: &'s mut Scope,
|
||||
this_ptr: &'s mut Dynamic,
|
||||
expr: &Expr,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<(Target<'s>, Position)> {
|
||||
// Make sure that the pointer indirection is taken only when absolutely necessary.
|
||||
|
||||
let (index, var_pos) = match expr {
|
||||
// Check if the variable is `this`
|
||||
Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => {
|
||||
return this_ptr.as_mut().map_or_else(
|
||||
|| Err(ERR::ErrorUnboundThis(*pos).into()),
|
||||
|val| Ok(((*val).into(), *pos)),
|
||||
)
|
||||
return if this_ptr.is_null() {
|
||||
Err(ERR::ErrorUnboundThis(*pos).into())
|
||||
} else {
|
||||
Ok((this_ptr.into(), *pos))
|
||||
};
|
||||
}
|
||||
_ if global.always_search_scope => (0, expr.start_position()),
|
||||
Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos),
|
||||
@@ -165,7 +159,7 @@ impl Engine {
|
||||
Expr::Variable(v, None, pos)
|
||||
if lib
|
||||
.iter()
|
||||
.flat_map(|&m| m.iter_script_fn())
|
||||
.flat_map(|m| m.iter_script_fn())
|
||||
.any(|(_, _, f, ..)| f == v.3.as_str()) =>
|
||||
{
|
||||
let val: Dynamic =
|
||||
@@ -178,7 +172,7 @@ impl Engine {
|
||||
|
||||
// Check the variable resolver, if any
|
||||
if let Some(ref resolve_var) = self.resolve_var {
|
||||
let context = EvalContext::new(self, scope, global, None, lib, this_ptr, level);
|
||||
let context = EvalContext::new(self, global, caches, lib, scope, this_ptr);
|
||||
let var_name = expr.get_variable_name(true).expect("`Expr::Variable`");
|
||||
match resolve_var(var_name, index, context) {
|
||||
Ok(Some(mut result)) => {
|
||||
@@ -196,8 +190,8 @@ impl Engine {
|
||||
// Find the variable in the scope
|
||||
let var_name = expr.get_variable_name(true).expect("`Expr::Variable`");
|
||||
|
||||
match scope.get_index(var_name) {
|
||||
Some((index, _)) => index,
|
||||
match scope.search(var_name) {
|
||||
Some(index) => index,
|
||||
None => {
|
||||
return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) {
|
||||
Some(val) => Ok((val.into(), var_pos)),
|
||||
@@ -214,136 +208,6 @@ impl Engine {
|
||||
Ok((val.into(), var_pos))
|
||||
}
|
||||
|
||||
/// Evaluate a function call expression.
|
||||
pub(crate) fn eval_fn_call_expr(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
expr: &FnCallExpr,
|
||||
pos: Position,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
let FnCallExpr {
|
||||
name, hashes, args, ..
|
||||
} = expr;
|
||||
|
||||
// Short-circuit native binary operator call if under Fast Operators mode
|
||||
if expr.is_native_operator && self.fast_operators() && (args.len() == 1 || args.len() == 2)
|
||||
{
|
||||
let mut lhs = self
|
||||
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
|
||||
.0
|
||||
.flatten();
|
||||
|
||||
let mut rhs = if args.len() == 2 {
|
||||
self.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)?
|
||||
.0
|
||||
.flatten()
|
||||
} else {
|
||||
Dynamic::UNIT
|
||||
};
|
||||
|
||||
let mut operands = [&mut lhs, &mut rhs];
|
||||
let operands = if args.len() == 2 {
|
||||
&mut operands[..]
|
||||
} else {
|
||||
&mut operands[0..1]
|
||||
};
|
||||
|
||||
let hash = calc_fn_params_hash(operands.iter().map(|a| a.type_id()));
|
||||
let hash = combine_hashes(hashes.native, hash);
|
||||
|
||||
let cache = caches.fn_resolution_cache_mut();
|
||||
let local_entry: CallableFunction;
|
||||
|
||||
let func = match cache.map.entry(hash) {
|
||||
Entry::Vacant(entry) => {
|
||||
let func = if args.len() == 2 {
|
||||
get_builtin_binary_op_fn(name, operands[0], operands[1])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(f) = func {
|
||||
if cache.filter.is_absent_and_set(hash) {
|
||||
// Do not cache "one-hit wonders"
|
||||
local_entry = CallableFunction::from_fn_builtin(f);
|
||||
&local_entry
|
||||
} else {
|
||||
// Cache repeated calls
|
||||
&entry
|
||||
.insert(Some(FnResolutionCacheEntry {
|
||||
func: CallableFunction::from_fn_builtin(f),
|
||||
source: None,
|
||||
}))
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.func
|
||||
}
|
||||
} else {
|
||||
let result = self.exec_fn_call(
|
||||
None, global, caches, lib, name, *hashes, operands, false, false, pos,
|
||||
level,
|
||||
);
|
||||
return result.map(|(v, ..)| v);
|
||||
}
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
if let Some(entry) = entry.into_mut() {
|
||||
&entry.func
|
||||
} else {
|
||||
let sig = gen_fn_call_signature(self, name, operands);
|
||||
return Err(ERR::ErrorFunctionNotFound(sig, pos).into());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let context = (self, name, None, &*global, lib, pos, level).into();
|
||||
let result = if func.is_plugin_fn() {
|
||||
func.get_plugin_fn().unwrap().call(context, operands)
|
||||
} else {
|
||||
func.get_native_fn().unwrap()(context, operands)
|
||||
};
|
||||
return self.check_return_value(result, pos);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if !expr.namespace.is_empty() {
|
||||
// Qualified function call
|
||||
let hash = hashes.native;
|
||||
let namespace = &expr.namespace;
|
||||
|
||||
return self.make_qualified_function_call(
|
||||
scope, global, caches, lib, this_ptr, namespace, name, args, hash, pos, level,
|
||||
);
|
||||
}
|
||||
|
||||
// Normal function call
|
||||
let (first_arg, args) = args.split_first().map_or_else(
|
||||
|| (None, args.as_ref()),
|
||||
|(first, rest)| (Some(first), rest),
|
||||
);
|
||||
|
||||
self.make_function_call(
|
||||
scope,
|
||||
global,
|
||||
caches,
|
||||
lib,
|
||||
this_ptr,
|
||||
name,
|
||||
first_arg,
|
||||
args,
|
||||
*hashes,
|
||||
expr.capture_parent_scope,
|
||||
expr.is_native_operator,
|
||||
pos,
|
||||
level,
|
||||
)
|
||||
}
|
||||
|
||||
/// Evaluate an expression.
|
||||
//
|
||||
// # Implementation Notes
|
||||
@@ -354,34 +218,30 @@ impl Engine {
|
||||
// Errors that are not recoverable, such as system errors or safety errors, can use `?`.
|
||||
pub(crate) fn eval_expr(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
lib: &[SharedModule],
|
||||
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
expr: &Expr,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
// Coded this way for better branch prediction.
|
||||
// Popular branches are lifted out of the `match` statement into their own branches.
|
||||
|
||||
// Function calls should account for a relatively larger portion of expressions because
|
||||
// binary operators are also function calls.
|
||||
if let Expr::FnCall(x, ..) = expr {
|
||||
if let Expr::FnCall(x, pos) = expr {
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset_debugger =
|
||||
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
||||
|
||||
let result =
|
||||
self.eval_fn_call_expr(scope, global, caches, lib, this_ptr, x, x.pos, level);
|
||||
|
||||
let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.reset_status(reset_debugger);
|
||||
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
|
||||
return result;
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
return self.eval_fn_call_expr(global, caches, lib, scope, this_ptr, x, *pos);
|
||||
}
|
||||
|
||||
// Then variable access.
|
||||
@@ -389,30 +249,32 @@ impl Engine {
|
||||
// will cost more than the mis-predicted `match` branch.
|
||||
if let Expr::Variable(x, index, var_pos) = expr {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, expr, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, expr)?;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
|
||||
this_ptr
|
||||
.as_deref()
|
||||
.cloned()
|
||||
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
|
||||
if this_ptr.is_null() {
|
||||
ERR::ErrorUnboundThis(*var_pos).into()
|
||||
} else {
|
||||
Ok(this_ptr.clone())
|
||||
}
|
||||
} else {
|
||||
self.search_namespace(scope, global, lib, this_ptr, expr, level)
|
||||
self.search_namespace(global, caches, lib, scope, this_ptr, expr)
|
||||
.map(|(val, ..)| val.take_or_clone())
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
let reset_debugger =
|
||||
self.run_debugger_with_reset(scope, global, lib, this_ptr, expr, level)?;
|
||||
let reset = self.run_debugger_with_reset(global, caches, lib, scope, this_ptr, expr)?;
|
||||
#[cfg(feature = "debugging")]
|
||||
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
|
||||
g.debugger.reset_status(reset)
|
||||
});
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
||||
self.track_operation(global, expr.position())?;
|
||||
|
||||
let result = match expr {
|
||||
match expr {
|
||||
// Constants
|
||||
Expr::DynamicConstant(x, ..) => Ok(x.as_ref().clone()),
|
||||
Expr::IntegerConstant(x, ..) => Ok((*x).into()),
|
||||
@@ -427,163 +289,115 @@ impl Engine {
|
||||
Expr::InterpolatedString(x, _) => {
|
||||
let mut concat = self.get_interned_string("").into();
|
||||
let target = &mut concat;
|
||||
let mut result = Ok(Dynamic::UNIT);
|
||||
|
||||
let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE);
|
||||
let root = ("", Position::NONE);
|
||||
|
||||
for expr in &**x {
|
||||
let item =
|
||||
match self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) {
|
||||
Ok(r) => r,
|
||||
err => {
|
||||
result = err;
|
||||
break;
|
||||
}
|
||||
};
|
||||
let result = x
|
||||
.iter()
|
||||
.try_for_each(|expr| {
|
||||
let item = self.eval_expr(global, caches, lib, scope, this_ptr, expr)?;
|
||||
|
||||
op_info.pos = expr.start_position();
|
||||
op_info.pos = expr.start_position();
|
||||
|
||||
if let Err(err) = self
|
||||
.eval_op_assignment(global, caches, lib, op_info, target, root, item, level)
|
||||
{
|
||||
result = Err(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.eval_op_assignment(global, caches, lib, &op_info, target, root, item)
|
||||
})
|
||||
.map(|_| concat.take_or_clone());
|
||||
|
||||
self.check_return_value(
|
||||
result.map(|_| concat.take_or_clone()),
|
||||
expr.start_position(),
|
||||
)
|
||||
self.check_return_value(result, expr.start_position())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Array(x, ..) => {
|
||||
let mut array = crate::Array::with_capacity(x.len());
|
||||
let mut result = Ok(Dynamic::UNIT);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let mut sizes = (0, 0, 0);
|
||||
let mut total_data_sizes = (0, 0, 0);
|
||||
|
||||
for item_expr in &**x {
|
||||
let value = match self
|
||||
.eval_expr(scope, global, caches, lib, this_ptr, item_expr, level)
|
||||
{
|
||||
Ok(r) => r.flatten(),
|
||||
err => {
|
||||
result = err;
|
||||
break;
|
||||
}
|
||||
};
|
||||
x.iter()
|
||||
.try_fold(
|
||||
crate::Array::with_capacity(x.len()),
|
||||
|mut array, item_expr| {
|
||||
let value = self
|
||||
.eval_expr(global, caches, lib, scope, this_ptr, item_expr)?
|
||||
.flatten();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let val_sizes = Self::calc_data_sizes(&value, true);
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if self.has_data_size_limit() {
|
||||
let val_sizes = Self::calc_data_sizes(&value, true);
|
||||
|
||||
array.push(value);
|
||||
total_data_sizes = (
|
||||
total_data_sizes.0 + val_sizes.0,
|
||||
total_data_sizes.1 + val_sizes.1,
|
||||
total_data_sizes.2 + val_sizes.2,
|
||||
);
|
||||
self.raise_err_if_over_data_size_limit(total_data_sizes)
|
||||
.map_err(|err| err.fill_position(item_expr.position()))?;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if self.has_data_size_limit() {
|
||||
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())?;
|
||||
}
|
||||
}
|
||||
array.push(value);
|
||||
|
||||
result.map(|_| array.into())
|
||||
Ok(array)
|
||||
},
|
||||
)
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Map(x, ..) => {
|
||||
let mut map = x.1.clone();
|
||||
let mut result = Ok(Dynamic::UNIT);
|
||||
|
||||
#[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 {
|
||||
let value = match self
|
||||
.eval_expr(scope, global, caches, lib, this_ptr, value_expr, level)
|
||||
{
|
||||
Ok(r) => r.flatten(),
|
||||
err => {
|
||||
result = err;
|
||||
break;
|
||||
x.0.iter()
|
||||
.try_fold(x.1.clone(), |mut map, (key, value_expr)| {
|
||||
let value = self
|
||||
.eval_expr(global, caches, lib, scope, this_ptr, value_expr)?
|
||||
.flatten();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if self.has_data_size_limit() {
|
||||
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)
|
||||
.map_err(|err| err.fill_position(value_expr.position()))?;
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let delta = Self::calc_data_sizes(&value, true);
|
||||
*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())
|
||||
Ok(map)
|
||||
})
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
Expr::And(x, ..) => {
|
||||
let lhs = self
|
||||
.eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level)
|
||||
.and_then(|v| {
|
||||
v.as_bool().map_err(|typ| {
|
||||
self.make_type_mismatch_err::<bool>(typ, x.lhs.position())
|
||||
})
|
||||
});
|
||||
Expr::And(x, ..) => Ok((self
|
||||
.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
|
||||
&& self
|
||||
.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.rhs.position()))?)
|
||||
.into()),
|
||||
|
||||
if let Ok(true) = lhs {
|
||||
self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level)
|
||||
.and_then(|v| {
|
||||
v.as_bool()
|
||||
.map_err(|typ| {
|
||||
self.make_type_mismatch_err::<bool>(typ, x.rhs.position())
|
||||
})
|
||||
.map(Into::into)
|
||||
})
|
||||
} else {
|
||||
lhs.map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Or(x, ..) => {
|
||||
let lhs = self
|
||||
.eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level)
|
||||
.and_then(|v| {
|
||||
v.as_bool().map_err(|typ| {
|
||||
self.make_type_mismatch_err::<bool>(typ, x.lhs.position())
|
||||
})
|
||||
});
|
||||
|
||||
if let Ok(false) = lhs {
|
||||
self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level)
|
||||
.and_then(|v| {
|
||||
v.as_bool()
|
||||
.map_err(|typ| {
|
||||
self.make_type_mismatch_err::<bool>(typ, x.rhs.position())
|
||||
})
|
||||
.map(Into::into)
|
||||
})
|
||||
} else {
|
||||
lhs.map(Into::into)
|
||||
}
|
||||
}
|
||||
Expr::Or(x, ..) => Ok((self
|
||||
.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
|
||||
|| self
|
||||
.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)?
|
||||
.as_bool()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.rhs.position()))?)
|
||||
.into()),
|
||||
|
||||
Expr::Coalesce(x, ..) => {
|
||||
let lhs = self.eval_expr(scope, global, caches, lib, this_ptr, &x.lhs, level);
|
||||
let value = self.eval_expr(global, caches, lib, scope, this_ptr, &x.lhs)?;
|
||||
|
||||
match lhs {
|
||||
Ok(value) if value.is::<()>() => {
|
||||
self.eval_expr(scope, global, caches, lib, this_ptr, &x.rhs, level)
|
||||
}
|
||||
Ok(_) | Err(_) => lhs,
|
||||
if value.is::<()>() {
|
||||
self.eval_expr(global, caches, lib, scope, this_ptr, &x.rhs)
|
||||
} else {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,8 +415,7 @@ impl Engine {
|
||||
*pos,
|
||||
))
|
||||
})?;
|
||||
let mut context =
|
||||
EvalContext::new(self, scope, global, Some(caches), lib, this_ptr, level);
|
||||
let mut context = EvalContext::new(self, global, caches, lib, scope, this_ptr);
|
||||
|
||||
let result = (custom_def.func)(&mut context, &expressions, &custom.state);
|
||||
|
||||
@@ -610,26 +423,19 @@ impl Engine {
|
||||
}
|
||||
|
||||
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
||||
Expr::Stmt(x) => {
|
||||
self.eval_stmt_block(scope, global, caches, lib, this_ptr, x, true, level)
|
||||
}
|
||||
Expr::Stmt(x) => self.eval_stmt_block(global, caches, lib, scope, this_ptr, x, true),
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(..) => {
|
||||
self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None)
|
||||
self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(..) => {
|
||||
self.eval_dot_index_chain(scope, global, caches, lib, this_ptr, expr, level, None)
|
||||
self.eval_dot_index_chain(global, caches, lib, scope, this_ptr, expr, &mut None)
|
||||
}
|
||||
|
||||
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
||||
};
|
||||
|
||||
#[cfg(feature = "debugging")]
|
||||
global.debugger.reset_status(reset_debugger);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//! Global runtime state.
|
||||
|
||||
use crate::{Dynamic, Engine, Identifier};
|
||||
use crate::{Dynamic, Engine, ImmutableString};
|
||||
use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{fmt, marker::PhantomData};
|
||||
|
||||
/// Collection of globally-defined constants.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub type GlobalConstants =
|
||||
crate::Shared<crate::Locked<std::collections::BTreeMap<crate::ImmutableString, Dynamic>>>;
|
||||
crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
|
||||
|
||||
/// _(internals)_ Global runtime states.
|
||||
/// Exported under the `internals` feature only.
|
||||
@@ -22,21 +22,24 @@ pub type GlobalConstants =
|
||||
// Most usage will be looking up a particular key from the list and then getting the module that
|
||||
// corresponds to that key.
|
||||
#[derive(Clone)]
|
||||
pub struct GlobalRuntimeState<'a> {
|
||||
/// Stack of module names.
|
||||
pub struct GlobalRuntimeState {
|
||||
/// Names of imported [modules][crate::Module].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
keys: crate::StaticVec<crate::ImmutableString>,
|
||||
imports: crate::StaticVec<ImmutableString>,
|
||||
/// Stack of imported [modules][crate::Module].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
modules: crate::StaticVec<crate::Shared<crate::Module>>,
|
||||
modules: crate::StaticVec<crate::SharedModule>,
|
||||
/// Source of the current context.
|
||||
///
|
||||
/// No source if the string is empty.
|
||||
pub source: Identifier,
|
||||
pub source: Option<ImmutableString>,
|
||||
/// Number of operations performed.
|
||||
pub num_operations: u64,
|
||||
/// Number of modules loaded.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub num_modules_loaded: usize,
|
||||
/// The current nesting level of function calls.
|
||||
pub level: usize,
|
||||
/// Level of the current scope.
|
||||
///
|
||||
/// The global (root) level is zero, a new block (or function call) is one level higher, and so on.
|
||||
@@ -69,24 +72,24 @@ pub struct GlobalRuntimeState<'a> {
|
||||
/// Debugging interface.
|
||||
#[cfg(feature = "debugging")]
|
||||
pub debugger: super::Debugger,
|
||||
/// Take care of the lifetime parameter.
|
||||
dummy: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl GlobalRuntimeState<'_> {
|
||||
impl GlobalRuntimeState {
|
||||
/// Create a new [`GlobalRuntimeState`] based on an [`Engine`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(engine: &Engine) -> Self {
|
||||
Self {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
keys: crate::StaticVec::new_const(),
|
||||
imports: crate::StaticVec::new_const(),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
modules: crate::StaticVec::new_const(),
|
||||
source: Identifier::new_const(),
|
||||
source: None,
|
||||
num_operations: 0,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
num_modules_loaded: 0,
|
||||
scope_level: 0,
|
||||
level: 0,
|
||||
always_search_scope: false,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
embedded_module_resolver: None,
|
||||
@@ -105,14 +108,11 @@ impl GlobalRuntimeState<'_> {
|
||||
} else {
|
||||
crate::eval::DebuggerStatus::CONTINUE
|
||||
},
|
||||
if let Some((ref init, ..)) = engine.debugger {
|
||||
init(engine)
|
||||
} else {
|
||||
Dynamic::UNIT
|
||||
match engine.debugger {
|
||||
Some((ref init, ..)) => init(engine),
|
||||
None => Dynamic::UNIT,
|
||||
},
|
||||
),
|
||||
|
||||
dummy: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
/// Get the length of the stack of globally-imported [modules][crate::Module].
|
||||
@@ -122,7 +122,7 @@ impl GlobalRuntimeState<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn num_imports(&self) -> usize {
|
||||
self.keys.len()
|
||||
self.modules.len()
|
||||
}
|
||||
/// Get the globally-imported [module][crate::Module] at a particular index.
|
||||
///
|
||||
@@ -130,7 +130,7 @@ impl GlobalRuntimeState<'_> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_shared_import(&self, index: usize) -> Option<crate::Shared<crate::Module>> {
|
||||
pub fn get_shared_import(&self, index: usize) -> Option<crate::SharedModule> {
|
||||
self.modules.get(index).cloned()
|
||||
}
|
||||
/// Get a mutable reference to the globally-imported [module][crate::Module] at a
|
||||
@@ -144,7 +144,7 @@ impl GlobalRuntimeState<'_> {
|
||||
pub(crate) fn get_shared_import_mut(
|
||||
&mut self,
|
||||
index: usize,
|
||||
) -> Option<&mut crate::Shared<crate::Module>> {
|
||||
) -> Option<&mut crate::SharedModule> {
|
||||
self.modules.get_mut(index)
|
||||
}
|
||||
/// Get the index of a globally-imported [module][crate::Module] by name.
|
||||
@@ -154,13 +154,11 @@ impl GlobalRuntimeState<'_> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn find_import(&self, name: &str) -> Option<usize> {
|
||||
let len = self.keys.len();
|
||||
|
||||
self.keys
|
||||
self.imports
|
||||
.iter()
|
||||
.rev()
|
||||
.position(|key| key.as_str() == name)
|
||||
.map(|i| len - 1 - i)
|
||||
.map(|i| self.imports.len() - 1 - i)
|
||||
}
|
||||
/// Push an imported [module][crate::Module] onto the stack.
|
||||
///
|
||||
@@ -169,10 +167,10 @@ impl GlobalRuntimeState<'_> {
|
||||
#[inline(always)]
|
||||
pub fn push_import(
|
||||
&mut self,
|
||||
name: impl Into<crate::ImmutableString>,
|
||||
module: impl Into<crate::Shared<crate::Module>>,
|
||||
name: impl Into<ImmutableString>,
|
||||
module: impl Into<crate::SharedModule>,
|
||||
) {
|
||||
self.keys.push(name.into());
|
||||
self.imports.push(name.into());
|
||||
self.modules.push(module.into());
|
||||
}
|
||||
/// Truncate the stack of globally-imported [modules][crate::Module] to a particular length.
|
||||
@@ -181,43 +179,51 @@ impl GlobalRuntimeState<'_> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub fn truncate_imports(&mut self, size: usize) {
|
||||
self.keys.truncate(size);
|
||||
self.imports.truncate(size);
|
||||
self.modules.truncate(size);
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
||||
self.keys
|
||||
self.imports
|
||||
.iter()
|
||||
.zip(self.modules.iter())
|
||||
.rev()
|
||||
.zip(self.modules.iter().rev())
|
||||
.map(|(name, module)| (name.as_str(), &**module))
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn iter_imports_raw(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
||||
self.keys.iter().rev().zip(self.modules.iter().rev())
|
||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||
self.imports.iter().zip(self.modules.iter()).rev()
|
||||
}
|
||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub fn scan_imports_raw(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
||||
self.keys.iter().zip(self.modules.iter())
|
||||
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||
self.imports.iter().zip(self.modules.iter())
|
||||
}
|
||||
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
||||
/// globally-imported [modules][crate::Module]?
|
||||
///
|
||||
/// Not available under `no_module`.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
|
||||
self.modules
|
||||
.iter()
|
||||
.any(|m| m.may_contain_dynamic_fn(hash_script))
|
||||
}
|
||||
/// Does the specified function hash key exist in the stack of globally-imported
|
||||
/// [modules][crate::Module]?
|
||||
@@ -240,11 +246,11 @@ impl GlobalRuntimeState<'_> {
|
||||
pub fn get_qualified_fn(
|
||||
&self,
|
||||
hash: u64,
|
||||
) -> Option<(&crate::func::CallableFunction, Option<&str>)> {
|
||||
) -> Option<(&crate::func::CallableFunction, Option<&ImmutableString>)> {
|
||||
self.modules
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id())))
|
||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||
}
|
||||
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
|
||||
/// globally-imported [modules][crate::Module]?
|
||||
@@ -271,14 +277,17 @@ impl GlobalRuntimeState<'_> {
|
||||
.find_map(|m| m.get_qualified_iter(id))
|
||||
}
|
||||
/// Get the current source.
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn source(&self) -> Option<&str> {
|
||||
if self.source.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.source.as_str())
|
||||
}
|
||||
self.source.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
/// Get the current source.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
|
||||
self.source.as_ref()
|
||||
}
|
||||
/// Get the pre-calculated index getter hash.
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
@@ -309,67 +318,64 @@ impl GlobalRuntimeState<'_> {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
impl IntoIterator for GlobalRuntimeState<'_> {
|
||||
type Item = (crate::ImmutableString, crate::Shared<crate::Module>);
|
||||
type IntoIter = std::iter::Zip<
|
||||
std::iter::Rev<smallvec::IntoIter<[crate::ImmutableString; 3]>>,
|
||||
std::iter::Rev<smallvec::IntoIter<[crate::Shared<crate::Module>; 3]>>,
|
||||
impl IntoIterator for GlobalRuntimeState {
|
||||
type Item = (ImmutableString, crate::SharedModule);
|
||||
type IntoIter = std::iter::Rev<
|
||||
std::iter::Zip<
|
||||
smallvec::IntoIter<[ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>,
|
||||
smallvec::IntoIter<[crate::SharedModule; crate::STATIC_VEC_INLINE_SIZE]>,
|
||||
>,
|
||||
>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.keys
|
||||
.into_iter()
|
||||
.rev()
|
||||
.zip(self.modules.into_iter().rev())
|
||||
self.imports.into_iter().zip(self.modules.into_iter()).rev()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
||||
type Item = (&'a crate::ImmutableString, &'a crate::Shared<crate::Module>);
|
||||
type IntoIter = std::iter::Zip<
|
||||
std::iter::Rev<std::slice::Iter<'a, crate::ImmutableString>>,
|
||||
std::iter::Rev<std::slice::Iter<'a, crate::Shared<crate::Module>>>,
|
||||
impl<'a> IntoIterator for &'a GlobalRuntimeState {
|
||||
type Item = (&'a ImmutableString, &'a crate::SharedModule);
|
||||
type IntoIter = std::iter::Rev<
|
||||
std::iter::Zip<
|
||||
std::slice::Iter<'a, ImmutableString>,
|
||||
std::slice::Iter<'a, crate::SharedModule>,
|
||||
>,
|
||||
>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
let x = self.keys.iter().rev().zip(self.modules.iter().rev());
|
||||
x
|
||||
self.imports.iter().zip(self.modules.iter()).rev()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
impl<K: Into<crate::ImmutableString>, M: Into<crate::Shared<crate::Module>>> Extend<(K, M)>
|
||||
for GlobalRuntimeState<'_>
|
||||
{
|
||||
impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
||||
for (k, m) in iter {
|
||||
self.keys.push(k.into());
|
||||
self.imports.push(k.into());
|
||||
self.modules.push(m.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for GlobalRuntimeState<'_> {
|
||||
#[inline]
|
||||
impl fmt::Debug for GlobalRuntimeState {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut f = f.debug_struct("GlobalRuntimeState");
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
f.field("imports", &self.keys.iter().zip(self.modules.iter()));
|
||||
f.field("imports", &self.scan_imports_raw().collect::<Vec<_>>());
|
||||
|
||||
f.field("source", &self.source)
|
||||
.field("num_operations", &self.num_operations)
|
||||
.field("num_modules_loaded", &self.num_modules_loaded);
|
||||
.field("num_operations", &self.num_operations);
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
f.field("fn_hash_indexing", &self.fn_hash_indexing);
|
||||
|
||||
#[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_function"))]
|
||||
|
||||
@@ -22,3 +22,36 @@ pub use eval_context::EvalContext;
|
||||
pub use global_state::GlobalConstants;
|
||||
pub use global_state::GlobalRuntimeState;
|
||||
pub use target::{calc_index, calc_offset_len, Target};
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
mod unchecked {
|
||||
use crate::{eval::GlobalRuntimeState, Dynamic, Engine, Position, RhaiResult, RhaiResultOf};
|
||||
|
||||
impl Engine {
|
||||
/// Check if the number of operations stay within limit.
|
||||
#[inline(always)]
|
||||
pub(crate) const fn track_operation(
|
||||
&self,
|
||||
_: &GlobalRuntimeState,
|
||||
_: Position,
|
||||
) -> RhaiResultOf<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check whether the size of a [`Dynamic`] is within limits.
|
||||
#[inline(always)]
|
||||
pub(crate) const fn check_data_size(&self, _: &Dynamic, _: Position) -> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1134
src/eval/stmt.rs
1134
src/eval/stmt.rs
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,12 @@
|
||||
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{Dynamic, Position, RhaiResultOf};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
// Calculate an offset+len pair given an actual length of the underlying array.
|
||||
//
|
||||
@@ -416,11 +419,20 @@ impl Deref for Target<'_> {
|
||||
|
||||
impl AsRef<Dynamic> for Target<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &Dynamic {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<Dynamic> for Target<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &Dynamic {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Target<'_> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Dynamic {
|
||||
@@ -440,6 +452,7 @@ impl DerefMut for Target<'_> {
|
||||
|
||||
impl AsMut<Dynamic> for Target<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_mut(&mut self) -> &mut Dynamic {
|
||||
self
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
use super::call::FnCallArgs;
|
||||
use super::native::FnBuiltin;
|
||||
use crate::engine::OP_CONTAINS;
|
||||
use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT};
|
||||
use crate::tokenizer::{Token, Token::*};
|
||||
use crate::{
|
||||
Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT,
|
||||
};
|
||||
use std::any::TypeId;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -63,11 +65,24 @@ fn is_numeric(type_id: TypeId) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// A function that returns `true`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::TRUE)
|
||||
}
|
||||
/// A function that returns `false`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
Ok(Dynamic::FALSE)
|
||||
}
|
||||
|
||||
/// Build in common binary operator implementations to avoid the cost of calling a registered function.
|
||||
///
|
||||
/// The return function will be registered as a _method_, so the first parameter cannot be consumed.
|
||||
#[must_use]
|
||||
pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
let type1 = x.type_id();
|
||||
let type2 = y.type_id();
|
||||
|
||||
@@ -131,46 +146,46 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op {
|
||||
"+" => return Some(impl_op!(INT => add(as_int, as_int))),
|
||||
"-" => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
||||
"*" => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
||||
"/" => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
"%" => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
"**" => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
"<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
Plus => return Some(impl_op!(INT => add(as_int, as_int))),
|
||||
Minus => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
||||
Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
||||
Divide => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
PowerOf => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
match op {
|
||||
"+" => return Some(impl_op!(INT => as_int + as_int)),
|
||||
"-" => return Some(impl_op!(INT => as_int - as_int)),
|
||||
"*" => return Some(impl_op!(INT => as_int * as_int)),
|
||||
"/" => return Some(impl_op!(INT => as_int / as_int)),
|
||||
"%" => return Some(impl_op!(INT => as_int % as_int)),
|
||||
"**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
">>" => return Some(impl_op!(INT => as_int >> as_int)),
|
||||
"<<" => return Some(impl_op!(INT => as_int << as_int)),
|
||||
Plus => return Some(impl_op!(INT => as_int + as_int)),
|
||||
Minus => return Some(impl_op!(INT => as_int - as_int)),
|
||||
Multiply => return Some(impl_op!(INT => as_int * as_int)),
|
||||
Divide => return Some(impl_op!(INT => as_int / as_int)),
|
||||
Modulo => return Some(impl_op!(INT => as_int % as_int)),
|
||||
PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
RightShift => return Some(impl_op!(INT => as_int >> as_int)),
|
||||
LeftShift => return Some(impl_op!(INT => as_int << as_int)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
return match op {
|
||||
"==" => Some(impl_op!(INT => as_int == as_int)),
|
||||
"!=" => Some(impl_op!(INT => as_int != as_int)),
|
||||
">" => Some(impl_op!(INT => as_int > as_int)),
|
||||
">=" => Some(impl_op!(INT => as_int >= as_int)),
|
||||
"<" => Some(impl_op!(INT => as_int < as_int)),
|
||||
"<=" => Some(impl_op!(INT => as_int <= as_int)),
|
||||
"&" => Some(impl_op!(INT => as_int & as_int)),
|
||||
"|" => Some(impl_op!(INT => as_int | as_int)),
|
||||
"^" => Some(impl_op!(INT => as_int ^ as_int)),
|
||||
".." => Some(|_, args| {
|
||||
EqualsTo => Some(impl_op!(INT => as_int == as_int)),
|
||||
NotEqualsTo => Some(impl_op!(INT => as_int != as_int)),
|
||||
GreaterThan => Some(impl_op!(INT => as_int > as_int)),
|
||||
GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)),
|
||||
LessThan => Some(impl_op!(INT => as_int < as_int)),
|
||||
LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)),
|
||||
Ampersand => Some(impl_op!(INT => as_int & as_int)),
|
||||
Pipe => Some(impl_op!(INT => as_int | as_int)),
|
||||
XOr => Some(impl_op!(INT => as_int ^ as_int)),
|
||||
ExclusiveRange => Some(|_, args| {
|
||||
let x = args[0].as_int().expect(BUILTIN);
|
||||
let y = args[1].as_int().expect(BUILTIN);
|
||||
Ok((x..y).into())
|
||||
}),
|
||||
"..=" => Some(|_, args| {
|
||||
InclusiveRange => Some(|_, args| {
|
||||
let x = args[0].as_int().expect(BUILTIN);
|
||||
let y = args[1].as_int().expect(BUILTIN);
|
||||
Ok((x..=y).into())
|
||||
@@ -181,47 +196,65 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
|
||||
if type1 == TypeId::of::<bool>() {
|
||||
return match op {
|
||||
"==" => Some(impl_op!(bool => as_bool == as_bool)),
|
||||
"!=" => Some(impl_op!(bool => as_bool != as_bool)),
|
||||
">" => Some(impl_op!(bool => as_bool > as_bool)),
|
||||
">=" => Some(impl_op!(bool => as_bool >= as_bool)),
|
||||
"<" => Some(impl_op!(bool => as_bool < as_bool)),
|
||||
"<=" => Some(impl_op!(bool => as_bool <= as_bool)),
|
||||
"&" => Some(impl_op!(bool => as_bool & as_bool)),
|
||||
"|" => Some(impl_op!(bool => as_bool | as_bool)),
|
||||
"^" => Some(impl_op!(bool => as_bool ^ as_bool)),
|
||||
EqualsTo => Some(impl_op!(bool => as_bool == as_bool)),
|
||||
NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)),
|
||||
GreaterThan => Some(impl_op!(bool => as_bool > as_bool)),
|
||||
GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)),
|
||||
LessThan => Some(impl_op!(bool => as_bool < as_bool)),
|
||||
LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)),
|
||||
Ampersand => Some(impl_op!(bool => as_bool & as_bool)),
|
||||
Pipe => Some(impl_op!(bool => as_bool | as_bool)),
|
||||
XOr => Some(impl_op!(bool => as_bool ^ as_bool)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<ImmutableString>() {
|
||||
return match op {
|
||||
"+" => Some(impl_op!(ImmutableString + ImmutableString)),
|
||||
"-" => Some(impl_op!(ImmutableString - ImmutableString)),
|
||||
"==" => Some(impl_op!(ImmutableString == ImmutableString)),
|
||||
"!=" => Some(impl_op!(ImmutableString != ImmutableString)),
|
||||
">" => Some(impl_op!(ImmutableString > ImmutableString)),
|
||||
">=" => Some(impl_op!(ImmutableString >= ImmutableString)),
|
||||
"<" => Some(impl_op!(ImmutableString < ImmutableString)),
|
||||
"<=" => Some(impl_op!(ImmutableString <= ImmutableString)),
|
||||
OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))),
|
||||
Plus => Some(|_ctx, args| {
|
||||
let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if !s1.is_empty() && !s2.is_empty() {
|
||||
let total_len = s1.len() + s2.len();
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, total_len))?;
|
||||
}
|
||||
|
||||
Ok((s1 + s2).into())
|
||||
}),
|
||||
Minus => Some(impl_op!(ImmutableString - ImmutableString)),
|
||||
EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)),
|
||||
NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)),
|
||||
GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)),
|
||||
GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)),
|
||||
LessThan => Some(impl_op!(ImmutableString < ImmutableString)),
|
||||
LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<char>() {
|
||||
return match op {
|
||||
"+" => Some(|_, args| {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let x = args[0].as_char().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
Ok(format!("{x}{y}").into())
|
||||
|
||||
let result = format!("{x}{y}");
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
||||
|
||||
Ok(result.into())
|
||||
}),
|
||||
"==" => Some(impl_op!(char => as_char == as_char)),
|
||||
"!=" => Some(impl_op!(char => as_char != as_char)),
|
||||
">" => Some(impl_op!(char => as_char > as_char)),
|
||||
">=" => Some(impl_op!(char => as_char >= as_char)),
|
||||
"<" => Some(impl_op!(char => as_char < as_char)),
|
||||
"<=" => Some(impl_op!(char => as_char <= as_char)),
|
||||
EqualsTo => Some(impl_op!(char => as_char == as_char)),
|
||||
NotEqualsTo => Some(impl_op!(char => as_char != as_char)),
|
||||
GreaterThan => Some(impl_op!(char => as_char > as_char)),
|
||||
GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)),
|
||||
LessThan => Some(impl_op!(char => as_char < as_char)),
|
||||
LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -231,7 +264,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
use crate::Blob;
|
||||
|
||||
return match op {
|
||||
"+" => Some(|_, args| {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN);
|
||||
let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN);
|
||||
|
||||
@@ -240,21 +273,30 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
} else if blob1.is_empty() {
|
||||
blob2.clone()
|
||||
} else {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob1.len() + blob2.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
|
||||
let mut blob = blob1.clone();
|
||||
blob.extend(blob2);
|
||||
blob
|
||||
}))
|
||||
}),
|
||||
"==" => Some(impl_op!(Blob == Blob)),
|
||||
"!=" => Some(impl_op!(Blob != Blob)),
|
||||
EqualsTo => Some(impl_op!(Blob == Blob)),
|
||||
NotEqualsTo => Some(impl_op!(Blob != Blob)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<()>() {
|
||||
return match op {
|
||||
"==" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||
"!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||
EqualsTo => Some(const_true_fn),
|
||||
NotEqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -265,19 +307,19 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||
return match op {
|
||||
"+" => Some(impl_op!(FLOAT => $xx + $yy)),
|
||||
"-" => Some(impl_op!(FLOAT => $xx - $yy)),
|
||||
"*" => Some(impl_op!(FLOAT => $xx * $yy)),
|
||||
"/" => Some(impl_op!(FLOAT => $xx / $yy)),
|
||||
"%" => Some(impl_op!(FLOAT => $xx % $yy)),
|
||||
"**" => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))),
|
||||
"==" => Some(impl_op!(FLOAT => $xx == $yy)),
|
||||
"!=" => Some(impl_op!(FLOAT => $xx != $yy)),
|
||||
">" => Some(impl_op!(FLOAT => $xx > $yy)),
|
||||
">=" => Some(impl_op!(FLOAT => $xx >= $yy)),
|
||||
"<" => Some(impl_op!(FLOAT => $xx < $yy)),
|
||||
"<=" => Some(impl_op!(FLOAT => $xx <= $yy)),
|
||||
_ => None,
|
||||
Plus => Some(impl_op!(FLOAT => $xx + $yy)),
|
||||
Minus => Some(impl_op!(FLOAT => $xx - $yy)),
|
||||
Multiply => Some(impl_op!(FLOAT => $xx * $yy)),
|
||||
Divide => Some(impl_op!(FLOAT => $xx / $yy)),
|
||||
Modulo => Some(impl_op!(FLOAT => $xx % $yy)),
|
||||
PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))),
|
||||
EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)),
|
||||
NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)),
|
||||
GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)),
|
||||
GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)),
|
||||
LessThan => Some(impl_op!(FLOAT => $xx < $yy)),
|
||||
LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -295,17 +337,17 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
|
||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
use crate::packages::arithmetic::decimal_functions::*;
|
||||
use crate::packages::arithmetic::decimal_functions::builtin::*;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op {
|
||||
"+" => return Some(impl_op!(from Decimal => add($xx, $yy))),
|
||||
"-" => return Some(impl_op!(from Decimal => subtract($xx, $yy))),
|
||||
"*" => return Some(impl_op!(from Decimal => multiply($xx, $yy))),
|
||||
"/" => return Some(impl_op!(from Decimal => divide($xx, $yy))),
|
||||
"%" => return Some(impl_op!(from Decimal => modulo($xx, $yy))),
|
||||
"**" => return Some(impl_op!(from Decimal => power($xx, $yy))),
|
||||
_ => ()
|
||||
Plus => return Some(impl_op!(from Decimal => add($xx, $yy))),
|
||||
Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))),
|
||||
Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))),
|
||||
Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))),
|
||||
Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))),
|
||||
PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
@@ -313,23 +355,23 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
match op {
|
||||
"+" => return Some(impl_op!(from Decimal => $xx + $yy)),
|
||||
"-" => return Some(impl_op!(from Decimal => $xx - $yy)),
|
||||
"*" => return Some(impl_op!(from Decimal => $xx * $yy)),
|
||||
"/" => return Some(impl_op!(from Decimal => $xx / $yy)),
|
||||
"%" => return Some(impl_op!(from Decimal => $xx % $yy)),
|
||||
"**" => return Some(impl_op!(from Decimal => $xx.powd($yy))),
|
||||
_ => ()
|
||||
Plus => return Some(impl_op!(from Decimal => $xx + $yy)),
|
||||
Minus => return Some(impl_op!(from Decimal => $xx - $yy)),
|
||||
Multiply => return Some(impl_op!(from Decimal => $xx * $yy)),
|
||||
Divide => return Some(impl_op!(from Decimal => $xx / $yy)),
|
||||
Modulo => return Some(impl_op!(from Decimal => $xx % $yy)),
|
||||
PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
return match op {
|
||||
"==" => Some(impl_op!(from Decimal => $xx == $yy)),
|
||||
"!=" => Some(impl_op!(from Decimal => $xx != $yy)),
|
||||
">" => Some(impl_op!(from Decimal => $xx > $yy)),
|
||||
">=" => Some(impl_op!(from Decimal => $xx >= $yy)),
|
||||
"<" => Some(impl_op!(from Decimal => $xx < $yy)),
|
||||
"<=" => Some(impl_op!(from Decimal => $xx <= $yy)),
|
||||
_ => None
|
||||
EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)),
|
||||
NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)),
|
||||
GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)),
|
||||
GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)),
|
||||
LessThan => Some(impl_op!(from Decimal => $xx < $yy)),
|
||||
LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)),
|
||||
_ => None
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -354,17 +396,23 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
}
|
||||
|
||||
return match op {
|
||||
"+" => Some(|_, args| {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let x = args[0].as_char().expect(BUILTIN);
|
||||
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
Ok(format!("{x}{y}").into())
|
||||
let result = format!("{x}{y}");
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
||||
|
||||
Ok(result.into())
|
||||
}),
|
||||
"==" => Some(impl_op!(get_s1s2(==))),
|
||||
"!=" => Some(impl_op!(get_s1s2(!=))),
|
||||
">" => Some(impl_op!(get_s1s2(>))),
|
||||
">=" => Some(impl_op!(get_s1s2(>=))),
|
||||
"<" => Some(impl_op!(get_s1s2(<))),
|
||||
"<=" => Some(impl_op!(get_s1s2(<=))),
|
||||
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
||||
GreaterThan => Some(impl_op!(get_s1s2(>))),
|
||||
GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))),
|
||||
LessThan => Some(impl_op!(get_s1s2(<))),
|
||||
LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -380,45 +428,50 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
}
|
||||
|
||||
return match op {
|
||||
"+" => Some(|_, args| {
|
||||
Plus => Some(|_ctx, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
Ok((x + y).into())
|
||||
let result = x + y;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, result.len()))?;
|
||||
|
||||
Ok(result.into())
|
||||
}),
|
||||
"-" => Some(|_, args| {
|
||||
Minus => Some(|_, args| {
|
||||
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
Ok((x - y).into())
|
||||
}),
|
||||
"==" => Some(impl_op!(get_s1s2(==))),
|
||||
"!=" => Some(impl_op!(get_s1s2(!=))),
|
||||
">" => Some(impl_op!(get_s1s2(>))),
|
||||
">=" => Some(impl_op!(get_s1s2(>=))),
|
||||
"<" => Some(impl_op!(get_s1s2(<))),
|
||||
"<=" => Some(impl_op!(get_s1s2(<=))),
|
||||
OP_CONTAINS => Some(|_, args| {
|
||||
let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let c = args[1].as_char().expect(BUILTIN);
|
||||
Ok(s.contains(c).into())
|
||||
}),
|
||||
EqualsTo => Some(impl_op!(get_s1s2(==))),
|
||||
NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
|
||||
GreaterThan => Some(impl_op!(get_s1s2(>))),
|
||||
GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))),
|
||||
LessThan => Some(impl_op!(get_s1s2(<))),
|
||||
LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
// () op string
|
||||
if (type1, type2) == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) {
|
||||
return match op {
|
||||
"+" => Some(|_, args| Ok(args[1].clone())),
|
||||
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||
Plus => Some(|_, args| Ok(args[1].clone())),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
}
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
// string op ()
|
||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) {
|
||||
return match op {
|
||||
"+" => Some(|_, args| Ok(args[0].clone())),
|
||||
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||
Plus => Some(|_, args| Ok(args[0].clone())),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
}
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -428,22 +481,20 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
if type1 == TypeId::of::<crate::Blob>() {
|
||||
use crate::Blob;
|
||||
|
||||
if type2 == TypeId::of::<INT>() {
|
||||
return match op {
|
||||
OP_CONTAINS => Some(|_, args| {
|
||||
let blob = &*args[0].read_lock::<Blob>().expect(BUILTIN);
|
||||
let x = (args[1].as_int().expect("`INT`") & 0x0000_00ff) as u8;
|
||||
Ok((!blob.is_empty() && blob.contains(&x)).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if type2 == TypeId::of::<char>() {
|
||||
return match op {
|
||||
"+" => Some(|_, args| {
|
||||
let mut buf = [0_u8; 4];
|
||||
Plus => Some(|_ctx, args| {
|
||||
let mut blob = args[0].read_lock::<Blob>().expect(BUILTIN).clone();
|
||||
let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf);
|
||||
let mut buf = [0_u8; 4];
|
||||
let x = args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob.len() + x.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
|
||||
blob.extend(x.as_bytes());
|
||||
Ok(Dynamic::from_blob(blob))
|
||||
}),
|
||||
@@ -452,17 +503,6 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
}
|
||||
}
|
||||
|
||||
// map op string
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if (type1, type2) == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) {
|
||||
use crate::Map;
|
||||
|
||||
return match op {
|
||||
OP_CONTAINS => Some(impl_op!(Map.contains_key(ImmutableString.as_str()))),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
// Non-compatible ranges
|
||||
if (type1, type2)
|
||||
== (
|
||||
@@ -476,48 +516,28 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
)
|
||||
{
|
||||
return match op {
|
||||
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||
"==" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
Equals => Some(const_false_fn),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle ranges here because ranges are implemented as custom type
|
||||
if type1 == TypeId::of::<ExclusiveRange>() {
|
||||
if type2 == TypeId::of::<INT>() {
|
||||
return match op {
|
||||
OP_CONTAINS => Some(|_, args| {
|
||||
let range = &*args[0].read_lock::<ExclusiveRange>().expect(BUILTIN);
|
||||
let x = args[1].as_int().expect("`INT`");
|
||||
Ok(range.contains(&x).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if type1 == type2 {
|
||||
return match op {
|
||||
"==" => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
|
||||
"!=" => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
|
||||
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
|
||||
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<InclusiveRange>() {
|
||||
if type2 == TypeId::of::<INT>() {
|
||||
return match op {
|
||||
OP_CONTAINS => Some(|_, args| {
|
||||
let range = &*args[0].read_lock::<InclusiveRange>().expect(BUILTIN);
|
||||
let x = args[1].as_int().expect("`INT`");
|
||||
Ok(range.contains(&x).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if type1 == type2 {
|
||||
return match op {
|
||||
"==" => Some(impl_op!(InclusiveRange == InclusiveRange)),
|
||||
"!=" => Some(impl_op!(InclusiveRange != InclusiveRange)),
|
||||
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
|
||||
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -531,8 +551,10 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
} else if type1 != type2 {
|
||||
// If the types are not the same, default to not compare
|
||||
match op {
|
||||
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
@@ -544,8 +566,10 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
// Default comparison operators for different types
|
||||
if type2 != type1 {
|
||||
return match op {
|
||||
"!=" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
|
||||
NotEqualsTo => Some(const_true_fn),
|
||||
EqualsTo | GreaterThan | GreaterThanEqualsTo | LessThan | LessThanEqualsTo => {
|
||||
Some(const_false_fn)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@@ -558,7 +582,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
||||
///
|
||||
/// The return function is registered as a _method_, so the first parameter cannot be consumed.
|
||||
#[must_use]
|
||||
pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||
let type1 = x.type_id();
|
||||
let type2 = y.type_id();
|
||||
|
||||
@@ -610,49 +634,49 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
match op {
|
||||
"+=" => return Some(impl_op!(INT => add(as_int, as_int))),
|
||||
"-=" => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
||||
"*=" => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
||||
"/=" => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
"%=" => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
"**=" => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
"<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))),
|
||||
MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))),
|
||||
MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))),
|
||||
DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))),
|
||||
ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))),
|
||||
PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))),
|
||||
RightShiftAssign => return Some(impl_op!(INT => shift_right(as_int, as_int))),
|
||||
LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
match op {
|
||||
"+=" => return Some(impl_op!(INT += as_int)),
|
||||
"-=" => return Some(impl_op!(INT -= as_int)),
|
||||
"*=" => return Some(impl_op!(INT *= as_int)),
|
||||
"/=" => return Some(impl_op!(INT /= as_int)),
|
||||
"%=" => return Some(impl_op!(INT %= as_int)),
|
||||
"**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
">>=" => return Some(impl_op!(INT >>= as_int)),
|
||||
"<<=" => return Some(impl_op!(INT <<= as_int)),
|
||||
PlusAssign => return Some(impl_op!(INT += as_int)),
|
||||
MinusAssign => return Some(impl_op!(INT -= as_int)),
|
||||
MultiplyAssign => return Some(impl_op!(INT *= as_int)),
|
||||
DivideAssign => return Some(impl_op!(INT /= as_int)),
|
||||
ModuloAssign => return Some(impl_op!(INT %= as_int)),
|
||||
PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
|
||||
RightShiftAssign => return Some(impl_op!(INT >>= as_int)),
|
||||
LeftShiftAssign => return Some(impl_op!(INT <<= as_int)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
return match op {
|
||||
"&=" => Some(impl_op!(INT &= as_int)),
|
||||
"|=" => Some(impl_op!(INT |= as_int)),
|
||||
"^=" => Some(impl_op!(INT ^= as_int)),
|
||||
AndAssign => Some(impl_op!(INT &= as_int)),
|
||||
OrAssign => Some(impl_op!(INT |= as_int)),
|
||||
XOrAssign => Some(impl_op!(INT ^= as_int)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<bool>() {
|
||||
return match op {
|
||||
"&=" => Some(impl_op!(bool = x && as_bool)),
|
||||
"|=" => Some(impl_op!(bool = x || as_bool)),
|
||||
AndAssign => Some(impl_op!(bool = x && as_bool)),
|
||||
OrAssign => Some(impl_op!(bool = x || as_bool)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if type1 == TypeId::of::<char>() {
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
PlusAssign => Some(|_, args| {
|
||||
let y = args[1].as_char().expect(BUILTIN);
|
||||
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
||||
Ok((*x = format!("{x}{y}").into()).into())
|
||||
@@ -663,13 +687,21 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
|
||||
if type1 == TypeId::of::<ImmutableString>() {
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = std::mem::take(second[0]).cast::<ImmutableString>();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if !x.is_empty() && !y.is_empty() {
|
||||
let total_len = x.len() + y.len();
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, total_len))?;
|
||||
}
|
||||
|
||||
Ok((*x += y).into())
|
||||
}),
|
||||
"-=" => Some(|_, args| {
|
||||
MinusAssign => Some(|_, args| {
|
||||
let (first, second) = args.split_first_mut().expect(BUILTIN);
|
||||
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
|
||||
let y = std::mem::take(second[0]).cast::<ImmutableString>();
|
||||
@@ -679,15 +711,55 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if type1 == TypeId::of::<crate::Array>() {
|
||||
use crate::packages::array_basic::array_functions::*;
|
||||
use crate::Array;
|
||||
|
||||
return match op {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let x = std::mem::take(args[1]).cast::<Array>();
|
||||
|
||||
if x.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let _array_is_empty = args[0].read_lock::<Array>().expect(BUILTIN).is_empty();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if !_array_is_empty {
|
||||
_ctx.engine().check_data_size(
|
||||
&*args[0].read_lock().expect(BUILTIN),
|
||||
crate::Position::NONE,
|
||||
)?;
|
||||
}
|
||||
|
||||
let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
|
||||
|
||||
Ok(append(array, x).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if type1 == TypeId::of::<crate::Blob>() {
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
use crate::Blob;
|
||||
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let blob2 = std::mem::take(args[1]).cast::<Blob>();
|
||||
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||
Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into())
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob1.len() + blob2.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
|
||||
Ok(append(blob1, blob2).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
@@ -699,13 +771,13 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||
return match op {
|
||||
"+=" => Some(impl_op!($x += $yy)),
|
||||
"-=" => Some(impl_op!($x -= $yy)),
|
||||
"*=" => Some(impl_op!($x *= $yy)),
|
||||
"/=" => Some(impl_op!($x /= $yy)),
|
||||
"%=" => Some(impl_op!($x %= $yy)),
|
||||
"**=" => Some(impl_op!($x => $xx.powf($yy as $x))),
|
||||
_ => None,
|
||||
PlusAssign => Some(impl_op!($x += $yy)),
|
||||
MinusAssign => Some(impl_op!($x -= $yy)),
|
||||
MultiplyAssign => Some(impl_op!($x *= $yy)),
|
||||
DivideAssign => Some(impl_op!($x /= $yy)),
|
||||
ModuloAssign => Some(impl_op!($x %= $yy)),
|
||||
PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -722,17 +794,17 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
|
||||
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
use crate::packages::arithmetic::decimal_functions::*;
|
||||
use crate::packages::arithmetic::decimal_functions::builtin::*;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
return match op {
|
||||
"+=" => Some(impl_op!(from $x => add($xx, $yy))),
|
||||
"-=" => Some(impl_op!(from $x => subtract($xx, $yy))),
|
||||
"*=" => Some(impl_op!(from $x => multiply($xx, $yy))),
|
||||
"/=" => Some(impl_op!(from $x => divide($xx, $yy))),
|
||||
"%=" => Some(impl_op!(from $x => modulo($xx, $yy))),
|
||||
"**=" => Some(impl_op!(from $x => power($xx, $yy))),
|
||||
_ => None,
|
||||
PlusAssign => Some(impl_op!(from $x => add($xx, $yy))),
|
||||
MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))),
|
||||
MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))),
|
||||
DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))),
|
||||
ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))),
|
||||
PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
@@ -740,13 +812,13 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
|
||||
#[cfg(feature = "unchecked")]
|
||||
return match op {
|
||||
"+=" => Some(impl_op!(from $x += $yy)),
|
||||
"-=" => Some(impl_op!(from $x -= $yy)),
|
||||
"*=" => Some(impl_op!(from $x *= $yy)),
|
||||
"/=" => Some(impl_op!(from $x /= $yy)),
|
||||
"%=" => Some(impl_op!(from $x %= $yy)),
|
||||
"**=" => Some(impl_op!(from $x => $xx.powd($yy))),
|
||||
_ => None,
|
||||
PlusAssign => Some(impl_op!(from $x += $yy)),
|
||||
MinusAssign => Some(impl_op!(from $x -= $yy)),
|
||||
MultiplyAssign => Some(impl_op!(from $x *= $yy)),
|
||||
DivideAssign => Some(impl_op!(from $x /= $yy)),
|
||||
ModuloAssign => Some(impl_op!(from $x %= $yy)),
|
||||
PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -761,25 +833,45 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
// string op= char
|
||||
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
|
||||
return match op {
|
||||
"+=" => Some(impl_op!(ImmutableString += as_char as char)),
|
||||
"-=" => Some(impl_op!(ImmutableString -= as_char as char)),
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let mut buf = [0_u8; 4];
|
||||
let ch = &*args[1].as_char().expect(BUILTIN).encode_utf8(&mut buf);
|
||||
let mut x = args[0].write_lock::<ImmutableString>().expect(BUILTIN);
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, x.len() + ch.len()))?;
|
||||
|
||||
Ok((*x += ch).into())
|
||||
}),
|
||||
MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
// char op= string
|
||||
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
let mut ch = args[0].as_char().expect(BUILTIN).to_string();
|
||||
ch.push_str(
|
||||
args[1]
|
||||
.read_lock::<ImmutableString>()
|
||||
.expect(BUILTIN)
|
||||
.as_str(),
|
||||
);
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let ch = {
|
||||
let s = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
||||
|
||||
let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN);
|
||||
Ok((*x = ch.into()).into())
|
||||
if s.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let mut ch = args[0].as_char().expect(BUILTIN).to_string();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((0, 0, ch.len() + s.len()))?;
|
||||
|
||||
ch.push_str(s);
|
||||
ch
|
||||
};
|
||||
|
||||
*args[0].write_lock::<Dynamic>().expect(BUILTIN) = ch.into();
|
||||
|
||||
Ok(Dynamic::UNIT)
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
@@ -791,21 +883,21 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
use crate::packages::array_basic::array_functions::*;
|
||||
use crate::Array;
|
||||
|
||||
if type2 == TypeId::of::<crate::Array>() {
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
let array2 = std::mem::take(args[1]).cast::<Array>();
|
||||
let array1 = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
|
||||
Ok(append(array1, array2).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
let x = std::mem::take(args[1]);
|
||||
let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
|
||||
Ok(push(array, x).into())
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
{
|
||||
let x = std::mem::take(args[1]);
|
||||
let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
|
||||
push(array, x);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().check_data_size(
|
||||
&*args[0].read_lock().expect(BUILTIN),
|
||||
crate::Position::NONE,
|
||||
)?;
|
||||
|
||||
Ok(Dynamic::UNIT)
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
@@ -817,11 +909,18 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
|
||||
// blob op= int
|
||||
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
let x = args[1].as_int().expect("`INT`");
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let x = args[1].as_int().expect(BUILTIN);
|
||||
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||
Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into())
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?;
|
||||
|
||||
Ok(push(blob, x).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
@@ -829,11 +928,18 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
|
||||
// blob op= char
|
||||
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
let x = args[1].as_char().expect("`char`");
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let x = args[1].as_char().expect(BUILTIN);
|
||||
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||
Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into())
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((blob.len() + 1, 0, 0))?;
|
||||
|
||||
Ok(append_char(blob, x).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
@@ -841,11 +947,26 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
||||
|
||||
// blob op= string
|
||||
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
|
||||
use crate::packages::blob_basic::blob_functions::*;
|
||||
|
||||
return match op {
|
||||
"+=" => Some(|_, args| {
|
||||
PlusAssign => Some(|_ctx, args| {
|
||||
let s = std::mem::take(args[1]).cast::<ImmutableString>();
|
||||
|
||||
if s.is_empty() {
|
||||
return Ok(Dynamic::UNIT);
|
||||
}
|
||||
|
||||
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||
Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into())
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
_ctx.engine().raise_err_if_over_data_size_limit((
|
||||
blob.len() + s.len(),
|
||||
0,
|
||||
0,
|
||||
))?;
|
||||
|
||||
Ok(append_str(blob, &s).into())
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
898
src/func/call.rs
898
src/func/call.rs
File diff suppressed because it is too large
Load Diff
@@ -27,12 +27,14 @@ pub enum CallableFunction {
|
||||
}
|
||||
|
||||
impl fmt::Debug for CallableFunction {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Pure(..) => write!(f, "NativePureFunction"),
|
||||
Self::Method(..) => write!(f, "NativeMethod"),
|
||||
Self::Iterator(..) => write!(f, "NativeIterator"),
|
||||
Self::Plugin(..) => write!(f, "PluginFunction"),
|
||||
Self::Pure(..) => f.write_str("NativePureFunction"),
|
||||
Self::Method(..) => f.write_str("NativeMethod"),
|
||||
Self::Iterator(..) => f.write_str("NativeIterator"),
|
||||
Self::Plugin(..) => f.write_str("PluginFunction"),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f),
|
||||
@@ -43,10 +45,10 @@ impl fmt::Debug for CallableFunction {
|
||||
impl fmt::Display for CallableFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Pure(..) => write!(f, "NativePureFunction"),
|
||||
Self::Method(..) => write!(f, "NativeMethod"),
|
||||
Self::Iterator(..) => write!(f, "NativeIterator"),
|
||||
Self::Plugin(..) => write!(f, "PluginFunction"),
|
||||
Self::Pure(..) => f.write_str("NativePureFunction"),
|
||||
Self::Method(..) => f.write_str("NativeMethod"),
|
||||
Self::Iterator(..) => f.write_str("NativeIterator"),
|
||||
Self::Plugin(..) => f.write_str("PluginFunction"),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(s) => fmt::Display::fmt(s, f),
|
||||
@@ -197,7 +199,7 @@ impl CallableFunction {
|
||||
Self::Script(..) => None,
|
||||
}
|
||||
}
|
||||
/// Create a new [`CallableFunction::Method`] from `FnBuiltin`.
|
||||
/// Create a new [`CallableFunction::Method`] from a built-in function.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_fn_builtin(func: FnBuiltin) -> Self {
|
||||
|
||||
@@ -46,6 +46,7 @@ pub trait Func<ARGS, RET> {
|
||||
/// func(123, "hello")? == false; // call the anonymous function
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
#[must_use]
|
||||
fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output;
|
||||
|
||||
/// Create a Rust closure from a script.
|
||||
@@ -79,6 +80,7 @@ pub trait Func<ARGS, RET> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[must_use]
|
||||
fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output>;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Module containing utilities to hash functions and function calls.
|
||||
|
||||
use crate::config;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
@@ -40,6 +41,7 @@ pub struct StraightHasher(u64);
|
||||
|
||||
impl Hasher for StraightHasher {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
@@ -65,6 +67,7 @@ impl BuildHasher for StraightHasherBuilder {
|
||||
type Hasher = StraightHasher;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn build_hasher(&self) -> Self::Hasher {
|
||||
StraightHasher(ALT_ZERO_HASH)
|
||||
}
|
||||
@@ -74,7 +77,12 @@ impl BuildHasher for StraightHasherBuilder {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_hasher() -> ahash::AHasher {
|
||||
ahash::AHasher::default()
|
||||
match config::hashing::get_ahash_seed() {
|
||||
Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => {
|
||||
ahash::RandomState::with_seeds(*seed1, *seed2, *seed3, *seed4).build_hasher()
|
||||
}
|
||||
_ => ahash::AHasher::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate a non-zero [`u64`] hash key from a namespace-qualified variable name.
|
||||
@@ -148,7 +156,7 @@ pub fn calc_fn_hash<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate a non-zero [`u64`] hash key from a list of parameter types.
|
||||
/// Calculate a non-zero [`u64`] hash key from a base [`u64`] hash key and a list of parameter types.
|
||||
///
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
///
|
||||
@@ -157,10 +165,12 @@ pub fn calc_fn_hash<'a>(
|
||||
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn calc_fn_params_hash(
|
||||
pub fn calc_fn_hash_full(
|
||||
base: u64,
|
||||
params: impl IntoIterator<Item = TypeId, IntoIter = impl ExactSizeIterator<Item = TypeId>>,
|
||||
) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
base.hash(s);
|
||||
let iter = params.into_iter();
|
||||
let len = iter.len();
|
||||
iter.for_each(|t| {
|
||||
@@ -173,17 +183,3 @@ pub fn calc_fn_params_hash(
|
||||
r => r,
|
||||
}
|
||||
}
|
||||
|
||||
/// Combine two [`u64`] hashes by taking the XOR of them.
|
||||
///
|
||||
/// # Zeros
|
||||
///
|
||||
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn combine_hashes(a: u64, b: u64) -> u64 {
|
||||
match a ^ b {
|
||||
0 => ALT_ZERO_HASH,
|
||||
r => r,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,17 @@ pub mod script;
|
||||
|
||||
pub use args::FuncArgs;
|
||||
pub use builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub use call::gen_qualified_fn_call_signature;
|
||||
pub use call::{gen_fn_call_signature, FnCallArgs};
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
pub use call::ensure_no_data_race;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use call::is_anonymous_fn;
|
||||
pub use call::FnCallArgs;
|
||||
pub use callable_function::CallableFunction;
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub use func::Func;
|
||||
pub use hashing::{
|
||||
calc_fn_hash, calc_fn_params_hash, calc_var_hash, combine_hashes, get_hasher, StraightHashMap,
|
||||
};
|
||||
pub use hashing::{calc_fn_hash, calc_fn_hash_full, calc_var_hash, get_hasher, StraightHashMap};
|
||||
#[cfg(feature = "internals")]
|
||||
pub use native::NativeCallContextStore;
|
||||
pub use native::{
|
||||
locked_read, locked_write, shared_get_mut, shared_make_mut, shared_take, shared_take_or_clone,
|
||||
shared_try_take, FnAny, FnPlugin, IteratorFn, Locked, NativeCallContext, SendSync, Shared,
|
||||
|
||||
@@ -4,11 +4,11 @@ use super::call::FnCallArgs;
|
||||
use crate::ast::FnCallHashes;
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::plugin::PluginFunction;
|
||||
use crate::tokenizer::{Token, TokenizeState};
|
||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult,
|
||||
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
||||
RhaiResultOf, SharedModule, StaticVec, VarDefInfo, ERR,
|
||||
};
|
||||
use std::any::type_name;
|
||||
#[cfg(feature = "no_std")]
|
||||
@@ -72,91 +72,73 @@ pub struct NativeCallContext<'a> {
|
||||
/// Function source, if any.
|
||||
source: Option<&'a str>,
|
||||
/// The current [`GlobalRuntimeState`], if any.
|
||||
global: Option<&'a GlobalRuntimeState<'a>>,
|
||||
global: &'a GlobalRuntimeState,
|
||||
/// The current stack of loaded [modules][Module].
|
||||
lib: &'a [&'a Module],
|
||||
lib: &'a [SharedModule],
|
||||
/// [Position] of the function call.
|
||||
pos: Position,
|
||||
/// The current nesting level of function calls.
|
||||
level: usize,
|
||||
}
|
||||
|
||||
impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
|
||||
/// _(internals)_ Context of a native Rust function call.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[cfg(feature = "internals")]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NativeCallContextStore {
|
||||
/// Name of function called.
|
||||
pub fn_name: String,
|
||||
/// Function source, if any.
|
||||
pub source: Option<String>,
|
||||
/// The current [`GlobalRuntimeState`], if any.
|
||||
pub global: GlobalRuntimeState,
|
||||
/// The current stack of loaded [modules][Module].
|
||||
pub lib: StaticVec<SharedModule>,
|
||||
/// [Position] of the function call.
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
#[cfg(feature = "internals")]
|
||||
impl NativeCallContextStore {
|
||||
/// Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> {
|
||||
NativeCallContext::from_stored_data(engine, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a>
|
||||
From<(
|
||||
&'a Engine,
|
||||
&'a S,
|
||||
Option<&'a S>,
|
||||
&'a GlobalRuntimeState<'a>,
|
||||
&'a M,
|
||||
&'a str,
|
||||
Option<&'a str>,
|
||||
&'a GlobalRuntimeState,
|
||||
&'a [SharedModule],
|
||||
Position,
|
||||
usize,
|
||||
)> for NativeCallContext<'a>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(
|
||||
value: (
|
||||
&'a Engine,
|
||||
&'a S,
|
||||
Option<&'a S>,
|
||||
&'a str,
|
||||
Option<&'a str>,
|
||||
&'a GlobalRuntimeState,
|
||||
&'a M,
|
||||
&'a [SharedModule],
|
||||
Position,
|
||||
usize,
|
||||
),
|
||||
) -> Self {
|
||||
Self {
|
||||
engine: value.0,
|
||||
fn_name: value.1.as_ref(),
|
||||
source: value.2.map(<_>::as_ref),
|
||||
global: Some(value.3),
|
||||
lib: value.4.as_ref(),
|
||||
fn_name: value.1,
|
||||
source: value.2,
|
||||
global: value.3,
|
||||
lib: value.4,
|
||||
pos: value.5,
|
||||
level: value.6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
|
||||
From<(&'a Engine, &'a S, &'a M)> for NativeCallContext<'a>
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(value: (&'a Engine, &'a S, &'a M)) -> Self {
|
||||
Self {
|
||||
engine: value.0,
|
||||
fn_name: value.1.as_ref(),
|
||||
source: None,
|
||||
global: None,
|
||||
lib: value.2.as_ref(),
|
||||
pos: Position::NONE,
|
||||
level: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NativeCallContext<'a> {
|
||||
/// _(internals)_ Create a new [`NativeCallContext`].
|
||||
/// Exported under the `metadata` feature only.
|
||||
#[deprecated(
|
||||
since = "1.3.0",
|
||||
note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly."
|
||||
)]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
engine: &'a Engine,
|
||||
fn_name: &'a (impl AsRef<str> + 'a + ?Sized),
|
||||
lib: &'a [&Module],
|
||||
) -> Self {
|
||||
Self {
|
||||
engine,
|
||||
fn_name: fn_name.as_ref(),
|
||||
source: None,
|
||||
global: None,
|
||||
lib,
|
||||
pos: Position::NONE,
|
||||
level: 0,
|
||||
}
|
||||
}
|
||||
/// _(internals)_ Create a new [`NativeCallContext`].
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
@@ -167,23 +149,52 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[must_use]
|
||||
pub fn new_with_all_fields(
|
||||
engine: &'a Engine,
|
||||
fn_name: &'a (impl AsRef<str> + 'a + ?Sized),
|
||||
source: Option<&'a (impl AsRef<str> + 'a + ?Sized)>,
|
||||
fn_name: &'a str,
|
||||
source: Option<&'a str>,
|
||||
global: &'a GlobalRuntimeState,
|
||||
lib: &'a [&Module],
|
||||
lib: &'a [SharedModule],
|
||||
pos: Position,
|
||||
level: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
engine,
|
||||
fn_name: fn_name.as_ref(),
|
||||
source: source.map(<_>::as_ref),
|
||||
global: Some(global),
|
||||
fn_name,
|
||||
source,
|
||||
global,
|
||||
lib,
|
||||
pos,
|
||||
level,
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self {
|
||||
Self {
|
||||
engine,
|
||||
fn_name: &context.fn_name,
|
||||
source: context.source.as_ref().map(String::as_str),
|
||||
global: &context.global,
|
||||
lib: &context.lib,
|
||||
pos: context.pos,
|
||||
}
|
||||
}
|
||||
/// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`].
|
||||
/// Exported under the `internals` feature only.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn store_data(&self) -> NativeCallContextStore {
|
||||
NativeCallContextStore {
|
||||
fn_name: self.fn_name.to_string(),
|
||||
source: self.source.map(|s| s.to_string()),
|
||||
global: self.global.clone(),
|
||||
lib: self.lib.iter().cloned().collect(),
|
||||
pos: self.pos,
|
||||
}
|
||||
}
|
||||
|
||||
/// The current [`Engine`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
@@ -206,7 +217,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn call_level(&self) -> usize {
|
||||
self.level
|
||||
self.global.level
|
||||
}
|
||||
/// The current source.
|
||||
#[inline(always)]
|
||||
@@ -218,7 +229,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn tag(&self) -> Option<&Dynamic> {
|
||||
self.global.as_ref().map(|g| &g.tag)
|
||||
Some(&self.global.tag)
|
||||
}
|
||||
/// Get an iterator over the current set of modules imported via `import` statements
|
||||
/// in reverse order.
|
||||
@@ -227,7 +238,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline]
|
||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||
self.global.iter().flat_map(|&g| g.iter_imports())
|
||||
self.global.iter_imports()
|
||||
}
|
||||
/// Get an iterator over the current set of modules imported via `import` statements in reverse order.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@@ -235,8 +246,8 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[inline]
|
||||
pub(crate) fn iter_imports_raw(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&crate::ImmutableString, &Shared<Module>)> {
|
||||
self.global.iter().flat_map(|&g| g.iter_imports_raw())
|
||||
) -> impl Iterator<Item = (&crate::ImmutableString, &SharedModule)> {
|
||||
self.global.iter_imports_raw()
|
||||
}
|
||||
/// _(internals)_ The current [`GlobalRuntimeState`], if any.
|
||||
/// Exported under the `internals` feature only.
|
||||
@@ -245,21 +256,21 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> {
|
||||
pub const fn global_runtime_state(&self) -> &GlobalRuntimeState {
|
||||
self.global
|
||||
}
|
||||
/// Get an iterator over the namespaces containing definitions of all script-defined functions
|
||||
/// in reverse order (i.e. parent namespaces are iterated after child namespaces).
|
||||
#[inline]
|
||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
||||
self.lib.iter().copied()
|
||||
self.lib.iter().map(|m| m.as_ref())
|
||||
}
|
||||
/// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn namespaces(&self) -> &[&Module] {
|
||||
pub const fn namespaces(&self) -> &[SharedModule] {
|
||||
self.lib
|
||||
}
|
||||
/// Call a function inside the call context with the provided arguments.
|
||||
@@ -274,7 +285,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
|
||||
let result = self.call_fn_raw(fn_name, false, false, &mut args)?;
|
||||
let result = self._call_fn_raw(fn_name, false, false, false, &mut args)?;
|
||||
|
||||
let typ = self.engine().map_type_name(result.type_name());
|
||||
|
||||
@@ -283,7 +294,32 @@ impl<'a> NativeCallContext<'a> {
|
||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||
})
|
||||
}
|
||||
/// Call a function inside the call context.
|
||||
/// Call a registered native Rust function inside the call context with the provided arguments.
|
||||
///
|
||||
/// This is often useful because Rust functions typically only want to cross-call other
|
||||
/// registered Rust functions and not have to worry about scripted functions hijacking the
|
||||
/// process unknowingly (or deliberately).
|
||||
#[inline]
|
||||
pub fn call_native_fn<T: Variant + Clone>(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
args: impl FuncArgs,
|
||||
) -> RhaiResultOf<T> {
|
||||
let mut arg_values = StaticVec::new_const();
|
||||
args.parse(&mut arg_values);
|
||||
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
|
||||
let result = self._call_fn_raw(fn_name, true, false, false, &mut args)?;
|
||||
|
||||
let typ = self.engine().map_type_name(result.type_name());
|
||||
|
||||
result.try_cast().ok_or_else(|| {
|
||||
let t = self.engine().map_type_name(type_name::<T>()).into();
|
||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||
})
|
||||
}
|
||||
/// Call a function (native Rust or scripted) inside the call context.
|
||||
///
|
||||
/// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for
|
||||
/// a script-defined function (or the object of a method call).
|
||||
@@ -302,6 +338,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
///
|
||||
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
||||
/// not consumed.
|
||||
#[inline(always)]
|
||||
pub fn call_fn_raw(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
@@ -309,15 +346,81 @@ impl<'a> NativeCallContext<'a> {
|
||||
is_method_call: bool,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
let mut global = self
|
||||
.global
|
||||
.cloned()
|
||||
.unwrap_or_else(|| GlobalRuntimeState::new(self.engine()));
|
||||
let mut caches = Caches::new();
|
||||
let name = fn_name.as_ref();
|
||||
let native_only = !is_valid_function_name(name);
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let native_only = native_only && !crate::parser::is_anonymous_fn(name);
|
||||
|
||||
self._call_fn_raw(fn_name, native_only, is_ref_mut, is_method_call, args)
|
||||
}
|
||||
/// Call a registered native Rust function inside the call context.
|
||||
///
|
||||
/// This is often useful because Rust functions typically only want to cross-call other
|
||||
/// registered Rust functions and not have to worry about scripted functions hijacking the
|
||||
/// process unknowingly (or deliberately).
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid
|
||||
/// unnecessarily cloning the arguments.
|
||||
///
|
||||
/// **DO NOT** reuse the arguments after this call. If they are needed afterwards, clone them
|
||||
/// _before_ calling this function.
|
||||
///
|
||||
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
||||
/// not consumed.
|
||||
#[inline(always)]
|
||||
pub fn call_native_fn_raw(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
is_ref_mut: bool,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
self._call_fn_raw(fn_name, true, is_ref_mut, false, args)
|
||||
}
|
||||
|
||||
/// Call a function (native Rust or scripted) inside the call context.
|
||||
fn _call_fn_raw(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
native_only: bool,
|
||||
is_ref_mut: bool,
|
||||
is_method_call: bool,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
let mut global = &mut self.global.clone();
|
||||
let caches = &mut Caches::new();
|
||||
|
||||
let fn_name = fn_name.as_ref();
|
||||
let op_token = Token::lookup_symbol_from_syntax(fn_name);
|
||||
let op_token = op_token.as_ref();
|
||||
let args_len = args.len();
|
||||
|
||||
global.level += 1;
|
||||
|
||||
if native_only {
|
||||
return self
|
||||
.engine()
|
||||
.exec_native_fn_call(
|
||||
global,
|
||||
caches,
|
||||
self.lib,
|
||||
fn_name,
|
||||
op_token,
|
||||
calc_fn_hash(None, fn_name, args_len),
|
||||
args,
|
||||
is_ref_mut,
|
||||
Position::NONE,
|
||||
)
|
||||
.map(|(r, ..)| r);
|
||||
}
|
||||
|
||||
// Native or script
|
||||
|
||||
let hash = if is_method_call {
|
||||
FnCallHashes::from_all(
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
@@ -330,17 +433,17 @@ impl<'a> NativeCallContext<'a> {
|
||||
|
||||
self.engine()
|
||||
.exec_fn_call(
|
||||
None,
|
||||
&mut global,
|
||||
&mut caches,
|
||||
global,
|
||||
caches,
|
||||
self.lib,
|
||||
None,
|
||||
fn_name,
|
||||
op_token,
|
||||
hash,
|
||||
args,
|
||||
is_ref_mut,
|
||||
is_method_call,
|
||||
Position::NONE,
|
||||
self.level + 1,
|
||||
)
|
||||
.map(|(r, ..)| r)
|
||||
}
|
||||
|
||||
@@ -29,4 +29,14 @@ pub trait PluginFunction {
|
||||
/// Is this plugin function a method?
|
||||
#[must_use]
|
||||
fn is_method_call(&self) -> bool;
|
||||
|
||||
/// Is this plugin function pure?
|
||||
///
|
||||
/// This defaults to `true` such that any old implementation that has constant-checking code
|
||||
/// inside the function itself will continue to work.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn is_pure(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,33 +86,37 @@ pub trait RegisterNativeFunction<ARGS, RET, RESULT> {
|
||||
/// _(metadata)_ Get the type name of this function's return value.
|
||||
/// Exported under the `metadata` feature only.
|
||||
#[cfg(feature = "metadata")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn return_type_name() -> &'static str;
|
||||
fn return_type_name() -> &'static str {
|
||||
std::any::type_name::<RET>()
|
||||
}
|
||||
}
|
||||
|
||||
const EXPECT_ARGS: &str = "arguments";
|
||||
|
||||
macro_rules! check_constant {
|
||||
($ctx:ident, $args:ident) => {
|
||||
($abi:ident, $ctx:ident, $args:ident) => {
|
||||
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
|
||||
{
|
||||
let args_len = $args.len();
|
||||
|
||||
if args_len > 0 && $args[0].is_read_only() {
|
||||
let deny = match $ctx.fn_name() {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
f if args_len == 2 && f.starts_with(crate::engine::FN_SET) => true,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
crate::engine::FN_IDX_SET if args_len == 3 => true,
|
||||
_ => false,
|
||||
};
|
||||
if deny {
|
||||
return Err(crate::ERR::ErrorAssignmentToConstant(
|
||||
String::new(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
if stringify!($abi) == "Method" && !$args.is_empty() {
|
||||
let deny = match $args.len() {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
3 if $ctx.fn_name() == crate::engine::FN_IDX_SET && $args[0].is_read_only() => true,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
2 if $ctx.fn_name().starts_with(crate::engine::FN_SET)
|
||||
&& $args[0].is_read_only() =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if deny {
|
||||
return Err(crate::ERR::ErrorNonPureMethodCallOnConstant(
|
||||
$ctx.fn_name().to_string(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -138,11 +142,10 @@ macro_rules! def_register {
|
||||
#[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 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 {
|
||||
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!(_ctx, args);
|
||||
check_constant!($abi, _ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
@@ -164,11 +167,10 @@ macro_rules! def_register {
|
||||
#[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 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 {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!(ctx, args);
|
||||
check_constant!($abi, ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
@@ -194,7 +196,7 @@ macro_rules! def_register {
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |_ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!(_ctx, args);
|
||||
check_constant!($abi, _ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
@@ -217,7 +219,7 @@ macro_rules! def_register {
|
||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||
CallableFunction::$abi(Shared::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
check_constant!(ctx, args);
|
||||
check_constant!($abi, ctx, args);
|
||||
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().expect(EXPECT_ARGS)); )*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use super::call::FnCallArgs;
|
||||
use crate::ast::ScriptFnDef;
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::{Dynamic, Engine, Module, Position, RhaiError, RhaiResult, Scope, ERR};
|
||||
use crate::{Dynamic, Engine, Position, RhaiError, RhaiResult, Scope, SharedModule, ERR};
|
||||
use std::mem;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -24,16 +24,15 @@ impl Engine {
|
||||
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
|
||||
pub(crate) fn call_script_fn(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
lib: &[SharedModule],
|
||||
scope: &mut Scope,
|
||||
this_ptr: &mut Dynamic,
|
||||
fn_def: &ScriptFnDef,
|
||||
args: &mut FnCallArgs,
|
||||
rewind_scope: bool,
|
||||
pos: Position,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
@@ -54,7 +53,7 @@ impl Engine {
|
||||
|
||||
Err(ERR::ErrorInFunctionCall(
|
||||
name,
|
||||
source.unwrap_or_else(|| global.source.to_string()),
|
||||
source.unwrap_or_else(|| global.source().unwrap_or("").to_string()),
|
||||
err,
|
||||
pos,
|
||||
)
|
||||
@@ -63,12 +62,10 @@ impl Engine {
|
||||
|
||||
assert!(fn_def.params.len() == args.len());
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, pos)?;
|
||||
self.track_operation(global, pos)?;
|
||||
|
||||
// Check for stack overflow
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if level > self.max_call_levels() {
|
||||
if global.level > self.max_call_levels() {
|
||||
return Err(ERR::ErrorStackOverflow(pos).into());
|
||||
}
|
||||
|
||||
@@ -129,8 +126,8 @@ impl Engine {
|
||||
lib
|
||||
} else {
|
||||
caches.push_fn_resolution_cache();
|
||||
lib_merged.push(&**fn_lib);
|
||||
lib_merged.extend(lib.iter().copied());
|
||||
lib_merged.push(fn_lib.clone());
|
||||
lib_merged.extend(lib.iter().cloned());
|
||||
&lib_merged
|
||||
},
|
||||
Some(mem::replace(&mut global.constants, constants.clone())),
|
||||
@@ -142,20 +139,19 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
{
|
||||
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
||||
self.run_debugger(scope, global, lib, this_ptr, &node, level)?;
|
||||
self.run_debugger(global, caches, lib, scope, this_ptr, &node)?;
|
||||
}
|
||||
|
||||
// Evaluate the function
|
||||
let mut _result = self
|
||||
.eval_stmt_block(
|
||||
scope,
|
||||
global,
|
||||
caches,
|
||||
lib,
|
||||
scope,
|
||||
this_ptr,
|
||||
&fn_def.body,
|
||||
rewind_scope,
|
||||
level,
|
||||
)
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
@@ -181,7 +177,7 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
{
|
||||
let trigger = match global.debugger.status {
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= level,
|
||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
||||
crate::eval::DebuggerStatus::Next(.., true) => true,
|
||||
_ => false,
|
||||
};
|
||||
@@ -192,7 +188,7 @@ impl Engine {
|
||||
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
|
||||
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
|
||||
};
|
||||
match self.run_debugger_raw(scope, global, lib, this_ptr, node, event, level) {
|
||||
match self.run_debugger_raw(global, caches, lib, scope, this_ptr, node, event) {
|
||||
Ok(_) => (),
|
||||
Err(err) => _result = Err(err),
|
||||
}
|
||||
@@ -228,9 +224,9 @@ impl Engine {
|
||||
#[must_use]
|
||||
pub(crate) fn has_script_fn(
|
||||
&self,
|
||||
_global: Option<&GlobalRuntimeState>,
|
||||
_global: &GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
lib: &[SharedModule],
|
||||
hash_script: u64,
|
||||
) -> bool {
|
||||
let cache = caches.fn_resolution_cache_mut();
|
||||
@@ -247,16 +243,13 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
let result = result ||
|
||||
// Then check imported modules
|
||||
_global.map_or(false, |m| m.contains_qualified_fn(hash_script))
|
||||
_global.contains_qualified_fn(hash_script)
|
||||
// Then check sub-modules
|
||||
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|
||||
|
||||
if !result {
|
||||
if cache.filter.is_absent(hash_script) {
|
||||
cache.filter.mark(hash_script);
|
||||
} else {
|
||||
cache.map.insert(hash_script, None);
|
||||
}
|
||||
if !result && !cache.filter.is_absent_and_set(hash_script) {
|
||||
// Do not cache "one-hit wonders"
|
||||
cache.map.insert(hash_script, None);
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
25
src/lib.rs
25
src/lib.rs
@@ -83,6 +83,7 @@ use std::prelude::v1::*;
|
||||
// Internal modules
|
||||
mod api;
|
||||
mod ast;
|
||||
pub mod config;
|
||||
mod engine;
|
||||
mod eval;
|
||||
mod func;
|
||||
@@ -206,7 +207,7 @@ pub use eval::EvalContext;
|
||||
pub use func::{NativeCallContext, RegisterNativeFunction};
|
||||
pub use module::{FnNamespace, Module};
|
||||
pub use tokenizer::Position;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
pub use types::Instant;
|
||||
pub use types::{
|
||||
Dynamic, EvalAltResult, FnPtr, ImmutableString, LexError, ParseError, ParseErrorType, Scope,
|
||||
@@ -227,7 +228,7 @@ pub mod debugger {
|
||||
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
||||
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
||||
#[cfg(not(feature = "internals"))]
|
||||
pub(crate) type Identifier = SmartString;
|
||||
type Identifier = SmartString;
|
||||
|
||||
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
||||
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
||||
@@ -240,7 +241,10 @@ pub use func::Shared;
|
||||
/// Alias to [`RefCell`][std::cell::RefCell] or [`RwLock`][std::sync::RwLock] depending on the `sync` feature flag.
|
||||
pub use func::Locked;
|
||||
|
||||
pub(crate) use func::{calc_fn_hash, calc_fn_params_hash, calc_var_hash, combine_hashes};
|
||||
use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash};
|
||||
|
||||
/// A shared [`Module`].
|
||||
type SharedModule = Shared<Module>;
|
||||
|
||||
pub use rhai_codegen::*;
|
||||
|
||||
@@ -343,6 +347,9 @@ pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeS
|
||||
#[cfg(feature = "metadata")]
|
||||
pub use api::definitions::Definitions;
|
||||
|
||||
/// Number of items to keep inline for [`StaticVec`].
|
||||
const STATIC_VEC_INLINE_SIZE: usize = 3;
|
||||
|
||||
/// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a
|
||||
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
||||
///
|
||||
@@ -375,7 +382,7 @@ pub use api::definitions::Definitions;
|
||||
/// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels
|
||||
/// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get).
|
||||
#[cfg(not(feature = "internals"))]
|
||||
type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
||||
type StaticVec<T> = smallvec::SmallVec<[T; STATIC_VEC_INLINE_SIZE]>;
|
||||
|
||||
/// _(internals)_ Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec),
|
||||
/// which is a [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
||||
@@ -410,7 +417,11 @@ type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
||||
/// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels
|
||||
/// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get).
|
||||
#[cfg(feature = "internals")]
|
||||
pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
||||
pub type StaticVec<T> = smallvec::SmallVec<[T; STATIC_VEC_INLINE_SIZE]>;
|
||||
|
||||
/// Number of items to keep inline for [`FnArgsVec`].
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
const FN_ARGS_VEC_INLINE_SIZE: usize = 5;
|
||||
|
||||
/// Inline arguments storage for function calls.
|
||||
///
|
||||
@@ -425,14 +436,14 @@ pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
||||
///
|
||||
/// Under `no_closure`, this type aliases to [`StaticVec`][crate::StaticVec] instead.
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
type FnArgsVec<T> = smallvec::SmallVec<[T; 5]>;
|
||||
type FnArgsVec<T> = smallvec::SmallVec<[T; FN_ARGS_VEC_INLINE_SIZE]>;
|
||||
|
||||
/// Inline arguments storage for function calls.
|
||||
/// This type aliases to [`StaticVec`][crate::StaticVec].
|
||||
#[cfg(feature = "no_closure")]
|
||||
type FnArgsVec<T> = crate::StaticVec<T>;
|
||||
|
||||
pub(crate) type SmartString = smartstring::SmartString<smartstring::LazyCompact>;
|
||||
type SmartString = smartstring::SmartString<smartstring::LazyCompact>;
|
||||
|
||||
// Compiler guards against mutually-exclusive feature flags
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::func::{
|
||||
};
|
||||
use crate::types::{dynamic::Variant, BloomFilterU64, CustomTypesCollection};
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Identifier, ImmutableString,
|
||||
NativeCallContext, RhaiResultOf, Shared, SmartString, StaticVec,
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Identifier, ImmutableString, NativeCallContext,
|
||||
RhaiResultOf, Shared, SharedModule, SmartString, StaticVec,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -72,7 +72,7 @@ pub struct FuncInfo {
|
||||
/// Function access mode.
|
||||
pub access: FnAccess,
|
||||
/// Function name.
|
||||
pub name: Identifier,
|
||||
pub name: ImmutableString,
|
||||
/// Number of parameters.
|
||||
pub num_params: usize,
|
||||
/// Parameter types (if applicable).
|
||||
@@ -150,9 +150,10 @@ pub fn calc_native_fn_hash<'a>(
|
||||
fn_name: &str,
|
||||
params: &[TypeId],
|
||||
) -> u64 {
|
||||
let hash_script = calc_fn_hash(modules, fn_name, params.len());
|
||||
let hash_params = calc_fn_params_hash(params.iter().copied());
|
||||
combine_hashes(hash_script, hash_params)
|
||||
calc_fn_hash_full(
|
||||
calc_fn_hash(modules, fn_name, params.len()),
|
||||
params.iter().copied(),
|
||||
)
|
||||
}
|
||||
|
||||
/// A module which may contain variables, sub-modules, external Rust functions,
|
||||
@@ -160,8 +161,7 @@ pub fn calc_native_fn_hash<'a>(
|
||||
#[derive(Clone)]
|
||||
pub struct Module {
|
||||
/// ID identifying the module.
|
||||
/// No ID if string is empty.
|
||||
id: Identifier,
|
||||
id: Option<ImmutableString>,
|
||||
/// Module documentation.
|
||||
#[cfg(feature = "metadata")]
|
||||
doc: crate::SmartString,
|
||||
@@ -172,7 +172,7 @@ pub struct Module {
|
||||
/// Custom types.
|
||||
custom_types: Option<CustomTypesCollection>,
|
||||
/// Sub-modules.
|
||||
modules: Option<BTreeMap<Identifier, Shared<Module>>>,
|
||||
modules: Option<BTreeMap<Identifier, SharedModule>>,
|
||||
/// [`Module`] variables.
|
||||
variables: Option<BTreeMap<Identifier, Dynamic>>,
|
||||
/// Flattened collection of all [`Module`] variables, including those in sub-modules.
|
||||
@@ -196,12 +196,15 @@ pub struct Module {
|
||||
|
||||
impl Default for Module {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Module {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("Module");
|
||||
|
||||
@@ -289,7 +292,7 @@ impl Module {
|
||||
#[must_use]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
id: Identifier::new_const(),
|
||||
id: None,
|
||||
#[cfg(feature = "metadata")]
|
||||
doc: crate::SmartString::new_const(),
|
||||
internal: false,
|
||||
@@ -321,18 +324,14 @@ impl Module {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn id(&self) -> Option<&str> {
|
||||
if self.id_raw().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.id_raw())
|
||||
}
|
||||
self.id.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
/// Get the ID of the [`Module`] as an [`Identifier`], if any.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn id_raw(&self) -> &Identifier {
|
||||
&self.id
|
||||
pub(crate) const fn id_raw(&self) -> Option<&ImmutableString> {
|
||||
self.id.as_ref()
|
||||
}
|
||||
|
||||
/// Set the ID of the [`Module`].
|
||||
@@ -348,8 +347,15 @@ impl Module {
|
||||
/// assert_eq!(module.id(), Some("hello"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_id(&mut self, id: impl Into<Identifier>) -> &mut Self {
|
||||
self.id = id.into();
|
||||
pub fn set_id(&mut self, id: impl Into<ImmutableString>) -> &mut Self {
|
||||
let id = id.into();
|
||||
|
||||
if id.is_empty() {
|
||||
self.id = None;
|
||||
} else {
|
||||
self.id = Some(id);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@@ -367,7 +373,7 @@ impl Module {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn clear_id(&mut self) -> &mut Self {
|
||||
self.id.clear();
|
||||
self.id = None;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -431,7 +437,7 @@ impl Module {
|
||||
/// Clear the [`Module`].
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
self.id.clear();
|
||||
self.id = None;
|
||||
#[cfg(feature = "metadata")]
|
||||
self.doc.clear();
|
||||
self.internal = false;
|
||||
@@ -491,12 +497,12 @@ impl Module {
|
||||
#[inline(always)]
|
||||
pub fn set_custom_type_raw(
|
||||
&mut self,
|
||||
type_name: impl Into<Identifier>,
|
||||
type_path: impl Into<Identifier>,
|
||||
name: impl Into<Identifier>,
|
||||
) -> &mut Self {
|
||||
self.custom_types
|
||||
.get_or_insert_with(CustomTypesCollection::new)
|
||||
.add(type_name, name);
|
||||
.add(type_path, name);
|
||||
self
|
||||
}
|
||||
/// Get the display name of a registered custom type.
|
||||
@@ -748,7 +754,7 @@ impl Module {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
|
||||
pub(crate) fn get_sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, SharedModule> {
|
||||
// We must assume that the user has changed the sub-modules
|
||||
// (otherwise why take a mutable reference?)
|
||||
self.all_functions = None;
|
||||
@@ -816,7 +822,7 @@ impl Module {
|
||||
pub fn set_sub_module(
|
||||
&mut self,
|
||||
name: impl Into<Identifier>,
|
||||
sub_module: impl Into<Shared<Module>>,
|
||||
sub_module: impl Into<SharedModule>,
|
||||
) -> &mut Self {
|
||||
self.modules
|
||||
.get_or_insert_with(|| Default::default())
|
||||
@@ -1022,8 +1028,7 @@ impl Module {
|
||||
|
||||
let name = name.as_ref();
|
||||
let hash_script = calc_fn_hash(None, name, param_types.len());
|
||||
let hash_params = calc_fn_params_hash(param_types.iter().copied());
|
||||
let hash_fn = combine_hashes(hash_script, hash_params);
|
||||
let hash_fn = calc_fn_hash_full(hash_script, param_types.iter().copied());
|
||||
|
||||
if is_dynamic {
|
||||
self.dynamic_functions_filter.mark(hash_script);
|
||||
@@ -1739,10 +1744,9 @@ impl Module {
|
||||
}
|
||||
|
||||
if let Some(ref variables) = other.variables {
|
||||
if let Some(ref mut m) = self.variables {
|
||||
m.extend(variables.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
} else {
|
||||
self.variables = other.variables.clone();
|
||||
match self.variables {
|
||||
Some(ref mut m) => m.extend(variables.iter().map(|(k, v)| (k.clone(), v.clone()))),
|
||||
None => self.variables = other.variables.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1764,10 +1768,9 @@ impl Module {
|
||||
self.dynamic_functions_filter += &other.dynamic_functions_filter;
|
||||
|
||||
if let Some(ref type_iterators) = other.type_iterators {
|
||||
if let Some(ref mut t) = self.type_iterators {
|
||||
t.extend(type_iterators.iter().map(|(&k, v)| (k, v.clone())));
|
||||
} else {
|
||||
self.type_iterators = other.type_iterators.clone();
|
||||
match self.type_iterators {
|
||||
Some(ref mut t) => t.extend(type_iterators.iter().map(|(&k, v)| (k, v.clone()))),
|
||||
None => self.type_iterators = other.type_iterators.clone(),
|
||||
}
|
||||
}
|
||||
self.all_functions = None;
|
||||
@@ -1827,7 +1830,7 @@ impl Module {
|
||||
|
||||
/// Get an iterator to the sub-modules in the [`Module`].
|
||||
#[inline]
|
||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
|
||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
||||
self.modules
|
||||
.iter()
|
||||
.flat_map(|m| m.iter().map(|(k, m)| (k.as_str(), m)))
|
||||
@@ -1981,7 +1984,9 @@ impl Module {
|
||||
let orig_constants = std::mem::take(&mut global.constants);
|
||||
|
||||
// Run the script
|
||||
let result = engine.eval_ast_with_scope_raw(&mut scope, global, ast, 0);
|
||||
let caches = &mut crate::eval::Caches::new();
|
||||
|
||||
let result = engine.eval_ast_with_scope_raw(global, caches, &mut scope, ast);
|
||||
|
||||
// Create new module
|
||||
let mut module = Module::new();
|
||||
@@ -2077,7 +2082,7 @@ impl Module {
|
||||
});
|
||||
}
|
||||
|
||||
module.set_id(ast.source_raw().clone());
|
||||
module.id = ast.source_raw().cloned();
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
module.set_doc(ast.doc());
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR};
|
||||
use crate::{
|
||||
Engine, ModuleResolver, Position, RhaiResultOf, SharedModule, StaticVec, ERR,
|
||||
STATIC_VEC_INLINE_SIZE,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{ops::AddAssign, slice::Iter, vec::IntoIter};
|
||||
use std::{ops::AddAssign, slice::Iter};
|
||||
|
||||
/// [Module] resolution service that holds a collection of module resolvers,
|
||||
/// to be searched in sequential order.
|
||||
@@ -21,7 +24,7 @@ use std::{ops::AddAssign, slice::Iter, vec::IntoIter};
|
||||
/// engine.set_module_resolver(collection);
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
|
||||
pub struct ModuleResolversCollection(StaticVec<Box<dyn ModuleResolver>>);
|
||||
|
||||
impl ModuleResolversCollection {
|
||||
/// Create a new [`ModuleResolversCollection`].
|
||||
@@ -42,8 +45,8 @@ impl ModuleResolversCollection {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
pub const fn new() -> Self {
|
||||
Self(StaticVec::new_const())
|
||||
}
|
||||
/// Append a [module resolver][ModuleResolver] to the end.
|
||||
#[inline(always)]
|
||||
@@ -109,9 +112,10 @@ impl ModuleResolversCollection {
|
||||
|
||||
impl IntoIterator for ModuleResolversCollection {
|
||||
type Item = Box<dyn ModuleResolver>;
|
||||
type IntoIter = IntoIter<Box<dyn ModuleResolver>>;
|
||||
type IntoIter = smallvec::IntoIter<[Box<dyn ModuleResolver>; STATIC_VEC_INLINE_SIZE]>;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
@@ -134,7 +138,7 @@ impl ModuleResolver for ModuleResolversCollection {
|
||||
source_path: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
) -> RhaiResultOf<SharedModule> {
|
||||
for resolver in &self.0 {
|
||||
match resolver.resolve(engine, source_path, path, pos) {
|
||||
Ok(module) => return Ok(module),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR};
|
||||
use crate::{Engine, ModuleResolver, Position, RhaiResultOf, SharedModule, ERR};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -45,7 +45,7 @@ impl ModuleResolver for DummyModuleResolver {
|
||||
_: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
) -> RhaiResultOf<SharedModule> {
|
||||
Err(ERR::ErrorModuleNotFound(path.into(), pos).into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
use crate::eval::GlobalRuntimeState;
|
||||
use crate::func::{locked_read, locked_write};
|
||||
use crate::{
|
||||
Engine, Identifier, Locked, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared, ERR,
|
||||
Engine, Identifier, Locked, Module, ModuleResolver, Position, RhaiResultOf, Scope, Shared,
|
||||
SharedModule, ERR,
|
||||
};
|
||||
|
||||
use std::{
|
||||
@@ -51,11 +52,12 @@ pub struct FileModuleResolver {
|
||||
extension: Identifier,
|
||||
cache_enabled: bool,
|
||||
scope: Scope<'static>,
|
||||
cache: Locked<BTreeMap<PathBuf, Shared<Module>>>,
|
||||
cache: Locked<BTreeMap<PathBuf, SharedModule>>,
|
||||
}
|
||||
|
||||
impl Default for FileModuleResolver {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
@@ -194,8 +196,8 @@ impl FileModuleResolver {
|
||||
/// Get a reference to the file module resolver's [scope][Scope].
|
||||
///
|
||||
/// The [scope][Scope] is used for compiling module scripts.
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn scope(&self) -> &Scope {
|
||||
&self.scope
|
||||
}
|
||||
@@ -211,8 +213,8 @@ impl FileModuleResolver {
|
||||
/// Get a mutable reference to the file module resolver's [scope][Scope].
|
||||
///
|
||||
/// The [scope][Scope] is used for compiling module scripts.
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn scope_mut(&mut self) -> &mut Scope<'static> {
|
||||
&mut self.scope
|
||||
}
|
||||
@@ -257,7 +259,7 @@ impl FileModuleResolver {
|
||||
/// The next time this path is resolved, the script file will be loaded once again.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn clear_cache_for_path(&mut self, path: impl AsRef<Path>) -> Option<Shared<Module>> {
|
||||
pub fn clear_cache_for_path(&mut self, path: impl AsRef<Path>) -> Option<SharedModule> {
|
||||
locked_write(&self.cache)
|
||||
.remove_entry(path.as_ref())
|
||||
.map(|(.., v)| v)
|
||||
@@ -292,7 +294,7 @@ impl FileModuleResolver {
|
||||
source: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> Result<Shared<Module>, Box<crate::EvalAltResult>> {
|
||||
) -> Result<SharedModule, Box<crate::EvalAltResult>> {
|
||||
// Load relative paths from source if there is no base path specified
|
||||
let source_path = global
|
||||
.as_ref()
|
||||
@@ -321,10 +323,9 @@ impl FileModuleResolver {
|
||||
|
||||
let scope = Scope::new();
|
||||
|
||||
let m: Shared<_> = if let Some(global) = global {
|
||||
Module::eval_ast_as_new_raw(engine, scope, global, &ast)
|
||||
} else {
|
||||
Module::eval_ast_as_new(scope, &ast, engine)
|
||||
let m: Shared<_> = match global {
|
||||
Some(global) => Module::eval_ast_as_new_raw(engine, scope, global, &ast),
|
||||
None => Module::eval_ast_as_new(scope, &ast, engine),
|
||||
}
|
||||
.map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
|
||||
.into();
|
||||
@@ -344,7 +345,7 @@ impl ModuleResolver for FileModuleResolver {
|
||||
global: &mut GlobalRuntimeState,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
) -> RhaiResultOf<SharedModule> {
|
||||
self.impl_resolve(engine, Some(global), None, path, pos)
|
||||
}
|
||||
|
||||
@@ -355,7 +356,7 @@ impl ModuleResolver for FileModuleResolver {
|
||||
source: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
) -> RhaiResultOf<SharedModule> {
|
||||
self.impl_resolve(engine, None, source, path, pos)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::eval::GlobalRuntimeState;
|
||||
use crate::func::SendSync;
|
||||
use crate::{Engine, Module, Position, RhaiResultOf, Shared, AST};
|
||||
use crate::{Engine, Position, RhaiResultOf, SharedModule, AST};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -25,7 +25,7 @@ pub trait ModuleResolver: SendSync {
|
||||
source: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>>;
|
||||
) -> RhaiResultOf<SharedModule>;
|
||||
|
||||
/// Resolve a module based on a path string, given a [`GlobalRuntimeState`].
|
||||
///
|
||||
@@ -38,7 +38,7 @@ pub trait ModuleResolver: SendSync {
|
||||
global: &mut GlobalRuntimeState,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
) -> RhaiResultOf<SharedModule> {
|
||||
self.resolve(engine, global.source(), path, pos)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, Shared, SmartString, ERR,
|
||||
Engine, Identifier, Module, ModuleResolver, Position, RhaiResultOf, SharedModule, SmartString,
|
||||
ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -27,7 +28,7 @@ use std::{
|
||||
/// engine.set_module_resolver(resolver);
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct StaticModuleResolver(BTreeMap<Identifier, Shared<Module>>);
|
||||
pub struct StaticModuleResolver(BTreeMap<Identifier, SharedModule>);
|
||||
|
||||
impl StaticModuleResolver {
|
||||
/// Create a new [`StaticModuleResolver`].
|
||||
@@ -65,7 +66,7 @@ impl StaticModuleResolver {
|
||||
}
|
||||
/// Remove a [module][Module] given its path.
|
||||
#[inline(always)]
|
||||
pub fn remove(&mut self, path: &str) -> Option<Shared<Module>> {
|
||||
pub fn remove(&mut self, path: &str) -> Option<SharedModule> {
|
||||
self.0.remove(path)
|
||||
}
|
||||
/// Does the path exist?
|
||||
@@ -80,12 +81,12 @@ impl StaticModuleResolver {
|
||||
}
|
||||
/// Get an iterator of all the [modules][Module].
|
||||
#[inline]
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
||||
self.0.iter().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
/// Get a mutable iterator of all the [modules][Module].
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Shared<Module>)> {
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut SharedModule)> {
|
||||
self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
/// Get an iterator of all the [module][Module] paths.
|
||||
@@ -95,7 +96,7 @@ impl StaticModuleResolver {
|
||||
}
|
||||
/// Get an iterator of all the [modules][Module].
|
||||
#[inline(always)]
|
||||
pub fn values(&self) -> impl Iterator<Item = &Shared<Module>> {
|
||||
pub fn values(&self) -> impl Iterator<Item = &SharedModule> {
|
||||
self.0.values()
|
||||
}
|
||||
/// Remove all [modules][Module].
|
||||
@@ -130,18 +131,19 @@ impl StaticModuleResolver {
|
||||
}
|
||||
|
||||
impl IntoIterator for StaticModuleResolver {
|
||||
type Item = (Identifier, Shared<Module>);
|
||||
type IntoIter = IntoIter<SmartString, Shared<Module>>;
|
||||
type Item = (Identifier, SharedModule);
|
||||
type IntoIter = IntoIter<SmartString, SharedModule>;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a StaticModuleResolver {
|
||||
type Item = (&'a Identifier, &'a Shared<Module>);
|
||||
type IntoIter = Iter<'a, SmartString, Shared<Module>>;
|
||||
type Item = (&'a Identifier, &'a SharedModule);
|
||||
type IntoIter = Iter<'a, SmartString, SharedModule>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
@@ -157,7 +159,7 @@ impl ModuleResolver for StaticModuleResolver {
|
||||
_: Option<&str>,
|
||||
path: &str,
|
||||
pos: Position,
|
||||
) -> RhaiResultOf<Shared<Module>> {
|
||||
) -> RhaiResultOf<SharedModule> {
|
||||
self.0
|
||||
.get(path)
|
||||
.cloned()
|
||||
|
||||
244
src/optimizer.rs
244
src/optimizer.rs
@@ -8,11 +8,11 @@ use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT,
|
||||
use crate::eval::{Caches, GlobalRuntimeState};
|
||||
use crate::func::builtin::get_builtin_binary_op_fn;
|
||||
use crate::func::hashing::get_hasher;
|
||||
use crate::tokenizer::{Span, Token};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier,
|
||||
ImmutableString, Position, Scope, StaticVec, AST, INT,
|
||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position,
|
||||
Scope, StaticVec, AST, INT,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -38,6 +38,7 @@ pub enum OptimizationLevel {
|
||||
|
||||
impl Default for OptimizationLevel {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::Simple
|
||||
}
|
||||
@@ -49,18 +50,18 @@ struct OptimizerState<'a> {
|
||||
/// Has the [`AST`] been changed during this pass?
|
||||
changed: bool,
|
||||
/// Collection of constants to use for eager function evaluations.
|
||||
variables: StaticVec<(Identifier, AccessMode, Option<Dynamic>)>,
|
||||
variables: StaticVec<(Identifier, AccessMode, Dynamic)>,
|
||||
/// Activate constants propagation?
|
||||
propagate_constants: bool,
|
||||
/// An [`Engine`] instance for eager function evaluation.
|
||||
engine: &'a Engine,
|
||||
/// The global runtime state.
|
||||
global: GlobalRuntimeState<'a>,
|
||||
global: GlobalRuntimeState,
|
||||
/// Function resolution caches.
|
||||
caches: Caches<'a>,
|
||||
caches: Caches,
|
||||
/// [Module][crate::Module] containing script-defined functions.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
lib: &'a [&'a crate::Module],
|
||||
lib: &'a [crate::SharedModule],
|
||||
/// Optimization level.
|
||||
optimization_level: OptimizationLevel,
|
||||
}
|
||||
@@ -70,7 +71,7 @@ impl<'a> OptimizerState<'a> {
|
||||
#[inline(always)]
|
||||
pub fn new(
|
||||
engine: &'a Engine,
|
||||
#[cfg(not(feature = "no_function"))] lib: &'a [&'a crate::Module],
|
||||
#[cfg(not(feature = "no_function"))] lib: &'a [crate::SharedModule],
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -107,12 +108,7 @@ impl<'a> OptimizerState<'a> {
|
||||
}
|
||||
/// Add a new variable to the list.
|
||||
#[inline(always)]
|
||||
pub fn push_var(
|
||||
&mut self,
|
||||
name: impl Into<Identifier>,
|
||||
access: AccessMode,
|
||||
value: Option<Dynamic>,
|
||||
) {
|
||||
pub fn push_var(&mut self, name: impl Into<Identifier>, access: AccessMode, value: Dynamic) {
|
||||
self.variables.push((name.into(), access, value));
|
||||
}
|
||||
/// Look up a constant from the list.
|
||||
@@ -126,7 +122,8 @@ impl<'a> OptimizerState<'a> {
|
||||
if n == name {
|
||||
return match access {
|
||||
AccessMode::ReadWrite => None,
|
||||
AccessMode::ReadOnly => value.as_ref(),
|
||||
AccessMode::ReadOnly if value.is_null() => None,
|
||||
AccessMode::ReadOnly => Some(value),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -138,28 +135,27 @@ impl<'a> OptimizerState<'a> {
|
||||
pub fn call_fn_with_constant_arguments(
|
||||
&mut self,
|
||||
fn_name: &str,
|
||||
op_token: Option<&Token>,
|
||||
arg_values: &mut [Dynamic],
|
||||
) -> Option<Dynamic> {
|
||||
) -> Dynamic {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let lib = self.lib;
|
||||
#[cfg(feature = "no_function")]
|
||||
let lib = &[];
|
||||
|
||||
self.engine
|
||||
.call_native_fn(
|
||||
.exec_native_fn_call(
|
||||
&mut self.global,
|
||||
&mut self.caches,
|
||||
lib,
|
||||
fn_name,
|
||||
op_token,
|
||||
calc_fn_hash(None, fn_name, arg_values.len()),
|
||||
&mut arg_values.iter_mut().collect::<StaticVec<_>>(),
|
||||
false,
|
||||
false,
|
||||
Position::NONE,
|
||||
0,
|
||||
)
|
||||
.ok()
|
||||
.map(|(v, ..)| v)
|
||||
.map_or(Dynamic::NULL, |(v, ..)| v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,26 +165,30 @@ fn has_native_fn_override(
|
||||
hash_script: u64,
|
||||
arg_types: impl AsRef<[TypeId]>,
|
||||
) -> bool {
|
||||
let hash_params = calc_fn_params_hash(arg_types.as_ref().iter().copied());
|
||||
let hash = combine_hashes(hash_script, hash_params);
|
||||
let hash = calc_fn_hash_full(hash_script, arg_types.as_ref().iter().copied());
|
||||
|
||||
// First check the global namespace and packages, but skip modules that are standard because
|
||||
// they should never conflict with system functions.
|
||||
let result = engine
|
||||
if engine
|
||||
.global_modules
|
||||
.iter()
|
||||
.filter(|m| !m.standard)
|
||||
.any(|m| m.contains_fn(hash));
|
||||
.any(|m| m.contains_fn(hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
// Then check sub-modules
|
||||
let result = result
|
||||
|| engine
|
||||
.global_sub_modules
|
||||
.values()
|
||||
.any(|m| m.contains_qualified_fn(hash));
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
if engine
|
||||
.global_sub_modules
|
||||
.values()
|
||||
.any(|m| m.contains_qualified_fn(hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
result
|
||||
false
|
||||
}
|
||||
|
||||
/// Optimize a block of [statements][Stmt].
|
||||
@@ -254,7 +254,7 @@ fn optimize_stmt_block(
|
||||
});
|
||||
|
||||
// Optimize each statement in the block
|
||||
for stmt in statements.iter_mut() {
|
||||
for stmt in &mut statements {
|
||||
match stmt {
|
||||
Stmt::Var(x, options, ..) => {
|
||||
if options.contains(ASTFlags::CONSTANT) {
|
||||
@@ -265,13 +265,13 @@ fn optimize_stmt_block(
|
||||
state.push_var(
|
||||
x.0.as_str(),
|
||||
AccessMode::ReadOnly,
|
||||
x.1.get_literal_value(),
|
||||
x.1.get_literal_value().unwrap_or(Dynamic::NULL),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Add variables into the state
|
||||
optimize_expr(&mut x.1, state, false);
|
||||
state.push_var(x.0.as_str(), AccessMode::ReadWrite, None);
|
||||
state.push_var(x.0.as_str(), AccessMode::ReadWrite, Dynamic::NULL);
|
||||
}
|
||||
}
|
||||
// Optimize the statement
|
||||
@@ -432,15 +432,15 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
if !x.0.is_op_assignment()
|
||||
&& x.1.lhs.is_variable_access(true)
|
||||
&& matches!(&x.1.rhs, Expr::FnCall(x2, ..)
|
||||
if Token::lookup_from_syntax(&x2.name).map_or(false, |t| t.has_op_assignment())
|
||||
if Token::lookup_symbol_from_syntax(&x2.name).map_or(false, |t| t.has_op_assignment())
|
||||
&& x2.args.len() == 2
|
||||
&& x2.args[0].get_variable_name(true) == x.1.lhs.get_variable_name(true)
|
||||
) =>
|
||||
{
|
||||
match x.1.rhs {
|
||||
Expr::FnCall(ref mut x2, ..) => {
|
||||
Expr::FnCall(ref mut x2, pos) => {
|
||||
state.set_dirty();
|
||||
x.0 = OpAssignment::new_op_assignment_from_base(&x2.name, x2.pos);
|
||||
x.0 = OpAssignment::new_op_assignment_from_base(&x2.name, pos);
|
||||
x.1.rhs = mem::take(&mut x2.args[1]);
|
||||
}
|
||||
ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr),
|
||||
@@ -550,13 +550,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 }
|
||||
optimize_expr(&mut b.condition, state, false);
|
||||
|
||||
let else_stmt = if let Some(index) = def_case {
|
||||
let mut def_stmt =
|
||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
optimize_stmt(&mut def_stmt, state, true);
|
||||
def_stmt.into()
|
||||
} else {
|
||||
StmtBlock::NONE
|
||||
let else_stmt = match def_case {
|
||||
Some(index) => {
|
||||
let mut def_stmt =
|
||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
optimize_stmt(&mut def_stmt, state, true);
|
||||
def_stmt.into()
|
||||
}
|
||||
_ => StmtBlock::NONE,
|
||||
};
|
||||
|
||||
*stmt = Stmt::If(
|
||||
@@ -615,13 +616,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 }
|
||||
optimize_expr(&mut condition, state, false);
|
||||
|
||||
let else_stmt = if let Some(index) = def_case {
|
||||
let mut def_stmt =
|
||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
optimize_stmt(&mut def_stmt, state, true);
|
||||
def_stmt.into()
|
||||
} else {
|
||||
StmtBlock::NONE
|
||||
let else_stmt = match def_case {
|
||||
Some(index) => {
|
||||
let mut def_stmt =
|
||||
Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
optimize_stmt(&mut def_stmt, state, true);
|
||||
def_stmt.into()
|
||||
}
|
||||
_ => StmtBlock::NONE,
|
||||
};
|
||||
|
||||
let if_stmt =
|
||||
@@ -664,12 +666,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
// Promote the default case
|
||||
state.set_dirty();
|
||||
|
||||
if let Some(index) = def_case {
|
||||
let mut def_stmt = Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
optimize_stmt(&mut def_stmt, state, true);
|
||||
*stmt = def_stmt;
|
||||
} else {
|
||||
*stmt = StmtBlock::empty(*pos).into();
|
||||
match def_case {
|
||||
Some(index) => {
|
||||
let mut def_stmt = Stmt::Expr(mem::take(&mut expressions[*index].expr).into());
|
||||
optimize_stmt(&mut def_stmt, state, true);
|
||||
*stmt = def_stmt;
|
||||
}
|
||||
_ => *stmt = StmtBlock::empty(*pos).into(),
|
||||
}
|
||||
}
|
||||
// switch
|
||||
@@ -688,7 +691,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
optimize_expr(match_expr, state, false);
|
||||
|
||||
// Optimize blocks
|
||||
for b in expressions.iter_mut() {
|
||||
for b in expressions.as_mut() {
|
||||
optimize_expr(&mut b.condition, state, false);
|
||||
optimize_expr(&mut b.expr, state, false);
|
||||
|
||||
@@ -776,50 +779,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
*condition = Expr::Unit(*pos);
|
||||
}
|
||||
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
|
||||
|
||||
if body.len() == 1 {
|
||||
match body[0] {
|
||||
// while expr { break; } -> { expr; }
|
||||
Stmt::BreakLoop(options, pos) if options.contains(ASTFlags::BREAK) => {
|
||||
// Only a single break statement - turn into running the guard expression once
|
||||
state.set_dirty();
|
||||
if condition.is_unit() {
|
||||
*stmt = Stmt::Noop(pos);
|
||||
} else {
|
||||
let mut statements = vec![Stmt::Expr(mem::take(condition).into())];
|
||||
if preserve_result {
|
||||
statements.push(Stmt::Noop(pos));
|
||||
}
|
||||
*stmt = (statements, Span::new(pos, Position::NONE)).into();
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
// do { block } until true -> { block }
|
||||
Stmt::Do(x, options, ..)
|
||||
if matches!(x.0, Expr::BoolConstant(true, ..))
|
||||
&& options.contains(ASTFlags::NEGATED) =>
|
||||
{
|
||||
state.set_dirty();
|
||||
*stmt = (
|
||||
optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false),
|
||||
x.1.span(),
|
||||
)
|
||||
.into();
|
||||
}
|
||||
// do { block } while false -> { block }
|
||||
Stmt::Do(x, options, ..)
|
||||
if matches!(x.0, Expr::BoolConstant(false, ..))
|
||||
&& !options.contains(ASTFlags::NEGATED) =>
|
||||
{
|
||||
state.set_dirty();
|
||||
*stmt = (
|
||||
optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false),
|
||||
x.1.span(),
|
||||
)
|
||||
.into();
|
||||
}
|
||||
// do { block } while|until expr
|
||||
Stmt::Do(x, ..) => {
|
||||
@@ -893,27 +852,23 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
Stmt::Expr(expr) => {
|
||||
optimize_expr(expr, state, false);
|
||||
|
||||
match &mut **expr {
|
||||
// func(...)
|
||||
Expr::FnCall(x, pos) => {
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::FnCall(mem::take(x), *pos);
|
||||
}
|
||||
// {...};
|
||||
Expr::Stmt(x) => {
|
||||
if x.is_empty() {
|
||||
state.set_dirty();
|
||||
*stmt = Stmt::Noop(x.position());
|
||||
} else {
|
||||
state.set_dirty();
|
||||
*stmt = mem::take(&mut **x).into();
|
||||
}
|
||||
}
|
||||
// expr;
|
||||
_ => (),
|
||||
if matches!(**expr, Expr::FnCall(..) | Expr::Stmt(..)) {
|
||||
state.set_dirty();
|
||||
*stmt = match *mem::take(expr) {
|
||||
// func(...);
|
||||
Expr::FnCall(x, pos) => Stmt::FnCall(x, pos),
|
||||
// {};
|
||||
Expr::Stmt(x) if x.is_empty() => Stmt::Noop(x.position()),
|
||||
// {...};
|
||||
Expr::Stmt(x) => (*x).into(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// break expr;
|
||||
Stmt::BreakLoop(Some(ref mut expr), ..) => optimize_expr(expr, state, false),
|
||||
|
||||
// return expr;
|
||||
Stmt::Return(Some(ref mut expr), ..) => optimize_expr(expr, state, false),
|
||||
|
||||
@@ -1137,7 +1092,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||
&& x.args.len() == 1
|
||||
&& x.name == KEYWORD_FN_PTR
|
||||
&& x.args[0].is_constant()
|
||||
&& x.constant_args()
|
||||
=> {
|
||||
let fn_name = match x.args[0] {
|
||||
Expr::StringConstant(ref s, ..) => s.clone().into(),
|
||||
@@ -1161,8 +1116,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
Expr::FnCall(x, pos)
|
||||
if !x.is_qualified() // Non-qualified
|
||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||
//&& !is_valid_identifier(x.chars()) // cannot be scripted
|
||||
&& x.constant_args() // all arguments are constants
|
||||
=> {
|
||||
let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
|
||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||
@@ -1181,15 +1135,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
return;
|
||||
}
|
||||
// Overloaded operators can override built-in.
|
||||
_ if x.args.len() == 2 && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => {
|
||||
if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1])
|
||||
_ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => {
|
||||
if let Some(result) = get_builtin_binary_op_fn(x.op_token.as_ref().unwrap(), &arg_values[0], &arg_values[1])
|
||||
.and_then(|f| {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let lib = state.lib;
|
||||
#[cfg(feature = "no_function")]
|
||||
let lib = &[];
|
||||
let lib = &[][..];
|
||||
|
||||
let context = (state.engine, &x.name, lib).into();
|
||||
let context = (state.engine, x.name.as_str(),None, &state.global, lib, *pos).into();
|
||||
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||
(f)(context, &mut [ first, &mut second[0] ]).ok()
|
||||
}) {
|
||||
@@ -1204,7 +1158,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
||||
|
||||
// Move constant arguments
|
||||
for arg in x.args.iter_mut() {
|
||||
for arg in &mut x.args {
|
||||
match arg {
|
||||
Expr::DynamicConstant(..) | Expr::Unit(..)
|
||||
| Expr::StringConstant(..) | Expr::CharConstant(..)
|
||||
@@ -1225,25 +1179,25 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
Expr::FnCall(x, pos)
|
||||
if !x.is_qualified() // non-qualified
|
||||
&& state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||
&& x.constant_args() // all arguments are constants
|
||||
=> {
|
||||
// First search for script-defined functions (can override built-in)
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let has_script_fn = state.lib.iter().copied().any(|m| m.get_script_fn(&x.name, x.args.len()).is_some());
|
||||
let has_script_fn = !x.hashes.is_native_only() && state.lib.iter().find_map(|m| m.get_script_fn(&x.name, x.args.len())).is_some();
|
||||
#[cfg(feature = "no_function")]
|
||||
let has_script_fn = false;
|
||||
|
||||
if !has_script_fn {
|
||||
let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
|
||||
let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<StaticVec<_>>>().unwrap();
|
||||
|
||||
let result = match x.name.as_str() {
|
||||
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
|
||||
KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
|
||||
_ => state.call_fn_with_constant_arguments(&x.name, arg_values)
|
||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE,
|
||||
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values)
|
||||
};
|
||||
|
||||
if let Some(result) = result {
|
||||
if !result.is_null() {
|
||||
state.set_dirty();
|
||||
*expr = Expr::from_dynamic(result, *pos);
|
||||
return;
|
||||
@@ -1254,7 +1208,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
}
|
||||
|
||||
// id(args ..) or xxx.id(args ..) -> optimize function call arguments
|
||||
Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => for arg in x.args.iter_mut() {
|
||||
Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => for arg in &mut x.args {
|
||||
optimize_expr(arg, state, false);
|
||||
|
||||
// Move constant arguments
|
||||
@@ -1303,7 +1257,7 @@ fn optimize_top_level(
|
||||
statements: StmtBlockContainer,
|
||||
engine: &Engine,
|
||||
scope: &Scope,
|
||||
#[cfg(not(feature = "no_function"))] lib: &[&crate::Module],
|
||||
#[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule],
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> StmtBlockContainer {
|
||||
let mut statements = statements;
|
||||
@@ -1329,15 +1283,15 @@ fn optimize_top_level(
|
||||
.rev()
|
||||
.flat_map(|m| m.iter_var())
|
||||
{
|
||||
state.push_var(name, AccessMode::ReadOnly, Some(value.clone()));
|
||||
state.push_var(name, AccessMode::ReadOnly, value.clone());
|
||||
}
|
||||
|
||||
// Add constants and variables from the scope
|
||||
for (name, constant, value) in scope.iter() {
|
||||
if constant {
|
||||
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
||||
state.push_var(name, AccessMode::ReadOnly, value);
|
||||
} else {
|
||||
state.push_var(name, AccessMode::ReadWrite, None);
|
||||
state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1357,7 +1311,7 @@ pub fn optimize_into_ast(
|
||||
let mut statements = statements;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let lib = {
|
||||
let lib: crate::Shared<_> = {
|
||||
let mut module = crate::Module::new();
|
||||
|
||||
if optimization_level != OptimizationLevel::None {
|
||||
@@ -1378,7 +1332,7 @@ pub fn optimize_into_ast(
|
||||
});
|
||||
}
|
||||
|
||||
let lib2 = &[&lib2];
|
||||
let lib2 = &[lib2.into()];
|
||||
|
||||
for fn_def in functions {
|
||||
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
|
||||
@@ -1396,7 +1350,7 @@ pub fn optimize_into_ast(
|
||||
}
|
||||
}
|
||||
|
||||
module
|
||||
module.into()
|
||||
};
|
||||
|
||||
statements.shrink_to_fit();
|
||||
@@ -1409,7 +1363,7 @@ pub fn optimize_into_ast(
|
||||
engine,
|
||||
scope,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
&[&lib],
|
||||
&[lib.clone()],
|
||||
optimization_level,
|
||||
),
|
||||
},
|
||||
|
||||
@@ -488,39 +488,29 @@ mod f64_functions {
|
||||
#[cfg(feature = "decimal")]
|
||||
#[export_module]
|
||||
pub mod decimal_functions {
|
||||
use num_traits::Pow;
|
||||
use rust_decimal::{prelude::Zero, Decimal, MathematicalOps};
|
||||
use rust_decimal::{prelude::Zero, Decimal};
|
||||
|
||||
#[rhai_fn(skip, return_raw)]
|
||||
pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub mod builtin {
|
||||
use rust_decimal::MathematicalOps;
|
||||
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn add(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
x.checked_add(y)
|
||||
.ok_or_else(|| make_err(format!("Addition overflow: {x} + {y}")))
|
||||
} else {
|
||||
Ok(x + y)
|
||||
}
|
||||
}
|
||||
#[rhai_fn(skip, return_raw)]
|
||||
pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn subtract(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
x.checked_sub(y)
|
||||
.ok_or_else(|| make_err(format!("Subtraction overflow: {x} - {y}")))
|
||||
} else {
|
||||
Ok(x - y)
|
||||
}
|
||||
}
|
||||
#[rhai_fn(skip, return_raw)]
|
||||
pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn multiply(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
x.checked_mul(y)
|
||||
.ok_or_else(|| make_err(format!("Multiplication overflow: {x} * {y}")))
|
||||
} else {
|
||||
Ok(x * y)
|
||||
}
|
||||
}
|
||||
#[rhai_fn(skip, return_raw)]
|
||||
pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn divide(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
// Detect division by zero
|
||||
if y == Decimal::zero() {
|
||||
Err(make_err(format!("Division by zero: {x} / {y}")))
|
||||
@@ -528,26 +518,16 @@ pub mod decimal_functions {
|
||||
x.checked_div(y)
|
||||
.ok_or_else(|| make_err(format!("Division overflow: {x} / {y}")))
|
||||
}
|
||||
} else {
|
||||
Ok(x / y)
|
||||
}
|
||||
}
|
||||
#[rhai_fn(skip, return_raw)]
|
||||
pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn modulo(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
x.checked_rem(y)
|
||||
.ok_or_else(|| make_err(format!("Modulo division by zero or overflow: {x} % {y}")))
|
||||
} else {
|
||||
Ok(x % y)
|
||||
}
|
||||
}
|
||||
#[rhai_fn(skip, return_raw)]
|
||||
pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
|
||||
x.checked_powd(y)
|
||||
.ok_or_else(|| make_err(format!("Exponential overflow: {x} ** {y}")))
|
||||
} else {
|
||||
Ok(x.pow(y))
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "-")]
|
||||
|
||||
@@ -236,9 +236,8 @@ pub mod array_functions {
|
||||
}
|
||||
|
||||
let check_sizes = match item.0 {
|
||||
crate::types::dynamic::Union::Array(..) | crate::types::dynamic::Union::Str(..) => {
|
||||
true
|
||||
}
|
||||
crate::types::dynamic::Union::Str(..) => true,
|
||||
crate::types::dynamic::Union::Array(..) => true,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
crate::types::dynamic::Union::Map(..) => true,
|
||||
_ => false,
|
||||
@@ -260,7 +259,7 @@ pub mod array_functions {
|
||||
s1 += s2;
|
||||
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((a1, m1, s1), Position::NONE)?;
|
||||
.raise_err_if_over_data_size_limit((a1, m1, s1))?;
|
||||
|
||||
guard.push(item.clone());
|
||||
arr_len += 1;
|
||||
@@ -834,9 +833,9 @@ pub mod array_functions {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
for item in array.iter_mut() {
|
||||
for item in array {
|
||||
if ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||
.or_else(|err| match *err {
|
||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||
if item.type_id() == value.type_id() {
|
||||
@@ -928,7 +927,7 @@ pub mod array_functions {
|
||||
|
||||
for (i, item) in array.iter_mut().enumerate().skip(start) {
|
||||
if ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||
.or_else(|err| match *err {
|
||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||
if item.type_id() == value.type_id() {
|
||||
@@ -2314,7 +2313,7 @@ pub mod array_functions {
|
||||
|
||||
for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
|
||||
if !ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2])
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2])
|
||||
.or_else(|err| match *err {
|
||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||
if a1.type_id() == a2.type_id() {
|
||||
|
||||
@@ -80,11 +80,8 @@ pub mod blob_functions {
|
||||
|
||||
// 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() {
|
||||
return Err(
|
||||
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
|
||||
);
|
||||
}
|
||||
_ctx.engine()
|
||||
.raise_err_if_over_data_size_limit((len, 0, 0))?;
|
||||
|
||||
let mut blob = Blob::new();
|
||||
blob.resize(len, (value & 0x0000_00ff) as u8);
|
||||
@@ -146,6 +143,21 @@ pub mod blob_functions {
|
||||
pub fn is_empty(blob: &mut Blob) -> bool {
|
||||
blob.len() == 0
|
||||
}
|
||||
/// Return `true` if the BLOB contains a specified byte value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.contains('h')); // prints true
|
||||
///
|
||||
/// print(text.contains('x')); // prints false
|
||||
/// ```
|
||||
#[rhai_fn(name = "contains")]
|
||||
pub fn contains(blob: &mut Blob, value: INT) -> bool {
|
||||
blob.contains(&((value & 0x0000_00ff) as u8))
|
||||
}
|
||||
/// Get the byte value at the `index` position in the BLOB.
|
||||
///
|
||||
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element).
|
||||
@@ -349,7 +361,6 @@ pub mod blob_functions {
|
||||
let _ctx = ctx;
|
||||
|
||||
// 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() {
|
||||
return Err(
|
||||
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
|
||||
|
||||
@@ -33,57 +33,47 @@ mod debugging_functions {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub fn back_trace(ctx: NativeCallContext) -> Array {
|
||||
if let Some(global) = ctx.global_runtime_state() {
|
||||
global
|
||||
.debugger
|
||||
.call_stack()
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| {
|
||||
fn_name != "back_trace" || !args.is_empty()
|
||||
})
|
||||
.map(
|
||||
|frame @ crate::debugger::CallStackFrame {
|
||||
fn_name: _fn_name,
|
||||
args: _args,
|
||||
source: _source,
|
||||
pos: _pos,
|
||||
}| {
|
||||
let display = frame.to_string();
|
||||
ctx.global_runtime_state()
|
||||
.debugger
|
||||
.call_stack()
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| {
|
||||
fn_name.as_str() != "back_trace" || !args.is_empty()
|
||||
})
|
||||
.map(
|
||||
|frame @ crate::debugger::CallStackFrame {
|
||||
fn_name: _fn_name,
|
||||
args: _args,
|
||||
source: _source,
|
||||
pos: _pos,
|
||||
}| {
|
||||
let display = frame.to_string();
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let mut map = Map::new();
|
||||
map.insert("display".into(), display.into());
|
||||
map.insert("fn_name".into(), _fn_name.into());
|
||||
if !_args.is_empty() {
|
||||
map.insert(
|
||||
"args".into(),
|
||||
Dynamic::from_array(_args.clone().to_vec()),
|
||||
);
|
||||
}
|
||||
if !_source.is_empty() {
|
||||
map.insert("source".into(), _source.into());
|
||||
}
|
||||
if !_pos.is_none() {
|
||||
map.insert(
|
||||
"line".into(),
|
||||
(_pos.line().unwrap() as crate::INT).into(),
|
||||
);
|
||||
map.insert(
|
||||
"position".into(),
|
||||
(_pos.position().unwrap_or(0) as crate::INT).into(),
|
||||
);
|
||||
}
|
||||
Dynamic::from_map(map)
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let mut map = Map::new();
|
||||
map.insert("display".into(), display.into());
|
||||
map.insert("fn_name".into(), _fn_name.into());
|
||||
if !_args.is_empty() {
|
||||
map.insert("args".into(), Dynamic::from_array(_args.clone().to_vec()));
|
||||
}
|
||||
#[cfg(feature = "no_object")]
|
||||
display.into()
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
} else {
|
||||
Array::new()
|
||||
}
|
||||
if let Some(source) = _source {
|
||||
map.insert("source".into(), source.into());
|
||||
}
|
||||
if !_pos.is_none() {
|
||||
map.insert("line".into(), (_pos.line().unwrap() as crate::INT).into());
|
||||
map.insert(
|
||||
"position".into(),
|
||||
(_pos.position().unwrap_or(0) as crate::INT).into(),
|
||||
);
|
||||
}
|
||||
Dynamic::from_map(map)
|
||||
}
|
||||
#[cfg(feature = "no_object")]
|
||||
display.into()
|
||||
},
|
||||
)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ mod fn_ptr_functions {
|
||||
/// ```
|
||||
#[rhai_fn(name = "name", get = "name", pure)]
|
||||
pub fn name(fn_ptr: &mut FnPtr) -> ImmutableString {
|
||||
fn_ptr.fn_name_raw().into()
|
||||
fn_ptr.fn_name_raw().clone()
|
||||
}
|
||||
|
||||
/// Return `true` if the function is an anonymous function.
|
||||
|
||||
@@ -47,6 +47,8 @@ pub struct StepRange<T: Debug + Copy + PartialOrd> {
|
||||
}
|
||||
|
||||
impl<T: Debug + Copy + PartialOrd> Debug for StepRange<T> {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(&format!("StepRange<{}>", type_name::<T>()))
|
||||
.field(&self.from)
|
||||
@@ -120,7 +122,7 @@ impl<T: Debug + Copy + PartialOrd> FusedIterator for StepRange<T> {}
|
||||
|
||||
// Bit-field iterator with step
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct BitRange(INT, INT, usize);
|
||||
pub struct BitRange(INT, usize);
|
||||
|
||||
impl BitRange {
|
||||
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
||||
@@ -136,7 +138,7 @@ impl BitRange {
|
||||
len as usize
|
||||
};
|
||||
|
||||
Ok(Self(value, 1 << from, len))
|
||||
Ok(Self(value >> from, len))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,19 +146,19 @@ impl Iterator for BitRange {
|
||||
type Item = bool;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.2 == 0 {
|
||||
if self.1 == 0 {
|
||||
None
|
||||
} else {
|
||||
let r = (self.0 & self.1) != 0;
|
||||
self.1 <<= 1;
|
||||
self.2 -= 1;
|
||||
let r = (self.0 & 0x0001) != 0;
|
||||
self.0 >>= 1;
|
||||
self.1 -= 1;
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.2, Some(self.2))
|
||||
(self.1, Some(self.1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +167,7 @@ impl FusedIterator for BitRange {}
|
||||
impl ExactSizeIterator for BitRange {
|
||||
#[inline(always)]
|
||||
fn len(&self) -> usize {
|
||||
self.2
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,6 +670,12 @@ mod range_functions {
|
||||
pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool {
|
||||
range.is_empty()
|
||||
}
|
||||
/// Return `true` if the range contains a specified value.
|
||||
#[rhai_fn(name = "contains")]
|
||||
pub fn contains_exclusive(range: &mut ExclusiveRange, value: INT) -> bool {
|
||||
range.contains(&value)
|
||||
}
|
||||
|
||||
/// Return the start of the inclusive range.
|
||||
#[rhai_fn(get = "start", name = "start", pure)]
|
||||
pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
|
||||
@@ -695,4 +703,9 @@ mod range_functions {
|
||||
pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool {
|
||||
range.is_empty()
|
||||
}
|
||||
/// Return `true` if the range contains a specified value.
|
||||
#[rhai_fn(name = "contains")]
|
||||
pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool {
|
||||
range.contains(&value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ mod core_functions {
|
||||
#[cfg(feature = "f32_float")]
|
||||
std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
|
||||
}
|
||||
|
||||
/// Block the current thread for a particular number of `seconds`.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub fn sleep(seconds: INT) {
|
||||
@@ -97,6 +96,23 @@ mod core_functions {
|
||||
}
|
||||
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"))]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::engine::OP_EQUALS;
|
||||
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")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -30,6 +30,20 @@ mod map_functions {
|
||||
pub fn is_empty(map: &mut Map) -> bool {
|
||||
map.len() == 0
|
||||
}
|
||||
/// Returns `true` if the object map contains a specified property.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let m = #{a: 1, b: 2, c: 3};
|
||||
///
|
||||
/// print(m.contains("b")); // prints true
|
||||
///
|
||||
/// print(m.contains("x")); // prints false
|
||||
/// ```
|
||||
pub fn contains(map: &mut Map, property: &str) -> bool {
|
||||
map.contains_key(property)
|
||||
}
|
||||
/// Get the value of the `property` in the object map and return a copy.
|
||||
///
|
||||
/// If `property` does not exist in the object map, `()` is returned.
|
||||
@@ -68,10 +82,11 @@ mod map_functions {
|
||||
/// print(m); // prints "#{a: 1, b: 42, c: 3, x: 0}"
|
||||
/// ```
|
||||
pub fn set(map: &mut Map, property: &str, value: Dynamic) {
|
||||
if let Some(value_ref) = map.get_mut(property) {
|
||||
*value_ref = value;
|
||||
} else {
|
||||
map.insert(property.into(), value);
|
||||
match map.get_mut(property) {
|
||||
Some(value_ref) => *value_ref = value,
|
||||
_ => {
|
||||
map.insert(property.into(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Clear the object map.
|
||||
@@ -198,7 +213,7 @@ mod map_functions {
|
||||
for (m1, v1) in map1 {
|
||||
if let Some(v2) = map2.get_mut(m1) {
|
||||
let equals = ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [v1, v2])?
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [v1, v2])?
|
||||
.as_bool()
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -290,6 +305,9 @@ mod map_functions {
|
||||
/// print(m.to_json()); // prints {"a":1, "b":2, "c":3}
|
||||
/// ```
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages.
|
||||
|
||||
use crate::{Engine, Module, Shared};
|
||||
use crate::{Engine, Module, SharedModule};
|
||||
|
||||
pub(crate) mod arithmetic;
|
||||
pub(crate) mod array_basic;
|
||||
@@ -38,18 +38,21 @@ pub use pkg_core::CorePackage;
|
||||
pub use pkg_std::StandardPackage;
|
||||
pub use string_basic::BasicStringPackage;
|
||||
pub use string_more::MoreStringPackage;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
pub use time_basic::BasicTimePackage;
|
||||
|
||||
/// Trait that all packages must implement.
|
||||
pub trait Package {
|
||||
/// Initialize the package.
|
||||
/// Functions should be registered into `module` here.
|
||||
#[cold]
|
||||
fn init(module: &mut Module);
|
||||
|
||||
/// Initialize the package with an [`Engine`].
|
||||
///
|
||||
/// Perform tasks such as registering custom operators/syntax.
|
||||
#[cold]
|
||||
#[inline]
|
||||
#[allow(unused_variables)]
|
||||
fn init_engine(engine: &mut Engine) {}
|
||||
|
||||
@@ -65,6 +68,8 @@ pub trait Package {
|
||||
///
|
||||
/// package.register_into_engine(&mut engine);
|
||||
/// ```
|
||||
#[cold]
|
||||
#[inline]
|
||||
fn register_into_engine(&self, engine: &mut Engine) -> &Self {
|
||||
Self::init_engine(engine);
|
||||
engine.register_global_module(self.as_shared_module());
|
||||
@@ -84,6 +89,8 @@ pub trait Package {
|
||||
/// package.register_into_engine_as(&mut engine, "core");
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cold]
|
||||
#[inline]
|
||||
fn register_into_engine_as(&self, engine: &mut Engine, name: &str) -> &Self {
|
||||
Self::init_engine(engine);
|
||||
engine.register_static_module(name, self.as_shared_module());
|
||||
@@ -92,7 +99,7 @@ pub trait Package {
|
||||
|
||||
/// Get a reference to a shared module from this package.
|
||||
#[must_use]
|
||||
fn as_shared_module(&self) -> Shared<Module>;
|
||||
fn as_shared_module(&self) -> SharedModule;
|
||||
}
|
||||
|
||||
/// Macro that makes it easy to define a _package_ (which is basically a shared [module][Module])
|
||||
@@ -133,7 +140,6 @@ macro_rules! def_package {
|
||||
fn as_shared_module(&self) -> $crate::Shared<$crate::Module> {
|
||||
self.0.clone()
|
||||
}
|
||||
#[inline]
|
||||
fn init($lib: &mut $crate::Module) {
|
||||
$($(
|
||||
$(#[$base_meta])* { <$base_pkg>::init($lib); }
|
||||
@@ -141,7 +147,6 @@ macro_rules! def_package {
|
||||
|
||||
$block
|
||||
}
|
||||
#[inline]
|
||||
fn init_engine(_engine: &mut $crate::Engine) {
|
||||
$($(
|
||||
$(#[$base_meta])* { <$base_pkg>::init_engine(_engine); }
|
||||
@@ -156,6 +161,7 @@ macro_rules! def_package {
|
||||
|
||||
impl Default for $package {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
@@ -193,12 +199,16 @@ macro_rules! def_package {
|
||||
}
|
||||
|
||||
impl Default for $package {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl $package {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
let mut module = $root::Module::new();
|
||||
<Self as $root::packages::Package>::init(&mut module);
|
||||
@@ -229,12 +239,16 @@ macro_rules! def_package {
|
||||
}
|
||||
|
||||
impl Default for $package {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl $package {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
let mut module = $root::Module::new();
|
||||
<Self as $root::packages::Package>::init(&mut module);
|
||||
|
||||
@@ -26,7 +26,7 @@ def_package! {
|
||||
#[cfg(not(feature = "no_index"))] BasicArrayPackage,
|
||||
#[cfg(not(feature = "no_index"))] BasicBlobPackage,
|
||||
#[cfg(not(feature = "no_object"))] BasicMapPackage,
|
||||
#[cfg(not(feature = "no_std"))] BasicTimePackage,
|
||||
#[cfg(not(feature = "no_time"))] BasicTimePackage,
|
||||
MoreStringPackage
|
||||
{
|
||||
lib.standard = true;
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn print_with_func(
|
||||
ctx: &NativeCallContext,
|
||||
value: &mut Dynamic,
|
||||
) -> crate::ImmutableString {
|
||||
match ctx.call_fn_raw(fn_name, true, false, &mut [value]) {
|
||||
match ctx.call_native_fn_raw(fn_name, true, &mut [value]) {
|
||||
Ok(result) if result.is::<crate::ImmutableString>() => {
|
||||
result.into_immutable_string().expect("`ImmutableString`")
|
||||
}
|
||||
|
||||
@@ -239,10 +239,9 @@ mod string_functions {
|
||||
/// Clear the string, making it empty.
|
||||
pub fn clear(string: &mut ImmutableString) {
|
||||
if !string.is_empty() {
|
||||
if let Some(s) = string.get_mut() {
|
||||
s.clear();
|
||||
} else {
|
||||
*string = ImmutableString::new();
|
||||
match string.get_mut() {
|
||||
Some(s) => s.clear(),
|
||||
_ => *string = ImmutableString::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,17 +286,20 @@ mod string_functions {
|
||||
/// print(text); // prints "hello"
|
||||
/// ```
|
||||
pub fn trim(string: &mut ImmutableString) {
|
||||
if let Some(s) = string.get_mut() {
|
||||
let trimmed = s.trim();
|
||||
match string.get_mut() {
|
||||
Some(s) => {
|
||||
let trimmed = s.trim();
|
||||
|
||||
if trimmed != s {
|
||||
*s = trimmed.into();
|
||||
if trimmed != s {
|
||||
*s = trimmed.into();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let trimmed = string.trim();
|
||||
None => {
|
||||
let trimmed = string.trim();
|
||||
|
||||
if trimmed != string {
|
||||
*string = trimmed.into();
|
||||
if trimmed != string {
|
||||
*string = trimmed.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -506,6 +508,37 @@ mod string_functions {
|
||||
*character = to_lower_char(*character);
|
||||
}
|
||||
|
||||
/// Return `true` if the string contains a specified string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.contains("hello")); // prints true
|
||||
///
|
||||
/// print(text.contains("hey")); // prints false
|
||||
/// ```
|
||||
pub fn contains(string: &str, match_string: &str) -> bool {
|
||||
string.contains(match_string)
|
||||
}
|
||||
|
||||
/// Return `true` if the string contains a specified character.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rhai
|
||||
/// let text = "hello, world!";
|
||||
///
|
||||
/// print(text.contains('h')); // prints true
|
||||
///
|
||||
/// print(text.contains('x')); // prints false
|
||||
/// ```
|
||||
#[rhai_fn(name = "contains")]
|
||||
pub fn contains_char(string: &str, character: char) -> bool {
|
||||
string.contains(character).into()
|
||||
}
|
||||
|
||||
/// Return `true` if the string starts with a specified string.
|
||||
///
|
||||
/// # Example
|
||||
@@ -1185,7 +1218,6 @@ mod string_functions {
|
||||
let _ctx = ctx;
|
||||
|
||||
// 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() {
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
"Length of string".to_string(),
|
||||
@@ -1203,7 +1235,6 @@ mod string_functions {
|
||||
p.push(character);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
||||
{
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
@@ -1247,7 +1278,6 @@ mod string_functions {
|
||||
let _ctx = ctx;
|
||||
|
||||
// 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() {
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
"Length of string".to_string(),
|
||||
@@ -1272,7 +1302,6 @@ mod string_functions {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
|
||||
{
|
||||
return Err(crate::ERR::ErrorDataTooLarge(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#![cfg(not(feature = "no_std"))]
|
||||
#![cfg(not(feature = "no_time"))]
|
||||
|
||||
use super::arithmetic::make_err as make_arithmetic_err;
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT};
|
||||
use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::FLOAT;
|
||||
|
||||
471
src/parser.rs
471
src/parser.rs
File diff suppressed because it is too large
Load Diff
181
src/serde/de.rs
181
src/serde/de.rs
@@ -8,12 +8,17 @@ use serde::{Deserialize, Deserializer};
|
||||
use std::prelude::v1::*;
|
||||
use std::{any::type_name, fmt};
|
||||
|
||||
/// Deserializer for [`Dynamic`][crate::Dynamic] which is kept as a reference.
|
||||
///
|
||||
/// The reference is necessary because the deserialized type may hold references
|
||||
/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic].
|
||||
struct DynamicDeserializer<'a> {
|
||||
value: &'a Dynamic,
|
||||
/// Deserializer for [`Dynamic`][crate::Dynamic].
|
||||
pub struct DynamicDeserializer<'de>(&'de Dynamic);
|
||||
|
||||
impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic {
|
||||
type Deserializer = DynamicDeserializer<'de>;
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn into_deserializer(self) -> Self::Deserializer {
|
||||
DynamicDeserializer(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DynamicDeserializer<'de> {
|
||||
@@ -21,28 +26,28 @@ impl<'de> DynamicDeserializer<'de> {
|
||||
///
|
||||
/// The reference is necessary because the deserialized type may hold references
|
||||
/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn from_dynamic(value: &'de Dynamic) -> Self {
|
||||
Self { value }
|
||||
pub const fn new(value: &'de Dynamic) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
/// Shortcut for a type conversion error.
|
||||
#[cold]
|
||||
#[inline(always)]
|
||||
fn type_error<T>(&self) -> RhaiResultOf<T> {
|
||||
self.type_error_str(type_name::<T>())
|
||||
}
|
||||
/// Shortcut for a type conversion error.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn type_error_str<T>(&self, error: &str) -> RhaiResultOf<T> {
|
||||
Err(ERR::ErrorMismatchOutputType(
|
||||
error.into(),
|
||||
self.value.type_name().into(),
|
||||
Position::NONE,
|
||||
Err(
|
||||
ERR::ErrorMismatchOutputType(error.into(), self.0.type_name().into(), Position::NONE)
|
||||
.into(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
fn deserialize_int<V: Visitor<'de>>(
|
||||
&mut self,
|
||||
v: crate::INT,
|
||||
visitor: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
#[inline(always)]
|
||||
fn deserialize_int<V: Visitor<'de>>(self, v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return visitor.visit_i64(v);
|
||||
#[cfg(feature = "only_i32")]
|
||||
@@ -102,10 +107,12 @@ impl<'de> DynamicDeserializer<'de> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn from_dynamic<'de, T: Deserialize<'de>>(value: &'de Dynamic) -> RhaiResultOf<T> {
|
||||
T::deserialize(&mut DynamicDeserializer::from_dynamic(value))
|
||||
T::deserialize(DynamicDeserializer::new(value))
|
||||
}
|
||||
|
||||
impl Error for RhaiError {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn custom<T: fmt::Display>(err: T) -> Self {
|
||||
LexError::ImproperSymbol(String::new(), err.to_string())
|
||||
.into_err(Position::NONE)
|
||||
@@ -113,11 +120,13 @@ impl Error for RhaiError {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
|
||||
type Error = RhaiError;
|
||||
|
||||
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
match self.value.0 {
|
||||
match self.0 .0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => self.deserialize_unit(visitor),
|
||||
Union::Bool(..) => self.deserialize_bool(visitor),
|
||||
Union::Str(..) => self.deserialize_str(visitor),
|
||||
@@ -149,7 +158,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(..) => self.deserialize_map(visitor),
|
||||
Union::FnPtr(..) => self.type_error(),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(..) => self.type_error(),
|
||||
|
||||
Union::Variant(ref value, ..) if value.is::<i8>() => self.deserialize_i8(visitor),
|
||||
@@ -171,110 +180,110 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
}
|
||||
|
||||
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?)
|
||||
visitor.visit_bool(self.0.as_bool().or_else(|_| self.type_error())?)
|
||||
}
|
||||
|
||||
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<i8>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_i8(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<i16>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_i16(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else if cfg!(feature = "only_i32") {
|
||||
self.type_error()
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<i32>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_i32(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else if cfg!(not(feature = "only_i32")) {
|
||||
self.type_error()
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<i64>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else if cfg!(not(feature = "only_i32")) {
|
||||
self.type_error()
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<i128>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<u8>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_u8(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<u16>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_u16(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<u32>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_u32(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<u64>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if let Ok(v) = self.value.as_int() {
|
||||
if let Ok(v) = self.0.as_int() {
|
||||
self.deserialize_int(v, visitor)
|
||||
} else {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<u128>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x))
|
||||
}
|
||||
@@ -283,7 +292,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return self
|
||||
.value
|
||||
.0
|
||||
.downcast_ref::<f32>()
|
||||
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x));
|
||||
|
||||
@@ -293,7 +302,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
return self
|
||||
.value
|
||||
.0
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f32())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
|
||||
@@ -307,7 +316,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
return self
|
||||
.value
|
||||
.0
|
||||
.downcast_ref::<f64>()
|
||||
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x));
|
||||
|
||||
@@ -317,7 +326,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
return self
|
||||
.value
|
||||
.0
|
||||
.downcast_ref::<rust_decimal::Decimal>()
|
||||
.and_then(|&x| x.to_f64())
|
||||
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
|
||||
@@ -329,13 +338,13 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
}
|
||||
|
||||
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<char>()
|
||||
.map_or_else(|| self.type_error(), |&x| visitor.visit_char(x))
|
||||
}
|
||||
|
||||
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
self.value.downcast_ref::<ImmutableString>().map_or_else(
|
||||
self.0.downcast_ref::<ImmutableString>().map_or_else(
|
||||
|| self.type_error(),
|
||||
|x| visitor.visit_borrowed_str(x.as_str()),
|
||||
)
|
||||
@@ -348,7 +357,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
fn deserialize_bytes<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return self
|
||||
.value
|
||||
.0
|
||||
.downcast_ref::<crate::Blob>()
|
||||
.map_or_else(|| self.type_error(), |x| _visitor.visit_bytes(x));
|
||||
|
||||
@@ -361,7 +370,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
}
|
||||
|
||||
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
if self.value.is::<()>() {
|
||||
if self.0.is::<()>() {
|
||||
visitor.visit_none()
|
||||
} else {
|
||||
visitor.visit_some(self)
|
||||
@@ -369,7 +378,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
}
|
||||
|
||||
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
self.value
|
||||
self.0
|
||||
.downcast_ref::<()>()
|
||||
.map_or_else(|| self.type_error(), |_| visitor.visit_unit())
|
||||
}
|
||||
@@ -392,7 +401,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return self.value.downcast_ref::<crate::Array>().map_or_else(
|
||||
return self.0.downcast_ref::<crate::Array>().map_or_else(
|
||||
|| self.type_error(),
|
||||
|arr| _visitor.visit_seq(IterateDynamicArray::new(arr.iter())),
|
||||
);
|
||||
@@ -416,7 +425,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
|
||||
fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
return self.value.downcast_ref::<crate::Map>().map_or_else(
|
||||
return self.0.downcast_ref::<crate::Map>().map_or_else(
|
||||
|| self.type_error(),
|
||||
|map| {
|
||||
_visitor.visit_map(IterateMap::new(
|
||||
@@ -445,11 +454,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
if let Some(s) = self.value.read_lock::<ImmutableString>() {
|
||||
if let Some(s) = self.0.read_lock::<ImmutableString>() {
|
||||
visitor.visit_enum(s.as_str().into_deserializer())
|
||||
} else {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
return self.value.downcast_ref::<crate::Map>().map_or_else(
|
||||
return self.0.downcast_ref::<crate::Map>().map_or_else(
|
||||
|| self.type_error(),
|
||||
|map| {
|
||||
let mut iter = map.iter();
|
||||
@@ -458,7 +467,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
if let (Some((key, value)), None) = (first, second) {
|
||||
visitor.visit_enum(EnumDeserializer {
|
||||
tag: key,
|
||||
content: DynamicDeserializer::from_dynamic(value),
|
||||
content: DynamicDeserializer::new(value),
|
||||
})
|
||||
} else {
|
||||
self.type_error()
|
||||
@@ -470,10 +479,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
@@ -481,13 +492,14 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
||||
|
||||
/// `SeqAccess` implementation for arrays.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
struct IterateDynamicArray<'a, ITER: Iterator<Item = &'a Dynamic>> {
|
||||
struct IterateDynamicArray<'de, ITER: Iterator<Item = &'de Dynamic>> {
|
||||
/// Iterator for a stream of [`Dynamic`][crate::Dynamic] values.
|
||||
iter: ITER,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateDynamicArray<'a, ITER> {
|
||||
impl<'de, ITER: Iterator<Item = &'de Dynamic>> IterateDynamicArray<'de, ITER> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(iter: ITER) -> Self {
|
||||
Self { iter }
|
||||
@@ -495,8 +507,8 @@ impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateDynamicArray<'a, ITER> {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> serde::de::SeqAccess<'de>
|
||||
for IterateDynamicArray<'a, ITER>
|
||||
impl<'de, ITER: Iterator<Item = &'de Dynamic>> serde::de::SeqAccess<'de>
|
||||
for IterateDynamicArray<'de, ITER>
|
||||
{
|
||||
type Error = RhaiError;
|
||||
|
||||
@@ -506,17 +518,15 @@ impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> serde::de::SeqAccess<'de>
|
||||
) -> RhaiResultOf<Option<T::Value>> {
|
||||
// Deserialize each item coming out of the iterator.
|
||||
match self.iter.next() {
|
||||
Some(item) => seed.deserialize(item.into_deserializer()).map(Some),
|
||||
None => Ok(None),
|
||||
Some(item) => seed
|
||||
.deserialize(&mut DynamicDeserializer::from_dynamic(item))
|
||||
.map(Some),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `MapAccess` implementation for maps.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
struct IterateMap<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>> {
|
||||
struct IterateMap<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> {
|
||||
// Iterator for a stream of [`Dynamic`][crate::Dynamic] keys.
|
||||
keys: K,
|
||||
// Iterator for a stream of [`Dynamic`][crate::Dynamic] values.
|
||||
@@ -524,7 +534,8 @@ struct IterateMap<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynami
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
impl<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>> IterateMap<'a, K, V> {
|
||||
impl<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> IterateMap<'de, K, V> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn new(keys: K, values: V) -> Self {
|
||||
Self { keys, values }
|
||||
@@ -532,8 +543,8 @@ impl<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>> IterateMa
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
impl<'a: 'de, 'de, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>>
|
||||
serde::de::MapAccess<'de> for IterateMap<'a, K, V>
|
||||
impl<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> serde::de::MapAccess<'de>
|
||||
for IterateMap<'de, K, V>
|
||||
{
|
||||
type Error = RhaiError;
|
||||
|
||||
@@ -542,11 +553,9 @@ impl<'a: 'de, 'de, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>>
|
||||
seed: S,
|
||||
) -> RhaiResultOf<Option<S::Value>> {
|
||||
// Deserialize each `Identifier` key coming out of the keys iterator.
|
||||
match self.keys.next() {
|
||||
match self.keys.next().map(<_>::into_deserializer) {
|
||||
Some(d) => seed.deserialize(d).map(Some),
|
||||
None => Ok(None),
|
||||
Some(item) => seed
|
||||
.deserialize(&mut super::str::StringSliceDeserializer::from_str(item))
|
||||
.map(Some),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,20 +564,18 @@ impl<'a: 'de, 'de, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>>
|
||||
seed: S,
|
||||
) -> RhaiResultOf<S::Value> {
|
||||
// Deserialize each value item coming out of the iterator.
|
||||
seed.deserialize(&mut DynamicDeserializer::from_dynamic(
|
||||
self.values.next().unwrap(),
|
||||
))
|
||||
seed.deserialize(self.values.next().unwrap().into_deserializer())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
struct EnumDeserializer<'t, 'de: 't> {
|
||||
tag: &'t str,
|
||||
struct EnumDeserializer<'de> {
|
||||
tag: &'de str,
|
||||
content: DynamicDeserializer<'de>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> {
|
||||
impl<'de> serde::de::EnumAccess<'de> for EnumDeserializer<'de> {
|
||||
type Error = RhaiError;
|
||||
type Variant = Self;
|
||||
|
||||
@@ -582,26 +589,30 @@ impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> {
|
||||
impl<'de> serde::de::VariantAccess<'de> for EnumDeserializer<'de> {
|
||||
type Error = RhaiError;
|
||||
|
||||
fn unit_variant(mut self) -> RhaiResultOf<()> {
|
||||
Deserialize::deserialize(&mut self.content)
|
||||
#[inline(always)]
|
||||
fn unit_variant(self) -> RhaiResultOf<()> {
|
||||
Deserialize::deserialize(self.content)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn newtype_variant_seed<T: serde::de::DeserializeSeed<'de>>(
|
||||
mut self,
|
||||
self,
|
||||
seed: T,
|
||||
) -> RhaiResultOf<T::Value> {
|
||||
seed.deserialize(&mut self.content)
|
||||
seed.deserialize(self.content)
|
||||
}
|
||||
|
||||
fn tuple_variant<V: Visitor<'de>>(mut self, len: usize, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
#[inline(always)]
|
||||
fn tuple_variant<V: Visitor<'de>>(self, len: usize, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
self.content.deserialize_tuple(len, visitor)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn struct_variant<V: Visitor<'de>>(
|
||||
mut self,
|
||||
self,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
|
||||
@@ -1,31 +1,41 @@
|
||||
//! Implementations of [`serde::Deserialize`].
|
||||
|
||||
use crate::{Dynamic, ImmutableString, INT};
|
||||
use serde::de::{Deserialize, Deserializer, Error, Visitor};
|
||||
use crate::{Dynamic, Identifier, ImmutableString, Scope, INT};
|
||||
use serde::{
|
||||
de::{Error, SeqAccess, Visitor},
|
||||
Deserialize, Deserializer,
|
||||
};
|
||||
use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
struct DynamicVisitor;
|
||||
|
||||
impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
impl<'de> Visitor<'de> for DynamicVisitor {
|
||||
type Value = Dynamic;
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("any type that can be converted into a Dynamic")
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_bool<E: Error>(self, v: bool) -> Result<Self::Value, E> {
|
||||
Ok(v.into())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_i8<E: Error>(self, v: i8) -> Result<Self::Value, E> {
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_i16<E: Error>(self, v: i16) -> Result<Self::Value, E> {
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_i32<E: Error>(self, v: i32) -> Result<Self::Value, E> {
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
#[inline]
|
||||
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
@@ -38,12 +48,15 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
self.visit_i32(v as i32)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_u8<E: Error>(self, v: u8) -> Result<Self::Value, E> {
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_u16<E: Error>(self, v: u16) -> Result<Self::Value, E> {
|
||||
Ok(INT::from(v).into())
|
||||
}
|
||||
#[inline]
|
||||
fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
@@ -56,6 +69,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
self.visit_i32(v as i32)
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as u64 {
|
||||
@@ -72,6 +86,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline(always)]
|
||||
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
return self.visit_f64(v as f64);
|
||||
@@ -79,6 +94,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
return Ok(v.into());
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline(always)]
|
||||
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
return Ok(v.into());
|
||||
@@ -88,6 +104,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[inline]
|
||||
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
|
||||
use rust_decimal::Decimal;
|
||||
use std::convert::TryFrom;
|
||||
@@ -98,6 +115,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
}
|
||||
#[cfg(feature = "no_float")]
|
||||
#[cfg(feature = "decimal")]
|
||||
#[inline]
|
||||
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
|
||||
use rust_decimal::Decimal;
|
||||
use std::convert::TryFrom;
|
||||
@@ -107,29 +125,35 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
.map_err(Error::custom)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
|
||||
self.visit_string(v.to_string())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||
Ok(v.into())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_borrowed_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||
self.visit_str(v)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
|
||||
fn visit_newtype_struct<D: Deserializer<'d>>(self, de: D) -> Result<Self::Value, D::Error> {
|
||||
#[inline(always)]
|
||||
fn visit_newtype_struct<D: Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
|
||||
Deserialize::deserialize(de)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
fn visit_seq<A: serde::de::SeqAccess<'d>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
||||
fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
|
||||
let mut arr = crate::Array::new();
|
||||
|
||||
while let Some(v) = seq.next_element()? {
|
||||
@@ -140,7 +164,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
fn visit_map<M: serde::de::MapAccess<'d>>(self, mut map: M) -> Result<Self::Value, M::Error> {
|
||||
fn visit_map<M: serde::de::MapAccess<'de>>(self, mut map: M) -> Result<Self::Value, M::Error> {
|
||||
let mut m = crate::Map::new();
|
||||
|
||||
while let Some((k, v)) = map.next_entry()? {
|
||||
@@ -151,15 +175,68 @@ impl<'d> Visitor<'d> for DynamicVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Deserialize<'d> for Dynamic {
|
||||
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> {
|
||||
de.deserialize_any(DynamicVisitor)
|
||||
impl<'de> Deserialize<'de> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
deserializer.deserialize_any(DynamicVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Deserialize<'d> for ImmutableString {
|
||||
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> {
|
||||
let s: String = Deserialize::deserialize(de)?;
|
||||
impl<'de> Deserialize<'de> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
Ok(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Scope<'de> {
|
||||
#[inline(always)]
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
#[derive(Debug, Clone, Hash, Deserialize)]
|
||||
struct ScopeEntry {
|
||||
pub name: Identifier,
|
||||
pub value: Dynamic,
|
||||
#[serde(default)]
|
||||
pub is_constant: bool,
|
||||
}
|
||||
|
||||
struct VecVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for VecVisitor {
|
||||
type Value = Scope<'static>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a sequence")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut scope = match access.size_hint() {
|
||||
Some(size) => Scope::with_capacity(size),
|
||||
None => Scope::new(),
|
||||
};
|
||||
|
||||
while let Some(ScopeEntry {
|
||||
name,
|
||||
value,
|
||||
is_constant,
|
||||
}) = access.next_element()?
|
||||
{
|
||||
if is_constant {
|
||||
scope.push_constant_dynamic(name, value);
|
||||
} else {
|
||||
scope.push_dynamic(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(scope)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_seq(VecVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ mod deserialize;
|
||||
mod metadata;
|
||||
mod ser;
|
||||
mod serialize;
|
||||
mod str;
|
||||
|
||||
pub use de::from_dynamic;
|
||||
pub use ser::to_dynamic;
|
||||
pub use de::{from_dynamic, DynamicDeserializer};
|
||||
pub use ser::{to_dynamic, DynamicSerializer};
|
||||
|
||||
109
src/serde/ser.rs
109
src/serde/ser.rs
@@ -1,6 +1,6 @@
|
||||
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
|
||||
|
||||
use crate::{Dynamic, Position, RhaiError, RhaiResult, RhaiResultOf, ERR};
|
||||
use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR};
|
||||
use serde::ser::{
|
||||
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
|
||||
};
|
||||
@@ -9,10 +9,10 @@ use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Serializer for [`Dynamic`][crate::Dynamic] which is kept as a reference.
|
||||
struct DynamicSerializer {
|
||||
/// Serializer for [`Dynamic`][crate::Dynamic].
|
||||
pub struct DynamicSerializer {
|
||||
/// Buffer to hold a temporary key.
|
||||
_key: Dynamic,
|
||||
_key: Identifier,
|
||||
/// Buffer to hold a temporary value.
|
||||
_value: Dynamic,
|
||||
}
|
||||
@@ -20,10 +20,10 @@ struct DynamicSerializer {
|
||||
impl DynamicSerializer {
|
||||
/// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value.
|
||||
#[must_use]
|
||||
pub const fn new(_value: Dynamic) -> Self {
|
||||
pub const fn new(value: Dynamic) -> Self {
|
||||
Self {
|
||||
_key: Dynamic::UNIT,
|
||||
_value,
|
||||
_key: Identifier::new_const(),
|
||||
_value: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,10 +105,12 @@ impl Serializer for &mut DynamicSerializer {
|
||||
#[cfg(feature = "no_object")]
|
||||
type SerializeStructVariant = serde::ser::Impossible<Dynamic, RhaiError>;
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_bool(self, v: bool) -> RhaiResultOf<Self::Ok> {
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
@@ -116,6 +118,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
return self.serialize_i32(i32::from(v));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
@@ -123,6 +126,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
return self.serialize_i32(i32::from(v));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
@@ -130,6 +134,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
return Ok(v.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
@@ -143,6 +148,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as i128 {
|
||||
@@ -158,6 +164,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
@@ -165,6 +172,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
return self.serialize_i32(i32::from(v));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
return self.serialize_i64(i64::from(v));
|
||||
@@ -172,6 +180,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
return self.serialize_i32(i32::from(v));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
{
|
||||
@@ -185,6 +194,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as u64 {
|
||||
@@ -200,6 +210,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
if v > i64::MAX as u128 {
|
||||
@@ -215,6 +226,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
|
||||
return Ok(Dynamic::from(v));
|
||||
@@ -231,6 +243,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
|
||||
return Ok(Dynamic::from(v));
|
||||
@@ -247,14 +260,17 @@ impl Serializer for &mut DynamicSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_char(self, v: char) -> RhaiResultOf<Self::Ok> {
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_str(self, v: &str) -> RhaiResultOf<Self::Ok> {
|
||||
Ok(v.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_bytes(self, _v: &[u8]) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return Ok(Dynamic::from_blob(_v.to_vec()));
|
||||
@@ -262,28 +278,33 @@ impl Serializer for &mut DynamicSerializer {
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"BLOB's are not supported with 'no_index'".into(),
|
||||
"BLOB's are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_none(self) -> RhaiResultOf<Self::Ok> {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> RhaiResultOf<Self::Ok> {
|
||||
value.serialize(&mut *self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_unit(self) -> RhaiResultOf<Self::Ok> {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> RhaiResultOf<Self::Ok> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
@@ -293,6 +314,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_newtype_struct<T: ?Sized + Serialize>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
@@ -301,6 +323,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
value.serialize(&mut *self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_variant<T: ?Sized + Serialize>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
@@ -316,28 +339,31 @@ impl Serializer for &mut DynamicSerializer {
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_seq(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeSeq> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return Ok(DynamicSerializer::new(crate::Array::new().into()));
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"arrays are not supported with 'no_index'".into(),
|
||||
"arrays are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_tuple(self, len: usize) -> RhaiResultOf<Self::SerializeTuple> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
@@ -346,6 +372,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
@@ -362,24 +389,26 @@ impl Serializer for &mut DynamicSerializer {
|
||||
#[cfg(any(feature = "no_object", feature = "no_index"))]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"tuples are not supported with 'no_index' or 'no_object'".into(),
|
||||
"tuples are not supported under 'no_index' or 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_map(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeMap> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
return Ok(DynamicSerializer::new(crate::Map::new().into()));
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
@@ -388,6 +417,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
self.serialize_map(Some(len))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
@@ -403,7 +433,7 @@ impl Serializer for &mut DynamicSerializer {
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -425,20 +455,21 @@ impl SerializeSeq for DynamicSerializer {
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"arrays are not supported with 'no_index'".into(),
|
||||
"arrays are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
// Close the sequence.
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return Ok(self._value);
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"arrays are not supported with 'no_index'".into(),
|
||||
"arrays are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -460,19 +491,20 @@ impl SerializeTuple for DynamicSerializer {
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"tuples are not supported with 'no_index'".into(),
|
||||
"tuples are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return Ok(self._value);
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"tuples are not supported with 'no_index'".into(),
|
||||
"tuples are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -494,19 +526,20 @@ impl SerializeTupleStruct for DynamicSerializer {
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"tuples are not supported with 'no_index'".into(),
|
||||
"tuples are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
return Ok(self._value);
|
||||
#[cfg(feature = "no_index")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"tuples are not supported with 'no_index'".into(),
|
||||
"tuples are not supported under 'no_index'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -520,13 +553,19 @@ impl SerializeMap for DynamicSerializer {
|
||||
fn serialize_key<T: ?Sized + Serialize>(&mut self, _key: &T) -> RhaiResultOf<()> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
self._key = _key.serialize(&mut *self)?;
|
||||
let key = _key.serialize(&mut *self)?;
|
||||
self._key = key
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| {
|
||||
ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE)
|
||||
})?
|
||||
.into();
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -535,20 +574,16 @@ impl SerializeMap for DynamicSerializer {
|
||||
fn serialize_value<T: ?Sized + Serialize>(&mut self, _value: &T) -> RhaiResultOf<()> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let key = std::mem::take(&mut self._key)
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| {
|
||||
ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE)
|
||||
})?;
|
||||
let key = std::mem::take(&mut self._key);
|
||||
let value = _value.serialize(&mut *self)?;
|
||||
let map = self._value.downcast_mut::<crate::Map>().unwrap();
|
||||
map.insert(key.into(), value);
|
||||
map.insert(key, value);
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -573,19 +608,20 @@ impl SerializeMap for DynamicSerializer {
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
return Ok(self._value);
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -611,19 +647,20 @@ impl SerializeStruct for DynamicSerializer {
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
return Ok(self._value);
|
||||
#[cfg(feature = "no_object")]
|
||||
return Err(ERR::ErrorMismatchDataType(
|
||||
"".into(),
|
||||
"object maps are not supported with 'no_object'".into(),
|
||||
"object maps are not supported under 'no_object'".into(),
|
||||
Position::NONE,
|
||||
)
|
||||
.into());
|
||||
@@ -632,7 +669,7 @@ impl SerializeStruct for DynamicSerializer {
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
struct TupleVariantSerializer {
|
||||
pub struct TupleVariantSerializer {
|
||||
variant: &'static str,
|
||||
array: crate::Array,
|
||||
}
|
||||
@@ -649,13 +686,14 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
make_variant(self.variant, self.array.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
struct StructVariantSerializer {
|
||||
pub struct StructVariantSerializer {
|
||||
variant: &'static str,
|
||||
map: crate::Map,
|
||||
}
|
||||
@@ -665,6 +703,7 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer {
|
||||
type Ok = Dynamic;
|
||||
type Error = RhaiError;
|
||||
|
||||
#[inline]
|
||||
fn serialize_field<T: ?Sized + Serialize>(
|
||||
&mut self,
|
||||
key: &'static str,
|
||||
@@ -675,12 +714,14 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> RhaiResultOf<Self::Ok> {
|
||||
make_variant(self.variant, self.map.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline]
|
||||
fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult {
|
||||
let mut map = crate::Map::new();
|
||||
map.insert(variant.into(), value);
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
//! Implementations of [`serde::Serialize`].
|
||||
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::{Dynamic, ImmutableString};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use crate::{Dynamic, ImmutableString, Scope};
|
||||
use serde::{ser::SerializeSeq, Serialize, Serializer};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use serde::ser::SerializeMap;
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
use crate::types::dynamic::Variant;
|
||||
|
||||
impl Serialize for Dynamic {
|
||||
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => ser.serialize_unit(),
|
||||
Union::Bool(x, ..) => ser.serialize_bool(x),
|
||||
Union::Str(ref s, ..) => ser.serialize_str(s.as_str()),
|
||||
@@ -37,10 +39,9 @@ impl Serialize for Dynamic {
|
||||
Union::Decimal(ref x, ..) => {
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
if let Some(v) = x.to_f64() {
|
||||
ser.serialize_f64(v)
|
||||
} else {
|
||||
ser.serialize_str(&x.to_string())
|
||||
match x.to_f64() {
|
||||
Some(v) => ser.serialize_f64(v),
|
||||
None => ser.serialize_str(&x.to_string()),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
@@ -48,10 +49,9 @@ impl Serialize for Dynamic {
|
||||
Union::Decimal(ref x, ..) => {
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
if let Some(v) = x.to_f32() {
|
||||
ser.serialize_f32(v)
|
||||
} else {
|
||||
ser.serialize_str(&x.to_string())
|
||||
match x.to_f32() {
|
||||
Some(v) => ser.serialize_f32(v),
|
||||
_ => ser.serialize_str(&x.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ impl Serialize for Dynamic {
|
||||
map.end()
|
||||
}
|
||||
Union::FnPtr(ref f, ..) => ser.serialize_str(f.fn_name()),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()),
|
||||
|
||||
Union::Variant(ref v, ..) => ser.serialize_str((***v).type_name()),
|
||||
@@ -83,7 +83,38 @@ impl Serialize for Dynamic {
|
||||
}
|
||||
|
||||
impl Serialize for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||
ser.serialize_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Scope<'_> {
|
||||
#[inline(always)]
|
||||
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||
#[derive(Debug, Clone, Hash, Serialize)]
|
||||
struct ScopeEntry<'a> {
|
||||
pub name: &'a str,
|
||||
pub value: &'a Dynamic,
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
pub is_constant: bool,
|
||||
}
|
||||
|
||||
fn is_false(value: &bool) -> bool {
|
||||
!value
|
||||
}
|
||||
|
||||
let mut ser = ser.serialize_seq(Some(self.len()))?;
|
||||
|
||||
for (name, is_constant, value) in self.iter_raw() {
|
||||
let entry = ScopeEntry {
|
||||
name,
|
||||
value,
|
||||
is_constant,
|
||||
};
|
||||
ser.serialize_element(&entry)?;
|
||||
}
|
||||
|
||||
ser.end()
|
||||
}
|
||||
}
|
||||
|
||||
143
src/serde/str.rs
143
src/serde/str.rs
@@ -1,143 +0,0 @@
|
||||
//! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`].
|
||||
|
||||
use crate::{Position, RhaiError, RhaiResultOf, ERR};
|
||||
use serde::de::{Deserializer, Visitor};
|
||||
use std::any::type_name;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Deserializer for `ImmutableString`.
|
||||
pub struct StringSliceDeserializer<'a> {
|
||||
value: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> StringSliceDeserializer<'a> {
|
||||
/// Create an `ImmutableStringDeserializer` from an `&str` reference.
|
||||
#[must_use]
|
||||
pub const fn from_str(value: &'a str) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
/// Shortcut for a type conversion error.
|
||||
fn type_error<T>(&self) -> RhaiResultOf<T> {
|
||||
Err(
|
||||
ERR::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), Position::NONE)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> {
|
||||
type Error = RhaiError;
|
||||
|
||||
fn deserialize_any<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_str(v)
|
||||
}
|
||||
fn deserialize_bool<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_i8<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_i16<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_i32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_i64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_u8<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_u16<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_u32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_u64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_f32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_f64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_char<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_str<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
|
||||
// Only allow deserialization into a string.
|
||||
v.visit_borrowed_str(self.value)
|
||||
}
|
||||
fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_str(visitor)
|
||||
}
|
||||
fn deserialize_bytes<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_byte_buf<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_option<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_unit<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_unit_struct<V: Visitor<'de>>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
v: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_unit(v)
|
||||
}
|
||||
fn deserialize_newtype_struct<V: Visitor<'de>>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
v: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
v.visit_newtype_struct(self)
|
||||
}
|
||||
fn deserialize_seq<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_tuple<V: Visitor<'de>>(self, _len: usize, v: V) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_seq(v)
|
||||
}
|
||||
fn deserialize_tuple_struct<V: Visitor<'de>>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
v: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_seq(v)
|
||||
}
|
||||
fn deserialize_map<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_struct<V: Visitor<'de>>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_fields: &'static [&'static str],
|
||||
v: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_map(v)
|
||||
}
|
||||
fn deserialize_enum<V: Visitor<'de>>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
_: V,
|
||||
) -> RhaiResultOf<V::Value> {
|
||||
self.type_error()
|
||||
}
|
||||
fn deserialize_identifier<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_str(v)
|
||||
}
|
||||
fn deserialize_ignored_any<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
|
||||
self.deserialize_any(v)
|
||||
}
|
||||
}
|
||||
18
src/tests.rs
18
src/tests.rs
@@ -22,15 +22,7 @@ fn check_struct_sizes() {
|
||||
);
|
||||
assert_eq!(
|
||||
size_of::<tokenizer::Token>(),
|
||||
if IS_32_BIT {
|
||||
if cfg!(feature = "decimal") {
|
||||
24
|
||||
} else {
|
||||
16
|
||||
}
|
||||
} else {
|
||||
32
|
||||
}
|
||||
if IS_32_BIT { 8 } else { 16 }
|
||||
);
|
||||
assert_eq!(size_of::<ast::Expr>(), if PACKED { 12 } else { 16 });
|
||||
assert_eq!(size_of::<Option<ast::Expr>>(), if PACKED { 12 } else { 16 });
|
||||
@@ -43,19 +35,19 @@ fn check_struct_sizes() {
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
{
|
||||
assert_eq!(size_of::<Scope>(), 536);
|
||||
assert_eq!(size_of::<FnPtr>(), 80);
|
||||
assert_eq!(size_of::<FnPtr>(), 64);
|
||||
assert_eq!(size_of::<LexError>(), 56);
|
||||
assert_eq!(
|
||||
size_of::<ParseError>(),
|
||||
if cfg!(feature = "no_position") { 8 } else { 16 }
|
||||
);
|
||||
assert_eq!(size_of::<EvalAltResult>(), 72);
|
||||
assert_eq!(size_of::<EvalAltResult>(), 64);
|
||||
assert_eq!(
|
||||
size_of::<NativeCallContext>(),
|
||||
if cfg!(feature = "no_position") {
|
||||
72
|
||||
64
|
||||
} else {
|
||||
80
|
||||
72
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
175
src/tokenizer.rs
175
src/tokenizer.rs
@@ -32,7 +32,7 @@ pub struct TokenizerControlBlock {
|
||||
|
||||
impl TokenizerControlBlock {
|
||||
/// Create a new `TokenizerControlBlock`.
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
@@ -97,7 +97,7 @@ impl Position {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `line` is zero.
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(line: u16, position: u16) -> Self {
|
||||
assert!(line != 0, "line cannot be zero");
|
||||
@@ -220,6 +220,7 @@ impl Position {
|
||||
|
||||
impl Default for Position {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::START
|
||||
}
|
||||
@@ -241,6 +242,8 @@ impl fmt::Display for Position {
|
||||
}
|
||||
|
||||
impl fmt::Debug for Position {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_none() {
|
||||
f.write_str("none")
|
||||
@@ -297,6 +300,8 @@ pub struct Span {
|
||||
}
|
||||
|
||||
impl Default for Span {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
@@ -313,7 +318,7 @@ impl Span {
|
||||
Self { start, end }
|
||||
}
|
||||
/// Is this [`Span`] non-existent?
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_none(&self) -> bool {
|
||||
self.start.is_none() && self.end.is_none()
|
||||
@@ -333,7 +338,6 @@ impl Span {
|
||||
}
|
||||
|
||||
impl fmt::Display for Span {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let _f = f;
|
||||
|
||||
@@ -360,6 +364,8 @@ impl fmt::Display for Span {
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
@@ -381,15 +387,15 @@ pub enum Token {
|
||||
///
|
||||
/// Requires the `decimal` feature.
|
||||
#[cfg(feature = "decimal")]
|
||||
DecimalConstant(rust_decimal::Decimal),
|
||||
DecimalConstant(Box<rust_decimal::Decimal>),
|
||||
/// An identifier.
|
||||
Identifier(Identifier),
|
||||
Identifier(Box<Identifier>),
|
||||
/// A character constant.
|
||||
CharConstant(char),
|
||||
/// A string constant.
|
||||
StringConstant(SmartString),
|
||||
StringConstant(Box<SmartString>),
|
||||
/// An interpolated string.
|
||||
InterpolatedString(SmartString),
|
||||
InterpolatedString(Box<SmartString>),
|
||||
/// `{`
|
||||
LeftBrace,
|
||||
/// `}`
|
||||
@@ -570,14 +576,14 @@ pub enum Token {
|
||||
/// A lexer error.
|
||||
LexError(Box<LexError>),
|
||||
/// A comment block.
|
||||
Comment(SmartString),
|
||||
Comment(Box<SmartString>),
|
||||
/// A reserved symbol.
|
||||
Reserved(SmartString),
|
||||
Reserved(Box<SmartString>),
|
||||
/// A custom keyword.
|
||||
///
|
||||
/// Not available under `no_custom_syntax`.
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Custom(SmartString),
|
||||
Custom(Box<SmartString>),
|
||||
/// End of the input stream.
|
||||
EOF,
|
||||
}
|
||||
@@ -699,7 +705,7 @@ impl Token {
|
||||
FloatConstant(f) => f.to_string().into(),
|
||||
#[cfg(feature = "decimal")]
|
||||
DecimalConstant(d) => d.to_string().into(),
|
||||
StringConstant(..) => "string".into(),
|
||||
StringConstant(s) => format!("\"{s}\"").into(),
|
||||
InterpolatedString(..) => "string".into(),
|
||||
CharConstant(c) => c.to_string().into(),
|
||||
Identifier(s) => s.to_string().into(),
|
||||
@@ -793,9 +799,9 @@ impl Token {
|
||||
})
|
||||
}
|
||||
|
||||
/// Reverse lookup a token from a piece of syntax.
|
||||
/// Reverse lookup a symbol token from a piece of syntax.
|
||||
#[must_use]
|
||||
pub fn lookup_from_syntax(syntax: &str) -> Option<Self> {
|
||||
pub fn lookup_symbol_from_syntax(syntax: &str) -> Option<Self> {
|
||||
use Token::*;
|
||||
|
||||
Some(match syntax {
|
||||
@@ -873,19 +879,11 @@ impl Token {
|
||||
"**" => PowerOf,
|
||||
"**=" => PowerOfAssign,
|
||||
|
||||
#[cfg(feature = "no_object")]
|
||||
"?." => Reserved(syntax.into()),
|
||||
#[cfg(feature = "no_index")]
|
||||
"?[" => Reserved(syntax.into()),
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
"fn" => Fn,
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
"private" => Private,
|
||||
|
||||
#[cfg(feature = "no_function")]
|
||||
"fn" | "private" => Reserved(syntax.into()),
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
"import" => Import,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@@ -893,29 +891,43 @@ impl Token {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
"as" => As,
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Is a piece of syntax a reserved keyword?
|
||||
#[must_use]
|
||||
pub fn is_reserved_keyword(syntax: &str) -> bool {
|
||||
match syntax {
|
||||
#[cfg(feature = "no_object")]
|
||||
"?." => true,
|
||||
#[cfg(feature = "no_index")]
|
||||
"?[" => true,
|
||||
#[cfg(feature = "no_function")]
|
||||
"fn" | "private" => true,
|
||||
#[cfg(feature = "no_module")]
|
||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||
"import" | "export" | "as" => true,
|
||||
|
||||
// List of reserved operators
|
||||
"===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)"
|
||||
| "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => Reserved(syntax.into()),
|
||||
| "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => true,
|
||||
|
||||
// List of reserved keywords
|
||||
"public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var"
|
||||
| "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case"
|
||||
| "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
||||
| "async" | "await" | "yield" => Reserved(syntax.into()),
|
||||
| "async" | "await" | "yield" => true,
|
||||
|
||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => {
|
||||
Reserved(syntax.into())
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
crate::engine::KEYWORD_IS_DEF_FN => Reserved(syntax.into()),
|
||||
crate::engine::KEYWORD_IS_DEF_FN => true,
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this token [`EOF`][Token::EOF]?
|
||||
@@ -1097,8 +1109,8 @@ impl Token {
|
||||
pub(crate) fn into_function_name_for_override(self) -> Result<SmartString, Self> {
|
||||
match self {
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
Self::Custom(s) if is_valid_function_name(&s) => Ok(s),
|
||||
Self::Identifier(s) if is_valid_function_name(&s) => Ok(s),
|
||||
Self::Custom(s) if is_valid_function_name(&s) => Ok(*s),
|
||||
Self::Identifier(s) if is_valid_function_name(&s) => Ok(*s),
|
||||
_ => Err(self),
|
||||
}
|
||||
}
|
||||
@@ -1510,7 +1522,7 @@ fn get_next_token_inner(
|
||||
let return_comment = return_comment || is_doc_comment(comment.as_ref().expect("`Some`"));
|
||||
|
||||
if return_comment {
|
||||
return Some((Token::Comment(comment.expect("`Some`")), start_pos));
|
||||
return Some((Token::Comment(comment.expect("`Some`").into()), start_pos));
|
||||
}
|
||||
if state.comment_level > 0 {
|
||||
// Reached EOF without ending comment block
|
||||
@@ -1524,9 +1536,9 @@ fn get_next_token_inner(
|
||||
|(err, err_pos)| Some((Token::LexError(err.into()), err_pos)),
|
||||
|(result, interpolated, start_pos)| {
|
||||
if interpolated {
|
||||
Some((Token::InterpolatedString(result), start_pos))
|
||||
Some((Token::InterpolatedString(result.into()), start_pos))
|
||||
} else {
|
||||
Some((Token::StringConstant(result), start_pos))
|
||||
Some((Token::StringConstant(result.into()), start_pos))
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1676,13 +1688,16 @@ fn get_next_token_inner(
|
||||
// Then try decimal
|
||||
#[cfg(feature = "decimal")]
|
||||
let num = num.or_else(|_| {
|
||||
rust_decimal::Decimal::from_str(&result).map(Token::DecimalConstant)
|
||||
rust_decimal::Decimal::from_str(&result)
|
||||
.map(Box::new)
|
||||
.map(Token::DecimalConstant)
|
||||
});
|
||||
|
||||
// Then try decimal in scientific notation
|
||||
#[cfg(feature = "decimal")]
|
||||
let num = num.or_else(|_| {
|
||||
rust_decimal::Decimal::from_scientific(&result)
|
||||
.map(Box::new)
|
||||
.map(Token::DecimalConstant)
|
||||
});
|
||||
|
||||
@@ -1697,11 +1712,11 @@ fn get_next_token_inner(
|
||||
// letter or underscore ...
|
||||
#[cfg(not(feature = "unicode-xid-ident"))]
|
||||
('a'..='z' | '_' | 'A'..='Z', ..) => {
|
||||
return Some(get_identifier(stream, pos, start_pos, c));
|
||||
return Some(get_token_as_identifier(stream, pos, start_pos, c));
|
||||
}
|
||||
#[cfg(feature = "unicode-xid-ident")]
|
||||
(ch, ..) if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' => {
|
||||
return Some(get_identifier(stream, pos, start_pos, c));
|
||||
return Some(get_token_as_identifier(stream, pos, start_pos, c));
|
||||
}
|
||||
|
||||
// " - string literal
|
||||
@@ -1709,7 +1724,7 @@ fn get_next_token_inner(
|
||||
return parse_string_literal(stream, state, pos, c, false, true, false)
|
||||
.map_or_else(
|
||||
|(err, err_pos)| Some((Token::LexError(err.into()), err_pos)),
|
||||
|(result, ..)| Some((Token::StringConstant(result), start_pos)),
|
||||
|(result, ..)| Some((Token::StringConstant(result.into()), start_pos)),
|
||||
);
|
||||
}
|
||||
// ` - string literal
|
||||
@@ -1737,9 +1752,9 @@ fn get_next_token_inner(
|
||||
|(err, err_pos)| Some((Token::LexError(err.into()), err_pos)),
|
||||
|(result, interpolated, ..)| {
|
||||
if interpolated {
|
||||
Some((Token::InterpolatedString(result), start_pos))
|
||||
Some((Token::InterpolatedString(result.into()), start_pos))
|
||||
} else {
|
||||
Some((Token::StringConstant(result), start_pos))
|
||||
Some((Token::StringConstant(result.into()), start_pos))
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1786,7 +1801,7 @@ fn get_next_token_inner(
|
||||
// Parentheses
|
||||
('(', '*') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("(*".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("(*".into())), start_pos));
|
||||
}
|
||||
('(', ..) => return Some((Token::LeftParen, start_pos)),
|
||||
(')', ..) => return Some((Token::RightParen, start_pos)),
|
||||
@@ -1802,7 +1817,7 @@ fn get_next_token_inner(
|
||||
return Some((Token::MapStart, start_pos));
|
||||
}
|
||||
// Shebang
|
||||
('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)),
|
||||
('#', '!') => return Some((Token::Reserved(Box::new("#!".into())), start_pos)),
|
||||
|
||||
('#', ' ') => {
|
||||
eat_next(stream, pos);
|
||||
@@ -1812,10 +1827,10 @@ fn get_next_token_inner(
|
||||
} else {
|
||||
"#"
|
||||
};
|
||||
return Some((Token::Reserved(token.into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new(token.into())), start_pos));
|
||||
}
|
||||
|
||||
('#', ..) => return Some((Token::Reserved("#".into()), start_pos)),
|
||||
('#', ..) => return Some((Token::Reserved(Box::new("#".into())), start_pos)),
|
||||
|
||||
// Operators
|
||||
('+', '=') => {
|
||||
@@ -1824,7 +1839,7 @@ fn get_next_token_inner(
|
||||
}
|
||||
('+', '+') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("++".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("++".into())), start_pos));
|
||||
}
|
||||
('+', ..) if !state.next_token_cannot_be_unary => {
|
||||
return Some((Token::UnaryPlus, start_pos))
|
||||
@@ -1839,11 +1854,11 @@ fn get_next_token_inner(
|
||||
}
|
||||
('-', '>') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("->".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("->".into())), start_pos));
|
||||
}
|
||||
('-', '-') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("--".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("--".into())), start_pos));
|
||||
}
|
||||
('-', ..) if !state.next_token_cannot_be_unary => {
|
||||
return Some((Token::UnaryMinus, start_pos))
|
||||
@@ -1852,7 +1867,7 @@ fn get_next_token_inner(
|
||||
|
||||
('*', ')') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("*)".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("*)".into())), start_pos));
|
||||
}
|
||||
('*', '=') => {
|
||||
eat_next(stream, pos);
|
||||
@@ -1925,7 +1940,7 @@ fn get_next_token_inner(
|
||||
.borrow_mut()
|
||||
.global_comments
|
||||
.push(comment),
|
||||
_ => return Some((Token::Comment(comment), start_pos)),
|
||||
_ => return Some((Token::Comment(comment.into()), start_pos)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1953,7 +1968,7 @@ fn get_next_token_inner(
|
||||
scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
|
||||
|
||||
if let Some(comment) = comment {
|
||||
return Some((Token::Comment(comment), start_pos));
|
||||
return Some((Token::Comment(comment.into()), start_pos));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1972,7 +1987,7 @@ fn get_next_token_inner(
|
||||
match stream.peek_next() {
|
||||
Some('.') => {
|
||||
eat_next(stream, pos);
|
||||
Token::Reserved("...".into())
|
||||
Token::Reserved(Box::new("...".into()))
|
||||
}
|
||||
Some('=') => {
|
||||
eat_next(stream, pos);
|
||||
@@ -1990,7 +2005,7 @@ fn get_next_token_inner(
|
||||
|
||||
if stream.peek_next() == Some('=') {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("===".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("===".into())), start_pos));
|
||||
}
|
||||
|
||||
return Some((Token::EqualsTo, start_pos));
|
||||
@@ -2007,18 +2022,18 @@ fn get_next_token_inner(
|
||||
|
||||
if stream.peek_next() == Some('<') {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("::<".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("::<".into())), start_pos));
|
||||
}
|
||||
|
||||
return Some((Token::DoubleColon, start_pos));
|
||||
}
|
||||
(':', '=') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved(":=".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new(":=".into())), start_pos));
|
||||
}
|
||||
(':', ';') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved(":;".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new(":;".into())), start_pos));
|
||||
}
|
||||
(':', ..) => return Some((Token::Colon, start_pos)),
|
||||
|
||||
@@ -2028,7 +2043,7 @@ fn get_next_token_inner(
|
||||
}
|
||||
('<', '-') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("<-".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("<-".into())), start_pos));
|
||||
}
|
||||
('<', '<') => {
|
||||
eat_next(stream, pos);
|
||||
@@ -2045,7 +2060,7 @@ fn get_next_token_inner(
|
||||
}
|
||||
('<', '|') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("<|".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("<|".into())), start_pos));
|
||||
}
|
||||
('<', ..) => return Some((Token::LessThan, start_pos)),
|
||||
|
||||
@@ -2073,14 +2088,14 @@ fn get_next_token_inner(
|
||||
|
||||
if stream.peek_next() == Some('=') {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("!==".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("!==".into())), start_pos));
|
||||
}
|
||||
|
||||
return Some((Token::NotEqualsTo, start_pos));
|
||||
}
|
||||
('!', '.') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("!.".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("!.".into())), start_pos));
|
||||
}
|
||||
('!', ..) => return Some((Token::Bang, start_pos)),
|
||||
|
||||
@@ -2094,7 +2109,7 @@ fn get_next_token_inner(
|
||||
}
|
||||
('|', '>') => {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("|>".into()), start_pos));
|
||||
return Some((Token::Reserved(Box::new("|>".into())), start_pos));
|
||||
}
|
||||
('|', ..) => return Some((Token::Pipe, start_pos)),
|
||||
|
||||
@@ -2114,7 +2129,7 @@ fn get_next_token_inner(
|
||||
}
|
||||
('^', ..) => return Some((Token::XOr, start_pos)),
|
||||
|
||||
('~', ..) => return Some((Token::Reserved("~".into()), start_pos)),
|
||||
('~', ..) => return Some((Token::Reserved(Box::new("~".into())), start_pos)),
|
||||
|
||||
('%', '=') => {
|
||||
eat_next(stream, pos);
|
||||
@@ -2122,9 +2137,9 @@ fn get_next_token_inner(
|
||||
}
|
||||
('%', ..) => return Some((Token::Modulo, start_pos)),
|
||||
|
||||
('@', ..) => return Some((Token::Reserved("@".into()), start_pos)),
|
||||
('@', ..) => return Some((Token::Reserved(Box::new("@".into())), start_pos)),
|
||||
|
||||
('$', ..) => return Some((Token::Reserved("$".into()), start_pos)),
|
||||
('$', ..) => return Some((Token::Reserved(Box::new("$".into())), start_pos)),
|
||||
|
||||
('?', '.') => {
|
||||
eat_next(stream, pos);
|
||||
@@ -2132,7 +2147,7 @@ fn get_next_token_inner(
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Token::Elvis,
|
||||
#[cfg(feature = "no_object")]
|
||||
Token::Reserved("?.".into()),
|
||||
Token::Reserved(Box::new("?.".into())),
|
||||
start_pos,
|
||||
));
|
||||
}
|
||||
@@ -2146,11 +2161,11 @@ fn get_next_token_inner(
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Token::QuestionBracket,
|
||||
#[cfg(feature = "no_index")]
|
||||
Token::Reserved("?[".into()),
|
||||
Token::Reserved(Box::new("?[".into())),
|
||||
start_pos,
|
||||
));
|
||||
}
|
||||
('?', ..) => return Some((Token::Reserved("?".into()), start_pos)),
|
||||
('?', ..) => return Some((Token::Reserved(Box::new("?".into())), start_pos)),
|
||||
|
||||
(ch, ..) if ch.is_whitespace() => (),
|
||||
|
||||
@@ -2168,8 +2183,8 @@ fn get_next_token_inner(
|
||||
Some((Token::EOF, *pos))
|
||||
}
|
||||
|
||||
/// Get the next identifier.
|
||||
fn get_identifier(
|
||||
/// Get the next token, parsing it as an identifier.
|
||||
fn get_token_as_identifier(
|
||||
stream: &mut impl InputStream,
|
||||
pos: &mut Position,
|
||||
start_pos: Position,
|
||||
@@ -2188,20 +2203,20 @@ fn get_identifier(
|
||||
}
|
||||
}
|
||||
|
||||
let is_valid_identifier = is_valid_identifier(identifier.chars());
|
||||
|
||||
if let Some(token) = Token::lookup_from_syntax(&identifier) {
|
||||
if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) {
|
||||
return (token, start_pos);
|
||||
} else if Token::is_reserved_keyword(&identifier) {
|
||||
return (Token::Reserved(Box::new(identifier)), start_pos);
|
||||
}
|
||||
|
||||
if !is_valid_identifier {
|
||||
if !is_valid_identifier(&identifier) {
|
||||
return (
|
||||
Token::LexError(LERR::MalformedIdentifier(identifier.to_string()).into()),
|
||||
start_pos,
|
||||
);
|
||||
}
|
||||
|
||||
(Token::Identifier(identifier), start_pos)
|
||||
(Token::Identifier(identifier.into()), start_pos)
|
||||
}
|
||||
|
||||
/// Is a keyword allowed as a function?
|
||||
@@ -2222,10 +2237,10 @@ pub fn is_keyword_function(name: &str) -> bool {
|
||||
/// _(internals)_ Is a text string a valid identifier?
|
||||
/// Exported under the `internals` feature only.
|
||||
#[must_use]
|
||||
pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
|
||||
pub fn is_valid_identifier(name: &str) -> bool {
|
||||
let mut first_alphabetic = false;
|
||||
|
||||
for ch in name {
|
||||
for ch in name.chars() {
|
||||
match ch {
|
||||
'_' => (),
|
||||
_ if is_id_first_alphabetic(ch) => first_alphabetic = true,
|
||||
@@ -2243,7 +2258,7 @@ pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_valid_function_name(name: &str) -> bool {
|
||||
is_valid_identifier(name.chars())
|
||||
is_valid_identifier(name) && !is_keyword_function(name)
|
||||
}
|
||||
|
||||
/// Is a character valid to start an identifier?
|
||||
@@ -2382,7 +2397,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
}
|
||||
// Reserved keyword/symbol
|
||||
Some((Token::Reserved(s), pos)) => (match
|
||||
(&*s,
|
||||
(s.as_str(),
|
||||
#[cfg(not(feature = "no_custom_syntax"))]
|
||||
(!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)),
|
||||
#[cfg(feature = "no_custom_syntax")]
|
||||
@@ -2422,7 +2437,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
(.., true) => unreachable!("no custom operators"),
|
||||
// Reserved keyword that is not custom and disabled.
|
||||
(token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => {
|
||||
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"});
|
||||
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
|
||||
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
|
||||
},
|
||||
// Reserved keyword/operator that is not custom.
|
||||
@@ -2438,7 +2453,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => {
|
||||
if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) {
|
||||
// Disabled standard keyword/symbol
|
||||
(Token::Custom(token.literal_syntax().into()), pos)
|
||||
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
|
||||
} else {
|
||||
// Active standard keyword - should never be a custom keyword!
|
||||
unreachable!("{:?} is an active keyword", token)
|
||||
@@ -2446,7 +2461,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
||||
}
|
||||
// Disabled symbol
|
||||
Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => {
|
||||
(Token::Reserved(token.literal_syntax().into()), pos)
|
||||
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
|
||||
}
|
||||
// Normal symbol
|
||||
Some(r) => r,
|
||||
|
||||
@@ -17,6 +17,8 @@ pub struct CustomTypeInfo {
|
||||
pub struct CustomTypesCollection(BTreeMap<Identifier, CustomTypeInfo>);
|
||||
|
||||
impl fmt::Debug for CustomTypesCollection {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("CustomTypesCollection ")?;
|
||||
f.debug_map().entries(self.0.iter()).finish()
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! Helper module which defines the [`Dynamic`] data type and the
|
||||
//! [`Any`] trait to to allow custom type handling.
|
||||
//! Helper module which defines the [`Dynamic`] data type.
|
||||
|
||||
use crate::func::SendSync;
|
||||
use crate::{reify, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -14,11 +12,13 @@ use std::{
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub use super::Variant;
|
||||
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
pub use std::time::Instant;
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
#[cfg(target_family = "wasm")]
|
||||
pub use instant::Instant;
|
||||
|
||||
@@ -26,105 +26,6 @@ pub use instant::Instant;
|
||||
#[allow(dead_code)]
|
||||
const CHECKED: &str = "data type was checked";
|
||||
|
||||
mod private {
|
||||
use crate::func::SendSync;
|
||||
use std::any::Any;
|
||||
|
||||
/// A sealed trait that prevents other crates from implementing [`Variant`][super::Variant].
|
||||
pub trait Sealed {}
|
||||
|
||||
impl<T: Any + Clone + SendSync> Sealed for T {}
|
||||
}
|
||||
|
||||
/// _(internals)_ Trait to represent any type.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented.
|
||||
///
|
||||
/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type.
|
||||
/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`].
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub trait Variant: Any + private::Sealed {
|
||||
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
|
||||
#[must_use]
|
||||
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
|
||||
/// Get the name of this type.
|
||||
#[must_use]
|
||||
fn type_name(&self) -> &'static str;
|
||||
|
||||
/// Clone this [`Variant`] trait object.
|
||||
#[must_use]
|
||||
fn clone_object(&self) -> Box<dyn Variant>;
|
||||
}
|
||||
|
||||
/// _(internals)_ Trait to represent any type.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented.
|
||||
#[cfg(feature = "sync")]
|
||||
pub trait Variant: Any + Send + Sync + private::Sealed {
|
||||
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
|
||||
#[must_use]
|
||||
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
|
||||
/// Get the name of this type.
|
||||
#[must_use]
|
||||
fn type_name(&self) -> &'static str;
|
||||
|
||||
/// Clone this [`Variant`] trait object.
|
||||
#[must_use]
|
||||
fn clone_object(&self) -> Box<dyn Variant>;
|
||||
}
|
||||
|
||||
impl<T: Any + Clone + SendSync> Variant for T {
|
||||
#[inline(always)]
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
#[inline(always)]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
#[inline(always)]
|
||||
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any> {
|
||||
self
|
||||
}
|
||||
#[inline(always)]
|
||||
fn type_name(&self) -> &'static str {
|
||||
type_name::<T>()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clone_object(&self) -> Box<dyn Variant> {
|
||||
Box::new(self.clone()) as Box<dyn Variant>
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn Variant {
|
||||
/// Is this [`Variant`] a specific type?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id()
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ Modes of access.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||
@@ -154,6 +55,9 @@ pub struct Dynamic(pub(crate) Union);
|
||||
///
|
||||
/// Most variants are boxed to reduce the size.
|
||||
pub enum Union {
|
||||
/// An error value which should not exist.
|
||||
Null,
|
||||
|
||||
/// The Unit value - ().
|
||||
Unit((), Tag, AccessMode),
|
||||
/// A boolean value.
|
||||
@@ -183,7 +87,7 @@ pub enum Union {
|
||||
/// A function pointer.
|
||||
FnPtr(Box<FnPtr>, Tag, AccessMode),
|
||||
/// A timestamp value.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
TimeStamp(Box<Instant>, Tag, AccessMode),
|
||||
|
||||
/// Any type as a trait object.
|
||||
@@ -277,6 +181,8 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub const fn tag(&self) -> Tag {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(_, tag, _)
|
||||
| Union::Bool(_, tag, _)
|
||||
| Union::Str(_, tag, _)
|
||||
@@ -293,7 +199,7 @@ impl Dynamic {
|
||||
Union::Array(_, tag, _) | Union::Blob(_, tag, _) => tag,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, tag, _) => tag,
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(_, tag, _) => tag,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(_, tag, _) => tag,
|
||||
@@ -302,6 +208,8 @@ impl Dynamic {
|
||||
/// Attach arbitrary data to this [`Dynamic`].
|
||||
pub fn set_tag(&mut self, value: Tag) -> &mut Self {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(_, ref mut tag, _)
|
||||
| Union::Bool(_, ref mut tag, _)
|
||||
| Union::Str(_, ref mut tag, _)
|
||||
@@ -318,13 +226,19 @@ impl Dynamic {
|
||||
Union::Array(_, ref mut tag, _) | Union::Blob(_, ref mut tag, _) => *tag = value,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_, ref mut tag, _) => *tag = value,
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(_, ref mut tag, _) => *tag = value,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(_, ref mut tag, _) => *tag = value,
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Is this [`Dynamic`] null?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn is_null(&self) -> bool {
|
||||
matches!(self.0, Union::Null)
|
||||
}
|
||||
/// Does this [`Dynamic`] hold a variant data type instead of one of the supported system
|
||||
/// primitive types?
|
||||
#[inline(always)]
|
||||
@@ -390,7 +304,7 @@ impl Dynamic {
|
||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||
return matches!(self.0, Union::FnPtr(..));
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<crate::Instant>() {
|
||||
return matches!(self.0, Union::TimeStamp(..));
|
||||
}
|
||||
@@ -406,6 +320,8 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => TypeId::of::<()>(),
|
||||
Union::Bool(..) => TypeId::of::<bool>(),
|
||||
Union::Str(..) => TypeId::of::<ImmutableString>(),
|
||||
@@ -422,7 +338,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(..) => TypeId::of::<crate::Map>(),
|
||||
Union::FnPtr(..) => TypeId::of::<FnPtr>(),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(..) => TypeId::of::<Instant>(),
|
||||
|
||||
Union::Variant(ref v, ..) => (***v).type_id(),
|
||||
@@ -440,6 +356,8 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => "()",
|
||||
Union::Bool(..) => "bool",
|
||||
Union::Str(..) => "string",
|
||||
@@ -456,7 +374,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(..) => "map",
|
||||
Union::FnPtr(..) => "Fn",
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(..) => "timestamp",
|
||||
|
||||
Union::Variant(ref v, ..) => (***v).type_name(),
|
||||
@@ -484,6 +402,8 @@ impl Hash for Dynamic {
|
||||
mem::discriminant(&self.0).hash(state);
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => (),
|
||||
Union::Bool(ref b, ..) => b.hash(state),
|
||||
Union::Str(ref s, ..) => s.hash(state),
|
||||
@@ -506,7 +426,7 @@ impl Hash for Dynamic {
|
||||
|
||||
Union::Variant(..) => unimplemented!("{} cannot be hashed", self.type_name()),
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(..) => unimplemented!("{} cannot be hashed", self.type_name()),
|
||||
}
|
||||
}
|
||||
@@ -515,7 +435,9 @@ impl Hash for Dynamic {
|
||||
impl fmt::Display for Dynamic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
Union::Unit(..) => write!(f, ""),
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(..) => Ok(()),
|
||||
Union::Bool(ref v, ..) => fmt::Display::fmt(v, f),
|
||||
Union::Str(ref v, ..) => fmt::Display::fmt(v, f),
|
||||
Union::Char(ref v, ..) => fmt::Display::fmt(v, f),
|
||||
@@ -531,7 +453,7 @@ impl fmt::Display for Dynamic {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(..) => fmt::Debug::fmt(self, f),
|
||||
Union::FnPtr(ref v, ..) => fmt::Display::fmt(v, f),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(..) => f.write_str("<timestamp>"),
|
||||
|
||||
Union::Variant(ref v, ..) => {
|
||||
@@ -604,8 +526,12 @@ impl fmt::Display for Dynamic {
|
||||
}
|
||||
|
||||
impl fmt::Debug for Dynamic {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(ref v, ..) => fmt::Debug::fmt(v, f),
|
||||
Union::Bool(ref v, ..) => fmt::Debug::fmt(v, f),
|
||||
Union::Str(ref v, ..) => fmt::Debug::fmt(v, f),
|
||||
@@ -624,7 +550,7 @@ impl fmt::Debug for Dynamic {
|
||||
if i > 0 && i % 8 == 0 {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
write!(f, "{:02x}", v)
|
||||
write!(f, "{v:02x}")
|
||||
})?;
|
||||
f.write_str("]")
|
||||
}
|
||||
@@ -634,7 +560,7 @@ impl fmt::Debug for Dynamic {
|
||||
fmt::Debug::fmt(v, f)
|
||||
}
|
||||
Union::FnPtr(ref v, ..) => fmt::Debug::fmt(v, f),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(..) => write!(f, "<timestamp>"),
|
||||
|
||||
Union::Variant(ref v, ..) => {
|
||||
@@ -716,6 +642,8 @@ impl Clone for Dynamic {
|
||||
/// The cloned copy is marked read-write even if the original is read-only.
|
||||
fn clone(&self) -> Self {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(v, tag, ..) => Self(Union::Unit(v, tag, ReadWrite)),
|
||||
Union::Bool(v, tag, ..) => Self(Union::Bool(v, tag, ReadWrite)),
|
||||
Union::Str(ref v, tag, ..) => Self(Union::Str(v.clone(), tag, ReadWrite)),
|
||||
@@ -732,7 +660,7 @@ impl Clone for Dynamic {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(ref v, tag, ..) => Self(Union::Map(v.clone(), tag, ReadWrite)),
|
||||
Union::FnPtr(ref v, tag, ..) => Self(Union::FnPtr(v.clone(), tag, ReadWrite)),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(ref v, tag, ..) => Self(Union::TimeStamp(v.clone(), tag, ReadWrite)),
|
||||
|
||||
Union::Variant(ref v, tag, ..) => Self(Union::Variant(
|
||||
@@ -763,6 +691,9 @@ use std::f32::consts as FloatConstants;
|
||||
use std::f64::consts as FloatConstants;
|
||||
|
||||
impl Dynamic {
|
||||
/// A [`Dynamic`] containing a `null`.
|
||||
pub(crate) const NULL: Self = Self(Union::Null);
|
||||
|
||||
/// A [`Dynamic`] containing a `()`.
|
||||
pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing a `true`.
|
||||
@@ -973,8 +904,8 @@ impl Dynamic {
|
||||
}
|
||||
/// Create a new [`Dynamic`] from an [`Instant`].
|
||||
///
|
||||
/// Not available under `no-std`.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
/// Not available under `no-std` or `no_time`.
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_timestamp(value: Instant) -> Self {
|
||||
@@ -985,6 +916,8 @@ impl Dynamic {
|
||||
#[must_use]
|
||||
pub(crate) const fn access_mode(&self) -> AccessMode {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(.., access)
|
||||
| Union::Bool(.., access)
|
||||
| Union::Str(.., access)
|
||||
@@ -1001,7 +934,7 @@ impl Dynamic {
|
||||
Union::Array(.., access) | Union::Blob(.., access) => access,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(.., access) => access,
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(.., access) => access,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(.., access) => access,
|
||||
@@ -1010,6 +943,8 @@ impl Dynamic {
|
||||
/// Set the [`AccessMode`] for this [`Dynamic`].
|
||||
pub(crate) fn set_access_mode(&mut self, typ: AccessMode) -> &mut Self {
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Unit(.., ref mut access)
|
||||
| Union::Bool(.., ref mut access)
|
||||
| Union::Str(.., ref mut access)
|
||||
@@ -1025,7 +960,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref mut a, _, ref mut access) => {
|
||||
*access = typ;
|
||||
for v in a.iter_mut() {
|
||||
for v in a.as_mut() {
|
||||
v.set_access_mode(typ);
|
||||
}
|
||||
}
|
||||
@@ -1038,7 +973,7 @@ impl Dynamic {
|
||||
v.set_access_mode(typ);
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(.., ref mut access) => *access = typ,
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(.., ref mut access) => *access = typ,
|
||||
@@ -1172,7 +1107,7 @@ impl Dynamic {
|
||||
reify!(value, |v: crate::Map| return v.into());
|
||||
reify!(value, |v: FnPtr| return v.into());
|
||||
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
reify!(value, |v: Instant| return v.into());
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
reify!(value, |v: crate::Shared<crate::Locked<Self>>| return v
|
||||
@@ -1204,6 +1139,7 @@ impl Dynamic {
|
||||
let _access = self.access_mode();
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
Union::Shared(..) => self,
|
||||
_ => Self(Union::Shared(
|
||||
crate::Locked::new(self).into(),
|
||||
@@ -1248,6 +1184,8 @@ impl Dynamic {
|
||||
reify!(self, |v: T| return Some(v));
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
|
||||
Union::Int(v, ..) => reify!(v => Option<T>),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(v, ..) => reify!(*v => Option<T>),
|
||||
@@ -1265,7 +1203,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(v, ..) => reify!(*v => Option<T>),
|
||||
Union::FnPtr(v, ..) => reify!(*v => Option<T>),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
Union::TimeStamp(v, ..) => reify!(*v => Option<T>),
|
||||
Union::Unit(v, ..) => reify!(v => Option<T>),
|
||||
Union::Variant(v, ..) => (*v).as_boxed_any().downcast().ok().map(|x| *x),
|
||||
@@ -1564,7 +1502,7 @@ impl Dynamic {
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||
return match self.0 {
|
||||
Union::TimeStamp(ref v, ..) => v.as_ref().as_any().downcast_ref::<T>(),
|
||||
@@ -1582,6 +1520,7 @@ impl Dynamic {
|
||||
}
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
Union::Variant(ref v, ..) => (***v).as_any().downcast_ref::<T>(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(..) => None,
|
||||
@@ -1662,7 +1601,7 @@ impl Dynamic {
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||
return match self.0 {
|
||||
Union::TimeStamp(ref mut v, ..) => v.as_mut().as_any_mut().downcast_mut::<T>(),
|
||||
@@ -1680,6 +1619,7 @@ impl Dynamic {
|
||||
}
|
||||
|
||||
match self.0 {
|
||||
Union::Null => unreachable!(),
|
||||
Union::Variant(ref mut v, ..) => (***v).as_any_mut().downcast_mut::<T>(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(..) => None,
|
||||
@@ -2056,7 +1996,7 @@ impl From<FnPtr> for Dynamic {
|
||||
Self(Union::FnPtr(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
impl From<Instant> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: Instant) -> Self {
|
||||
|
||||
@@ -23,6 +23,7 @@ use std::prelude::v1::*;
|
||||
/// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`].
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub enum EvalAltResult {
|
||||
/// System error. Wrapped values are the error message and the internal error.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
@@ -83,6 +84,8 @@ pub enum EvalAltResult {
|
||||
|
||||
/// Data race detected when accessing a variable. Wrapped value is the variable name.
|
||||
ErrorDataRace(String, Position),
|
||||
/// Calling a non-pure method on a constant. Wrapped value is the function name.
|
||||
ErrorNonPureMethodCallOnConstant(String, Position),
|
||||
/// Assignment to a constant variable. Wrapped value is the variable name.
|
||||
ErrorAssignmentToConstant(String, Position),
|
||||
/// Inappropriate property access. Wrapped value is the property name.
|
||||
@@ -114,7 +117,7 @@ pub enum EvalAltResult {
|
||||
/// Breaking out of loops - not an error if within a loop.
|
||||
/// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement).
|
||||
/// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement).
|
||||
LoopBreak(bool, Position),
|
||||
LoopBreak(bool, Dynamic, Position),
|
||||
/// Not an error: Value returned from a script via the `return` keyword.
|
||||
/// Wrapped value is the result value.
|
||||
Return(Dynamic, Position),
|
||||
@@ -125,47 +128,45 @@ impl Error for EvalAltResult {}
|
||||
impl fmt::Display for EvalAltResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ErrorSystem(s, err) => match s.as_str() {
|
||||
"" => write!(f, "{}", err),
|
||||
s => write!(f, "{}: {}", s, err),
|
||||
}?,
|
||||
Self::ErrorSystem(s, err) if s.is_empty() => write!(f, "{err}")?,
|
||||
Self::ErrorSystem(s, err) => write!(f, "{s}: {err}")?,
|
||||
|
||||
Self::ErrorParsing(p, ..) => write!(f, "Syntax error: {}", p)?,
|
||||
Self::ErrorParsing(p, ..) => write!(f, "Syntax error: {p}")?,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::ErrorInFunctionCall(s, src, err, ..) if crate::parser::is_anonymous_fn(s) => {
|
||||
write!(f, "{} in call to closure", err)?;
|
||||
write!(f, "{err} in call to closure")?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
write!(f, " @ '{src}'")?;
|
||||
}
|
||||
}
|
||||
Self::ErrorInFunctionCall(s, src, err, ..) => {
|
||||
write!(f, "{} in call to function {}", err, s)?;
|
||||
write!(f, "{err} in call to function {s}")?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
write!(f, " @ '{src}'")?;
|
||||
}
|
||||
}
|
||||
|
||||
Self::ErrorInModule(s, err, ..) if s.is_empty() => {
|
||||
write!(f, "Error in module > {}", err)?
|
||||
write!(f, "Error in module > {err}")?
|
||||
}
|
||||
Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{}' > {}", s, err)?,
|
||||
Self::ErrorInModule(s, err, ..) => write!(f, "Error in module '{s}' > {err}")?,
|
||||
|
||||
Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {}", s)?,
|
||||
Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {}", s)?,
|
||||
Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {}", s)?,
|
||||
Self::ErrorPropertyNotFound(s, ..) => write!(f, "Property not found: {}", s)?,
|
||||
Self::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {}", s)?,
|
||||
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {}", s)?,
|
||||
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {}", s)?,
|
||||
Self::ErrorVariableExists(s, ..) => write!(f, "Variable already defined: {s}")?,
|
||||
Self::ErrorForbiddenVariable(s, ..) => write!(f, "Forbidden variable name: {s}")?,
|
||||
Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {s}")?,
|
||||
Self::ErrorPropertyNotFound(s, ..) => write!(f, "Property not found: {s}")?,
|
||||
Self::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {s}")?,
|
||||
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {s}")?,
|
||||
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {s}")?,
|
||||
Self::ErrorDataRace(s, ..) => {
|
||||
write!(f, "Data race detected when accessing variable: {}", s)?
|
||||
write!(f, "Data race detected when accessing variable: {s}")?
|
||||
}
|
||||
Self::ErrorDotExpr(s, ..) => match s.as_str() {
|
||||
"" => f.write_str("Malformed dot expression"),
|
||||
s => f.write_str(s),
|
||||
}?,
|
||||
Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {}", s)?,
|
||||
|
||||
Self::ErrorDotExpr(s, ..) if s.is_empty() => f.write_str("Malformed dot expression")?,
|
||||
Self::ErrorDotExpr(s, ..) => f.write_str(s)?,
|
||||
|
||||
Self::ErrorIndexingType(s, ..) => write!(f, "Indexer unavailable: {s}")?,
|
||||
Self::ErrorUnboundThis(..) => f.write_str("'this' not bound")?,
|
||||
Self::ErrorFor(..) => f.write_str("For loop expects an iterable type")?,
|
||||
Self::ErrorTooManyOperations(..) => f.write_str("Too many operations")?,
|
||||
@@ -180,63 +181,89 @@ impl fmt::Display for EvalAltResult {
|
||||
{
|
||||
write!(f, "Runtime error")?
|
||||
}
|
||||
Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {}", d)?,
|
||||
Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {d}")?,
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {}", s)?,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..)
|
||||
if s.starts_with(crate::engine::FN_GET) =>
|
||||
{
|
||||
let prop = &s[crate::engine::FN_GET.len()..];
|
||||
write!(
|
||||
f,
|
||||
"Property {prop} is not pure and cannot be accessed on a constant"
|
||||
)?
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..)
|
||||
if s.starts_with(crate::engine::FN_SET) =>
|
||||
{
|
||||
let prop = &s[crate::engine::FN_SET.len()..];
|
||||
write!(f, "Cannot modify property {prop} of constant")?
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => {
|
||||
write!(
|
||||
f,
|
||||
"Indexer is not pure and cannot be accessed on a constant"
|
||||
)?
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_SET => {
|
||||
write!(f, "Cannot assign to indexer of constant")?
|
||||
}
|
||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) => {
|
||||
write!(f, "Non-pure method {s} cannot be called on constant")?
|
||||
}
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant {s}")?,
|
||||
Self::ErrorMismatchOutputType(e, a, ..) => match (a.as_str(), e.as_str()) {
|
||||
("", e) => write!(f, "Output type incorrect, expecting {}", e),
|
||||
(a, "") => write!(f, "Output type incorrect: {}", a),
|
||||
(a, e) => write!(f, "Output type incorrect: {} (expecting {})", a, e),
|
||||
("", e) => write!(f, "Output type incorrect, expecting {e}"),
|
||||
(a, "") => write!(f, "Output type incorrect: {a}"),
|
||||
(a, e) => write!(f, "Output type incorrect: {a} (expecting {e})"),
|
||||
}?,
|
||||
Self::ErrorMismatchDataType(e, a, ..) => match (a.as_str(), e.as_str()) {
|
||||
("", e) => write!(f, "Data type incorrect, expecting {}", e),
|
||||
(a, "") => write!(f, "Data type incorrect: {}", a),
|
||||
(a, e) => write!(f, "Data type incorrect: {} (expecting {})", a, e),
|
||||
}?,
|
||||
Self::ErrorArithmetic(s, ..) => match s.as_str() {
|
||||
"" => f.write_str("Arithmetic error"),
|
||||
s => f.write_str(s),
|
||||
("", e) => write!(f, "Data type incorrect, expecting {e}"),
|
||||
(a, "") => write!(f, "Data type incorrect: {a}"),
|
||||
(a, e) => write!(f, "Data type incorrect: {a} (expecting {e})"),
|
||||
}?,
|
||||
|
||||
Self::ErrorArithmetic(s, ..) if s.is_empty() => f.write_str("Arithmetic error")?,
|
||||
Self::ErrorArithmetic(s, ..) => f.write_str(s)?,
|
||||
|
||||
Self::LoopBreak(true, ..) => f.write_str("'break' not inside a loop")?,
|
||||
Self::LoopBreak(false, ..) => f.write_str("'continue' not inside a loop")?,
|
||||
|
||||
Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?,
|
||||
|
||||
Self::ErrorArrayBounds(max, index, ..) => match max {
|
||||
0 => write!(f, "Array index {} out of bounds: array is empty", index),
|
||||
0 => write!(f, "Array index {index} out of bounds: array is empty"),
|
||||
1 => write!(
|
||||
f,
|
||||
"Array index {} out of bounds: only 1 element in array",
|
||||
index
|
||||
"Array index {index} out of bounds: only 1 element in array",
|
||||
),
|
||||
_ => write!(
|
||||
f,
|
||||
"Array index {} out of bounds: only {} elements in array",
|
||||
index, max
|
||||
"Array index {index} out of bounds: only {max} elements in array",
|
||||
),
|
||||
}?,
|
||||
Self::ErrorStringBounds(max, index, ..) => match max {
|
||||
0 => write!(f, "String index {} out of bounds: string is empty", index),
|
||||
0 => write!(f, "String index {index} out of bounds: string is empty"),
|
||||
1 => write!(
|
||||
f,
|
||||
"String index {} out of bounds: only 1 character in string",
|
||||
index
|
||||
"String index {index} out of bounds: only 1 character in string",
|
||||
),
|
||||
_ => write!(
|
||||
f,
|
||||
"String index {} out of bounds: only {} characters in string",
|
||||
index, max
|
||||
"String index {index} out of bounds: only {max} characters in string",
|
||||
),
|
||||
}?,
|
||||
Self::ErrorBitFieldBounds(max, index, ..) => write!(
|
||||
f,
|
||||
"Bit-field index {} out of bounds: only {} bits in bit-field",
|
||||
index, max
|
||||
"Bit-field index {index} out of bounds: only {max} bits in bit-field",
|
||||
)?,
|
||||
Self::ErrorDataTooLarge(typ, ..) => write!(f, "{} exceeds maximum limit", typ)?,
|
||||
Self::ErrorDataTooLarge(typ, ..) => write!(f, "{typ} exceeds maximum limit")?,
|
||||
|
||||
Self::ErrorCustomSyntax(s, tokens, ..) => write!(f, "{}: {}", s, tokens.join(" "))?,
|
||||
Self::ErrorCustomSyntax(s, tokens, ..) => write!(f, "{s}: {}", tokens.join(" "))?,
|
||||
}
|
||||
|
||||
// Do not write any position if None
|
||||
@@ -258,9 +285,9 @@ impl<T: AsRef<str>> From<T> for EvalAltResult {
|
||||
|
||||
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
#[inline(always)]
|
||||
fn from(err: T) -> Self {
|
||||
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
|
||||
Into::<EvalAltResult>::into(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +330,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorIndexNotFound(..)
|
||||
| Self::ErrorModuleNotFound(..)
|
||||
| Self::ErrorDataRace(..)
|
||||
| Self::ErrorNonPureMethodCallOnConstant(..)
|
||||
| Self::ErrorAssignmentToConstant(..)
|
||||
| Self::ErrorMismatchOutputType(..)
|
||||
| Self::ErrorDotExpr(..)
|
||||
@@ -371,7 +399,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorStackOverflow(..)
|
||||
| Self::ErrorRuntime(..) => (),
|
||||
|
||||
Self::ErrorFunctionNotFound(f, ..) => {
|
||||
Self::ErrorFunctionNotFound(f, ..) | Self::ErrorNonPureMethodCallOnConstant(f, ..) => {
|
||||
map.insert("function".into(), f.into());
|
||||
}
|
||||
Self::ErrorInFunctionCall(f, s, ..) => {
|
||||
@@ -466,6 +494,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorIndexNotFound(.., pos)
|
||||
| Self::ErrorModuleNotFound(.., pos)
|
||||
| Self::ErrorDataRace(.., pos)
|
||||
| Self::ErrorNonPureMethodCallOnConstant(.., pos)
|
||||
| Self::ErrorAssignmentToConstant(.., pos)
|
||||
| Self::ErrorMismatchOutputType(.., pos)
|
||||
| Self::ErrorDotExpr(.., pos)
|
||||
@@ -494,6 +523,7 @@ impl EvalAltResult {
|
||||
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub fn take_position(&mut self) -> Position {
|
||||
let pos = self.position();
|
||||
self.set_position(Position::NONE);
|
||||
@@ -524,6 +554,7 @@ impl EvalAltResult {
|
||||
| Self::ErrorIndexNotFound(.., pos)
|
||||
| Self::ErrorModuleNotFound(.., pos)
|
||||
| Self::ErrorDataRace(.., pos)
|
||||
| Self::ErrorNonPureMethodCallOnConstant(.., pos)
|
||||
| Self::ErrorAssignmentToConstant(.., pos)
|
||||
| Self::ErrorMismatchOutputType(.., pos)
|
||||
| Self::ErrorDotExpr(.., pos)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//! The `FnPtr` type.
|
||||
|
||||
use crate::tokenizer::is_valid_identifier;
|
||||
use crate::eval::GlobalRuntimeState;
|
||||
use crate::tokenizer::is_valid_function_name;
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{
|
||||
Dynamic, Engine, FuncArgs, Identifier, Module, NativeCallContext, Position, RhaiError,
|
||||
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
||||
Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult,
|
||||
RhaiResultOf, SharedModule, StaticVec, AST, ERR,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -18,11 +19,13 @@ use std::{
|
||||
/// to be passed onto a function during a call.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct FnPtr {
|
||||
name: Identifier,
|
||||
name: ImmutableString,
|
||||
curry: StaticVec<Dynamic>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FnPtr {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_curried() {
|
||||
self.curry
|
||||
@@ -40,13 +43,16 @@ impl fmt::Debug for FnPtr {
|
||||
impl FnPtr {
|
||||
/// Create a new function pointer.
|
||||
#[inline(always)]
|
||||
pub fn new(name: impl Into<Identifier>) -> RhaiResultOf<Self> {
|
||||
pub fn new(name: impl Into<ImmutableString>) -> RhaiResultOf<Self> {
|
||||
name.into().try_into()
|
||||
}
|
||||
/// Create a new function pointer without checking its parameters.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn new_unchecked(name: impl Into<Identifier>, curry: StaticVec<Dynamic>) -> Self {
|
||||
pub(crate) fn new_unchecked(
|
||||
name: impl Into<ImmutableString>,
|
||||
curry: StaticVec<Dynamic>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
curry,
|
||||
@@ -61,13 +67,13 @@ impl FnPtr {
|
||||
/// Get the name of the function.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn fn_name_raw(&self) -> &Identifier {
|
||||
pub(crate) const fn fn_name_raw(&self) -> &ImmutableString {
|
||||
&self.name
|
||||
}
|
||||
/// Get the underlying data of the function pointer.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn take_data(self) -> (Identifier, StaticVec<Dynamic>) {
|
||||
pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
|
||||
(self.name, self.curry)
|
||||
}
|
||||
/// Get the curried arguments.
|
||||
@@ -101,7 +107,7 @@ impl FnPtr {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_anonymous(&self) -> bool {
|
||||
self.name.starts_with(crate::engine::FN_ANONYMOUS)
|
||||
crate::func::is_anonymous_fn(&self.name)
|
||||
}
|
||||
/// Call the function pointer with curried arguments (if any).
|
||||
/// The function may be script-defined (not available under `no_function`) or native Rust.
|
||||
@@ -145,17 +151,19 @@ impl FnPtr {
|
||||
let mut arg_values = crate::StaticVec::new_const();
|
||||
args.parse(&mut arg_values);
|
||||
|
||||
let lib = [
|
||||
let lib: &[SharedModule] = &[
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
_ast.as_ref(),
|
||||
AsRef::<SharedModule>::as_ref(ast).clone(),
|
||||
];
|
||||
let lib = if lib.first().map_or(true, |m: &&Module| m.is_empty()) {
|
||||
&lib[0..0]
|
||||
let lib = if lib.first().map_or(true, |m| m.is_empty()) {
|
||||
&[][..]
|
||||
} else {
|
||||
&lib
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
let ctx = NativeCallContext::new(engine, self.fn_name(), lib);
|
||||
|
||||
let global = &GlobalRuntimeState::new(engine);
|
||||
|
||||
let ctx = (engine, self.fn_name(), None, global, lib, Position::NONE).into();
|
||||
|
||||
let result = self.call_raw(&ctx, None, arg_values)?;
|
||||
|
||||
@@ -244,12 +252,12 @@ impl fmt::Display for FnPtr {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Identifier> for FnPtr {
|
||||
impl TryFrom<ImmutableString> for FnPtr {
|
||||
type Error = RhaiError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: Identifier) -> RhaiResultOf<Self> {
|
||||
if is_valid_identifier(value.chars()) {
|
||||
#[inline(always)]
|
||||
fn try_from(value: ImmutableString) -> RhaiResultOf<Self> {
|
||||
if is_valid_function_name(&value) {
|
||||
Ok(Self {
|
||||
name: value,
|
||||
curry: StaticVec::new_const(),
|
||||
@@ -259,43 +267,3 @@ impl TryFrom<Identifier> for FnPtr {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<crate::ImmutableString> for FnPtr {
|
||||
type Error = RhaiError;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: crate::ImmutableString) -> RhaiResultOf<Self> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for FnPtr {
|
||||
type Error = RhaiError;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: String) -> RhaiResultOf<Self> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Box<str>> for FnPtr {
|
||||
type Error = RhaiError;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: Box<str>) -> RhaiResultOf<Self> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for FnPtr {
|
||||
type Error = RhaiError;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: &str) -> RhaiResultOf<Self> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ impl Deref for ImmutableString {
|
||||
|
||||
impl AsRef<SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &SmartString {
|
||||
&self.0
|
||||
}
|
||||
@@ -67,6 +68,7 @@ impl AsRef<SmartString> for ImmutableString {
|
||||
|
||||
impl AsRef<str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
@@ -74,6 +76,7 @@ impl AsRef<str> for ImmutableString {
|
||||
|
||||
impl Borrow<SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &SmartString {
|
||||
&self.0
|
||||
}
|
||||
@@ -81,6 +84,7 @@ impl Borrow<SmartString> for ImmutableString {
|
||||
|
||||
impl Borrow<str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
@@ -143,6 +147,7 @@ impl FromStr for ImmutableString {
|
||||
type Err = ();
|
||||
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s: SmartString = s.into();
|
||||
Ok(Self(s.into()))
|
||||
@@ -151,6 +156,7 @@ impl FromStr for ImmutableString {
|
||||
|
||||
impl FromIterator<char> for ImmutableString {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
@@ -158,6 +164,7 @@ impl FromIterator<char> for ImmutableString {
|
||||
|
||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().copied().collect::<SmartString>().into())
|
||||
}
|
||||
@@ -165,6 +172,7 @@ impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||
|
||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
@@ -172,6 +180,7 @@ impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||
|
||||
impl FromIterator<String> for ImmutableString {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
@@ -179,6 +188,7 @@ impl FromIterator<String> for ImmutableString {
|
||||
|
||||
impl FromIterator<SmartString> for ImmutableString {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn from_iter<T: IntoIterator<Item = SmartString>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
@@ -192,7 +202,8 @@ impl fmt::Display for ImmutableString {
|
||||
}
|
||||
|
||||
impl fmt::Debug for ImmutableString {
|
||||
#[inline(always)]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.as_str(), f)
|
||||
}
|
||||
@@ -623,7 +634,7 @@ impl ImmutableString {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn into_owned(mut self) -> String {
|
||||
self.make_mut(); // Make sure it is unique reference
|
||||
let _ = self.make_mut(); // Make sure it is unique reference
|
||||
shared_take(self.0).into() // Should succeed
|
||||
}
|
||||
/// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references).
|
||||
@@ -631,6 +642,7 @@ impl ImmutableString {
|
||||
///
|
||||
/// If there are other references to the same string, a cloned copy is used.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn make_mut(&mut self) -> &mut SmartString {
|
||||
shared_make_mut(&mut self.0)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! A strings interner type.
|
||||
|
||||
use super::BloomFilterU64;
|
||||
use crate::func::{hashing::get_hasher, StraightHashMap};
|
||||
use crate::ImmutableString;
|
||||
#[cfg(feature = "no_std")]
|
||||
@@ -14,37 +17,39 @@ use std::{
|
||||
};
|
||||
|
||||
/// Maximum number of strings interned.
|
||||
pub const MAX_INTERNED_STRINGS: usize = 256;
|
||||
pub const MAX_INTERNED_STRINGS: usize = 1024;
|
||||
|
||||
/// Maximum length of strings interned.
|
||||
pub const MAX_STRING_LEN: usize = 24;
|
||||
|
||||
/// _(internals)_ A factory of identifiers from text strings.
|
||||
/// _(internals)_ A cache for interned strings.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// Normal identifiers, property getters and setters are interned separately.
|
||||
pub struct StringsInterner<'a> {
|
||||
/// Maximum number of strings interned.
|
||||
pub capacity: usize,
|
||||
/// Maximum string length.
|
||||
pub max_string_len: usize,
|
||||
/// Normal strings.
|
||||
strings: StraightHashMap<ImmutableString>,
|
||||
/// Cached strings.
|
||||
cache: StraightHashMap<ImmutableString>,
|
||||
/// Bloom filter to avoid caching "one-hit wonders".
|
||||
filter: BloomFilterU64,
|
||||
/// Take care of the lifetime parameter.
|
||||
dummy: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl Default for StringsInterner<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StringsInterner<'_> {
|
||||
#[inline]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_list().entries(self.strings.values()).finish()
|
||||
f.debug_list().entries(self.cache.values()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +61,8 @@ impl StringsInterner<'_> {
|
||||
Self {
|
||||
capacity: MAX_INTERNED_STRINGS,
|
||||
max_string_len: MAX_STRING_LEN,
|
||||
strings: StraightHashMap::default(),
|
||||
cache: StraightHashMap::default(),
|
||||
filter: BloomFilterU64::new(),
|
||||
dummy: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -65,7 +71,7 @@ impl StringsInterner<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get<S: AsRef<str> + Into<ImmutableString>>(&mut self, text: S) -> ImmutableString {
|
||||
self.get_with_mapper(Into::into, text)
|
||||
self.get_with_mapper("", Into::into, text)
|
||||
}
|
||||
|
||||
/// Get an identifier from a text string, adding it to the interner if necessary.
|
||||
@@ -73,20 +79,23 @@ impl StringsInterner<'_> {
|
||||
#[must_use]
|
||||
pub fn get_with_mapper<S: AsRef<str>>(
|
||||
&mut self,
|
||||
id: &str,
|
||||
mapper: impl Fn(S) -> ImmutableString,
|
||||
text: S,
|
||||
) -> ImmutableString {
|
||||
let key = text.as_ref();
|
||||
|
||||
if key.len() > MAX_STRING_LEN {
|
||||
let hasher = &mut get_hasher();
|
||||
id.hash(hasher);
|
||||
key.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
// Cache long strings only on the second try to avoid caching "one-hit wonders".
|
||||
if key.len() > MAX_STRING_LEN && self.filter.is_absent_and_set(hash) {
|
||||
return mapper(text);
|
||||
}
|
||||
|
||||
let hasher = &mut get_hasher();
|
||||
key.hash(hasher);
|
||||
let key = hasher.finish();
|
||||
|
||||
let result = match self.strings.entry(key) {
|
||||
let result = match self.cache.entry(hash) {
|
||||
Entry::Occupied(e) => return e.get().clone(),
|
||||
Entry::Vacant(e) => {
|
||||
let value = mapper(text);
|
||||
@@ -94,49 +103,54 @@ impl StringsInterner<'_> {
|
||||
if value.strong_count() > 1 {
|
||||
return value;
|
||||
}
|
||||
|
||||
e.insert(value).clone()
|
||||
}
|
||||
};
|
||||
|
||||
// If the interner is over capacity, remove the longest entry that has the lowest count
|
||||
if self.strings.len() > self.capacity {
|
||||
// Leave some buffer to grow when shrinking the cache.
|
||||
// We leave at least two entries, one for the empty string, and one for the string
|
||||
// that has just been inserted.
|
||||
let max = if self.capacity < 5 {
|
||||
2
|
||||
} else {
|
||||
self.capacity - 3
|
||||
};
|
||||
|
||||
while self.strings.len() > max {
|
||||
let (_, _, n) =
|
||||
self.strings
|
||||
.iter()
|
||||
.fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| {
|
||||
if k != key
|
||||
&& (v.strong_count() < c || (v.strong_count() == c && v.len() > x))
|
||||
{
|
||||
(v.len(), v.strong_count(), k)
|
||||
} else {
|
||||
(x, c, n)
|
||||
}
|
||||
});
|
||||
|
||||
self.strings.remove(&n);
|
||||
}
|
||||
}
|
||||
// Throttle the cache upon exit
|
||||
self.throttle_cache(hash);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// If the interner is over capacity, remove the longest entry that has the lowest count
|
||||
fn throttle_cache(&mut self, hash: u64) {
|
||||
if self.cache.len() <= self.capacity {
|
||||
return;
|
||||
}
|
||||
|
||||
// Leave some buffer to grow when shrinking the cache.
|
||||
// We leave at least two entries, one for the empty string, and one for the string
|
||||
// that has just been inserted.
|
||||
let max = if self.capacity < 5 {
|
||||
2
|
||||
} else {
|
||||
self.capacity - 3
|
||||
};
|
||||
|
||||
while self.cache.len() > max {
|
||||
let (_, _, n) = self
|
||||
.cache
|
||||
.iter()
|
||||
.fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| {
|
||||
if k != hash && (v.strong_count() < c || (v.strong_count() == c && v.len() > x))
|
||||
{
|
||||
(v.len(), v.strong_count(), k)
|
||||
} else {
|
||||
(x, c, n)
|
||||
}
|
||||
});
|
||||
|
||||
self.cache.remove(&n);
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of strings interned.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.strings.len()
|
||||
self.cache.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if there are no interned strings.
|
||||
@@ -144,28 +158,28 @@ impl StringsInterner<'_> {
|
||||
#[must_use]
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.strings.is_empty()
|
||||
self.cache.is_empty()
|
||||
}
|
||||
|
||||
/// Clear all interned strings.
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn clear(&mut self) {
|
||||
self.strings.clear();
|
||||
self.cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Self> for StringsInterner<'_> {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
self.strings.extend(rhs.strings.into_iter());
|
||||
self.cache.extend(rhs.cache.into_iter());
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&Self> for StringsInterner<'_> {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: &Self) {
|
||||
self.strings
|
||||
.extend(rhs.strings.iter().map(|(&k, v)| (k, v.clone())));
|
||||
self.cache
|
||||
.extend(rhs.cache.iter().map(|(&k, v)| (k, v.clone())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,20 @@ pub mod fn_ptr;
|
||||
pub mod immutable_string;
|
||||
pub mod interner;
|
||||
pub mod parse_error;
|
||||
pub mod restore;
|
||||
pub mod scope;
|
||||
pub mod variant;
|
||||
|
||||
pub use bloom_filter::BloomFilterU64;
|
||||
pub use custom_types::{CustomTypeInfo, CustomTypesCollection};
|
||||
pub use dynamic::Dynamic;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(feature = "no_time"))]
|
||||
pub use dynamic::Instant;
|
||||
pub use error::EvalAltResult;
|
||||
pub use fn_ptr::FnPtr;
|
||||
pub use immutable_string::ImmutableString;
|
||||
pub use interner::StringsInterner;
|
||||
pub use parse_error::{LexError, ParseError, ParseErrorType};
|
||||
pub use restore::RestoreOnDrop;
|
||||
pub use scope::Scope;
|
||||
pub use variant::Variant;
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::prelude::v1::*;
|
||||
/// Error encountered when tokenizing the script text.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub enum LexError {
|
||||
/// An unexpected symbol is encountered when tokenizing the script text.
|
||||
UnexpectedInput(String),
|
||||
@@ -37,11 +38,11 @@ impl Error for LexError {}
|
||||
impl fmt::Display for LexError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
|
||||
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s),
|
||||
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s),
|
||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s),
|
||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s),
|
||||
Self::UnexpectedInput(s) => write!(f, "Unexpected '{s}'"),
|
||||
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{s}'"),
|
||||
Self::MalformedNumber(s) => write!(f, "Invalid number: '{s}'"),
|
||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{s}'"),
|
||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{s}'"),
|
||||
Self::UnterminatedString => f.write_str("Open string is not terminated"),
|
||||
Self::StringTooLong(max) => write!(
|
||||
f,
|
||||
@@ -49,7 +50,7 @@ impl fmt::Display for LexError {
|
||||
max
|
||||
),
|
||||
Self::ImproperSymbol(s, d) if d.is_empty() => {
|
||||
write!(f, "Invalid symbol encountered: '{}'", s)
|
||||
write!(f, "Invalid symbol encountered: '{s}'")
|
||||
}
|
||||
Self::ImproperSymbol(.., d) => f.write_str(d),
|
||||
}
|
||||
@@ -58,8 +59,8 @@ impl fmt::Display for LexError {
|
||||
|
||||
impl LexError {
|
||||
/// Convert a [`LexError`] into a [`ParseError`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn into_err(self, pos: Position) -> ParseError {
|
||||
ParseError(Box::new(self.into()), pos)
|
||||
}
|
||||
@@ -72,6 +73,7 @@ impl LexError {
|
||||
/// massive code changes to remove/add back enum variants in match statements.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
#[non_exhaustive]
|
||||
#[must_use]
|
||||
pub enum ParseErrorType {
|
||||
/// The script ends prematurely.
|
||||
UnexpectedEOF,
|
||||
@@ -171,7 +173,8 @@ pub enum ParseErrorType {
|
||||
|
||||
impl ParseErrorType {
|
||||
/// Make a [`ParseError`] using the current type and position.
|
||||
#[inline(always)]
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||
ParseError(self.into(), pos)
|
||||
@@ -181,77 +184,69 @@ impl ParseErrorType {
|
||||
impl fmt::Display for ParseErrorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::BadInput(err) => write!(f, "{}", err),
|
||||
Self::BadInput(err) => write!(f, "{err}"),
|
||||
|
||||
Self::UnknownOperator(s) => write!(f, "Unknown operator: '{}'", s),
|
||||
Self::UnknownOperator(s) => write!(f, "Unknown operator: '{s}'"),
|
||||
|
||||
Self::MalformedCallExpr(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid expression in function call arguments"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::MalformedIndexExpr(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid index in indexing expression"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::MalformedInExpr(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid 'in' expression"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::MalformedCapture(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid capturing"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::MalformedCallExpr(s) if s.is_empty() => f.write_str(s),
|
||||
Self::MalformedCallExpr(..) => f.write_str("Invalid expression in function call arguments"),
|
||||
|
||||
Self::MalformedIndexExpr(s) if s.is_empty() => f.write_str("Invalid index in indexing expression"),
|
||||
Self::MalformedIndexExpr(s) => f.write_str(s),
|
||||
|
||||
Self::MalformedInExpr(s) if s.is_empty() => f.write_str("Invalid 'in' expression"),
|
||||
Self::MalformedInExpr(s) => f.write_str(s),
|
||||
|
||||
Self::MalformedCapture(s) if s.is_empty() => f.write_str("Invalid capturing"),
|
||||
Self::MalformedCapture(s) => f.write_str(s),
|
||||
|
||||
Self::FnDuplicatedDefinition(s, n) => {
|
||||
write!(f, "Function {} with ", s)?;
|
||||
write!(f, "Function {s} with ")?;
|
||||
match n {
|
||||
0 => f.write_str("no parameters already exists"),
|
||||
1 => f.write_str("1 parameter already exists"),
|
||||
_ => write!(f, "{} parameters already exists", n),
|
||||
_ => write!(f, "{n} parameters already exists"),
|
||||
}
|
||||
}
|
||||
Self::FnMissingBody(s) => match s.as_str() {
|
||||
"" => f.write_str("Expecting body statement block for anonymous function"),
|
||||
s => write!(f, "Expecting body statement block for function {}", s)
|
||||
},
|
||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function {}", s),
|
||||
Self::FnDuplicatedParam(s, arg) => write!(f, "Duplicated parameter {} for function {}", arg, s),
|
||||
|
||||
Self::DuplicatedProperty(s) => write!(f, "Duplicated property for object map literal: {}", s),
|
||||
Self::FnMissingBody(s) if s.is_empty() => f.write_str("Expecting body statement block for anonymous function"),
|
||||
Self::FnMissingBody(s) => write!(f, "Expecting body statement block for function {s}"),
|
||||
|
||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function {s}"),
|
||||
Self::FnDuplicatedParam(s, arg) => write!(f, "Duplicated parameter {arg} for function {s}"),
|
||||
|
||||
Self::DuplicatedProperty(s) => write!(f, "Duplicated property for object map literal: {s}"),
|
||||
#[allow(deprecated)]
|
||||
Self::DuplicatedSwitchCase => f.write_str("Duplicated switch case"),
|
||||
Self::DuplicatedVariable(s) => write!(f, "Duplicated variable name: {}", s),
|
||||
Self::DuplicatedVariable(s) => write!(f, "Duplicated variable name: {s}"),
|
||||
|
||||
Self::VariableExists(s) => write!(f, "Variable already defined: {}", s),
|
||||
Self::VariableUndefined(s) => write!(f, "Undefined variable: {}", s),
|
||||
Self::ModuleUndefined(s) => write!(f, "Undefined module: {}", s),
|
||||
Self::VariableExists(s) => write!(f, "Variable already defined: {s}"),
|
||||
Self::VariableUndefined(s) => write!(f, "Undefined variable: {s}"),
|
||||
Self::ModuleUndefined(s) => write!(f, "Undefined module: {s}"),
|
||||
|
||||
Self::MismatchedType(r, a) => write!(f, "Expecting {}, not {}", r, a),
|
||||
Self::MismatchedType(r, a) => write!(f, "Expecting {r}, not {a}"),
|
||||
Self::ExprExpected(s) => write!(f, "Expecting {} expression", s),
|
||||
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
||||
Self::MissingToken(token, s) => write!(f, "Expecting '{token}' {s}"),
|
||||
|
||||
Self::MissingSymbol(s) if s.is_empty() => f.write_str("Expecting a symbol"),
|
||||
Self::MissingSymbol(s) => f.write_str(s),
|
||||
|
||||
Self::AssignmentToConstant(s) => match s.as_str() {
|
||||
"" => f.write_str("Cannot assign to a constant value"),
|
||||
s => write!(f, "Cannot assign to constant {}", s)
|
||||
},
|
||||
Self::AssignmentToInvalidLHS(s) => match s.as_str() {
|
||||
"" => f.write_str("Expression cannot be assigned to"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::AssignmentToConstant(s) if s.is_empty() => f.write_str("Cannot assign to a constant value"),
|
||||
Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant {s}"),
|
||||
|
||||
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
|
||||
Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{}' is a reserved keyword", s),
|
||||
Self::Reserved(s) => write!(f, "'{}' is a reserved symbol", s),
|
||||
Self::AssignmentToInvalidLHS(s) if s.is_empty() => f.write_str("Expression cannot be assigned to"),
|
||||
Self::AssignmentToInvalidLHS(s) => f.write_str(s),
|
||||
|
||||
Self::LiteralTooLarge(typ, max) => write!(f, "{typ} exceeds the maximum limit ({max})"),
|
||||
Self::Reserved(s) if is_valid_identifier(s.as_str()) => write!(f, "'{s}' is a reserved keyword"),
|
||||
Self::Reserved(s) => write!(f, "'{s}' is a reserved symbol"),
|
||||
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
||||
Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),
|
||||
Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"),
|
||||
Self::WrongSwitchCaseCondition => f.write_str("This switch case cannot have a condition"),
|
||||
Self::PropertyExpected => f.write_str("Expecting name of a property"),
|
||||
Self::VariableExpected => f.write_str("Expecting name of a variable"),
|
||||
Self::ForbiddenVariable(s) => write!(f, "Forbidden variable name: {}", s),
|
||||
Self::ForbiddenVariable(s) => write!(f, "Forbidden variable name: {s}"),
|
||||
Self::WrongFnDefinition => f.write_str("Function definitions must be at global level and cannot be inside a block or another function"),
|
||||
Self::FnMissingName => f.write_str("Expecting function name in function declaration"),
|
||||
Self::WrongDocComment => f.write_str("Doc-comment must be followed immediately by a function definition"),
|
||||
@@ -277,6 +272,7 @@ impl From<LexError> for ParseErrorType {
|
||||
|
||||
/// Error when parsing a script.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
#[must_use]
|
||||
pub struct ParseError(
|
||||
/// Parse error type.
|
||||
pub Box<ParseErrorType>,
|
||||
|
||||
65
src/types/restore.rs
Normal file
65
src/types/restore.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
//! Facility to run state restoration logic at the end of scope.
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Run custom restoration logic upon the end of scope.
|
||||
#[must_use]
|
||||
pub struct RestoreOnDrop<'a, T, R: FnOnce(&mut T)> {
|
||||
value: &'a mut T,
|
||||
restore: Option<R>,
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
|
||||
/// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at
|
||||
/// the end of scope only when `need_restore` is `true`.
|
||||
///
|
||||
/// Beware that the end of scope means the end of its lifetime, not necessarily waiting until
|
||||
/// the current block scope is exited.
|
||||
#[inline(always)]
|
||||
pub fn lock_if(need_restore: bool, value: &'a mut T, restore: R) -> Self {
|
||||
Self {
|
||||
value,
|
||||
restore: if need_restore { Some(restore) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at
|
||||
/// the end of scope.
|
||||
///
|
||||
/// Beware that the end of scope means the end of its lifetime, not necessarily waiting until
|
||||
/// the current block scope is exited.
|
||||
#[inline(always)]
|
||||
pub fn lock(value: &'a mut T, restore: R) -> Self {
|
||||
Self {
|
||||
value,
|
||||
restore: Some(restore),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
|
||||
#[inline(always)]
|
||||
fn drop(&mut self) {
|
||||
if let Some(restore) = self.restore.take() {
|
||||
restore(self.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Module that defines the [`Scope`] type representing a function call-stack scope.
|
||||
|
||||
use super::dynamic::{AccessMode, Variant};
|
||||
use crate::{Dynamic, Identifier};
|
||||
use crate::{Dynamic, Identifier, ImmutableString};
|
||||
use smallvec::SmallVec;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -22,6 +22,14 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
|
||||
/// Currently the lifetime parameter is not used, but it is not guaranteed to remain unused for
|
||||
/// future versions. Until then, `'static` can be used.
|
||||
///
|
||||
/// # Constant Generic Parameter
|
||||
///
|
||||
/// There is a constant generic parameter that indicates how many entries to keep inline.
|
||||
/// As long as the number of entries does not exceed this limit, no allocations occur.
|
||||
/// The default is 8.
|
||||
///
|
||||
/// A larger value makes [`Scope`] larger, but reduces the chance of allocations.
|
||||
///
|
||||
/// # Thread Safety
|
||||
///
|
||||
/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it
|
||||
@@ -61,19 +69,18 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
|
||||
//
|
||||
// [`Dynamic`] is reasonably small so packing it tightly improves cache performance.
|
||||
#[derive(Debug, Hash, Default)]
|
||||
pub struct Scope<'a> {
|
||||
pub struct Scope<'a, const N: usize = SCOPE_ENTRIES_INLINED> {
|
||||
/// Current value of the entry.
|
||||
values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
|
||||
/// Name of the entry.
|
||||
names: SmallVec<[Identifier; SCOPE_ENTRIES_INLINED]>,
|
||||
/// Aliases of the entry.
|
||||
aliases: SmallVec<[Vec<Identifier>; SCOPE_ENTRIES_INLINED]>,
|
||||
aliases: SmallVec<[Vec<ImmutableString>; SCOPE_ENTRIES_INLINED]>,
|
||||
/// Phantom to keep the lifetime parameter in order not to break existing code.
|
||||
dummy: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Scope<'_> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for (i, (name, constant, value)) in self.iter_raw().enumerate() {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@@ -118,10 +125,10 @@ impl Clone for Scope<'_> {
|
||||
}
|
||||
|
||||
impl IntoIterator for Scope<'_> {
|
||||
type Item = (String, Dynamic, Vec<Identifier>);
|
||||
type Item = (String, Dynamic, Vec<ImmutableString>);
|
||||
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(
|
||||
self.values
|
||||
@@ -133,10 +140,10 @@ impl IntoIterator for Scope<'_> {
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Scope<'_> {
|
||||
type Item = (&'a Identifier, &'a Dynamic, &'a Vec<Identifier>);
|
||||
type Item = (&'a Identifier, &'a Dynamic, &'a Vec<ImmutableString>);
|
||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(
|
||||
self.values
|
||||
@@ -170,6 +177,28 @@ impl Scope<'_> {
|
||||
dummy: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Create a new [`Scope`] with a particular capacity.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::with_capacity(10);
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
values: SmallVec::with_capacity(capacity),
|
||||
names: SmallVec::with_capacity(capacity),
|
||||
aliases: SmallVec::with_capacity(capacity),
|
||||
dummy: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Empty the [`Scope`].
|
||||
///
|
||||
/// # Example
|
||||
@@ -378,7 +407,7 @@ impl Scope<'_> {
|
||||
/// Find an entry in the [`Scope`], starting from the last.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> {
|
||||
pub(crate) fn search(&self, name: &str) -> Option<usize> {
|
||||
let len = self.len();
|
||||
|
||||
self.names
|
||||
@@ -388,7 +417,7 @@ impl Scope<'_> {
|
||||
.find_map(|(i, key)| {
|
||||
if name == key {
|
||||
let index = len - 1 - i;
|
||||
Some((index, self.values[index].access_mode()))
|
||||
Some(index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -438,10 +467,11 @@ impl Scope<'_> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_constant(&self, name: &str) -> Option<bool> {
|
||||
self.get_index(name).map(|(.., access)| match access {
|
||||
AccessMode::ReadWrite => false,
|
||||
AccessMode::ReadOnly => true,
|
||||
})
|
||||
self.search(name)
|
||||
.map(|n| match self.values[n].access_mode() {
|
||||
AccessMode::ReadWrite => false,
|
||||
AccessMode::ReadOnly => true,
|
||||
})
|
||||
}
|
||||
/// Update the value of the named entry in the [`Scope`] if it already exists and is not constant.
|
||||
/// Push a new entry with the value into the [`Scope`] if the name doesn't exist or if the
|
||||
@@ -474,7 +504,10 @@ impl Scope<'_> {
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
match self.get_index(name.as_ref()) {
|
||||
match self
|
||||
.search(name.as_ref())
|
||||
.map(|n| (n, self.values[n].access_mode()))
|
||||
{
|
||||
None | Some((.., AccessMode::ReadOnly)) => {
|
||||
self.push(name, value);
|
||||
}
|
||||
@@ -513,7 +546,10 @@ impl Scope<'_> {
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
match self.get_index(name.as_ref()) {
|
||||
match self
|
||||
.search(name.as_ref())
|
||||
.map(|n| (n, self.values[n].access_mode()))
|
||||
{
|
||||
None => {
|
||||
self.push(name, value);
|
||||
}
|
||||
@@ -547,7 +583,7 @@ impl Scope<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get(&self, name: &str) -> Option<&Dynamic> {
|
||||
self.get_index(name).map(|(index, _)| &self.values[index])
|
||||
self.search(name).map(|index| &self.values[index])
|
||||
}
|
||||
/// Remove the last entry in the [`Scope`] by the specified name and return its value.
|
||||
///
|
||||
@@ -578,7 +614,7 @@ impl Scope<'_> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn remove<T: Variant + Clone>(&mut self, name: &str) -> Option<T> {
|
||||
self.get_index(name).and_then(|(index, _)| {
|
||||
self.search(name).and_then(|index| {
|
||||
self.names.remove(index);
|
||||
self.aliases.remove(index);
|
||||
self.values.remove(index).try_cast()
|
||||
@@ -610,9 +646,9 @@ impl Scope<'_> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
||||
self.get_index(name)
|
||||
.and_then(move |(index, access)| match access {
|
||||
AccessMode::ReadWrite => Some(self.get_mut_by_index(index)),
|
||||
self.search(name)
|
||||
.and_then(move |n| match self.values[n].access_mode() {
|
||||
AccessMode::ReadWrite => Some(self.get_mut_by_index(n)),
|
||||
AccessMode::ReadOnly => None,
|
||||
})
|
||||
}
|
||||
@@ -633,7 +669,7 @@ impl Scope<'_> {
|
||||
/// Panics if the index is out of bounds.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline]
|
||||
pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||
pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: ImmutableString) -> &mut Self {
|
||||
let aliases = self.aliases.get_mut(index).unwrap();
|
||||
if aliases.is_empty() || !aliases.contains(&alias) {
|
||||
aliases.push(alias);
|
||||
@@ -654,11 +690,11 @@ impl Scope<'_> {
|
||||
pub fn set_alias(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
alias: impl Into<Identifier>,
|
||||
alias: impl Into<ImmutableString>,
|
||||
) {
|
||||
if let Some((index, ..)) = self.get_index(name.as_ref()) {
|
||||
if let Some(index) = self.search(name.as_ref()) {
|
||||
let alias = match alias.into() {
|
||||
x if x.is_empty() => name.into(),
|
||||
x if x.is_empty() => name.into().into(),
|
||||
x => x,
|
||||
};
|
||||
self.add_alias_by_index(index, alias);
|
||||
@@ -690,9 +726,10 @@ impl Scope<'_> {
|
||||
scope
|
||||
}
|
||||
/// Get an iterator to entries in the [`Scope`].
|
||||
#[inline]
|
||||
#[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<ImmutableString>)> {
|
||||
self.names
|
||||
.into_iter()
|
||||
.zip(self.values.into_iter().zip(self.aliases.into_iter()))
|
||||
|
||||
105
src/types/variant.rs
Normal file
105
src/types/variant.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
//! [`Variant`] trait to to allow custom type handling.
|
||||
|
||||
use crate::func::SendSync;
|
||||
use std::any::{type_name, Any, TypeId};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
mod private {
|
||||
use crate::func::SendSync;
|
||||
use std::any::Any;
|
||||
|
||||
/// A sealed trait that prevents other crates from implementing [`Variant`][super::Variant].
|
||||
pub trait Sealed {}
|
||||
|
||||
impl<T: Any + Clone + SendSync> Sealed for T {}
|
||||
}
|
||||
|
||||
/// _(internals)_ Trait to represent any type.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented.
|
||||
///
|
||||
/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type.
|
||||
/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`].
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub trait Variant: Any + private::Sealed {
|
||||
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
|
||||
#[must_use]
|
||||
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
|
||||
/// Get the name of this type.
|
||||
#[must_use]
|
||||
fn type_name(&self) -> &'static str;
|
||||
|
||||
/// Clone this [`Variant`] trait object.
|
||||
#[must_use]
|
||||
fn clone_object(&self) -> Box<dyn Variant>;
|
||||
}
|
||||
|
||||
/// _(internals)_ Trait to represent any type.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented.
|
||||
#[cfg(feature = "sync")]
|
||||
pub trait Variant: Any + Send + Sync + private::Sealed {
|
||||
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
|
||||
#[must_use]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
|
||||
#[must_use]
|
||||
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
|
||||
/// Get the name of this type.
|
||||
#[must_use]
|
||||
fn type_name(&self) -> &'static str;
|
||||
|
||||
/// Clone this [`Variant`] trait object.
|
||||
#[must_use]
|
||||
fn clone_object(&self) -> Box<dyn Variant>;
|
||||
}
|
||||
|
||||
impl<T: Any + Clone + SendSync> Variant for T {
|
||||
#[inline(always)]
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
#[inline(always)]
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
#[inline(always)]
|
||||
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any> {
|
||||
self
|
||||
}
|
||||
#[inline(always)]
|
||||
fn type_name(&self) -> &'static str {
|
||||
type_name::<T>()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn clone_object(&self) -> Box<dyn Variant> {
|
||||
Box::new(self.clone()) as Box<dyn Variant>
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn Variant {
|
||||
/// Is this [`Variant`] a specific type?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
TypeId::of::<T>() == self.type_id()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user