Merge pull request #674 from schungx/master

Clippy and other refactors.
This commit is contained in:
Stephen Chung 2022-11-25 13:59:11 +08:00 committed by GitHub
commit e78c3239f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 2501 additions and 1906 deletions

View File

@ -4,6 +4,11 @@ Rhai Release Notes
Version 1.12.0 Version 1.12.0
============== ==============
Buf fixes
---------
* Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating.
Net features Net features
------------ ------------
@ -20,6 +25,7 @@ Enhancements
* The `TypeBuilder` type and `CustomType` trait are no longer marked as volatile. * The `TypeBuilder` type and `CustomType` trait are no longer marked as volatile.
* `FuncArgs` is also implemented for arrays. * `FuncArgs` is also implemented for arrays.
* `Engine::set_XXX` API can now be chained. * `Engine::set_XXX` API can now be chained.
* `EvalContext::scope_mut` now returns `&mut Scope` instead of `&mut &mut Scope`.
Version 1.11.0 Version 1.11.0

View File

@ -1,3 +1,3 @@
//! This file is automatically recreated during build time by `build.rs` from `build.template`. //! This file is automatically recreated during build time by `build.rs` from `build.template`.
pub(crate) const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; pub const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}};

View File

@ -5,6 +5,8 @@ edition = "2018"
resolver = "2" resolver = "2"
authors = ["jhwgh1968", "Stephen Chung"] authors = ["jhwgh1968", "Stephen Chung"]
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust" description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
keywords = ["rhai", "scripting", "scripting-engine", "scripting-language", "embedded", "plugin", "macros", "code-generation"]
categories = ["no-std", "embedded", "wasm", "parser-implementations"]
homepage = "https://rhai.rs/book/plugins/index.html" homepage = "https://rhai.rs/book/plugins/index.html"
repository = "https://github.com/rhaiscript/rhai" repository = "https://github.com/rhaiscript/rhai"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"

View File

@ -313,7 +313,7 @@ impl Parse for ExportedFn {
} }
} }
let skip_slots = if pass_context { 1 } else { 0 }; let skip_slots = usize::from(pass_context);
// Determine whether function generates a special calling convention for a mutable receiver. // Determine whether function generates a special calling convention for a mutable receiver.
let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) { let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) {
@ -485,12 +485,12 @@ impl ExportedFn {
} }
pub fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> { pub fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
let skip = if self.pass_context { 1 } else { 0 }; let skip = usize::from(self.pass_context);
self.signature.inputs.iter().skip(skip) self.signature.inputs.iter().skip(skip)
} }
pub fn arg_count(&self) -> usize { pub fn arg_count(&self) -> usize {
let skip = if self.pass_context { 1 } else { 0 }; let skip = usize::from(self.pass_context);
self.signature.inputs.len() - skip self.signature.inputs.len() - skip
} }

View File

@ -8,7 +8,7 @@ pub fn main() {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub fn main() { pub fn main() {
use rhai::{CallFnOptions, Dynamic, Engine, ImmutableString, Map, Scope, AST}; use rhai::{CallFnOptions, Dynamic, Engine, Map, Scope, AST};
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
const SCRIPT_FILE: &str = "event_handler_js/script.rhai"; const SCRIPT_FILE: &str = "event_handler_js/script.rhai";

View File

@ -18,6 +18,7 @@ use std::{
/// Options for calling a script-defined function via [`Engine::call_fn_with_options`]. /// Options for calling a script-defined function via [`Engine::call_fn_with_options`].
#[derive(Debug, Hash)] #[derive(Debug, Hash)]
#[non_exhaustive] #[non_exhaustive]
#[must_use]
pub struct CallFnOptions<'t> { pub struct CallFnOptions<'t> {
/// A value for binding to the `this` pointer (if any). /// A value for binding to the `this` pointer (if any).
pub this_ptr: Option<&'t mut Dynamic>, pub this_ptr: Option<&'t mut Dynamic>,
@ -120,7 +121,7 @@ impl Engine {
name: impl AsRef<str>, name: impl AsRef<str>,
args: impl FuncArgs, args: impl FuncArgs,
) -> RhaiResultOf<T> { ) -> RhaiResultOf<T> {
self.call_fn_with_options(Default::default(), scope, ast, name, args) self.call_fn_with_options(CallFnOptions::default(), scope, ast, name, args)
} }
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments. /// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
/// ///
@ -255,25 +256,28 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
crate::func::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()) { ast.shared_lib()
self.call_script_fn( .get_script_fn(name, args.len())
global, .map_or_else(
caches, || Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into()),
scope, |fn_def| {
this_ptr, self.call_script_fn(
fn_def, global,
args, caches,
rewind_scope, scope,
Position::NONE, this_ptr,
fn_def,
args,
rewind_scope,
Position::NONE,
)
},
) )
} else {
Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into())
}
}); });
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
if self.debugger.is_some() { if self.debugger.is_some() {
global.debugger.status = crate::eval::DebuggerStatus::Terminate; global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
let node = &crate::ast::Stmt::Noop(Position::NONE); let node = &crate::ast::Stmt::Noop(Position::NONE);
self.run_debugger(global, caches, scope, this_ptr, node)?; self.run_debugger(global, caches, scope, this_ptr, node)?;
} }

View File

@ -1,5 +1,6 @@
//! Module that defines the public compilation API of [`Engine`]. //! Module that defines the public compilation API of [`Engine`].
use crate::func::native::locked_write;
use crate::parser::{ParseResult, ParseState}; use crate::parser::{ParseResult, ParseState};
use crate::{Engine, OptimizationLevel, Scope, AST}; use crate::{Engine, OptimizationLevel, Scope, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -221,7 +222,8 @@ impl Engine {
scripts.as_ref(), scripts.as_ref(),
self.token_mapper.as_ref().map(<_>::as_ref), self.token_mapper.as_ref().map(<_>::as_ref),
); );
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control); let interned_strings = &mut *locked_write(&self.interned_strings);
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
let mut _ast = self.parse(&mut stream.peekable(), &mut state, optimization_level)?; let mut _ast = self.parse(&mut stream.peekable(), &mut state, optimization_level)?;
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
_ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n")); _ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n"));
@ -294,7 +296,8 @@ impl Engine {
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref)); self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
let mut peekable = stream.peekable(); let mut peekable = stream.peekable();
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control); let interned_strings = &mut *locked_write(&self.interned_strings);
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
self.parse_global_expr(&mut peekable, &mut state, |_| {}, self.optimization_level) self.parse_global_expr(&mut peekable, &mut state, |_| {}, self.optimization_level)
} }
} }

View File

@ -217,6 +217,7 @@ impl Engine {
scope_may_be_changed: bool, scope_may_be_changed: bool,
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static, func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
) -> ParseResult<&mut Self> { ) -> ParseResult<&mut Self> {
#[allow(clippy::wildcard_imports)]
use markers::*; use markers::*;
let mut segments = StaticVec::<ImmutableString>::new(); let mut segments = StaticVec::<ImmutableString>::new();
@ -256,19 +257,29 @@ impl Engine {
// Standard or reserved keyword/symbol not in first position // Standard or reserved keyword/symbol not in first position
_ if !segments.is_empty() && token.is_some() => { _ if !segments.is_empty() && token.is_some() => {
// Make it a custom keyword/symbol if it is disabled or reserved // Make it a custom keyword/symbol if it is disabled or reserved
if ((!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s)) if (self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s))
|| token.map_or(false, |v| v.is_reserved())) || token.map_or(false, |v| v.is_reserved()))
&& (self.custom_keywords.is_empty() && !self
|| !self.custom_keywords.contains_key(s)) .custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(s))
{ {
self.custom_keywords.insert(s.into(), None); self.custom_keywords
.get_or_insert_with(Default::default)
.insert(s.into(), None);
} }
s.into() s.into()
} }
// Standard keyword in first position but not disabled // Standard keyword in first position but not disabled
_ if segments.is_empty() _ if segments.is_empty()
&& token.as_ref().map_or(false, Token::is_standard_keyword) && token.as_ref().map_or(false, Token::is_standard_keyword)
&& (self.disabled_symbols.is_empty() || !self.disabled_symbols.contains(s)) => && !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s)) =>
{ {
return Err(LexError::ImproperSymbol( return Err(LexError::ImproperSymbol(
s.to_string(), s.to_string(),
@ -282,12 +293,19 @@ impl Engine {
// Identifier in first position // Identifier in first position
_ if segments.is_empty() && is_valid_identifier(s) => { _ if segments.is_empty() && is_valid_identifier(s) => {
// Make it a custom keyword/symbol if it is disabled or reserved // Make it a custom keyword/symbol if it is disabled or reserved
if (!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s)) if self
|| token.map_or(false, |v| v.is_reserved()) .disabled_symbols
&& self.custom_keywords.is_empty() .as_ref()
|| !self.custom_keywords.contains_key(s) .map_or(false, |m| m.contains(s))
|| (token.map_or(false, |v| v.is_reserved())
&& !self
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(s)))
{ {
self.custom_keywords.insert(s.into(), None); self.custom_keywords
.get_or_insert_with(Default::default)
.insert(s.into(), None);
} }
s.into() s.into()
} }
@ -372,14 +390,16 @@ impl Engine {
scope_may_be_changed: bool, scope_may_be_changed: bool,
func: impl Fn(&mut EvalContext, &[Expression], &Dynamic) -> RhaiResult + SendSync + 'static, func: impl Fn(&mut EvalContext, &[Expression], &Dynamic) -> RhaiResult + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
self.custom_syntax.insert( self.custom_syntax
key.into(), .get_or_insert_with(Default::default)
CustomSyntax { .insert(
parse: Box::new(parse), key.into(),
func: Box::new(func), CustomSyntax {
scope_may_be_changed, parse: Box::new(parse),
}, func: Box::new(func),
); scope_may_be_changed,
},
);
self self
} }
} }

View File

@ -2,7 +2,7 @@
#![cfg(feature = "internals")] #![cfg(feature = "internals")]
#![cfg(feature = "metadata")] #![cfg(feature = "metadata")]
use crate::module::FuncInfo; use crate::module::{FuncInfo, ModuleFlags};
use crate::tokenizer::{is_valid_function_name, Token}; use crate::tokenizer::{is_valid_function_name, Token};
use crate::{Engine, FnAccess, FnPtr, Module, Scope, INT}; use crate::{Engine, FnAccess, FnPtr, Module, Scope, INT};
@ -77,7 +77,6 @@ pub struct DefinitionsConfig {
impl Default for DefinitionsConfig { impl Default for DefinitionsConfig {
#[inline(always)] #[inline(always)]
#[must_use]
fn default() -> Self { fn default() -> Self {
Self { Self {
write_headers: false, write_headers: false,
@ -105,13 +104,13 @@ impl Definitions<'_> {
/// Headers are always present in content that is expected to be written to a file /// Headers are always present in content that is expected to be written to a file
/// (i.e. `write_to*` and `*_file` methods). /// (i.e. `write_to*` and `*_file` methods).
#[inline(always)] #[inline(always)]
pub fn with_headers(mut self, headers: bool) -> Self { pub const fn with_headers(mut self, headers: bool) -> Self {
self.config.write_headers = headers; self.config.write_headers = headers;
self self
} }
/// Include standard packages when writing definition files. /// Include standard packages when writing definition files.
#[inline(always)] #[inline(always)]
pub fn include_standard_packages(mut self, include_standard_packages: bool) -> Self { pub const fn include_standard_packages(mut self, include_standard_packages: bool) -> Self {
self.config.include_standard_packages = include_standard_packages; self.config.include_standard_packages = include_standard_packages;
self self
} }
@ -129,7 +128,6 @@ impl Definitions<'_> {
} }
/// Get the configuration. /// Get the configuration.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) const fn config(&self) -> &DefinitionsConfig { pub(crate) const fn config(&self) -> &DefinitionsConfig {
&self.config &self.config
} }
@ -177,19 +175,19 @@ impl Definitions<'_> {
let mut def_file = String::from("module static;\n\n"); let mut def_file = String::from("module static;\n\n");
if config.include_standard_packages { if config.include_standard_packages {
def_file += &self.builtin_functions_operators_impl(&config); def_file += &Self::builtin_functions_operators_impl(config);
def_file += "\n"; def_file += "\n";
def_file += &self.builtin_functions_impl(&config); def_file += &Self::builtin_functions_impl(config);
def_file += "\n"; def_file += "\n";
} }
def_file += &self.static_module_impl(&config); def_file += &self.static_module_impl(config);
def_file += "\n"; def_file += "\n";
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
use std::fmt::Write; use std::fmt::Write;
for (module_name, module_def) in self.modules_impl(&config) { for (module_name, module_def) in self.modules_impl(config) {
write!( write!(
&mut def_file, &mut def_file,
"\nmodule {module_name} {{\n{module_def}\n}}\n" "\nmodule {module_name} {{\n{module_def}\n}}\n"
@ -199,7 +197,7 @@ impl Definitions<'_> {
def_file += "\n"; def_file += "\n";
} }
def_file += &self.scope_items_impl(&config); def_file += &self.scope_items_impl(config);
def_file += "\n"; def_file += "\n";
@ -220,11 +218,11 @@ impl Definitions<'_> {
vec![ vec![
( (
"__builtin__.d.rhai".to_string(), "__builtin__.d.rhai".to_string(),
self.builtin_functions_impl(&config), Self::builtin_functions_impl(config),
), ),
( (
"__builtin-operators__.d.rhai".to_string(), "__builtin-operators__.d.rhai".to_string(),
self.builtin_functions_operators_impl(&config), Self::builtin_functions_operators_impl(config),
), ),
] ]
} else { } else {
@ -233,18 +231,18 @@ impl Definitions<'_> {
.into_iter() .into_iter()
.chain(std::iter::once(( .chain(std::iter::once((
"__static__.d.rhai".to_string(), "__static__.d.rhai".to_string(),
self.static_module_impl(&config), self.static_module_impl(config),
))) )))
.chain(self.scope.iter().map(move |_| { .chain(self.scope.iter().map(move |_| {
( (
"__scope__.d.rhai".to_string(), "__scope__.d.rhai".to_string(),
self.scope_items_impl(&config), self.scope_items_impl(config),
) )
})) }))
.chain( .chain(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
self.modules_impl(&config) self.modules_impl(config)
.map(|(name, def)| (format!("{name}.d.rhai"), def)) .map(|(name, def)| (format!("{name}.d.rhai"), def))
}, },
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
@ -258,12 +256,12 @@ impl Definitions<'_> {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn builtin_functions(&self) -> String { pub fn builtin_functions(&self) -> String {
self.builtin_functions_impl(&self.config) Self::builtin_functions_impl(self.config)
} }
/// Return definitions for all builtin functions. /// Return definitions for all builtin functions.
#[must_use] #[must_use]
fn builtin_functions_impl(&self, config: &DefinitionsConfig) -> String { fn builtin_functions_impl(config: DefinitionsConfig) -> String {
let def = include_str!("builtin-functions.d.rhai"); let def = include_str!("builtin-functions.d.rhai");
if config.write_headers { if config.write_headers {
@ -277,12 +275,12 @@ impl Definitions<'_> {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn builtin_functions_operators(&self) -> String { pub fn builtin_functions_operators(&self) -> String {
self.builtin_functions_operators_impl(&self.config) Self::builtin_functions_operators_impl(self.config)
} }
/// Return definitions for all builtin operators. /// Return definitions for all builtin operators.
#[must_use] #[must_use]
fn builtin_functions_operators_impl(&self, config: &DefinitionsConfig) -> String { fn builtin_functions_operators_impl(config: DefinitionsConfig) -> String {
let def = include_str!("builtin-operators.d.rhai"); let def = include_str!("builtin-operators.d.rhai");
if config.write_headers { if config.write_headers {
@ -296,22 +294,28 @@ impl Definitions<'_> {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn static_module(&self) -> String { pub fn static_module(&self) -> String {
self.static_module_impl(&self.config) self.static_module_impl(self.config)
} }
/// Return definitions for all globally available functions and constants. /// Return definitions for all globally available functions and constants.
#[must_use] #[must_use]
fn static_module_impl(&self, config: &DefinitionsConfig) -> String { fn static_module_impl(&self, config: DefinitionsConfig) -> String {
let mut s = if config.write_headers { let mut s = if config.write_headers {
String::from("module static;\n\n") String::from("module static;\n\n")
} else { } else {
String::new() String::new()
}; };
let exclude_flags = if self.config.include_standard_packages {
ModuleFlags::empty()
} else {
ModuleFlags::STANDARD_LIB
};
self.engine self.engine
.global_modules .global_modules
.iter() .iter()
.filter(|m| self.config.include_standard_packages || !m.standard) .filter(|m| !m.flags.contains(exclude_flags))
.enumerate() .enumerate()
.for_each(|(i, m)| { .for_each(|(i, m)| {
if i > 0 { if i > 0 {
@ -327,12 +331,12 @@ impl Definitions<'_> {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn scope_items(&self) -> String { pub fn scope_items(&self) -> String {
self.scope_items_impl(&self.config) self.scope_items_impl(self.config)
} }
/// Return definitions for all items inside the [`Scope`], if any. /// Return definitions for all items inside the [`Scope`], if any.
#[must_use] #[must_use]
fn scope_items_impl(&self, config: &DefinitionsConfig) -> String { fn scope_items_impl(&self, config: DefinitionsConfig) -> String {
let mut s = if config.write_headers { let mut s = if config.write_headers {
String::from("module static;\n\n") String::from("module static;\n\n")
} else { } else {
@ -352,19 +356,20 @@ impl Definitions<'_> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
pub fn modules(&self) -> impl Iterator<Item = (String, String)> + '_ { pub fn modules(&self) -> impl Iterator<Item = (String, String)> + '_ {
self.modules_impl(&self.config) self.modules_impl(self.config)
} }
/// Return a (module name, definitions) pair for each registered static [module][Module]. /// Return a (module name, definitions) pair for each registered static [module][Module].
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
fn modules_impl( fn modules_impl(
&self, &self,
config: &DefinitionsConfig, config: DefinitionsConfig,
) -> impl Iterator<Item = (String, String)> + '_ { ) -> impl Iterator<Item = (String, String)> + '_ {
let mut m = self let mut m = self
.engine .engine
.global_sub_modules .global_sub_modules
.iter() .iter()
.flat_map(|m| m.iter())
.map(move |(name, module)| { .map(move |(name, module)| {
( (
name.to_string(), name.to_string(),
@ -425,10 +430,11 @@ impl Module {
} }
let mut func_infos = self.iter_fn().collect::<Vec<_>>(); let mut func_infos = self.iter_fn().collect::<Vec<_>>();
func_infos.sort_by(|a, b| match a.name.cmp(&b.name) { func_infos.sort_by(|a, b| match a.metadata.name.cmp(&b.metadata.name) {
Ordering::Equal => match a.num_params.cmp(&b.num_params) { Ordering::Equal => match a.metadata.num_params.cmp(&b.metadata.num_params) {
Ordering::Equal => (a.params_info.join("") + a.return_type.as_str()) Ordering::Equal => (a.metadata.params_info.join("")
.cmp(&(b.params_info.join("") + b.return_type.as_str())), + a.metadata.return_type.as_str())
.cmp(&(b.metadata.params_info.join("") + b.metadata.return_type.as_str())),
o => o, o => o,
}, },
o => o, o => o,
@ -440,13 +446,17 @@ impl Module {
} }
first = false; first = false;
if f.access != FnAccess::Private { if f.metadata.access != FnAccess::Private {
#[cfg(not(feature = "no_custom_syntax"))] let operator =
let operator = def.engine.custom_keywords.contains_key(f.name.as_str()) !f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name);
|| (!f.name.contains('$') && !is_valid_function_name(f.name.as_str()));
#[cfg(feature = "no_custom_syntax")] #[cfg(not(feature = "no_custom_syntax"))]
let operator = !f.name.contains('$') && !is_valid_function_name(&f.name); let operator = operator
|| def
.engine
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
f.write_definition(writer, def, operator)?; f.write_definition(writer, def, operator)?;
} }
@ -464,7 +474,7 @@ impl FuncInfo {
def: &Definitions, def: &Definitions,
operator: bool, operator: bool,
) -> fmt::Result { ) -> fmt::Result {
for comment in &*self.comments { for comment in &*self.metadata.comments {
writeln!(writer, "{comment}")?; writeln!(writer, "{comment}")?;
} }
@ -474,29 +484,33 @@ impl FuncInfo {
writer.write_str("fn ")?; writer.write_str("fn ")?;
} }
if let Some(name) = self.name.strip_prefix("get$") { if let Some(name) = self.metadata.name.strip_prefix("get$") {
write!(writer, "get {name}(")?; write!(writer, "get {name}(")?;
} else if let Some(name) = self.name.strip_prefix("set$") { } else if let Some(name) = self.metadata.name.strip_prefix("set$") {
write!(writer, "set {name}(")?; write!(writer, "set {name}(")?;
} else { } else {
write!(writer, "{}(", self.name)?; write!(writer, "{}(", self.metadata.name)?;
} }
let mut first = true; let mut first = true;
for i in 0..self.num_params { for i in 0..self.metadata.num_params {
if !first { if !first {
writer.write_str(", ")?; writer.write_str(", ")?;
} }
first = false; first = false;
let (param_name, param_type) = self.params_info.get(i).map_or(("_", "?".into()), |s| { let (param_name, param_type) =
let mut s = s.splitn(2, ':'); self.metadata
( .params_info
s.next().unwrap_or("_").split(' ').last().unwrap(), .get(i)
s.next() .map_or(("_", "?".into()), |s| {
.map_or(Cow::Borrowed("?"), |ty| def_type_name(ty, def.engine)), let mut s = s.splitn(2, ':');
) (
}); s.next().unwrap_or("_").split(' ').last().unwrap(),
s.next()
.map_or(Cow::Borrowed("?"), |ty| def_type_name(ty, def.engine)),
)
});
if operator { if operator {
write!(writer, "{param_type}")?; write!(writer, "{param_type}")?;
@ -508,7 +522,7 @@ impl FuncInfo {
write!( write!(
writer, writer,
") -> {};", ") -> {};",
def_type_name(&self.return_type, def.engine) def_type_name(&self.metadata.return_type, def.engine)
)?; )?;
Ok(()) Ok(())

View File

@ -3,8 +3,8 @@
use crate::func::RegisterNativeFunction; use crate::func::RegisterNativeFunction;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext, Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, NativeCallContext,
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, TypeBuilder, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -534,7 +534,7 @@ impl Position {
} }
#[allow(deprecated)] #[allow(deprecated)]
impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> { impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
/// Register a custom fallible function. /// Register a custom fallible function.
/// ///
/// # Deprecated /// # Deprecated
@ -642,3 +642,19 @@ impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
self.with_indexer_set(set_fn) self.with_indexer_set(set_fn)
} }
} }
impl Module {
/// Create a new [`Module`] with a pre-sized capacity for functions.
///
/// # Deprecated
///
/// This method is deprecated. Use `new` instead.
///
/// This method will be removed in the next major version.
#[inline(always)]
#[must_use]
#[deprecated(since = "1.12.0", note = "use `new` instead")]
pub fn with_capacity(_capacity: usize) -> Self {
Self::new()
}
}

View File

@ -1,6 +1,7 @@
//! Module that defines the public evaluation API of [`Engine`]. //! Module that defines the public evaluation API of [`Engine`].
use crate::eval::{Caches, GlobalRuntimeState}; use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::native::locked_write;
use crate::parser::ParseState; use crate::parser::ParseState;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
@ -69,7 +70,7 @@ impl Engine {
) -> RhaiResultOf<T> { ) -> RhaiResultOf<T> {
let ast = self.compile_with_scope_and_optimization_level( let ast = self.compile_with_scope_and_optimization_level(
scope, scope,
&[script], [script],
self.optimization_level, self.optimization_level,
)?; )?;
self.eval_ast_with_scope(scope, &ast) self.eval_ast_with_scope(scope, &ast)
@ -117,20 +118,25 @@ impl Engine {
script: &str, script: &str,
) -> RhaiResultOf<T> { ) -> RhaiResultOf<T> {
let scripts = [script]; let scripts = [script];
let (stream, tokenizer_control) = let ast = {
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref)); let interned_strings = &mut *locked_write(&self.interned_strings);
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
// No need to optimize a lone expression let (stream, tokenizer_control) =
let ast = self.parse_global_expr( self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
&mut stream.peekable(),
&mut state, let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|_| {},
#[cfg(not(feature = "no_optimize"))] // No need to optimize a lone expression
OptimizationLevel::None, self.parse_global_expr(
#[cfg(feature = "no_optimize")] &mut stream.peekable(),
OptimizationLevel::default(), &mut state,
)?; |_| {},
#[cfg(not(feature = "no_optimize"))]
OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
OptimizationLevel::default(),
)?
};
self.eval_ast_with_scope(scope, &ast) self.eval_ast_with_scope(scope, &ast)
} }
@ -238,7 +244,7 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
if self.debugger.is_some() { if self.debugger.is_some() {
global.debugger.status = crate::eval::DebuggerStatus::Terminate; global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
let mut this = Dynamic::NULL; let mut this = Dynamic::NULL;
let node = &crate::ast::Stmt::Noop(Position::NONE); let node = &crate::ast::Stmt::Noop(Position::NONE);

View File

@ -360,7 +360,7 @@ impl Engine {
+ SendSync + SendSync
+ 'static, + 'static,
) -> &mut Self { ) -> &mut Self {
self.debugger = Some((Box::new(init), Box::new(callback))); self.debugger = Some(Box::new((Box::new(init), Box::new(callback))));
self self
} }
} }

View File

@ -1,7 +1,8 @@
//! Module that defines JSON manipulation functions for [`Engine`]. //! Module that defines JSON manipulation functions for [`Engine`].
#![cfg(not(feature = "no_object"))] #![cfg(not(feature = "no_object"))]
use crate::parser::ParseState; use crate::func::native::locked_write;
use crate::parser::{ParseSettingFlags, ParseState};
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::{Engine, LexError, Map, OptimizationLevel, RhaiResultOf, Scope}; use crate::{Engine, LexError, Map, OptimizationLevel, RhaiResultOf, Scope};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -116,18 +117,21 @@ impl Engine {
}, },
); );
let scope = Scope::new(); let ast = {
let mut state = ParseState::new(self, &scope, Default::default(), tokenizer_control); let scope = Scope::new();
let interned_strings = &mut *locked_write(&self.interned_strings);
let mut state = ParseState::new(&scope, interned_strings, tokenizer_control);
let ast = self.parse_global_expr( self.parse_global_expr(
&mut stream.peekable(), &mut stream.peekable(),
&mut state, &mut state,
|s| s.allow_unquoted_map_properties = false, |s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
OptimizationLevel::None, OptimizationLevel::None,
#[cfg(feature = "no_optimize")] #[cfg(feature = "no_optimize")]
OptimizationLevel::default(), OptimizationLevel::default(),
)?; )?
};
self.eval_ast(&ast) self.eval_ast(&ast)
} }
@ -165,7 +169,7 @@ pub fn format_map_as_json(map: &Map) -> String {
result.push(':'); result.push(':');
if let Some(val) = value.read_lock::<Map>() { if let Some(val) = value.read_lock::<Map>() {
result.push_str(&format_map_as_json(&*val)); result.push_str(&format_map_as_json(&val));
} else if value.is_unit() { } else if value.is_unit() {
result.push_str("null"); result.push_str("null");
} else { } else {

View File

@ -107,7 +107,9 @@ impl Engine {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self { pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
self.disabled_symbols.insert(symbol.into()); self.disabled_symbols
.get_or_insert_with(Default::default)
.insert(symbol.into());
self self
} }
@ -163,24 +165,30 @@ impl Engine {
// Active standard keywords cannot be made custom // Active standard keywords cannot be made custom
// Disabled keywords are OK // Disabled keywords are OK
Some(token) if token.is_standard_keyword() => { Some(token) if token.is_standard_keyword() => {
if self.disabled_symbols.is_empty() if !self
|| !self.disabled_symbols.contains(&*token.syntax()) .disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax()))
{ {
return Err(format!("'{keyword}' is a reserved keyword")); return Err(format!("'{keyword}' is a reserved keyword"));
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if token.is_standard_symbol() => { Some(token) if token.is_standard_symbol() => {
if self.disabled_symbols.is_empty() if !self
|| !self.disabled_symbols.contains(&*token.syntax()) .disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax()))
{ {
return Err(format!("'{keyword}' is a reserved operator")); return Err(format!("'{keyword}' is a reserved operator"));
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) Some(token)
if self.disabled_symbols.is_empty() if !self
|| !self.disabled_symbols.contains(&*token.syntax()) => .disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax())) =>
{ {
return Err(format!("'{keyword}' is a reserved symbol")) return Err(format!("'{keyword}' is a reserved symbol"))
} }
@ -190,6 +198,7 @@ impl Engine {
// Add to custom keywords // Add to custom keywords
self.custom_keywords self.custom_keywords
.get_or_insert_with(Default::default)
.insert(keyword.into(), Some(precedence)); .insert(keyword.into(), Some(precedence));
Ok(self) Ok(self)
@ -198,7 +207,7 @@ impl Engine {
/// Get the default value of the custom state for each evaluation run. /// Get the default value of the custom state for each evaluation run.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn default_tag(&self) -> &Dynamic { pub const fn default_tag(&self) -> &Dynamic {
&self.def_tag &self.def_tag
} }
/// Get a mutable reference to the default value of the custom state for each evaluation run. /// Get a mutable reference to the default value of the custom state for each evaluation run.

View File

@ -683,13 +683,14 @@ impl Engine {
name: impl AsRef<str>, name: impl AsRef<str>,
module: SharedModule, module: SharedModule,
) -> &mut Self { ) -> &mut Self {
use std::collections::BTreeMap;
fn register_static_module_raw( fn register_static_module_raw(
root: &mut std::collections::BTreeMap<Identifier, SharedModule>, root: &mut BTreeMap<Identifier, SharedModule>,
name: &str, name: &str,
module: SharedModule, module: SharedModule,
) { ) {
let separator = crate::tokenizer::Token::DoubleColon.syntax(); let separator = crate::tokenizer::Token::DoubleColon.literal_syntax();
let separator = separator.as_ref();
if name.contains(separator) { if name.contains(separator) {
let mut iter = name.splitn(2, separator); let mut iter = name.splitn(2, separator);
@ -718,7 +719,11 @@ impl Engine {
} }
} }
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module); register_static_module_raw(
self.global_sub_modules.get_or_insert_with(Default::default),
name.as_ref(),
module,
);
self self
} }
/// _(metadata)_ Generate a list of all registered functions. /// _(metadata)_ Generate a list of all registered functions.
@ -738,15 +743,21 @@ impl Engine {
signatures.extend(self.global_namespace().gen_fn_signatures()); signatures.extend(self.global_namespace().gen_fn_signatures());
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
for (name, m) in &self.global_sub_modules { for (name, m) in self.global_sub_modules.iter().flat_map(|m| m.iter()) {
signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}"))); signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}")));
} }
let exclude_flags = if include_packages {
crate::module::ModuleFlags::INTERNAL
} else {
crate::module::ModuleFlags::INTERNAL | crate::module::ModuleFlags::STANDARD_LIB
};
signatures.extend( signatures.extend(
self.global_modules self.global_modules
.iter() .iter()
.skip(1) .skip(1)
.filter(|m| !m.internal && (include_packages || !m.standard)) .filter(|m| !m.flags.contains(exclude_flags))
.flat_map(|m| m.gen_fn_signatures()), .flat_map(|m| m.gen_fn_signatures()),
); );

View File

@ -1,6 +1,7 @@
//! Module that defines the public evaluation API of [`Engine`]. //! Module that defines the public evaluation API of [`Engine`].
use crate::eval::{Caches, GlobalRuntimeState}; use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::native::locked_write;
use crate::parser::ParseState; use crate::parser::ParseState;
use crate::{Engine, RhaiResultOf, Scope, AST}; use crate::{Engine, RhaiResultOf, Scope, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -56,10 +57,16 @@ impl Engine {
#[inline] #[inline]
pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> { pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
let scripts = [script]; let scripts = [script];
let (stream, tokenizer_control) = let ast = {
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref)); let interned_strings = &mut *locked_write(&self.interned_strings);
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
let ast = self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?; let (stream, tokenizer_control) =
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?
};
self.run_ast_with_scope(scope, &ast) self.run_ast_with_scope(scope, &ast)
} }
/// Evaluate an [`AST`]. /// Evaluate an [`AST`].
@ -130,7 +137,7 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
if self.debugger.is_some() { if self.debugger.is_some() {
global.debugger.status = crate::eval::DebuggerStatus::Terminate; global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
let mut this = crate::Dynamic::NULL; let mut this = crate::Dynamic::NULL;
let node = &crate::ast::Stmt::Noop(crate::Position::NONE); let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
self.run_debugger(global, caches, scope, &mut this, node)?; self.run_debugger(global, caches, scope, &mut this, node)?;

View File

@ -139,8 +139,8 @@ pub fn format_type(typ: &str, is_return_type: bool) -> std::borrow::Cow<str> {
} else { } else {
format!("&mut {r}").into() format!("&mut {r}").into()
}; };
} else if typ.contains(" ") { } else if typ.contains(' ') {
let typ = typ.replace(" ", ""); let typ = typ.replace(' ', "");
let r = format_type(&typ, is_return_type); let r = format_type(&typ, is_return_type);
return r.into_owned().into(); return r.into_owned().into();
} }
@ -205,6 +205,7 @@ impl Engine {
return self return self
.global_sub_modules .global_sub_modules
.iter() .iter()
.flat_map(|m| m.iter())
.find_map(|(_, m)| m.get_custom_type(name)); .find_map(|(_, m)| m.get_custom_type(name));
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
return None; return None;
@ -238,6 +239,7 @@ impl Engine {
return self return self
.global_sub_modules .global_sub_modules
.iter() .iter()
.flat_map(|m| m.iter())
.find_map(|(_, m)| m.get_custom_type(name)); .find_map(|(_, m)| m.get_custom_type(name));
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
return None; return None;

View File

@ -772,7 +772,7 @@ impl AST {
/// Not available under `no_function`. /// Not available under `no_function`.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a { pub fn iter_functions(&self) -> impl Iterator<Item = super::ScriptFnMetadata> {
self.lib self.lib
.iter_script_fn() .iter_script_fn()
.map(|(.., fn_def)| fn_def.as_ref().into()) .map(|(.., fn_def)| fn_def.as_ref().into())
@ -942,7 +942,7 @@ impl Borrow<crate::Module> for AST {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
fn borrow(&self) -> &crate::Module { fn borrow(&self) -> &crate::Module {
&self.shared_lib() self.shared_lib()
} }
} }
@ -1012,7 +1012,20 @@ impl PartialEq for ASTNode<'_> {
impl Eq for ASTNode<'_> {} impl Eq for ASTNode<'_> {}
impl ASTNode<'_> { impl ASTNode<'_> {
/// Is this [`ASTNode`] a [`Stmt`]?
#[inline(always)]
#[must_use]
pub const fn is_stmt(&self) -> bool {
matches!(self, Self::Stmt(..))
}
/// Is this [`ASTNode`] an [`Expr`]?
#[inline(always)]
#[must_use]
pub const fn is_expr(&self) -> bool {
matches!(self, Self::Expr(..))
}
/// Get the [`Position`] of this [`ASTNode`]. /// Get the [`Position`] of this [`ASTNode`].
#[inline]
#[must_use] #[must_use]
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {

View File

@ -173,7 +173,7 @@ impl FnCallHashes {
/// The hash returned is never zero. /// The hash returned is never zero.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn native(&self) -> u64 { pub const fn native(&self) -> u64 {
self.native.get() self.native.get()
} }
/// Get the script hash. /// Get the script hash.
@ -361,7 +361,7 @@ impl fmt::Debug for Expr {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut display_pos = format!(" @ {:?}", self.start_position()); let mut display_pos = self.start_position();
match self { match self {
Self::DynamicConstant(value, ..) => write!(f, "{value:?}"), Self::DynamicConstant(value, ..) => write!(f, "{value:?}"),
@ -395,7 +395,7 @@ impl fmt::Debug for Expr {
write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?; write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?;
let pos = x.1.position(); let pos = x.1.position();
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = pos;
} }
} }
f.write_str(&x.3)?; f.write_str(&x.3)?;
@ -413,7 +413,7 @@ impl fmt::Debug for Expr {
Self::Stmt(x) => { Self::Stmt(x) => {
let pos = x.span(); let pos = x.span();
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = pos.start();
} }
f.write_str("ExprStmtBlock")?; f.write_str("ExprStmtBlock")?;
f.debug_list().entries(x.iter()).finish() f.debug_list().entries(x.iter()).finish()
@ -421,7 +421,7 @@ impl fmt::Debug for Expr {
Self::FnCall(x, ..) => fmt::Debug::fmt(x, f), Self::FnCall(x, ..) => fmt::Debug::fmt(x, f),
Self::Index(x, options, pos) => { Self::Index(x, options, pos) => {
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = *pos;
} }
let mut f = f.debug_struct("Index"); let mut f = f.debug_struct("Index");
@ -434,7 +434,7 @@ impl fmt::Debug for Expr {
} }
Self::Dot(x, options, pos) => { Self::Dot(x, options, pos) => {
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = *pos;
} }
let mut f = f.debug_struct("Dot"); let mut f = f.debug_struct("Dot");
@ -454,7 +454,7 @@ impl fmt::Debug for Expr {
}; };
if !pos.is_none() { if !pos.is_none() {
display_pos = format!(" @ {pos:?}"); display_pos = *pos;
} }
f.debug_struct(op_name) f.debug_struct(op_name)
@ -466,7 +466,7 @@ impl fmt::Debug for Expr {
Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(), Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(),
}?; }?;
f.write_str(&display_pos) write!(f, " @ {display_pos:?}")
} }
} }
@ -837,18 +837,12 @@ impl Expr {
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Self::Custom(..) => false, Self::Custom(..) => false,
Self::Variable(..) => match token { Self::Variable(..) => matches!(
Token::LeftParen => true, token,
Token::Unit => true, Token::LeftParen | Token::Unit | Token::Bang | Token::DoubleColon
Token::Bang => true, ),
Token::DoubleColon => true,
_ => false,
},
Self::Property(..) => match token { Self::Property(..) => matches!(token, Token::LeftParen),
Token::LeftParen => true,
_ => false,
},
} }
} }
/// Recursively walk this expression. /// Recursively walk this expression.

View File

@ -6,7 +6,7 @@ use std::prelude::v1::*;
/// A type representing the access mode of a function. /// A type representing the access mode of a function.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "metadata", derive(serde::Serialize))] #[cfg_attr(feature = "metadata", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "metadata", serde(rename_all = "camelCase"))] #[cfg_attr(feature = "metadata", serde(rename_all = "camelCase"))]
#[non_exhaustive] #[non_exhaustive]
pub enum FnAccess { pub enum FnAccess {

View File

@ -2,7 +2,7 @@
#![cfg(not(feature = "no_function"))] #![cfg(not(feature = "no_function"))]
use super::{FnAccess, StmtBlock}; use super::{FnAccess, StmtBlock};
use crate::{ImmutableString, StaticVec}; use crate::{FnArgsVec, ImmutableString};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{fmt, hash::Hash}; use std::{fmt, hash::Hash};
@ -42,7 +42,7 @@ pub struct ScriptFnDef {
/// Function access mode. /// Function access mode.
pub access: FnAccess, pub access: FnAccess,
/// Names of function parameters. /// Names of function parameters.
pub params: StaticVec<ImmutableString>, pub params: FnArgsVec<ImmutableString>,
/// _(metadata)_ Function doc-comments (if any). /// _(metadata)_ Function doc-comments (if any).
/// Exported under the `metadata` feature only. /// Exported under the `metadata` feature only.
/// ///
@ -72,7 +72,7 @@ impl fmt::Display for ScriptFnDef {
self.params self.params
.iter() .iter()
.map(|s| s.as_str()) .map(|s| s.as_str())
.collect::<StaticVec<_>>() .collect::<FnArgsVec<_>>()
.join(", ") .join(", ")
) )
} }
@ -121,7 +121,7 @@ impl fmt::Display for ScriptFnMetadata<'_> {
self.params self.params
.iter() .iter()
.copied() .copied()
.collect::<StaticVec<_>>() .collect::<FnArgsVec<_>>()
.join(", ") .join(", ")
) )
} }

View File

@ -926,10 +926,7 @@ impl Stmt {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn is_control_flow_break(&self) -> bool { pub const fn is_control_flow_break(&self) -> bool {
match self { matches!(self, Self::Return(..) | Self::BreakLoop(..))
Self::Return(..) | Self::BreakLoop(..) => true,
_ => false,
}
} }
/// Recursively walk this statement. /// Recursively walk this statement.
/// Return `false` from the callback to terminate the walk. /// Return `false` from the callback to terminate the walk.

View File

@ -61,7 +61,7 @@ fn print_current_source(
) { ) {
let current_source = &mut *context let current_source = &mut *context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.state_mut() .state_mut()
.write_lock::<ImmutableString>() .write_lock::<ImmutableString>()
.unwrap(); .unwrap();
@ -241,7 +241,7 @@ fn debug_callback(
DebuggerEvent::End => println!("\x1b[31m! Script end\x1b[39m"), DebuggerEvent::End => println!("\x1b[31m! Script end\x1b[39m"),
DebuggerEvent::Step => (), DebuggerEvent::Step => (),
DebuggerEvent::BreakPoint(n) => { DebuggerEvent::BreakPoint(n) => {
match context.global_runtime_state().debugger.break_points()[n] { match context.global_runtime_state().debugger().break_points()[n] {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
BreakPoint::AtPosition { .. } => (), BreakPoint::AtPosition { .. } => (),
BreakPoint::AtFunctionName { ref name, .. } BreakPoint::AtFunctionName { ref name, .. }
@ -260,7 +260,7 @@ fn debug_callback(
"! Return from function call '{}' => {:?}", "! Return from function call '{}' => {:?}",
context context
.global_runtime_state() .global_runtime_state()
.debugger .debugger()
.call_stack() .call_stack()
.last() .last()
.unwrap() .unwrap()
@ -273,7 +273,7 @@ fn debug_callback(
"! Return from function call '{}' with error: {}", "! Return from function call '{}' with error: {}",
context context
.global_runtime_state() .global_runtime_state()
.debugger .debugger()
.call_stack() .call_stack()
.last() .last()
.unwrap() .unwrap()
@ -373,7 +373,7 @@ fn debug_callback(
["backtrace" | "bt"] => { ["backtrace" | "bt"] => {
for frame in context for frame in context
.global_runtime_state() .global_runtime_state()
.debugger .debugger()
.call_stack() .call_stack()
.iter() .iter()
.rev() .rev()
@ -384,7 +384,7 @@ fn debug_callback(
["info" | "i", "break" | "b"] => Iterator::for_each( ["info" | "i", "break" | "b"] => Iterator::for_each(
context context
.global_runtime_state() .global_runtime_state()
.debugger .debugger()
.break_points() .break_points()
.iter() .iter()
.enumerate(), .enumerate(),
@ -402,13 +402,13 @@ fn debug_callback(
if let Ok(n) = n.parse::<usize>() { if let Ok(n) = n.parse::<usize>() {
let range = 1..=context let range = 1..=context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger()
.break_points() .break_points()
.len(); .len();
if range.contains(&n) { if range.contains(&n) {
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.get_mut(n - 1) .get_mut(n - 1)
.unwrap() .unwrap()
@ -425,13 +425,13 @@ fn debug_callback(
if let Ok(n) = n.parse::<usize>() { if let Ok(n) = n.parse::<usize>() {
let range = 1..=context let range = 1..=context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger()
.break_points() .break_points()
.len(); .len();
if range.contains(&n) { if range.contains(&n) {
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.get_mut(n - 1) .get_mut(n - 1)
.unwrap() .unwrap()
@ -448,13 +448,13 @@ fn debug_callback(
if let Ok(n) = n.parse::<usize>() { if let Ok(n) = n.parse::<usize>() {
let range = 1..=context let range = 1..=context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger()
.break_points() .break_points()
.len(); .len();
if range.contains(&n) { if range.contains(&n) {
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.remove(n - 1); .remove(n - 1);
println!("Break-point #{n} deleted.") println!("Break-point #{n} deleted.")
@ -468,7 +468,7 @@ fn debug_callback(
["delete" | "d"] => { ["delete" | "d"] => {
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.clear(); .clear();
println!("All break-points deleted."); println!("All break-points deleted.");
@ -483,7 +483,7 @@ fn debug_callback(
println!("Break-point added for {bp}"); println!("Break-point added for {bp}");
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.push(bp); .push(bp);
} else { } else {
@ -500,7 +500,7 @@ fn debug_callback(
println!("Break-point added for {bp}"); println!("Break-point added for {bp}");
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.push(bp); .push(bp);
} }
@ -523,7 +523,7 @@ fn debug_callback(
println!("Break-point added {bp}"); println!("Break-point added {bp}");
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.push(bp); .push(bp);
} else { } else {
@ -539,7 +539,7 @@ fn debug_callback(
println!("Break-point added for {bp}"); println!("Break-point added for {bp}");
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.push(bp); .push(bp);
} }
@ -553,7 +553,7 @@ fn debug_callback(
println!("Break-point added {bp}"); println!("Break-point added {bp}");
context context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.break_points_mut() .break_points_mut()
.push(bp); .push(bp);
} }

View File

@ -71,7 +71,7 @@ impl WhenTheHokmaSuppression {
#[inline] #[inline]
pub fn the_price_of_silence(self) { pub fn the_price_of_silence(self) {
self.hokma.lock.store(self.state, Ordering::SeqCst); self.hokma.lock.store(self.state, Ordering::SeqCst);
mem::forget(self) mem::forget(self);
} }
} }
@ -80,58 +80,65 @@ impl Drop for WhenTheHokmaSuppression {
fn drop(&mut self) { fn drop(&mut self) {
self.hokma self.hokma
.lock .lock
.store(self.state.wrapping_add(2), Ordering::SeqCst) .store(self.state.wrapping_add(2), Ordering::SeqCst);
} }
} }
#[inline(always)] #[inline(always)]
#[must_use]
fn hokmalock(address: usize) -> &'static HokmaLock { fn hokmalock(address: usize) -> &'static HokmaLock {
const LEN: usize = 787; const LEN: usize = 787;
#[allow(clippy::declare_interior_mutable_const)]
const LCK: HokmaLock = HokmaLock::new(); const LCK: HokmaLock = HokmaLock::new();
static RECORDS: [HokmaLock; LEN] = [LCK; LEN]; static RECORDS: [HokmaLock; LEN] = [LCK; LEN];
&RECORDS[address % LEN] &RECORDS[address % LEN]
} }
// Safety: lol, there is a reason its called `SusLock<T>` /// # Safety
///
/// LOL, there is a reason its called `SusLock`
#[must_use] #[must_use]
struct SusLock<T> pub struct SusLock<T: 'static> {
where
T: 'static,
{
initialized: AtomicBool, initialized: AtomicBool,
data: UnsafeCell<MaybeUninit<T>>, data: UnsafeCell<MaybeUninit<T>>,
_marker: PhantomData<T>, _marker: PhantomData<T>,
} }
impl<T> SusLock<T> impl<T: 'static> Default for SusLock<T> {
where #[inline(always)]
T: 'static, fn default() -> Self {
{ Self::new()
}
}
impl<T: 'static> SusLock<T> {
/// Create a new [`SusLock`].
#[inline] #[inline]
pub const fn new() -> SusLock<T> { pub const fn new() -> Self {
SusLock { Self {
initialized: AtomicBool::new(false), initialized: AtomicBool::new(false),
data: UnsafeCell::new(MaybeUninit::uninit()), data: UnsafeCell::new(MaybeUninit::uninit()),
_marker: PhantomData, _marker: PhantomData,
} }
} }
/// Is the [`SusLock`] initialized?
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn is_initialized(&self) -> bool { pub fn is_initialized(&self) -> bool {
self.initialized.load(Ordering::SeqCst) self.initialized.load(Ordering::SeqCst)
} }
/// Return the value of the [`SusLock`] (if initialized).
#[inline]
#[must_use] #[must_use]
pub fn get(&self) -> Option<&'static T> { pub fn get(&self) -> Option<&'static T> {
if self.initialized.load(Ordering::SeqCst) { if self.initialized.load(Ordering::SeqCst) {
let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); let hokma = hokmalock(self.data.get() as usize);
// we forgo the optimistic read, because we don't really care // we forgo the optimistic read, because we don't really care
let guard = hokma.write(); let guard = hokma.write();
let cast: *const T = self.data.get().cast(); let cast: *const T = self.data.get().cast();
let val = unsafe { mem::transmute::<*const T, &'static T>(cast) }; let val = unsafe { &*cast.cast::<T>() };
guard.the_price_of_silence(); guard.the_price_of_silence();
Some(val) Some(val)
} else { } else {
@ -139,11 +146,13 @@ where
} }
} }
/// Return the value of the [`SusLock`], initializing it if not yet done.
#[inline]
#[must_use] #[must_use]
pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T { pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T {
if !self.initialized.load(Ordering::SeqCst) { if !self.initialized.load(Ordering::SeqCst) {
self.initialized.store(true, Ordering::SeqCst); self.initialized.store(true, Ordering::SeqCst);
let hokma = hokmalock(unsafe { mem::transmute(self.data.get()) }); let hokma = hokmalock(self.data.get() as usize);
hokma.write(); hokma.write();
unsafe { unsafe {
self.data.get().write(MaybeUninit::new(f())); self.data.get().write(MaybeUninit::new(f()));
@ -153,7 +162,13 @@ where
self.get().unwrap() self.get().unwrap()
} }
pub fn set(&self, value: T) -> Result<(), T> { /// Initialize the value of the [`SusLock`].
///
/// # Error
///
/// If the [`SusLock`] has already been initialized, the current value is returned as error.
#[inline]
pub fn init(&self, value: T) -> Result<(), T> {
if self.initialized.load(Ordering::SeqCst) { if self.initialized.load(Ordering::SeqCst) {
Err(value) Err(value)
} else { } else {
@ -163,18 +178,15 @@ where
} }
} }
unsafe impl<T: Sync + Send> Sync for SusLock<T> where T: 'static {} unsafe impl<T: Sync + Send> Sync for SusLock<T> {}
unsafe impl<T: Send> Send for SusLock<T> where T: 'static {} unsafe impl<T: Send> Send for SusLock<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> where T: 'static {} impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> {}
impl<T> Drop for SusLock<T> impl<T: 'static> Drop for SusLock<T> {
where
T: 'static,
{
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
if self.initialized.load(Ordering::SeqCst) { if self.initialized.load(Ordering::SeqCst) {
unsafe { (&mut *self.data.get()).assume_init_drop() }; unsafe { (*self.data.get()).assume_init_drop() };
} }
} }
} }
@ -207,7 +219,7 @@ static AHASH_SEED: SusLock<Option<[u64; 4]>> = SusLock::new();
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> {
AHASH_SEED.set(new_seed) AHASH_SEED.init(new_seed)
} }
/// Get the current hashing Seed. /// Get the current hashing Seed.

View File

@ -1,3 +1,3 @@
//! This file is automatically recreated during build time by `build.rs` from `build.template`. //! This file is automatically recreated during build time by `build.rs` from `build.template`.
pub(crate) const AHASH_SEED: Option<[u64; 4]> = None; pub const AHASH_SEED: Option<[u64; 4]> = None;

View File

@ -5,6 +5,7 @@ use crate::func::native::{
locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback, locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback,
OnVarCallback, OnVarCallback,
}; };
use crate::module::ModuleFlags;
use crate::packages::{Package, StandardPackage}; use crate::packages::{Package, StandardPackage};
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::StringsInterner; use crate::types::StringsInterner;
@ -95,24 +96,27 @@ pub struct Engine {
pub(crate) global_modules: StaticVec<SharedModule>, pub(crate) global_modules: StaticVec<SharedModule>,
/// A collection of all sub-modules directly loaded into the Engine. /// A collection of all sub-modules directly loaded into the Engine.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, SharedModule>, pub(crate) global_sub_modules:
Option<Box<std::collections::BTreeMap<Identifier, SharedModule>>>,
/// A module resolution service. /// A module resolution service.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>, pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
/// An empty [`ImmutableString`] for cloning purposes. /// An empty [`ImmutableString`] for cloning purposes.
pub(crate) interned_strings: Locked<StringsInterner<'static>>, pub(crate) interned_strings: Locked<StringsInterner>,
/// A set of symbols to disable. /// A set of symbols to disable.
pub(crate) disabled_symbols: BTreeSet<Identifier>, pub(crate) disabled_symbols: Option<Box<BTreeSet<Identifier>>>,
/// A map containing custom keywords and precedence to recognize. /// A map containing custom keywords and precedence to recognize.
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
pub(crate) custom_keywords: std::collections::BTreeMap<Identifier, Option<Precedence>>, pub(crate) custom_keywords:
Option<Box<std::collections::BTreeMap<Identifier, Option<Precedence>>>>,
/// Custom syntax. /// Custom syntax.
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
pub(crate) custom_syntax: pub(crate) custom_syntax: Option<
std::collections::BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>, Box<std::collections::BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>>,
>,
/// Callback closure for filtering variable definition. /// Callback closure for filtering variable definition.
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>, pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
/// Callback closure for resolving variable access. /// Callback closure for resolving variable access.
@ -143,10 +147,12 @@ pub struct Engine {
/// Callback closure for debugging. /// Callback closure for debugging.
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
pub(crate) debugger: Option<( pub(crate) debugger: Option<
Box<crate::eval::OnDebuggingInit>, Box<(
Box<crate::eval::OnDebuggerCallback>, Box<crate::eval::OnDebuggingInit>,
)>, Box<crate::eval::OnDebuggerCallback>,
)>,
>,
} }
impl fmt::Debug for Engine { impl fmt::Debug for Engine {
@ -167,7 +173,8 @@ impl fmt::Debug for Engine {
"custom_syntax", "custom_syntax",
&self &self
.custom_syntax .custom_syntax
.keys() .iter()
.flat_map(|m| m.keys())
.map(crate::SmartString::as_str) .map(crate::SmartString::as_str)
.collect::<String>(), .collect::<String>(),
); );
@ -263,17 +270,17 @@ impl Engine {
global_modules: StaticVec::new_const(), global_modules: StaticVec::new_const(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
global_sub_modules: std::collections::BTreeMap::new(), global_sub_modules: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()), module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
interned_strings: StringsInterner::new().into(), interned_strings: StringsInterner::new().into(),
disabled_symbols: BTreeSet::new(), disabled_symbols: None,
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
custom_keywords: std::collections::BTreeMap::new(), custom_keywords: None,
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
custom_syntax: std::collections::BTreeMap::new(), custom_syntax: None,
def_var_filter: None, def_var_filter: None,
resolve_var: None, resolve_var: None,
@ -302,8 +309,8 @@ impl Engine {
}; };
// Add the global namespace module // Add the global namespace module
let mut global_namespace = Module::with_capacity(0); let mut global_namespace = Module::new();
global_namespace.internal = true; global_namespace.flags |= ModuleFlags::INTERNAL;
engine.global_modules.push(global_namespace.into()); engine.global_modules.push(global_namespace.into());
engine engine

View File

@ -72,7 +72,7 @@ impl Caches {
/// Push an empty function resolution cache onto the stack and make it current. /// Push an empty function resolution cache onto the stack and make it current.
#[inline(always)] #[inline(always)]
pub fn push_fn_resolution_cache(&mut self) { pub fn push_fn_resolution_cache(&mut self) {
self.0.push(Default::default()); self.0.push(FnResolutionCache::default());
} }
/// Rewind the function resolution caches stack to a particular size. /// Rewind the function resolution caches stack to a particular size.
#[inline(always)] #[inline(always)]

View File

@ -3,13 +3,36 @@
use super::{Caches, GlobalRuntimeState, Target}; use super::{Caches, GlobalRuntimeState, Target};
use crate::ast::{ASTFlags, Expr, OpAssignment}; use crate::ast::{ASTFlags, Expr, OpAssignment};
use crate::config::hashing::SusLock;
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::types::RestoreOnDrop; use crate::types::RestoreOnDrop;
use crate::{Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR}; use crate::{
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
};
use std::hash::Hash; use std::hash::Hash;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
/// Function call hashes to index getters and setters.
///
/// # Safety
///
/// Uses the extremely unsafe [`SusLock`]. Change to [`OnceCell`] when it is stabilized.
static INDEXER_HASHES: SusLock<(u64, u64)> = SusLock::new();
/// Get the pre-calculated index getter/setter hashes.
#[inline(always)]
#[must_use]
fn hash_idx() -> (u64, u64) {
*INDEXER_HASHES.get_or_init(|| {
(
calc_fn_hash(None, FN_IDX_GET, 2),
calc_fn_hash(None, FN_IDX_SET, 3),
)
})
}
/// Method of chaining. /// Method of chaining.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ChainType { pub enum ChainType {
@ -45,16 +68,21 @@ impl Engine {
idx: &mut Dynamic, idx: &mut Dynamic,
pos: Position, pos: Position,
) -> RhaiResultOf<Dynamic> { ) -> RhaiResultOf<Dynamic> {
let args = &mut [target, idx];
let hash = global.hash_idx_get();
let fn_name = crate::engine::FN_IDX_GET;
let orig_level = global.level; let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
self.exec_native_fn_call(global, caches, fn_name, None, hash, args, true, pos) self.exec_native_fn_call(
.map(|(r, ..)| r) global,
caches,
FN_IDX_GET,
None,
hash_idx().0,
&mut [target, idx],
true,
pos,
)
.map(|(r, ..)| r)
} }
/// Call a set indexer. /// Call a set indexer.
@ -69,15 +97,20 @@ impl Engine {
is_ref_mut: bool, is_ref_mut: bool,
pos: Position, pos: Position,
) -> RhaiResultOf<(Dynamic, bool)> { ) -> 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 orig_level = global.level; let orig_level = global.level;
global.level += 1; global.level += 1;
let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level);
self.exec_native_fn_call(global, caches, fn_name, None, hash, args, is_ref_mut, pos) self.exec_native_fn_call(
global,
caches,
FN_IDX_SET,
None,
hash_idx().1,
&mut [target, idx, new_val],
is_ref_mut,
pos,
)
} }
/// Get the value at the indexed position of a base type. /// Get the value at the indexed position of a base type.
@ -168,6 +201,7 @@ impl Engine {
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into() ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
})?; })?;
#[allow(clippy::cast_possible_truncation)]
if end <= start { if end <= start {
(0, 0) (0, 0)
} else if end == crate::INT_BITS && start == 0 { } else if end == crate::INT_BITS && start == 0 {
@ -193,6 +227,7 @@ impl Engine {
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into() ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
})?; })?;
#[allow(clippy::cast_possible_truncation)]
if end < start { if end < start {
(0, 0) (0, 0)
} else if end == crate::INT_BITS - 1 && start == 0 { } else if end == crate::INT_BITS - 1 && start == 0 {
@ -237,6 +272,7 @@ impl Engine {
Ok(Target::Bit { Ok(Target::Bit {
source: target, source: target,
value: bit_value.into(), value: bit_value.into(),
#[allow(clippy::cast_possible_truncation)]
bit: bit as u8, bit: bit as u8,
}) })
} }
@ -249,12 +285,14 @@ impl Engine {
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?; .map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
let (ch, offset) = if index >= 0 { let (ch, offset) = if index >= 0 {
#[allow(clippy::absurd_extreme_comparisons)]
if index >= crate::MAX_USIZE_INT { if index >= crate::MAX_USIZE_INT {
return Err( return Err(
ERR::ErrorStringBounds(s.chars().count(), index, idx_pos).into() ERR::ErrorStringBounds(s.chars().count(), index, idx_pos).into()
); );
} }
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let offset = index as usize; let offset = index as usize;
( (
s.chars().nth(offset).ok_or_else(|| { s.chars().nth(offset).ok_or_else(|| {
@ -271,6 +309,7 @@ impl Engine {
); );
} }
#[allow(clippy::cast_possible_truncation)]
let offset = abs_index as usize; let offset = abs_index as usize;
( (
// Count from end if negative // Count from end if negative
@ -317,7 +356,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
this_ptr: &mut Dynamic, this_ptr: &mut Dynamic,
expr: &Expr, expr: &Expr,
new_val: &mut Option<(Dynamic, &OpAssignment)>, new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResult { ) -> RhaiResult {
let chain_type = ChainType::from(expr); let chain_type = ChainType::from(expr);
@ -507,7 +546,7 @@ impl Engine {
target: &mut Target, target: &mut Target,
rhs: &Expr, rhs: &Expr,
idx_values: &mut FnArgsVec<Dynamic>, idx_values: &mut FnArgsVec<Dynamic>,
new_val: &mut Option<(Dynamic, &OpAssignment)>, new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResultOf<(Dynamic, bool)> { ) -> RhaiResultOf<(Dynamic, bool)> {
let is_ref_mut = target.is_ref(); let is_ref_mut = target.is_ref();
let op_pos = parent.position(); let op_pos = parent.position();
@ -576,7 +615,7 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(global, caches, scope, this_ptr, parent)?; self.run_debugger(global, caches, scope, this_ptr, parent)?;
let (new_val, op_info) = new_val.take().expect("`Some`"); let (new_val, op_info) = new_val.expect("`Some`");
let idx_val = &mut idx_values.pop().unwrap(); let idx_val = &mut idx_values.pop().unwrap();
let idx = &mut idx_val.clone(); let idx = &mut idx_val.clone();
@ -657,9 +696,10 @@ impl Engine {
let reset = let reset =
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?; self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let global = &mut *RestoreOnDrop::lock(global, move |g| { let global =
g.debugger.reset_status(reset) &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
}); g.debugger_mut().reset_status(reset)
});
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr {
name, hashes, args, .. name, hashes, args, ..
@ -686,12 +726,12 @@ impl Engine {
unreachable!("function call in dot chain should not be namespace-qualified") unreachable!("function call in dot chain should not be namespace-qualified")
} }
// {xxx:map}.id op= ??? // {xxx:map}.id op= ???
Expr::Property(x, pos) if target.is_map() && new_val.is_some() => { Expr::Property(x, pos) if new_val.is_some() && target.is_map() => {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(global, caches, scope, this_ptr, rhs)?; self.run_debugger(global, caches, scope, this_ptr, rhs)?;
let index = &mut x.2.clone().into(); let index = &mut x.2.clone().into();
let (new_val, op_info) = new_val.take().expect("`Some`"); let (new_val, op_info) = new_val.expect("`Some`");
{ {
let val_target = &mut self.get_indexed_mut( let val_target = &mut self.get_indexed_mut(
global, caches, target, index, *pos, op_pos, true, false, global, caches, target, index, *pos, op_pos, true, false,
@ -720,7 +760,7 @@ impl Engine {
self.run_debugger(global, caches, scope, this_ptr, rhs)?; self.run_debugger(global, caches, scope, this_ptr, rhs)?;
let ((getter, hash_get), (setter, hash_set), name) = &**x; let ((getter, hash_get), (setter, hash_set), name) = &**x;
let (mut new_val, op_info) = new_val.take().expect("`Some`"); let (mut new_val, op_info) = new_val.expect("`Some`");
if op_info.is_op_assignment() { if op_info.is_op_assignment() {
let args = &mut [target.as_mut()]; let args = &mut [target.as_mut()];
@ -826,9 +866,11 @@ impl Engine {
global, caches, scope, this_ptr, _node, global, caches, scope, this_ptr, _node,
)?; )?;
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let global = &mut *RestoreOnDrop::lock(global, move |g| { let global = &mut *RestoreOnDrop::lock_if(
g.debugger.reset_status(reset) reset.is_some(),
}); global,
move |g| g.debugger_mut().reset_status(reset),
);
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr {
name, hashes, args, .. name, hashes, args, ..
@ -950,9 +992,11 @@ impl Engine {
global, caches, scope, this_ptr, _node, global, caches, scope, this_ptr, _node,
)?; )?;
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let global = &mut *RestoreOnDrop::lock(global, move |g| { let global = &mut *RestoreOnDrop::lock_if(
g.debugger.reset_status(reset) reset.is_some(),
}); global,
move |g| g.debugger_mut().reset_status(reset),
);
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr {
name, hashes, args, .. name, hashes, args, ..

View File

@ -8,7 +8,7 @@ use std::borrow::Borrow;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
impl Engine { impl Dynamic {
/// Recursively calculate the sizes of a value. /// Recursively calculate the sizes of a value.
/// ///
/// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
@ -16,20 +16,20 @@ impl Engine {
/// # Panics /// # Panics
/// ///
/// Panics if any interior data is shared (should never happen). /// Panics if any interior data is shared (should never happen).
pub(crate) fn calc_data_sizes(value: &Dynamic, _top: bool) -> (usize, usize, usize) { pub(crate) fn calc_data_sizes(&self, _top: bool) -> (usize, usize, usize) {
match value.0 { match self.0 {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref arr, ..) => { Union::Array(ref arr, ..) => {
arr.iter() arr.iter()
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
Union::Array(..) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a + 1, mx + m, sx + s) (ax + a + 1, mx + m, sx + s)
} }
Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx), Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(..) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a + 1, mx + m, sx + s) (ax + a + 1, mx + m, sx + s)
} }
Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()), Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()),
@ -44,13 +44,13 @@ impl Engine {
.fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(..) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a, mx + m + 1, sx + s) (ax + a, mx + m + 1, sx + s)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref a, ..) => (ax + a.len(), mx, sx), Union::Blob(ref a, ..) => (ax + a.len(), mx, sx),
Union::Map(..) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = value.calc_data_sizes(false);
(ax + a, mx + m + 1, sx + s) (ax + a, mx + m + 1, sx + s)
} }
Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()), Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()),
@ -59,17 +59,17 @@ impl Engine {
} }
Union::Str(ref s, ..) => (0, 0, s.len()), Union::Str(ref s, ..) => (0, 0, s.len()),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(..) if _top => { Union::Shared(..) if _top => self.read_lock::<Self>().unwrap().calc_data_sizes(true),
Self::calc_data_sizes(&*value.read_lock::<Dynamic>().unwrap(), true)
}
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(..) => { Union::Shared(..) => {
unreachable!("shared values discovered within data: {}", value) unreachable!("shared values discovered within data: {}", self)
} }
_ => (0, 0, 0), _ => (0, 0, 0),
} }
} }
}
impl Engine {
/// Raise an error if any data size exceeds limit. /// Raise an error if any data size exceeds limit.
/// ///
/// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE] /// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE]
@ -125,7 +125,7 @@ impl Engine {
return Ok(value); return Ok(value);
} }
let sizes = Self::calc_data_sizes(value.borrow(), true); let sizes = value.borrow().calc_data_sizes(true);
self.raise_err_if_over_data_size_limit(sizes) self.raise_err_if_over_data_size_limit(sizes)
.map(|_| value) .map(|_| value)

View File

@ -193,7 +193,7 @@ impl BreakPoint {
/// Is this [`BreakPoint`] enabled? /// Is this [`BreakPoint`] enabled?
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn is_enabled(&self) -> bool { pub const fn is_enabled(&self) -> bool {
match self { match self {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
Self::AtPosition { enabled, .. } => *enabled, Self::AtPosition { enabled, .. } => *enabled,
@ -268,7 +268,7 @@ impl Debugger {
/// Create a new [`Debugger`]. /// Create a new [`Debugger`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn new(status: DebuggerStatus, state: Dynamic) -> Self { pub const fn new(status: DebuggerStatus, state: Dynamic) -> Self {
Self { Self {
status, status,
break_points: Vec::new(), break_points: Vec::new(),
@ -297,7 +297,7 @@ impl Debugger {
pos: Position, pos: Position,
) { ) {
self.call_stack.push(CallStackFrame { self.call_stack.push(CallStackFrame {
fn_name: fn_name.into(), fn_name,
args, args,
source, source,
pos, pos,
@ -419,7 +419,7 @@ impl Engine {
if let Some(cmd) = if let Some(cmd) =
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)? self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)?
{ {
global.debugger.status = cmd; global.debugger_mut().status = cmd;
} }
} }
@ -469,27 +469,28 @@ impl Engine {
_ => (), _ => (),
} }
let event = match global.debugger.status { if let Some(ref dbg) = global.debugger {
DebuggerStatus::Init => Some(DebuggerEvent::Start), let event = match dbg.status {
DebuggerStatus::CONTINUE => None, DebuggerStatus::Init => Some(DebuggerEvent::Start),
DebuggerStatus::NEXT if matches!(node, ASTNode::Stmt(..)) => Some(DebuggerEvent::Step), DebuggerStatus::NEXT if node.is_stmt() => Some(DebuggerEvent::Step),
DebuggerStatus::NEXT => None, DebuggerStatus::INTO if node.is_expr() => Some(DebuggerEvent::Step),
DebuggerStatus::INTO if matches!(node, ASTNode::Expr(..)) => Some(DebuggerEvent::Step), DebuggerStatus::STEP => Some(DebuggerEvent::Step),
DebuggerStatus::INTO => None, DebuggerStatus::Terminate => Some(DebuggerEvent::End),
DebuggerStatus::STEP => Some(DebuggerEvent::Step), _ => None,
DebuggerStatus::FunctionExit(..) => None, };
DebuggerStatus::Terminate => Some(DebuggerEvent::End),
};
let event = match event { let event = match event {
Some(e) => e, Some(e) => e,
None => match global.debugger.is_break_point(global.source(), node) { None => match dbg.is_break_point(global.source(), node) {
Some(bp) => DebuggerEvent::BreakPoint(bp), Some(bp) => DebuggerEvent::BreakPoint(bp),
None => return Ok(None), None => return Ok(None),
}, },
}; };
self.run_debugger_raw(global, caches, scope, this_ptr, node, event) self.run_debugger_raw(global, caches, scope, this_ptr, node, event)
} else {
Ok(None)
}
} }
/// Run the debugger callback unconditionally. /// Run the debugger callback unconditionally.
/// ///
@ -511,24 +512,26 @@ impl Engine {
let src = src.as_ref().map(|s| s.as_str()); let src = src.as_ref().map(|s| s.as_str());
let context = crate::EvalContext::new(self, global, caches, scope, this_ptr); let context = crate::EvalContext::new(self, global, caches, scope, this_ptr);
if let Some((.., ref on_debugger)) = self.debugger { if let Some(ref x) = self.debugger {
let (.., ref on_debugger) = **x;
let command = on_debugger(context, event, node, src, node.position())?; let command = on_debugger(context, event, node, src, node.position())?;
match command { match command {
DebuggerCommand::Continue => { DebuggerCommand::Continue => {
global.debugger.status = DebuggerStatus::CONTINUE; global.debugger_mut().status = DebuggerStatus::CONTINUE;
Ok(None) Ok(None)
} }
DebuggerCommand::Next => { DebuggerCommand::Next => {
global.debugger.status = DebuggerStatus::CONTINUE; global.debugger_mut().status = DebuggerStatus::CONTINUE;
Ok(Some(DebuggerStatus::NEXT)) Ok(Some(DebuggerStatus::NEXT))
} }
DebuggerCommand::StepOver => { DebuggerCommand::StepOver => {
global.debugger.status = DebuggerStatus::CONTINUE; global.debugger_mut().status = DebuggerStatus::CONTINUE;
Ok(Some(DebuggerStatus::STEP)) Ok(Some(DebuggerStatus::STEP))
} }
DebuggerCommand::StepInto => { DebuggerCommand::StepInto => {
global.debugger.status = DebuggerStatus::STEP; global.debugger_mut().status = DebuggerStatus::STEP;
Ok(None) Ok(None)
} }
DebuggerCommand::FunctionExit => { DebuggerCommand::FunctionExit => {
@ -542,7 +545,7 @@ impl Engine {
} }
_ => global.level, _ => global.level,
}; };
global.debugger.status = DebuggerStatus::FunctionExit(level); global.debugger_mut().status = DebuggerStatus::FunctionExit(level);
Ok(None) Ok(None)
} }
} }

View File

@ -61,8 +61,8 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
/// Get a mutable reference to the current [`Scope`]. /// Get a mutable reference to the current [`Scope`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn scope_mut(&mut self) -> &mut &'s mut Scope<'ps> { pub fn scope_mut(&mut self) -> &mut Scope<'ps> {
&mut self.scope self.scope
} }
/// Get an iterator over the current set of modules imported via `import` statements, /// Get an iterator over the current set of modules imported via `import` statements,
/// in reverse order (i.e. modules imported last come first). /// in reverse order (i.e. modules imported last come first).
@ -105,7 +105,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
self.global.lib.iter().map(|m| m.as_ref()) self.global.lib.iter().map(AsRef::as_ref)
} }
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions. /// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.

View File

@ -36,7 +36,12 @@ impl Engine {
// Do a text-match search if the index doesn't work // Do a text-match search if the index doesn't work
global.find_import(root).map_or_else( global.find_import(root).map_or_else(
|| self.global_sub_modules.get(root).cloned(), || {
self.global_sub_modules
.as_ref()
.and_then(|m| m.get(root))
.cloned()
},
|offset| global.get_shared_import(offset), |offset| global.get_shared_import(offset),
) )
} }
@ -156,7 +161,7 @@ impl Engine {
.any(|(_, _, f, ..)| f == v.3.as_str()) => .any(|(_, _, f, ..)| f == v.3.as_str()) =>
{ {
let val: Dynamic = let val: Dynamic =
crate::FnPtr::new_unchecked(v.3.as_str(), Default::default()).into(); crate::FnPtr::new_unchecked(v.3.as_str(), crate::StaticVec::default()).into();
return Ok(val.into()); return Ok(val.into());
} }
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get), Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
@ -186,14 +191,20 @@ impl Engine {
match scope.search(var_name) { match scope.search(var_name) {
Some(index) => index, Some(index) => index,
None => { None => {
return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) { return self
Some(val) => Ok(val.into()), .global_modules
None => Err(ERR::ErrorVariableNotFound( .iter()
var_name.to_string(), .find_map(|m| m.get_var(var_name))
expr.position(), .map_or_else(
|| {
Err(ERR::ErrorVariableNotFound(
var_name.to_string(),
expr.position(),
)
.into())
},
|val| Ok(val.into()),
) )
.into()),
}
} }
} }
}; };
@ -221,9 +232,10 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?; let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { let global =
g.debugger.reset_status(reset) &mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
}); g.debugger_mut().reset_status(reset)
});
self.track_operation(global, expr.position())?; self.track_operation(global, expr.position())?;
@ -254,9 +266,10 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?; let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| { let global =
g.debugger.reset_status(reset) &mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
}); g.debugger_mut().reset_status(reset)
});
self.track_operation(global, expr.position())?; self.track_operation(global, expr.position())?;
@ -307,7 +320,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() { if self.has_data_size_limit() {
let val_sizes = Self::calc_data_sizes(&value, true); let val_sizes = value.calc_data_sizes(true);
total_data_sizes = ( total_data_sizes = (
total_data_sizes.0 + val_sizes.0, total_data_sizes.0 + val_sizes.0,
@ -339,7 +352,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if self.has_data_size_limit() { if self.has_data_size_limit() {
let delta = Self::calc_data_sizes(&value, true); let delta = value.calc_data_sizes(true);
total_data_sizes = ( total_data_sizes = (
total_data_sizes.0 + delta.0, total_data_sizes.0 + delta.0,
total_data_sizes.1 + delta.1, total_data_sizes.1 + delta.1,
@ -393,13 +406,17 @@ impl Engine {
// The first token acts as the custom syntax's key // The first token acts as the custom syntax's key
let key_token = custom.tokens.first().unwrap(); let key_token = custom.tokens.first().unwrap();
// The key should exist, unless the AST is compiled in a different Engine // The key should exist, unless the AST is compiled in a different Engine
let custom_def = self.custom_syntax.get(key_token.as_str()).ok_or_else(|| { let custom_def = self
Box::new(ERR::ErrorCustomSyntax( .custom_syntax
format!("Invalid custom syntax prefix: {key_token}"), .as_ref()
custom.tokens.iter().map(<_>::to_string).collect(), .and_then(|m| m.get(key_token.as_str()))
*pos, .ok_or_else(|| {
)) Box::new(ERR::ErrorCustomSyntax(
})?; format!("Invalid custom syntax prefix: {key_token}"),
custom.tokens.iter().map(<_>::to_string).collect(),
*pos,
))
})?;
let mut context = EvalContext::new(self, global, caches, scope, this_ptr); let mut context = EvalContext::new(self, global, caches, scope, this_ptr);
(custom_def.func)(&mut context, &expressions, &custom.state) (custom_def.func)(&mut context, &expressions, &custom.state)
@ -411,13 +428,11 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(..) => { Expr::Index(..) => {
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None) self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None)
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(..) => { Expr::Dot(..) => self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None),
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None)
}
_ => unreachable!("expression cannot be evaluated: {:?}", expr), _ => unreachable!("expression cannot be evaluated: {:?}", expr),
} }

View File

@ -25,10 +25,11 @@ pub type GlobalConstants =
pub struct GlobalRuntimeState { pub struct GlobalRuntimeState {
/// Names of imported [modules][crate::Module]. /// Names of imported [modules][crate::Module].
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
imports: crate::StaticVec<ImmutableString>, imports: Option<Box<crate::StaticVec<ImmutableString>>>,
/// Stack of imported [modules][crate::Module]. /// Stack of imported [modules][crate::Module].
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: crate::StaticVec<crate::SharedModule>, modules: Option<Box<crate::StaticVec<crate::SharedModule>>>,
/// The current stack of loaded [modules][crate::Module] containing script-defined functions. /// The current stack of loaded [modules][crate::Module] containing script-defined functions.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub lib: crate::StaticVec<crate::SharedModule>, pub lib: crate::StaticVec<crate::SharedModule>,
@ -57,9 +58,6 @@ pub struct GlobalRuntimeState {
/// ///
/// When that happens, this flag is turned on. /// When that happens, this flag is turned on.
pub always_search_scope: bool, pub always_search_scope: bool,
/// Function call hashes to index getters and setters.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn_hash_indexing: (u64, u64),
/// Embedded [module][crate::Module] resolver. /// Embedded [module][crate::Module] resolver.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub embedded_module_resolver: pub embedded_module_resolver:
@ -74,7 +72,7 @@ pub struct GlobalRuntimeState {
pub tag: Dynamic, pub tag: Dynamic,
/// Debugging interface. /// Debugging interface.
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
pub debugger: super::Debugger, pub(crate) debugger: Option<super::Debugger>,
} }
impl GlobalRuntimeState { impl GlobalRuntimeState {
@ -84,9 +82,9 @@ impl GlobalRuntimeState {
pub fn new(engine: &Engine) -> Self { pub fn new(engine: &Engine) -> Self {
Self { Self {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
imports: crate::StaticVec::new_const(), imports: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
modules: crate::StaticVec::new_const(), modules: None,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
lib: crate::StaticVec::new_const(), lib: crate::StaticVec::new_const(),
source: None, source: None,
@ -98,11 +96,6 @@ impl GlobalRuntimeState {
always_search_scope: false, always_search_scope: false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
embedded_module_resolver: None, embedded_module_resolver: None,
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn_hash_indexing: (
crate::calc_fn_hash(None, crate::engine::FN_IDX_GET, 2),
crate::calc_fn_hash(None, crate::engine::FN_IDX_SET, 3),
),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
constants: None, constants: None,
@ -110,36 +103,28 @@ impl GlobalRuntimeState {
tag: engine.default_tag().clone(), tag: engine.default_tag().clone(),
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
debugger: crate::eval::Debugger::new( debugger: engine.debugger.as_ref().map(|x| {
if engine.debugger.is_some() { crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init, (x.0)(engine))
crate::eval::DebuggerStatus::Init }),
} else {
crate::eval::DebuggerStatus::CONTINUE
},
match engine.debugger {
Some((ref init, ..)) => init(engine),
None => Dynamic::UNIT,
},
),
} }
} }
/// Get the length of the stack of globally-imported [modules][crate::Module]. /// Get the length of the stack of globally-imported [modules][crate::Module].
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn num_imports(&self) -> usize { pub fn num_imports(&self) -> usize {
self.modules.len() self.modules.as_ref().map_or(0, |m| m.len())
} }
/// Get the globally-imported [module][crate::Module] at a particular index. /// Get the globally-imported [module][crate::Module] at a particular index.
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn get_shared_import(&self, index: usize) -> Option<crate::SharedModule> { pub fn get_shared_import(&self, index: usize) -> Option<crate::SharedModule> {
self.modules.get(index).cloned() self.modules.as_ref().and_then(|m| m.get(index).cloned())
} }
/// Get a mutable reference to the globally-imported [module][crate::Module] at a /// Get a mutable reference to the globally-imported [module][crate::Module] at a
/// particular index. /// particular index.
@ -147,13 +132,13 @@ impl GlobalRuntimeState {
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub(crate) fn get_shared_import_mut( pub(crate) fn get_shared_import_mut(
&mut self, &mut self,
index: usize, index: usize,
) -> Option<&mut crate::SharedModule> { ) -> Option<&mut crate::SharedModule> {
self.modules.get_mut(index) self.modules.as_mut().and_then(|m| m.get_mut(index))
} }
/// Get the index of a globally-imported [module][crate::Module] by name. /// Get the index of a globally-imported [module][crate::Module] by name.
/// ///
@ -162,33 +147,45 @@ impl GlobalRuntimeState {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn find_import(&self, name: &str) -> Option<usize> { pub fn find_import(&self, name: &str) -> Option<usize> {
self.imports self.imports.as_ref().and_then(|imports| {
.iter() imports
.rev() .iter()
.position(|key| key.as_str() == name) .rev()
.map(|i| self.imports.len() - 1 - i) .position(|key| key.as_str() == name)
.map(|i| imports.len() - 1 - i)
})
} }
/// Push an imported [module][crate::Module] onto the stack. /// Push an imported [module][crate::Module] onto the stack.
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline]
pub fn push_import( pub fn push_import(
&mut self, &mut self,
name: impl Into<ImmutableString>, name: impl Into<ImmutableString>,
module: impl Into<crate::SharedModule>, module: impl Into<crate::SharedModule>,
) { ) {
self.imports.push(name.into()); self.imports
self.modules.push(module.into()); .get_or_insert_with(|| crate::StaticVec::new_const().into())
.push(name.into());
self.modules
.get_or_insert_with(|| crate::StaticVec::new_const().into())
.push(module.into());
} }
/// Truncate the stack of globally-imported [modules][crate::Module] to a particular length. /// Truncate the stack of globally-imported [modules][crate::Module] to a particular length.
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline]
pub fn truncate_imports(&mut self, size: usize) { pub fn truncate_imports(&mut self, size: usize) {
self.imports.truncate(size); if size == 0 {
self.modules.truncate(size); self.imports = None;
self.modules = None;
} else if self.imports.is_some() {
self.imports.as_mut().unwrap().truncate(size);
self.modules.as_mut().unwrap().truncate(size);
}
} }
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
/// ///
@ -198,8 +195,9 @@ impl GlobalRuntimeState {
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> { pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
self.imports self.imports
.iter() .iter()
.zip(self.modules.iter()) .flat_map(|x| x.iter())
.rev() .rev()
.zip(self.modules.iter().flat_map(|x| x.iter()).rev())
.map(|(name, module)| (name.as_str(), &**module)) .map(|(name, module)| (name.as_str(), &**module))
} }
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
@ -210,7 +208,11 @@ impl GlobalRuntimeState {
pub(crate) fn iter_imports_raw( pub(crate) fn iter_imports_raw(
&self, &self,
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> { ) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
self.imports.iter().zip(self.modules.iter()).rev() self.imports
.iter()
.flat_map(|x| x.iter())
.rev()
.zip(self.modules.iter().flat_map(|x| x.iter()).rev())
} }
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order. /// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
/// ///
@ -220,18 +222,21 @@ impl GlobalRuntimeState {
pub fn scan_imports_raw( pub fn scan_imports_raw(
&self, &self,
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> { ) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
self.imports.iter().zip(self.modules.iter()) self.imports
.iter()
.flat_map(|x| x.iter())
.zip(self.modules.iter().flat_map(|x| x.iter()))
} }
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of /// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
/// globally-imported [modules][crate::Module]? /// globally-imported [modules][crate::Module]?
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline]
pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool { pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
self.modules self.modules.as_ref().map_or(false, |m| {
.iter() m.iter().any(|m| m.may_contain_dynamic_fn(hash_script))
.any(|m| m.may_contain_dynamic_fn(hash_script)) })
} }
/// Does the specified function hash key exist in the stack of globally-imported /// Does the specified function hash key exist in the stack of globally-imported
/// [modules][crate::Module]? /// [modules][crate::Module]?
@ -242,7 +247,9 @@ impl GlobalRuntimeState {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn contains_qualified_fn(&self, hash: u64) -> bool { pub fn contains_qualified_fn(&self, hash: u64) -> bool {
self.modules.iter().any(|m| m.contains_qualified_fn(hash)) self.modules
.as_ref()
.map_or(false, |m| m.iter().any(|m| m.contains_qualified_fn(hash)))
} }
/// Get the specified function via its hash key from the stack of globally-imported /// Get the specified function via its hash key from the stack of globally-imported
/// [modules][crate::Module]. /// [modules][crate::Module].
@ -255,10 +262,11 @@ impl GlobalRuntimeState {
&self, &self,
hash: u64, hash: u64,
) -> Option<(&crate::func::CallableFunction, Option<&ImmutableString>)> { ) -> Option<(&crate::func::CallableFunction, Option<&ImmutableString>)> {
self.modules self.modules.as_ref().and_then(|m| {
.iter() m.iter()
.rev() .rev()
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) .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 /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
/// globally-imported [modules][crate::Module]? /// globally-imported [modules][crate::Module]?
@ -269,7 +277,9 @@ impl GlobalRuntimeState {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn contains_iter(&self, id: std::any::TypeId) -> bool { pub fn contains_iter(&self, id: std::any::TypeId) -> bool {
self.modules.iter().any(|m| m.contains_qualified_iter(id)) self.modules
.as_ref()
.map_or(false, |m| m.iter().any(|m| m.contains_qualified_iter(id)))
} }
/// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported
/// [modules][crate::Module]. /// [modules][crate::Module].
@ -280,9 +290,8 @@ impl GlobalRuntimeState {
#[must_use] #[must_use]
pub fn get_iter(&self, id: std::any::TypeId) -> Option<&crate::func::IteratorFn> { pub fn get_iter(&self, id: std::any::TypeId) -> Option<&crate::func::IteratorFn> {
self.modules self.modules
.iter() .as_ref()
.rev() .and_then(|m| m.iter().rev().find_map(|m| m.get_qualified_iter(id)))
.find_map(|m| m.get_qualified_iter(id))
} }
/// Get the current source. /// Get the current source.
#[inline(always)] #[inline(always)]
@ -297,19 +306,24 @@ impl GlobalRuntimeState {
pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> { pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
self.source.as_ref() self.source.as_ref()
} }
/// Get the pre-calculated index getter hash.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] /// Return a reference to the debugging interface.
#[inline(always)] ///
#[must_use] /// # Panics
pub(crate) fn hash_idx_get(&mut self) -> u64 { ///
self.fn_hash_indexing.0 /// Panics if the debugging interface is not set.
#[cfg(feature = "debugging")]
pub fn debugger(&self) -> &super::Debugger {
self.debugger.as_ref().unwrap()
} }
/// Get the pre-calculated index setter hash. /// Return a mutable reference to the debugging interface.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] ///
#[inline(always)] /// # Panics
#[must_use] ///
pub(crate) fn hash_idx_set(&mut self) -> u64 { /// Panics if the debugging interface is not set.
self.fn_hash_indexing.1 #[cfg(feature = "debugging")]
pub fn debugger_mut(&mut self) -> &mut super::Debugger {
self.debugger.as_mut().unwrap()
} }
} }
@ -317,9 +331,16 @@ impl GlobalRuntimeState {
impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState { impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
#[inline] #[inline]
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
let imports = self
.imports
.get_or_insert_with(|| crate::StaticVec::new_const().into());
let modules = self
.modules
.get_or_insert_with(|| crate::StaticVec::new_const().into());
for (k, m) in iter { for (k, m) in iter {
self.imports.push(k.into()); imports.push(k.into());
self.modules.push(m.into()); modules.push(m.into());
} }
} }
} }
@ -344,9 +365,6 @@ impl fmt::Debug for GlobalRuntimeState {
.field("scope_level", &self.scope_level) .field("scope_level", &self.scope_level)
.field("always_search_scope", &self.always_search_scope); .field("always_search_scope", &self.always_search_scope);
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
f.field("fn_hash_indexing", &self.fn_hash_indexing);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
f.field("constants", &self.constants); f.field("constants", &self.constants);

View File

@ -21,7 +21,9 @@ pub use eval_context::EvalContext;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub use global_state::GlobalConstants; pub use global_state::GlobalConstants;
pub use global_state::GlobalRuntimeState; pub use global_state::GlobalRuntimeState;
pub use target::{calc_index, calc_offset_len, Target}; #[cfg(not(feature = "no_index"))]
pub use target::calc_offset_len;
pub use target::{calc_index, Target};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
mod unchecked { mod unchecked {

View File

@ -194,7 +194,9 @@ impl Engine {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?; let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, stmt)?;
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); let global = &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
g.debugger_mut().reset_status(reset)
});
// Coded this way for better branch prediction. // Coded this way for better branch prediction.
// Popular branches are lifted out of the `match` statement into their own branches. // Popular branches are lifted out of the `match` statement into their own branches.
@ -258,7 +260,7 @@ impl Engine {
rhs_val = self.get_interned_string(value).into(); rhs_val = self.get_interned_string(value).into();
} }
let _new_val = &mut Some((rhs_val, op_info)); let _new_val = Some((rhs_val, op_info));
// Must be either `var[index] op= val` or `var.prop op= val` // Must be either `var[index] op= val` or `var.prop op= val`
match lhs { match lhs {
@ -500,7 +502,8 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| { let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| {
self.global_sub_modules self.global_sub_modules
.values() .iter()
.flat_map(|m| m.values())
.find_map(|m| m.get_qualified_iter(iter_type)) .find_map(|m| m.get_qualified_iter(iter_type))
}); });
@ -533,6 +536,7 @@ impl Engine {
let index_value = x as INT; let index_value = x as INT;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[allow(clippy::absurd_extreme_comparisons)]
if index_value > crate::MAX_USIZE_INT { if index_value > crate::MAX_USIZE_INT {
return Err(ERR::ErrorArithmetic( return Err(ERR::ErrorArithmetic(
format!("for-loop counter overflow: {x}"), format!("for-loop counter overflow: {x}"),
@ -764,6 +768,8 @@ impl Engine {
// Import statement // Import statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(x, _pos) => { Stmt::Import(x, _pos) => {
use crate::ModuleResolver;
let (expr, export) = &**x; let (expr, export) = &**x;
// Guard against too many modules // Guard against too many modules
@ -777,8 +783,6 @@ impl Engine {
self.make_type_mismatch_err::<crate::ImmutableString>(typ, expr.position()) self.make_type_mismatch_err::<crate::ImmutableString>(typ, expr.position())
})?; })?;
use crate::ModuleResolver;
let path_pos = expr.start_position(); let path_pos = expr.start_position();
let resolver = global.embedded_module_resolver.clone(); let resolver = global.embedded_module_resolver.clone();
@ -799,10 +803,10 @@ impl Engine {
Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into()) Err(ERR::ErrorModuleNotFound(path.to_string(), path_pos).into())
})?; })?;
let (export, must_be_indexed) = if !export.is_empty() { let (export, must_be_indexed) = if export.is_empty() {
(export.name.clone(), true)
} else {
(self.const_empty_string(), false) (self.const_empty_string(), false)
} else {
(export.name.clone(), true)
}; };
if !must_be_indexed || module.is_indexed() { if !must_be_indexed || module.is_indexed() {
@ -824,13 +828,14 @@ impl Engine {
Stmt::Export(x, ..) => { Stmt::Export(x, ..) => {
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x; let (Ident { name, pos, .. }, Ident { name: alias, .. }) = &**x;
// Mark scope variables as public // Mark scope variables as public
if let Some(index) = scope.search(name) { scope.search(name).map_or_else(
let alias = if alias.is_empty() { name } else { alias }.clone(); || Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()),
scope.add_alias_by_index(index, alias.into()); |index| {
Ok(Dynamic::UNIT) let alias = if alias.is_empty() { name } else { alias }.clone();
} else { scope.add_alias_by_index(index, alias);
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()) Ok(Dynamic::UNIT)
} },
)
} }
// Share statement // Share statement
@ -838,20 +843,21 @@ impl Engine {
Stmt::Share(x) => { Stmt::Share(x) => {
x.iter() x.iter()
.try_for_each(|(name, index, pos)| { .try_for_each(|(name, index, pos)| {
if let Some(index) = index index
.map(|n| scope.len() - n.get()) .map(|n| scope.len() - n.get())
.or_else(|| scope.search(name)) .or_else(|| scope.search(name))
{ .map_or_else(
let val = scope.get_mut_by_index(index); || Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()),
|index| {
let val = scope.get_mut_by_index(index);
if !val.is_shared() { if !val.is_shared() {
// Replace the variable with a shared value. // Replace the variable with a shared value.
*val = std::mem::take(val).into_shared(); *val = std::mem::take(val).into_shared();
} }
Ok(()) Ok(())
} else { },
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()) )
}
}) })
.map(|_| Dynamic::UNIT) .map(|_| Dynamic::UNIT)
} }

View File

@ -13,8 +13,13 @@ use std::{
/// Negative starting positions count from the end. /// Negative starting positions count from the end.
/// ///
/// Values going over bounds are limited to the actual length. /// Values going over bounds are limited to the actual length.
#[cfg(not(feature = "no_index"))]
#[inline] #[inline]
#[allow(dead_code)] #[allow(
clippy::cast_sign_loss,
clippy::absurd_extreme_comparisons,
clippy::cast_possible_truncation
)]
pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) { pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (usize, usize) {
let start = if start < 0 { let start = if start < 0 {
let abs_start = start.unsigned_abs(); let abs_start = start.unsigned_abs();
@ -47,22 +52,26 @@ pub fn calc_offset_len(length: usize, start: crate::INT, len: crate::INT) -> (us
/// Values going over bounds call the provided closure to return a default value or an error. /// Values going over bounds call the provided closure to return a default value or an error.
#[inline] #[inline]
#[allow(dead_code)] #[allow(dead_code)]
#[allow(
clippy::cast_sign_loss,
clippy::cast_possible_truncation,
clippy::absurd_extreme_comparisons
)]
pub fn calc_index<E>( pub fn calc_index<E>(
length: usize, length: usize,
start: crate::INT, start: crate::INT,
negative_count_from_end: bool, negative_count_from_end: bool,
err_func: impl FnOnce() -> Result<usize, E>, err_func: impl FnOnce() -> Result<usize, E>,
) -> Result<usize, E> { ) -> Result<usize, E> {
if start < 0 { if start < 0 && negative_count_from_end {
if negative_count_from_end { let abs_start = start.unsigned_abs();
let abs_start = start.unsigned_abs() as usize; return Ok(if abs_start as u64 > crate::MAX_USIZE_INT as u64 {
0
// Count from end if negative } else {
if abs_start <= length { length - usize::min(abs_start as usize, length)
return Ok(length - abs_start); });
}
}
} }
if start <= crate::MAX_USIZE_INT && (start as usize) < length { if start <= crate::MAX_USIZE_INT && (start as usize) < length {
return Ok(start as usize); return Ok(start as usize);
} }
@ -315,7 +324,10 @@ impl<'a> Target<'a> {
let value = &mut *source.write_lock::<crate::Blob>().expect("`Blob`"); let value = &mut *source.write_lock::<crate::Blob>().expect("`Blob`");
value[*index] = (new_byte & 0x00ff) as u8; #[allow(clippy::cast_sign_loss)]
{
value[*index] = (new_byte & 0x00ff) as u8;
}
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::StringChar { Self::StringChar {

View File

@ -1,14 +1,18 @@
//! Built-in implementations for common operators. //! Built-in implementations for common operators.
#![allow(clippy::float_cmp)]
use super::call::FnCallArgs; use super::call::FnCallArgs;
use super::native::FnBuiltin; use super::native::FnBuiltin;
#[allow(clippy::enum_glob_use)]
use crate::tokenizer::{Token, Token::*}; use crate::tokenizer::{Token, Token::*};
use crate::{ use crate::{
Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, INT, Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult,
SmartString, INT,
}; };
use std::any::TypeId;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::TypeId, fmt::Write};
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::FLOAT; use crate::FLOAT;
@ -67,13 +71,11 @@ fn is_numeric(type_id: TypeId) -> bool {
/// A function that returns `true`. /// A function that returns `true`.
#[inline(always)] #[inline(always)]
#[must_use]
fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
Ok(Dynamic::TRUE) Ok(Dynamic::TRUE)
} }
/// A function that returns `false`. /// A function that returns `false`.
#[inline(always)] #[inline(always)]
#[must_use]
fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult { fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
Ok(Dynamic::FALSE) Ok(Dynamic::FALSE)
} }
@ -142,6 +144,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
if type1 == type2 { if type1 == type2 {
if type1 == TypeId::of::<INT>() { if type1 == TypeId::of::<INT>() {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::arith_basic::INT::functions::*; use crate::packages::arithmetic::arith_basic::INT::functions::*;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -241,7 +244,9 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
let result = format!("{x}{y}"); let mut result = SmartString::new_const();
result.push(x);
result.push(y);
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
_ctx.engine() _ctx.engine()
@ -337,6 +342,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
($x:ty, $xx:ident, $y:ty, $yy:ident) => { ($x:ty, $xx:ident, $y:ty, $yy:ident) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::decimal_functions::builtin::*; use crate::packages::arithmetic::decimal_functions::builtin::*;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -399,7 +405,10 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
Plus => Some(|_ctx, args| { Plus => Some(|_ctx, args| {
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN); let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
let result = format!("{x}{y}");
let mut result = SmartString::new_const();
result.push(x);
result.push_str(y);
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
_ctx.engine() _ctx.engine()
@ -523,24 +532,20 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
} }
// Handle ranges here because ranges are implemented as custom type // Handle ranges here because ranges are implemented as custom type
if type1 == TypeId::of::<ExclusiveRange>() { if type1 == TypeId::of::<ExclusiveRange>() && type1 == type2 {
if type1 == type2 { return match op {
return match op { EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), _ => None,
_ => None, };
};
}
} }
if type1 == TypeId::of::<InclusiveRange>() { if type1 == TypeId::of::<InclusiveRange>() && type1 == type2 {
if type1 == type2 { return match op {
return match op { EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), _ => None,
_ => None, };
};
}
} }
// One of the operands is a custom type, so it is never built-in // One of the operands is a custom type, so it is never built-in
@ -630,6 +635,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
if type1 == type2 { if type1 == type2 {
if type1 == TypeId::of::<INT>() { if type1 == TypeId::of::<INT>() {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::arith_basic::INT::functions::*; use crate::packages::arithmetic::arith_basic::INT::functions::*;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -679,7 +685,12 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
PlusAssign => Some(|_, args| { PlusAssign => Some(|_, args| {
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN); let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = format!("{x}{y}").into()).into())
let mut buf = SmartString::new_const();
write!(&mut buf, "{y}").unwrap();
buf.push(y);
Ok((*x = buf.into()).into())
}), }),
_ => None, _ => None,
}; };
@ -713,6 +724,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Array>() { if type1 == TypeId::of::<crate::Array>() {
#[allow(clippy::wildcard_imports)]
use crate::packages::array_basic::array_functions::*; use crate::packages::array_basic::array_functions::*;
use crate::Array; use crate::Array;
@ -744,6 +756,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Blob>() { if type1 == TypeId::of::<crate::Blob>() {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*; use crate::packages::blob_basic::blob_functions::*;
use crate::Blob; use crate::Blob;
@ -794,6 +807,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
($x:ident, $xx:ident, $y:ty, $yy:ident) => { ($x:ident, $xx:ident, $y:ty, $yy:ident) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::decimal_functions::builtin::*; use crate::packages::arithmetic::decimal_functions::builtin::*;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -880,6 +894,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
// array op= any // array op= any
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Array>() { if type1 == TypeId::of::<crate::Array>() {
#[allow(clippy::wildcard_imports)]
use crate::packages::array_basic::array_functions::*; use crate::packages::array_basic::array_functions::*;
use crate::Array; use crate::Array;
@ -909,6 +924,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
// blob op= int // blob op= int
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) { if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*; use crate::packages::blob_basic::blob_functions::*;
return match op { return match op {
@ -928,6 +944,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
// blob op= char // blob op= char
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) { if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*; use crate::packages::blob_basic::blob_functions::*;
return match op { return match op {
@ -947,6 +964,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
// blob op= string // blob op= string
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) { if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*; use crate::packages::blob_basic::blob_functions::*;
return match op { return match op {

View File

@ -90,11 +90,7 @@ impl<'a> ArgBackup<'a> {
/// exiting the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak. /// exiting the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
#[inline(always)] #[inline(always)]
pub fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) { pub fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) {
if let Some(p) = self.orig_mut.take() { args[0] = self.orig_mut.take().expect("`Some`");
args[0] = p;
} else {
unreachable!("`Some`");
}
} }
} }
@ -116,7 +112,7 @@ pub fn ensure_no_data_race(fn_name: &str, args: &FnCallArgs, is_ref_mut: bool) -
if let Some((n, ..)) = args if let Some((n, ..)) = args
.iter() .iter()
.enumerate() .enumerate()
.skip(if is_ref_mut { 1 } else { 0 }) .skip(usize::from(is_ref_mut))
.find(|(.., a)| a.is_locked()) .find(|(.., a)| a.is_locked())
{ {
return Err(ERR::ErrorDataRace( return Err(ERR::ErrorDataRace(
@ -216,7 +212,8 @@ impl Engine {
} else { } else {
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| { func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
self.global_sub_modules self.global_sub_modules
.values() .iter()
.flat_map(|m| m.values())
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw()))) .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
}) })
}; };
@ -256,7 +253,8 @@ impl Engine {
|| _global.may_contain_dynamic_fn(hash_base) || _global.may_contain_dynamic_fn(hash_base)
|| self || self
.global_sub_modules .global_sub_modules
.values() .iter()
.flat_map(|m| m.values())
.any(|m| m.may_contain_dynamic_fn(hash_base)); .any(|m| m.may_contain_dynamic_fn(hash_base));
// Set maximum bitmask when there are dynamic versions of the function // Set maximum bitmask when there are dynamic versions of the function
@ -277,7 +275,7 @@ impl Engine {
Some(token) if token.is_op_assignment() => { Some(token) if token.is_op_assignment() => {
let (first_arg, rest_args) = args.split_first().unwrap(); let (first_arg, rest_args) = args.split_first().unwrap();
get_builtin_op_assignment_fn(token, *first_arg, rest_args[0]) get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
.map(|f| FnResolutionCacheEntry { .map(|f| FnResolutionCacheEntry {
func: CallableFunction::Method(Shared::new(f)), func: CallableFunction::Method(Shared::new(f)),
source: None, source: None,
@ -344,7 +342,7 @@ impl Engine {
name: &str, name: &str,
op_token: Option<&Token>, op_token: Option<&Token>,
hash: u64, hash: u64,
mut args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref_mut: bool, is_ref_mut: bool,
pos: Position, pos: Position,
) -> RhaiResultOf<(Dynamic, bool)> { ) -> RhaiResultOf<(Dynamic, bool)> {
@ -368,7 +366,10 @@ impl Engine {
// Push a new call stack frame // Push a new call stack frame
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let orig_call_stack_len = global.debugger.call_stack().len(); let orig_call_stack_len = global
.debugger
.as_ref()
.map_or(0, |dbg| dbg.call_stack().len());
let backup = &mut ArgBackup::new(); let backup = &mut ArgBackup::new();
@ -381,14 +382,16 @@ impl Engine {
} }
let args = let args =
&mut *RestoreOnDrop::lock_if(swap, &mut args, move |a| backup.restore_first_arg(a)); &mut *RestoreOnDrop::lock_if(swap, args, move |a| backup.restore_first_arg(a));
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
if self.debugger.is_some() { if self.debugger.is_some() {
global.debugger.push_call_stack_frame( let source = source.clone().or_else(|| global.source.clone());
global.debugger_mut().push_call_stack_frame(
self.get_interned_string(name), self.get_interned_string(name),
args.iter().map(|v| (*v).clone()).collect(), args.iter().map(|v| (*v).clone()).collect(),
source.clone().or_else(|| global.source.clone()), source,
pos, pos,
); );
} }
@ -414,16 +417,16 @@ impl Engine {
}; };
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
{ if self.debugger.is_some() {
use crate::eval::{DebuggerEvent, DebuggerStatus}; use crate::eval::{DebuggerEvent, DebuggerStatus};
let trigger = match global.debugger.status { let trigger = match global.debugger().status {
DebuggerStatus::FunctionExit(n) => n >= global.level, DebuggerStatus::FunctionExit(n) => n >= global.level,
DebuggerStatus::Next(.., true) => true, DebuggerStatus::Next(.., true) => true,
_ => false, _ => false,
}; };
if trigger { if trigger {
let scope = &mut &mut Scope::new(); let scope = &mut Scope::new();
let mut this = Dynamic::NULL; let mut this = Dynamic::NULL;
let node = crate::ast::Stmt::Noop(pos); let node = crate::ast::Stmt::Noop(pos);
let node = (&node).into(); let node = (&node).into();
@ -440,7 +443,7 @@ impl Engine {
} }
// Pop the call stack // Pop the call stack
global.debugger.rewind_call_stack(orig_call_stack_len); global.debugger_mut().rewind_call_stack(orig_call_stack_len);
} }
let result = _result?; let result = _result?;
@ -597,12 +600,13 @@ impl Engine {
let num_params = _args[1].as_int().expect("`INT`"); let num_params = _args[1].as_int().expect("`INT`");
return Ok(( return Ok((
if num_params < 0 || num_params > crate::MAX_USIZE_INT { if (0..=crate::MAX_USIZE_INT).contains(&num_params) {
false #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
} else {
let hash_script = let hash_script =
calc_fn_hash(None, fn_name.as_str(), num_params as usize); calc_fn_hash(None, fn_name.as_str(), num_params as usize);
self.has_script_fn(global, caches, hash_script) self.has_script_fn(global, caches, hash_script)
} else {
false
} }
.into(), .into(),
false, false,
@ -647,12 +651,11 @@ impl Engine {
} }
let mut empty_scope; let mut empty_scope;
let scope = match _scope { let scope = if let Some(scope) = _scope {
Some(scope) => scope, scope
None => { } else {
empty_scope = Scope::new(); empty_scope = Scope::new();
&mut empty_scope &mut empty_scope
}
}; };
let orig_source = mem::replace(&mut global.source, source.clone()); let orig_source = mem::replace(&mut global.source, source.clone());
@ -676,7 +679,7 @@ impl Engine {
backup.change_first_arg_to_copy(_args); backup.change_first_arg_to_copy(_args);
} }
let args = &mut *RestoreOnDrop::lock_if(swap, &mut _args, move |a| { let args = &mut *RestoreOnDrop::lock_if(swap, _args, move |a| {
backup.restore_first_arg(a) backup.restore_first_arg(a)
}); });
@ -718,11 +721,15 @@ impl Engine {
// Do not match function exit for arguments // Do not match function exit for arguments
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let reset = global.debugger.clear_status_if(|status| { let reset = global.debugger.as_mut().and_then(|dbg| {
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) dbg.clear_status_if(|status| {
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
})
}); });
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let global = &mut *RestoreOnDrop::lock(global, move |g| g.debugger.reset_status(reset)); let global = &mut *RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
g.debugger_mut().reset_status(reset)
});
self.eval_expr(global, caches, scope, this_ptr, arg_expr) self.eval_expr(global, caches, scope, this_ptr, arg_expr)
.map(|r| (r, arg_expr.start_position())) .map(|r| (r, arg_expr.start_position()))
@ -901,7 +908,7 @@ impl Engine {
call_args = &mut _arg_values; call_args = &mut _arg_values;
} }
// Recalculate the hash based on the new function name and new arguments // Recalculate the hash based on the new function name and new arguments
hash = if !is_anon && !is_valid_function_name(&fn_name) { hash = if !is_anon && !is_valid_function_name(fn_name) {
FnCallHashes::from_native(calc_fn_hash( FnCallHashes::from_native(calc_fn_hash(
None, None,
fn_name, fn_name,
@ -963,7 +970,7 @@ impl Engine {
) -> RhaiResult { ) -> RhaiResult {
let mut first_arg = first_arg; let mut first_arg = first_arg;
let mut a_expr = args_expr; let mut a_expr = args_expr;
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len(); let mut total_args = usize::from(first_arg.is_some()) + a_expr.len();
let mut curry = FnArgsVec::new_const(); let mut curry = FnArgsVec::new_const();
let mut name = fn_name; let mut name = fn_name;
let mut hashes = hashes; let mut hashes = hashes;
@ -1077,9 +1084,10 @@ impl Engine {
.as_int() .as_int()
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?; .map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
return Ok(if num_params < 0 || num_params > crate::MAX_USIZE_INT { return Ok(if !(0..=crate::MAX_USIZE_INT).contains(&num_params) {
false false
} else { } else {
#[allow(clippy::cast_sign_loss)]
let hash_script = calc_fn_hash(None, &fn_name, num_params as usize); let hash_script = calc_fn_hash(None, &fn_name, num_params as usize);
self.has_script_fn(global, caches, hash_script) self.has_script_fn(global, caches, hash_script)
} }
@ -1437,7 +1445,7 @@ impl Engine {
// No optimizations because we only run it once // No optimizations because we only run it once
let ast = self.compile_with_scope_and_optimization_level( let ast = self.compile_with_scope_and_optimization_level(
&Scope::new(), &Scope::new(),
&[script], [script],
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
OptimizationLevel::None, OptimizationLevel::None,
#[cfg(feature = "no_optimize")] #[cfg(feature = "no_optimize")]

View File

@ -132,9 +132,7 @@ impl CallableFunction {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
match self { match self {
Self::Pure(..) | Self::Method(..) => true, Self::Pure(..) | Self::Method(..) | Self::Plugin(..) | Self::Iterator(..) => true,
Self::Plugin(..) => true,
Self::Iterator(..) => true,
Self::Script(..) => false, Self::Script(..) => false,
} }
} }
@ -147,8 +145,9 @@ impl CallableFunction {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
match self { match self {
Self::Plugin(..) => FnAccess::Public, Self::Plugin(..) | Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => {
Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => FnAccess::Public, FnAccess::Public
}
Self::Script(f) => f.access, Self::Script(f) => f.access,
} }
} }

View File

@ -80,7 +80,6 @@ pub trait Func<ARGS, RET> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[must_use]
fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output>; fn create_from_script(self, script: &str, entry_point: &str) -> ParseResult<Self::Output>;
} }

View File

@ -151,7 +151,7 @@ impl<'a> NativeCallContext<'a> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn new_with_all_fields( pub const fn new_with_all_fields(
engine: &'a Engine, engine: &'a Engine,
fn_name: &'a str, fn_name: &'a str,
source: Option<&'a str>, source: Option<&'a str>,
@ -182,7 +182,7 @@ impl<'a> NativeCallContext<'a> {
Self { Self {
engine, engine,
fn_name: &context.fn_name, fn_name: &context.fn_name,
source: context.source.as_ref().map(String::as_str), source: context.source.as_deref(),
global: &context.global, global: &context.global,
pos: context.pos, pos: context.pos,
} }
@ -240,7 +240,7 @@ impl<'a> NativeCallContext<'a> {
/// Custom state kept in a [`Dynamic`]. /// Custom state kept in a [`Dynamic`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn tag(&self) -> Option<&Dynamic> { pub const fn tag(&self) -> Option<&Dynamic> {
Some(&self.global.tag) Some(&self.global.tag)
} }
/// Get an iterator over the current set of modules imported via `import` statements /// Get an iterator over the current set of modules imported via `import` statements
@ -278,7 +278,7 @@ impl<'a> NativeCallContext<'a> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline] #[inline]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
self.global.lib.iter().map(|m| m.as_ref()) self.global.lib.iter().map(AsRef::as_ref)
} }
/// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions. /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.

View File

@ -83,7 +83,10 @@ impl Engine {
let orig_imports_len = global.num_imports(); let orig_imports_len = global.num_imports();
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let orig_call_stack_len = global.debugger.call_stack().len(); let orig_call_stack_len = global
.debugger
.as_ref()
.map_or(0, |dbg| dbg.call_stack().len());
// Put arguments into scope as variables // Put arguments into scope as variables
scope.extend(fn_def.params.iter().cloned().zip(args.iter_mut().map(|v| { scope.extend(fn_def.params.iter().cloned().zip(args.iter_mut().map(|v| {
@ -94,10 +97,12 @@ impl Engine {
// Push a new call stack frame // Push a new call stack frame
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
if self.debugger.is_some() { if self.debugger.is_some() {
global.debugger.push_call_stack_frame( let source = global.source.clone();
global.debugger_mut().push_call_stack_frame(
fn_def.name.clone(), fn_def.name.clone(),
scope.iter().skip(orig_scope_len).map(|(.., v)| v).collect(), scope.iter().skip(orig_scope_len).map(|(.., v)| v).collect(),
global.source.clone(), source,
pos, pos,
); );
} }
@ -126,7 +131,7 @@ impl Engine {
}; };
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
{ if self.debugger.is_some() {
let node = crate::ast::Stmt::Noop(fn_def.body.position()); let node = crate::ast::Stmt::Noop(fn_def.body.position());
self.run_debugger(global, caches, scope, this_ptr, &node)?; self.run_debugger(global, caches, scope, this_ptr, &node)?;
} }
@ -156,12 +161,13 @@ impl Engine {
}); });
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
{ if self.debugger.is_some() {
let trigger = match global.debugger.status { let trigger = match global.debugger_mut().status {
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level, crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
crate::eval::DebuggerStatus::Next(.., true) => true, crate::eval::DebuggerStatus::Next(.., true) => true,
_ => false, _ => false,
}; };
if trigger { if trigger {
let node = crate::ast::Stmt::Noop(fn_def.body.end_position().or_else(pos)); let node = crate::ast::Stmt::Noop(fn_def.body.end_position().or_else(pos));
let node = (&node).into(); let node = (&node).into();
@ -176,7 +182,11 @@ impl Engine {
} }
// Pop the call stack // Pop the call stack
global.debugger.rewind_call_stack(orig_call_stack_len); global
.debugger
.as_mut()
.unwrap()
.rewind_call_stack(orig_call_stack_len);
} }
// Remove all local variables and imported modules // Remove all local variables and imported modules
@ -226,7 +236,7 @@ impl Engine {
// Then check imported modules // Then check imported modules
global.contains_qualified_fn(hash_script) global.contains_qualified_fn(hash_script)
// Then check sub-modules // Then check sub-modules
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); || self.global_sub_modules.iter().flat_map(|m| m.values()).any(|m| m.contains_qualified_fn(hash_script));
if !result && !cache.filter.is_absent_and_set(hash_script) { if !result && !cache.filter.is_absent_and_set(hash_script) {
// Do not cache "one-hit wonders" // Do not cache "one-hit wonders"

View File

@ -65,11 +65,15 @@
// #![warn(clippy::undocumented_unsafe_blocks)] // #![warn(clippy::undocumented_unsafe_blocks)]
#![allow(clippy::unit_arg)] #![allow(clippy::unit_arg)]
#![allow(clippy::missing_errors_doc)] #![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::used_underscore_binding)] #![allow(clippy::used_underscore_binding)]
#![allow(clippy::inline_always)] #![allow(clippy::inline_always)]
#![allow(clippy::module_name_repetitions)] #![allow(clippy::module_name_repetitions)]
#![allow(clippy::negative_feature_names)] #![allow(clippy::negative_feature_names)]
#![allow(clippy::module_inception)] #![allow(clippy::module_inception)]
#![allow(clippy::box_collection)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::upper_case_acronyms)]
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
extern crate alloc; extern crate alloc;

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT,
use crate::eval::{Caches, GlobalRuntimeState}; use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::builtin::get_builtin_binary_op_fn; use crate::func::builtin::get_builtin_binary_op_fn;
use crate::func::hashing::get_hasher; use crate::func::hashing::get_hasher;
use crate::module::ModuleFlags;
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode; use crate::types::dynamic::AccessMode;
use crate::{ use crate::{
@ -168,7 +169,7 @@ fn has_native_fn_override(
if engine if engine
.global_modules .global_modules
.iter() .iter()
.filter(|m| !m.standard) .filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
.any(|m| m.contains_fn(hash)) .any(|m| m.contains_fn(hash))
{ {
return true; return true;
@ -178,7 +179,8 @@ fn has_native_fn_override(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if engine if engine
.global_sub_modules .global_sub_modules
.values() .iter()
.flat_map(|m| m.values())
.any(|m| m.contains_qualified_fn(hash)) .any(|m| m.contains_qualified_fn(hash))
{ {
return true; return true;

View File

@ -1,5 +1,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Position, RhaiError, RhaiResultOf, ERR, INT}; use crate::{def_package, Position, RhaiError, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -192,7 +193,7 @@ macro_rules! reg_functions {
def_package! { def_package! {
/// Basic arithmetic package. /// Basic arithmetic package.
pub ArithmeticPackage(lib) { pub ArithmeticPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "int", int_functions); combine_with_exported_module!(lib, "int", int_functions);
reg_functions!(lib += signed_basic; INT); reg_functions!(lib += signed_basic; INT);

View File

@ -2,6 +2,7 @@
use crate::engine::OP_EQUALS; use crate::engine::OP_EQUALS;
use crate::eval::{calc_index, calc_offset_len}; use crate::eval::{calc_index, calc_offset_len};
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext, def_package, Array, Dynamic, ExclusiveRange, FnPtr, InclusiveRange, NativeCallContext,
@ -14,7 +15,7 @@ use std::{any::TypeId, cmp::Ordering, mem};
def_package! { def_package! {
/// Package of basic array utilities. /// Package of basic array utilities.
pub BasicArrayPackage(lib) { pub BasicArrayPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "array", array_functions); combine_with_exported_module!(lib, "array", array_functions);
@ -229,6 +230,8 @@ pub mod array_functions {
// Check if array will be over max size limit // Check if array will be over max size limit
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
{ {
use crate::types::dynamic::Union;
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
return Err( return Err(
ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into(), ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into(),
@ -236,10 +239,10 @@ pub mod array_functions {
} }
let check_sizes = match item.0 { let check_sizes = match item.0 {
crate::types::dynamic::Union::Str(..) => true, Union::Str(..) => true,
crate::types::dynamic::Union::Array(..) => true, Union::Array(..) => true,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
crate::types::dynamic::Union::Map(..) => true, Union::Map(..) => true,
_ => false, _ => false,
}; };
@ -247,8 +250,8 @@ pub mod array_functions {
let mut arr_len = array.len(); let mut arr_len = array.len();
let mut arr = Dynamic::from_array(mem::take(array)); let mut arr = Dynamic::from_array(mem::take(array));
let (mut a1, mut m1, mut s1) = crate::Engine::calc_data_sizes(&arr, true); let (mut a1, mut m1, mut s1) = arr.calc_data_sizes(true);
let (a2, m2, s2) = crate::Engine::calc_data_sizes(&item, true); let (a2, m2, s2) = item.calc_data_sizes(true);
{ {
let mut guard = arr.write_lock::<Array>().unwrap(); let mut guard = arr.write_lock::<Array>().unwrap();

View File

@ -1,4 +1,5 @@
use crate::eval::calc_index; use crate::eval::calc_index;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, INT_BITS, def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, INT_BITS,
@ -10,7 +11,7 @@ use std::prelude::v1::*;
def_package! { def_package! {
/// Package of basic bit-field utilities. /// Package of basic bit-field utilities.
pub BitFieldPackage(lib) { pub BitFieldPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "bit_field", bit_field_functions); combine_with_exported_module!(lib, "bit_field", bit_field_functions);
} }
@ -142,7 +143,7 @@ mod bit_field_functions {
} }
// 2^bits - 1 // 2^bits - 1
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as crate::INT; let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as INT;
Ok(((value & (mask << bit)) >> bit) & mask) Ok(((value & (mask << bit)) >> bit) & mask)
} }
@ -229,7 +230,7 @@ mod bit_field_functions {
} }
// 2^bits - 1 // 2^bits - 1
let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as crate::INT; let mask = ((2 as UNSIGNED_INT).pow(bits as u32) - 1) as INT;
*value &= !(mask << bit); *value &= !(mask << bit);
*value |= (new_value & mask) << bit; *value |= (new_value & mask) << bit;

View File

@ -1,10 +1,11 @@
#![cfg(not(feature = "no_index"))] #![cfg(not(feature = "no_index"))]
use crate::eval::{calc_index, calc_offset_len}; use crate::eval::{calc_index, calc_offset_len};
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position, def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
RhaiResultOf, INT, INT_BYTES, MAX_USIZE_INT, RhaiResultOf, ERR, INT, INT_BYTES, MAX_USIZE_INT,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -16,7 +17,7 @@ use crate::{FLOAT, FLOAT_BYTES};
def_package! { def_package! {
/// Package of basic BLOB utilities. /// Package of basic BLOB utilities.
pub BasicBlobPackage(lib) { pub BasicBlobPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "blob", blob_functions); combine_with_exported_module!(lib, "blob", blob_functions);
combine_with_exported_module!(lib, "parse_int", parse_int_functions); combine_with_exported_module!(lib, "parse_int", parse_int_functions);
@ -362,9 +363,7 @@ pub mod blob_functions {
// Check if blob will be over max size limit // Check if blob will be over max size limit
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
return Err( return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into());
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
);
} }
if len > blob.len() { if len > blob.len() {

View File

@ -1,6 +1,7 @@
#![cfg(feature = "debugging")] #![cfg(feature = "debugging")]
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -17,7 +18,7 @@ use crate::Map;
def_package! { def_package! {
/// Package of basic debugging utilities. /// Package of basic debugging utilities.
pub DebuggingPackage(lib) { pub DebuggingPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "debugging", debugging_functions); combine_with_exported_module!(lib, "debugging", debugging_functions);
} }
@ -33,47 +34,57 @@ mod debugging_functions {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub fn back_trace(ctx: NativeCallContext) -> Array { pub fn back_trace(ctx: NativeCallContext) -> Array {
ctx.global_runtime_state() use crate::debugger::CallStackFrame;
.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"))] if let Some(ref debugger) = ctx.global_runtime_state().debugger {
{ debugger
let mut map = Map::new(); .call_stack()
map.insert("display".into(), display.into()); .iter()
map.insert("fn_name".into(), _fn_name.into()); .rev()
if !_args.is_empty() { .filter(|CallStackFrame { fn_name, args, .. }| {
map.insert("args".into(), Dynamic::from_array(_args.clone().to_vec())); fn_name.as_str() != "back_trace" || !args.is_empty()
})
.map(
|frame @ CallStackFrame {
fn_name: _fn_name,
args: _args,
source: _source,
pos: _pos,
}| {
let display = frame.to_string();
#[cfg(not(feature = "no_object"))]
{
use crate::INT;
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 let Some(source) = _source {
map.insert("source".into(), source.into());
}
if !_pos.is_none() {
map.insert("line".into(), (_pos.line().unwrap() as INT).into());
map.insert(
"position".into(),
(_pos.position().unwrap_or(0) as INT).into(),
);
}
Dynamic::from_map(map)
} }
if let Some(source) = _source { #[cfg(feature = "no_object")]
map.insert("source".into(), source.into()); display.into()
} },
if !_pos.is_none() { )
map.insert("line".into(), (_pos.line().unwrap() as crate::INT).into()); .collect()
map.insert( } else {
"position".into(), Array::new()
(_pos.position().unwrap_or(0) as crate::INT).into(), }
);
}
Dynamic::from_map(map)
}
#[cfg(feature = "no_object")]
display.into()
},
)
.collect()
} }
} }

View File

@ -1,3 +1,4 @@
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, FnPtr, ImmutableString, NativeCallContext}; use crate::{def_package, FnPtr, ImmutableString, NativeCallContext};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -6,7 +7,7 @@ use std::prelude::v1::*;
def_package! { def_package! {
/// Package of basic function pointer utilities. /// Package of basic function pointer utilities.
pub BasicFnPackage(lib) { pub BasicFnPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions);
} }

View File

@ -1,7 +1,8 @@
use crate::eval::calc_index; use crate::eval::calc_index;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT, INT_BITS, MAX_USIZE_INT, def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, ERR, INT, INT_BITS, MAX_USIZE_INT,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -65,14 +66,11 @@ impl<T: Copy + PartialOrd> StepRange<T> {
if let Some(n) = add(from, step) { if let Some(n) = add(from, step) {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if n == from { if n == from {
return Err(crate::ERR::ErrorInFunctionCall( return Err(ERR::ErrorInFunctionCall(
"range".to_string(), "range".to_string(),
String::new(), String::new(),
crate::ERR::ErrorArithmetic( ERR::ErrorArithmetic("step value cannot be zero".to_string(), Position::NONE)
"step value cannot be zero".to_string(), .into(),
Position::NONE,
)
.into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -127,7 +125,7 @@ pub struct BitRange(INT, usize);
impl BitRange { impl BitRange {
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> { pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
let from = calc_index(INT_BITS, from, true, || { let from = calc_index(INT_BITS, from, true, || {
crate::ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into() ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into()
})?; })?;
let len = if len < 0 { let len = if len < 0 {
@ -329,7 +327,7 @@ macro_rules! reg_range {
def_package! { def_package! {
/// Package of basic range iterators /// Package of basic range iterators
pub BasicIteratorPackage(lib) { pub BasicIteratorPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
reg_range!(lib | "range" => INT); reg_range!(lib | "range" => INT);

View File

@ -1,14 +1,18 @@
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::types::dynamic::Tag; use crate::types::dynamic::Tag;
use crate::{Dynamic, RhaiResultOf, ERR, INT}; use crate::{Dynamic, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
#[cfg(not(feature = "no_float"))]
use crate::FLOAT;
def_package! { def_package! {
/// Package of core language features. /// Package of core language features.
pub LanguageCorePackage(lib) { pub LanguageCorePackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "core", core_functions); combine_with_exported_module!(lib, "core", core_functions);
@ -75,10 +79,17 @@ mod core_functions {
} }
/// Block the current thread for a particular number of `seconds`. /// Block the current thread for a particular number of `seconds`.
///
/// # Example
///
/// ```rhai
/// // Do nothing for 10 seconds!
/// sleep(10.0);
/// ```
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[rhai_fn(name = "sleep")] #[rhai_fn(name = "sleep")]
pub fn sleep_float(seconds: crate::FLOAT) { pub fn sleep_float(seconds: FLOAT) {
if seconds <= 0.0 { if seconds <= 0.0 {
return; return;
} }
@ -89,6 +100,13 @@ mod core_functions {
std::thread::sleep(std::time::Duration::from_secs_f32(seconds)); std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
} }
/// Block the current thread for a particular number of `seconds`. /// Block the current thread for a particular number of `seconds`.
///
/// # Example
///
/// ```rhai
/// // Do nothing for 10 seconds!
/// sleep(10);
/// ```
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
pub fn sleep(seconds: INT) { pub fn sleep(seconds: INT) {
if seconds <= 0 { if seconds <= 0 {
@ -120,17 +138,24 @@ mod core_functions {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[export_module] #[export_module]
mod reflection_functions { mod reflection_functions {
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> crate::Array { use crate::Array;
/// Return an array of object maps containing metadata of all script-defined functions.
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array {
collect_fn_metadata(ctx, |_, _, _, _, _| true) collect_fn_metadata(ctx, |_, _, _, _, _| true)
} }
/// Return an array of object maps containing metadata of all script-defined functions
/// matching the specified name.
#[rhai_fn(name = "get_fn_metadata_list")] #[rhai_fn(name = "get_fn_metadata_list")]
pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> crate::Array { pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> Array {
collect_fn_metadata(ctx, |_, _, n, _, _| n == name) collect_fn_metadata(ctx, |_, _, n, _, _| n == name)
} }
/// Return an array of object maps containing metadata of all script-defined functions
/// matching the specified name and arity (number of parameters).
#[rhai_fn(name = "get_fn_metadata_list")] #[rhai_fn(name = "get_fn_metadata_list")]
pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> crate::Array { pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> Array {
if params < 0 || params > crate::MAX_USIZE_INT { if params < 0 || params > crate::MAX_USIZE_INT {
crate::Array::new() Array::new()
} else { } else {
collect_fn_metadata(ctx, |_, _, n, p, _| p == (params as usize) && n == name) collect_fn_metadata(ctx, |_, _, n, p, _| p == (params as usize) && n == name)
} }
@ -145,38 +170,47 @@ fn collect_fn_metadata(
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool
+ Copy, + Copy,
) -> crate::Array { ) -> crate::Array {
use crate::{ast::ScriptFnDef, Array, Map}; #[cfg(not(feature = "no_module"))]
use crate::Identifier;
use crate::{ast::ScriptFnDef, engine::FN_ANONYMOUS, Array, Map};
// Create a metadata record for a function. // Create a metadata record for a function.
fn make_metadata( fn make_metadata(
dict: &mut crate::types::StringsInterner, engine: &Engine,
#[cfg(not(feature = "no_module"))] namespace: crate::Identifier, #[cfg(not(feature = "no_module"))] namespace: Identifier,
func: &ScriptFnDef, func: &ScriptFnDef,
) -> Map { ) -> Map {
let mut map = Map::new(); let mut map = Map::new();
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if !namespace.is_empty() { if !namespace.is_empty() {
map.insert("namespace".into(), dict.get(namespace).into()); map.insert(
"namespace".into(),
engine.get_interned_string(namespace).into(),
);
} }
map.insert("name".into(), dict.get(func.name.as_str()).into()); map.insert(
"name".into(),
engine.get_interned_string(func.name.clone()).into(),
);
map.insert( map.insert(
"access".into(), "access".into(),
dict.get(match func.access { engine
FnAccess::Public => "public", .get_interned_string(match func.access {
FnAccess::Private => "private", FnAccess::Public => "public",
}) FnAccess::Private => "private",
.into(), })
.into(),
); );
map.insert( map.insert(
"is_anonymous".into(), "is_anonymous".into(),
func.name.starts_with(crate::engine::FN_ANONYMOUS).into(), func.name.starts_with(FN_ANONYMOUS).into(),
); );
map.insert( map.insert(
"params".into(), "params".into(),
func.params func.params
.iter() .iter()
.map(|p| dict.get(p.as_str()).into()) .map(|p| engine.get_interned_string(p.clone()).into())
.collect::<Array>() .collect::<Array>()
.into(), .into(),
); );
@ -186,7 +220,7 @@ fn collect_fn_metadata(
"comments".into(), "comments".into(),
func.comments func.comments
.iter() .iter()
.map(|s| dict.get(s.as_ref()).into()) .map(|s| engine.get_interned_string(s.as_ref()).into())
.collect::<Array>() .collect::<Array>()
.into(), .into(),
); );
@ -195,7 +229,7 @@ fn collect_fn_metadata(
map map
} }
let dict = &mut crate::types::StringsInterner::new(); let engine = ctx.engine();
let mut list = Array::new(); let mut list = Array::new();
ctx.iter_namespaces() ctx.iter_namespaces()
@ -204,9 +238,9 @@ fn collect_fn_metadata(
.for_each(|(.., f)| { .for_each(|(.., f)| {
list.push( list.push(
make_metadata( make_metadata(
dict, engine,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
crate::Identifier::new_const(), Identifier::new_const(),
f, f,
) )
.into(), .into(),
@ -221,9 +255,9 @@ fn collect_fn_metadata(
.for_each(|(.., f)| { .for_each(|(.., f)| {
list.push( list.push(
make_metadata( make_metadata(
dict, engine,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
crate::Identifier::new_const(), Identifier::new_const(),
f, f,
) )
.into(), .into(),
@ -233,15 +267,16 @@ fn collect_fn_metadata(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
ctx.engine() ctx.engine()
.global_sub_modules .global_sub_modules
.values() .iter()
.flat_map(|m| m.values())
.flat_map(|m| m.iter_script_fn()) .flat_map(|m| m.iter_script_fn())
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f)) .filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
.for_each(|(.., f)| { .for_each(|(.., f)| {
list.push( list.push(
make_metadata( make_metadata(
dict, engine,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
crate::Identifier::new_const(), Identifier::new_const(),
f, f,
) )
.into(), .into(),
@ -250,36 +285,31 @@ fn collect_fn_metadata(
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
{ {
use crate::{tokenizer::Token::DoubleColon, Shared, SmartString};
// Recursively scan modules for script-defined functions. // Recursively scan modules for script-defined functions.
fn scan_module( fn scan_module(
dict: &mut crate::types::StringsInterner, engine: &Engine,
list: &mut Array, list: &mut Array,
namespace: &str, namespace: &str,
module: &Module, module: &Module,
filter: impl Fn( filter: impl Fn(FnNamespace, FnAccess, &str, usize, &Shared<ScriptFnDef>) -> bool + Copy,
FnNamespace,
FnAccess,
&str,
usize,
&crate::Shared<crate::ast::ScriptFnDef>,
) -> bool
+ Copy,
) { ) {
module module
.iter_script_fn() .iter_script_fn()
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f)) .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
.for_each(|(.., f)| list.push(make_metadata(dict, namespace.into(), f).into())); .for_each(|(.., f)| list.push(make_metadata(engine, namespace.into(), f).into()));
for (ns, m) in module.iter_sub_modules() { for (name, m) in module.iter_sub_modules() {
let ns = format!( use std::fmt::Write;
"{namespace}{}{ns}",
crate::tokenizer::Token::DoubleColon.literal_syntax() let mut ns = SmartString::new_const();
); write!(&mut ns, "{namespace}{}{name}", DoubleColon.literal_syntax()).unwrap();
scan_module(dict, list, &ns, &**m, filter); scan_module(engine, list, &ns, m, filter);
} }
} }
for (ns, m) in ctx.iter_imports_raw() { for (ns, m) in ctx.iter_imports_raw() {
scan_module(dict, &mut list, ns, &**m, filter); scan_module(engine, &mut list, ns, m, filter);
} }
} }

View File

@ -1,4 +1,5 @@
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -38,7 +39,7 @@ macro_rules! reg_functions {
def_package! { def_package! {
/// Package of basic logic operators. /// Package of basic logic operators.
pub LogicPackage(lib) { pub LogicPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))] #[cfg(not(feature = "only_i64"))]
@ -92,6 +93,7 @@ mod logic_functions {
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[allow(clippy::cast_precision_loss)]
#[export_module] #[export_module]
mod f32_functions { mod f32_functions {
use crate::INT; use crate::INT;
@ -147,6 +149,7 @@ mod f32_functions {
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[allow(clippy::cast_precision_loss)]
#[export_module] #[export_module]
mod f64_functions { mod f64_functions {
use crate::INT; use crate::INT;

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_object"))] #![cfg(not(feature = "no_object"))]
use crate::engine::OP_EQUALS; use crate::engine::OP_EQUALS;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT}; use crate::{def_package, Dynamic, ImmutableString, Map, NativeCallContext, RhaiResultOf, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -12,7 +13,7 @@ use crate::Array;
def_package! { def_package! {
/// Package of basic object map utilities. /// Package of basic object map utilities.
pub BasicMapPackage(lib) { pub BasicMapPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "map", map_functions); combine_with_exported_module!(lib, "map", map_functions);
} }

View File

@ -1,5 +1,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Position, RhaiResultOf, ERR, INT}; use crate::{def_package, Position, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -54,7 +55,7 @@ macro_rules! reg_functions {
def_package! { def_package! {
/// Basic mathematical package. /// Basic mathematical package.
pub BasicMathPackage(lib) { pub BasicMathPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
// Integer functions // Integer functions
combine_with_exported_module!(lib, "int", int_functions); combine_with_exported_module!(lib, "int", int_functions);
@ -144,6 +145,7 @@ mod int_functions {
); );
} }
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
INT::from_str_radix(string.trim(), radix as u32).map_err(|err| { INT::from_str_radix(string.trim(), radix as u32).map_err(|err| {
ERR::ErrorArithmetic( ERR::ErrorArithmetic(
format!("Error parsing integer number '{string}': {err}"), format!("Error parsing integer number '{string}': {err}"),
@ -157,8 +159,6 @@ mod int_functions {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[export_module] #[export_module]
mod trig_functions { mod trig_functions {
use crate::FLOAT;
/// Return the sine of the floating-point number in radians. /// Return the sine of the floating-point number in radians.
pub fn sin(x: FLOAT) -> FLOAT { pub fn sin(x: FLOAT) -> FLOAT {
x.sin() x.sin()
@ -221,8 +221,6 @@ mod trig_functions {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[export_module] #[export_module]
mod float_functions { mod float_functions {
use crate::FLOAT;
/// Return the natural number _e_. /// Return the natural number _e_.
#[rhai_fn(name = "E")] #[rhai_fn(name = "E")]
pub fn e() -> FLOAT { pub fn e() -> FLOAT {
@ -312,6 +310,7 @@ mod float_functions {
/// Convert the floating-point number into an integer. /// Convert the floating-point number into an integer.
#[rhai_fn(name = "to_int", return_raw)] #[rhai_fn(name = "to_int", return_raw)]
pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> { pub fn f32_to_int(x: f32) -> RhaiResultOf<INT> {
#[allow(clippy::cast_precision_loss)]
if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f32) || x < (INT::MIN as f32)) { if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f32) || x < (INT::MIN as f32)) {
Err( Err(
ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE) ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
@ -324,6 +323,7 @@ mod float_functions {
/// Convert the floating-point number into an integer. /// Convert the floating-point number into an integer.
#[rhai_fn(name = "to_int", return_raw)] #[rhai_fn(name = "to_int", return_raw)]
pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> { pub fn f64_to_int(x: f64) -> RhaiResultOf<INT> {
#[allow(clippy::cast_precision_loss)]
if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f64) || x < (INT::MIN as f64)) { if cfg!(not(feature = "unchecked")) && (x > (INT::MAX as f64) || x < (INT::MIN as f64)) {
Err( Err(
ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE) ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
@ -356,7 +356,7 @@ mod float_functions {
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
#[rhai_fn(name = "to_float")] #[rhai_fn(name = "to_float")]
pub fn f32_to_f64(x: f32) -> f64 { pub fn f32_to_f64(x: f32) -> f64 {
x as f64 x.into()
} }
} }
@ -477,6 +477,7 @@ mod decimal_functions {
} }
} }
#[allow(clippy::cast_sign_loss)]
Ok(x.round_dp(digits as u32)) Ok(x.round_dp(digits as u32))
} }
/// Round the decimal number to the specified number of `digits` after the decimal point and return it. /// Round the decimal number to the specified number of `digits` after the decimal point and return it.
@ -494,6 +495,7 @@ mod decimal_functions {
} }
} }
#[allow(clippy::cast_sign_loss)]
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::AwayFromZero)) Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::AwayFromZero))
} }
/// Round the decimal number to the specified number of `digits` after the decimal point and return it. /// Round the decimal number to the specified number of `digits` after the decimal point and return it.
@ -511,6 +513,7 @@ mod decimal_functions {
} }
} }
#[allow(clippy::cast_sign_loss)]
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::ToZero)) Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::ToZero))
} }
/// Round the decimal number to the specified number of `digits` after the decimal point and return it. /// Round the decimal number to the specified number of `digits` after the decimal point and return it.
@ -528,6 +531,7 @@ mod decimal_functions {
} }
} }
#[allow(clippy::cast_sign_loss)]
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointAwayFromZero)) Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointAwayFromZero))
} }
/// Round the decimal number to the specified number of `digits` after the decimal point and return it. /// Round the decimal number to the specified number of `digits` after the decimal point and return it.
@ -545,6 +549,7 @@ mod decimal_functions {
} }
} }
#[allow(clippy::cast_sign_loss)]
Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointTowardZero)) Ok(x.round_dp_with_strategy(digits as u32, RoundingStrategy::MidpointTowardZero))
} }
/// Convert the decimal number into an integer. /// Convert the decimal number into an integer.
@ -562,14 +567,15 @@ mod decimal_functions {
return Some(n); return Some(n);
}); });
match n { n.map_or_else(
Some(n) => Ok(n), || {
_ => Err(ERR::ErrorArithmetic( Err(
format!("Integer overflow: to_int({x})"), ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
Position::NONE, .into(),
) )
.into()), },
} Ok,
)
} }
/// Return the integral part of the decimal number. /// Return the integral part of the decimal number.
#[rhai_fn(name = "int", get = "int")] #[rhai_fn(name = "int", get = "int")]

View File

@ -3,6 +3,7 @@ use std::prelude::v1::*;
use super::*; use super::*;
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
def_package! { def_package! {
/// Core package containing basic facilities. /// Core package containing basic facilities.
@ -23,6 +24,6 @@ def_package! {
BasicFnPackage, BasicFnPackage,
#[cfg(feature = "debugging")] DebuggingPackage #[cfg(feature = "debugging")] DebuggingPackage
{ {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
} }
} }

View File

@ -3,6 +3,7 @@ use std::prelude::v1::*;
use super::*; use super::*;
use crate::def_package; use crate::def_package;
use crate::module::ModuleFlags;
def_package! { def_package! {
/// Standard package containing all built-in features. /// Standard package containing all built-in features.
@ -29,6 +30,6 @@ def_package! {
#[cfg(not(feature = "no_time"))] BasicTimePackage, #[cfg(not(feature = "no_time"))] BasicTimePackage,
MoreStringPackage MoreStringPackage
{ {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
} }
} }

View File

@ -1,7 +1,8 @@
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, FnPtr, INT}; use crate::{def_package, FnPtr, ImmutableString, SmartString, INT};
use std::any::TypeId; use std::any::TypeId;
use std::fmt::{Binary, LowerHex, Octal}; use std::fmt::{Binary, LowerHex, Octal, Write};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -17,7 +18,7 @@ pub const FUNC_TO_DEBUG: &str = "to_debug";
def_package! { def_package! {
/// Package of basic string utilities (e.g. printing) /// Package of basic string utilities (e.g. printing)
pub BasicStringPackage(lib) { pub BasicStringPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "print_debug", print_debug_functions); combine_with_exported_module!(lib, "print_debug", print_debug_functions);
combine_with_exported_module!(lib, "number_formatting", number_formatting); combine_with_exported_module!(lib, "number_formatting", number_formatting);
@ -25,7 +26,7 @@ def_package! {
// Register characters iterator // Register characters iterator
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
lib.set_iter(TypeId::of::<ImmutableString>(), |value| Box::new( lib.set_iter(TypeId::of::<ImmutableString>(), |value| Box::new(
value.cast::<ImmutableString>().chars().map(Into::into).collect::<crate::Array>().into_iter() value.cast::<ImmutableString>().chars().map(Into::into).collect::<Array>().into_iter()
)); ));
} }
} }
@ -37,7 +38,7 @@ pub fn print_with_func(
fn_name: &str, fn_name: &str,
ctx: &NativeCallContext, ctx: &NativeCallContext,
value: &mut Dynamic, value: &mut Dynamic,
) -> crate::ImmutableString { ) -> ImmutableString {
match ctx.call_native_fn_raw(fn_name, true, &mut [value]) { match ctx.call_native_fn_raw(fn_name, true, &mut [value]) {
Ok(result) if result.is_string() => { Ok(result) if result.is_string() => {
result.into_immutable_string().expect("`ImmutableString`") result.into_immutable_string().expect("`ImmutableString`")
@ -49,8 +50,6 @@ pub fn print_with_func(
#[export_module] #[export_module]
mod print_debug_functions { mod print_debug_functions {
use crate::ImmutableString;
/// Convert the value of the `item` into a string. /// Convert the value of the `item` into a string.
#[rhai_fn(name = "print", pure)] #[rhai_fn(name = "print", pure)]
pub fn print_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString { pub fn print_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
@ -69,7 +68,9 @@ mod print_debug_functions {
/// Convert the value of the `item` into a string in debug format. /// Convert the value of the `item` into a string in debug format.
#[rhai_fn(name = "to_debug", pure)] #[rhai_fn(name = "to_debug", pure)]
pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString { pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
ctx.engine().map_type_name(&format!("{item:?}")).into() let mut buf = SmartString::new_const();
write!(&mut buf, "{item:?}").unwrap();
ctx.engine().map_type_name(&buf).into()
} }
/// Return the empty string. /// Return the empty string.
@ -86,7 +87,9 @@ mod print_debug_functions {
/// Convert the string into debug format. /// Convert the string into debug format.
#[rhai_fn(name = "debug", name = "to_debug", pure)] #[rhai_fn(name = "debug", name = "to_debug", pure)]
pub fn debug_string(string: &mut ImmutableString) -> ImmutableString { pub fn debug_string(string: &mut ImmutableString) -> ImmutableString {
format!("{string:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{string:?}").unwrap();
buf.into()
} }
/// Return the character into a string. /// Return the character into a string.
@ -97,7 +100,9 @@ mod print_debug_functions {
/// Convert the string into debug format. /// Convert the string into debug format.
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_char(character: char) -> ImmutableString { pub fn debug_char(character: char) -> ImmutableString {
format!("{character:?}").into() let mut buf = SmartString::new_const();
buf.push(character);
buf.into()
} }
/// Convert the function pointer into a string in debug format. /// Convert the function pointer into a string in debug format.
@ -114,7 +119,9 @@ mod print_debug_functions {
/// Convert the boolean value into a string in debug format. /// Convert the boolean value into a string in debug format.
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_bool(value: bool) -> ImmutableString { pub fn debug_bool(value: bool) -> ImmutableString {
format!("{value:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:?}").unwrap();
buf.into()
} }
/// Return the empty string. /// Return the empty string.
@ -147,14 +154,18 @@ mod print_debug_functions {
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f64(number: f64) -> ImmutableString { pub fn debug_f64(number: f64) -> ImmutableString {
let number = crate::types::FloatWrapper::new(number); let number = crate::types::FloatWrapper::new(number);
format!("{number:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{number:?}").unwrap();
buf.into()
} }
/// Convert the value of `number` into a string. /// Convert the value of `number` into a string.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "debug", name = "to_debug")] #[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f32(number: f32) -> ImmutableString { pub fn debug_f32(number: f32) -> ImmutableString {
let number = crate::types::FloatWrapper::new(number); let number = crate::types::FloatWrapper::new(number);
format!("{number:?}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{number:?}").unwrap();
buf.into()
} }
/// Convert the array into a string. /// Convert the array into a string.
@ -217,13 +228,19 @@ mod print_debug_functions {
#[export_module] #[export_module]
mod number_formatting { mod number_formatting {
fn to_hex<T: LowerHex>(value: T) -> ImmutableString { fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
format!("{value:x}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:x}").unwrap();
buf.into()
} }
fn to_octal<T: Octal>(value: T) -> ImmutableString { fn to_octal<T: Octal>(value: T) -> ImmutableString {
format!("{value:o}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:o}").unwrap();
buf.into()
} }
fn to_binary<T: Binary>(value: T) -> ImmutableString { fn to_binary<T: Binary>(value: T) -> ImmutableString {
format!("{value:b}").into() let mut buf = SmartString::new_const();
write!(&mut buf, "{value:b}").unwrap();
buf.into()
} }
/// Convert the `value` into a string in hex format. /// Convert the `value` into a string in hex format.

View File

@ -1,7 +1,8 @@
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{ use crate::{
def_package, Dynamic, ExclusiveRange, InclusiveRange, RhaiResultOf, StaticVec, INT, def_package, Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, Position, RhaiResultOf,
MAX_USIZE_INT, SmartString, StaticVec, ERR, INT, MAX_USIZE_INT,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -12,7 +13,7 @@ use super::string_basic::{print_with_func, FUNC_TO_STRING};
def_package! { def_package! {
/// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage] /// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage]
pub MoreStringPackage(lib) { pub MoreStringPackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "string", string_functions); combine_with_exported_module!(lib, "string", string_functions);
} }
@ -20,8 +21,6 @@ def_package! {
#[export_module] #[export_module]
mod string_functions { mod string_functions {
use crate::{ImmutableString, SmartString};
#[rhai_fn(name = "+", pure)] #[rhai_fn(name = "+", pure)]
pub fn add_append( pub fn add_append(
ctx: NativeCallContext, ctx: NativeCallContext,
@ -33,7 +32,9 @@ mod string_functions {
if s.is_empty() { if s.is_empty() {
string.clone() string.clone()
} else { } else {
format!("{string}{s}").into() let mut buf = SmartString::from(string.as_str());
buf.push_str(&s);
buf.into()
} }
} }
#[rhai_fn(name = "+=", name = "append")] #[rhai_fn(name = "+=", name = "append")]
@ -41,7 +42,9 @@ mod string_functions {
let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item); let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item);
if !s.is_empty() { if !s.is_empty() {
*string = format!("{string}{s}").into(); let mut buf = SmartString::from(string.as_str());
buf.push_str(&s);
*string = buf.into();
} }
} }
#[rhai_fn(name = "+", pure)] #[rhai_fn(name = "+", pure)]
@ -74,7 +77,10 @@ mod string_functions {
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_prepend_char(character: char, string: &str) -> ImmutableString { pub fn add_prepend_char(character: char, string: &str) -> ImmutableString {
format!("{character}{string}").into() let mut buf = SmartString::new_const();
buf.push(character);
buf.push_str(string);
buf.into()
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
@ -127,7 +133,7 @@ mod string_functions {
} }
#[rhai_fn(name = "+=", name = "append")] #[rhai_fn(name = "+=", name = "append")]
pub fn add(string: &mut ImmutableString, utf8: Blob) { pub fn add(string: &mut ImmutableString, utf8: Blob) {
let mut s = crate::SmartString::from(string.as_str()); let mut s = SmartString::from(string.as_str());
if !utf8.is_empty() { if !utf8.is_empty() {
s.push_str(&String::from_utf8_lossy(&utf8)); s.push_str(&String::from_utf8_lossy(&utf8));
*string = s.into(); *string = s.into();
@ -147,6 +153,25 @@ mod string_functions {
s.into() s.into()
} }
/// Convert the string into an UTF-8 encoded byte-stream as a BLOB.
///
/// # Example
///
/// ```rhai
/// let text = "朝には紅顔ありて夕べには白骨となる";
///
/// let bytes = text.to_blob();
///
/// print(bytes.len()); // prints 51
/// ```
pub fn to_blob(string: &str) -> Blob {
if string.is_empty() {
Blob::new()
} else {
string.as_bytes().into()
}
}
} }
/// Return the length of the string, in number of characters. /// Return the length of the string, in number of characters.
@ -188,25 +213,6 @@ mod string_functions {
string.len() as INT string.len() as INT
} }
} }
/// Convert the string into an UTF-8 encoded byte-stream as a BLOB.
///
/// # Example
///
/// ```rhai
/// let text = "朝には紅顔ありて夕べには白骨となる";
///
/// let bytes = text.to_blob();
///
/// print(bytes.len()); // prints 51
/// ```
#[cfg(not(feature = "no_index"))]
pub fn to_blob(string: &str) -> crate::Blob {
if string.is_empty() {
crate::Blob::new()
} else {
string.as_bytes().into()
}
}
/// Remove all occurrences of a sub-string from the string. /// Remove all occurrences of a sub-string from the string.
/// ///
/// # Example /// # Example
@ -239,9 +245,10 @@ mod string_functions {
/// Clear the string, making it empty. /// Clear the string, making it empty.
pub fn clear(string: &mut ImmutableString) { pub fn clear(string: &mut ImmutableString) {
if !string.is_empty() { if !string.is_empty() {
match string.get_mut() { if let Some(s) = string.get_mut() {
Some(s) => s.clear(), s.clear()
_ => *string = ImmutableString::new(), } else {
*string = ImmutableString::new()
} }
} }
} }
@ -265,6 +272,7 @@ mod string_functions {
/// ``` /// ```
pub fn truncate(string: &mut ImmutableString, len: INT) { pub fn truncate(string: &mut ImmutableString, len: INT) {
if len > 0 { if len > 0 {
#[allow(clippy::cast_sign_loss)]
let len = len.min(MAX_USIZE_INT) as usize; let len = len.min(MAX_USIZE_INT) as usize;
let chars: StaticVec<_> = string.chars().collect(); let chars: StaticVec<_> = string.chars().collect();
let copy = string.make_mut(); let copy = string.make_mut();
@ -286,20 +294,17 @@ mod string_functions {
/// print(text); // prints "hello" /// print(text); // prints "hello"
/// ``` /// ```
pub fn trim(string: &mut ImmutableString) { pub fn trim(string: &mut ImmutableString) {
match string.get_mut() { if let Some(s) = string.get_mut() {
Some(s) => { let trimmed = s.trim();
let trimmed = s.trim();
if trimmed != s { if trimmed != s {
*s = trimmed.into(); *s = trimmed.into();
}
} }
None => { } else {
let trimmed = string.trim(); let trimmed = string.trim();
if trimmed != string { if trimmed != string {
*string = trimmed.into(); *string = trimmed.into();
}
} }
} }
} }
@ -536,7 +541,7 @@ mod string_functions {
/// ``` /// ```
#[rhai_fn(name = "contains")] #[rhai_fn(name = "contains")]
pub fn contains_char(string: &str, character: char) -> bool { pub fn contains_char(string: &str, character: char) -> bool {
string.contains(character).into() string.contains(character)
} }
/// Return `true` if the string starts with a specified string. /// Return `true` if the string starts with a specified string.
@ -1219,11 +1224,9 @@ mod string_functions {
// Check if string will be over max size limit // Check if string will be over max size limit
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() { if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
return Err(crate::ERR::ErrorDataTooLarge( return Err(
"Length of string".to_string(), ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
crate::Position::NONE, );
)
.into());
} }
let orig_len = string.chars().count(); let orig_len = string.chars().count();
@ -1237,11 +1240,9 @@ mod string_functions {
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
{ {
return Err(crate::ERR::ErrorDataTooLarge( return Err(
"Length of string".to_string(), ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
crate::Position::NONE, );
)
.into());
} }
} }
@ -1279,11 +1280,9 @@ mod string_functions {
// Check if string will be over max size limit // Check if string will be over max size limit
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() { if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
return Err(crate::ERR::ErrorDataTooLarge( return Err(
"Length of string".to_string(), ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
crate::Position::NONE, );
)
.into());
} }
let mut str_len = string.chars().count(); let mut str_len = string.chars().count();
@ -1304,11 +1303,9 @@ mod string_functions {
if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size() if _ctx.engine().max_string_size() > 0 && string.len() > _ctx.engine().max_string_size()
{ {
return Err(crate::ERR::ErrorDataTooLarge( return Err(
"Length of string".to_string(), ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
crate::Position::NONE, );
)
.into());
} }
} }
@ -1317,7 +1314,7 @@ mod string_functions {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub mod arrays { pub mod arrays {
use crate::{Array, ImmutableString}; use crate::Array;
/// Split the string into two at the specified `index` position and return it both strings /// Split the string into two at the specified `index` position and return it both strings
/// as an array. /// as an array.

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_time"))] #![cfg(not(feature = "no_time"))]
use super::arithmetic::make_err as make_arithmetic_err; use super::arithmetic::make_err as make_arithmetic_err;
use crate::module::ModuleFlags;
use crate::plugin::*; use crate::plugin::*;
use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT}; use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
@ -16,7 +17,7 @@ use instant::{Duration, Instant};
def_package! { def_package! {
/// Package of basic timing utilities. /// Package of basic timing utilities.
pub BasicTimePackage(lib) { pub BasicTimePackage(lib) {
lib.standard = true; lib.flags |= ModuleFlags::STANDARD_LIB;
// Register date/time functions // Register date/time functions
combine_with_exported_module!(lib, "time", time_functions); combine_with_exported_module!(lib, "time", time_functions);

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@ impl<'de> DynamicDeserializer<'de> {
) )
} }
#[inline(always)] #[inline(always)]
fn deserialize_int<V: Visitor<'de>>(self, v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_int<V: Visitor<'de>>(v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return visitor.visit_i64(v); return visitor.visit_i64(v);
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
@ -185,7 +185,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else { } else {
self.0 self.0
.downcast_ref::<i8>() .downcast_ref::<i8>()
@ -195,7 +195,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else { } else {
self.0 self.0
.downcast_ref::<i16>() .downcast_ref::<i16>()
@ -205,7 +205,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else if cfg!(feature = "only_i32") { } else if cfg!(feature = "only_i32") {
self.type_error() self.type_error()
} else { } else {
@ -217,7 +217,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
self.type_error() self.type_error()
} else { } else {
@ -229,7 +229,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
self.type_error() self.type_error()
} else { } else {
@ -241,7 +241,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else { } else {
self.0 self.0
.downcast_ref::<u8>() .downcast_ref::<u8>()
@ -251,7 +251,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else { } else {
self.0 self.0
.downcast_ref::<u16>() .downcast_ref::<u16>()
@ -261,7 +261,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else { } else {
self.0 self.0
.downcast_ref::<u32>() .downcast_ref::<u32>()
@ -271,7 +271,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else { } else {
self.0 self.0
.downcast_ref::<u64>() .downcast_ref::<u64>()
@ -281,7 +281,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) Self::deserialize_int(v, visitor)
} else { } else {
self.0 self.0
.downcast_ref::<u128>() .downcast_ref::<u128>()
@ -296,21 +296,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
.downcast_ref::<f32>() .downcast_ref::<f32>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x)); .map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x));
#[cfg(feature = "no_float")] #[allow(unreachable_code)]
#[cfg(feature = "decimal")]
{ {
use rust_decimal::prelude::ToPrimitive; #[cfg(feature = "decimal")]
{
use rust_decimal::prelude::ToPrimitive;
return self return self
.0 .0
.downcast_ref::<rust_decimal::Decimal>() .downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f32()) .and_then(|&x| x.to_f32())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v)); .map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
}
self.type_error_str("f32")
} }
#[cfg(feature = "no_float")]
#[cfg(not(feature = "decimal"))]
return self.type_error_str("f32");
} }
fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
@ -320,21 +320,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
.downcast_ref::<f64>() .downcast_ref::<f64>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x)); .map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x));
#[cfg(feature = "no_float")] #[allow(unreachable_code)]
#[cfg(feature = "decimal")]
{ {
use rust_decimal::prelude::ToPrimitive; #[cfg(feature = "decimal")]
{
use rust_decimal::prelude::ToPrimitive;
return self return self
.0 .0
.downcast_ref::<rust_decimal::Decimal>() .downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f64()) .and_then(|&x| x.to_f64())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v)); .map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
}
self.type_error_str("f64")
} }
#[cfg(feature = "no_float")]
#[cfg(not(feature = "decimal"))]
return self.type_error_str("f64");
} }
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
@ -517,10 +517,9 @@ impl<'de, ITER: Iterator<Item = &'de Dynamic>> serde::de::SeqAccess<'de>
seed: T, seed: T,
) -> RhaiResultOf<Option<T::Value>> { ) -> RhaiResultOf<Option<T::Value>> {
// Deserialize each item coming out of the iterator. // Deserialize each item coming out of the iterator.
match self.iter.next() { self.iter.next().map_or(Ok(None), |item| {
Some(item) => seed.deserialize(item.into_deserializer()).map(Some), seed.deserialize(item.into_deserializer()).map(Some)
None => Ok(None), })
}
} }
} }
@ -553,10 +552,10 @@ impl<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> serde:
seed: S, seed: S,
) -> RhaiResultOf<Option<S::Value>> { ) -> RhaiResultOf<Option<S::Value>> {
// Deserialize each `Identifier` key coming out of the keys iterator. // Deserialize each `Identifier` key coming out of the keys iterator.
match self.keys.next().map(<_>::into_deserializer) { self.keys
Some(d) => seed.deserialize(d).map(Some), .next()
None => Ok(None), .map(<_>::into_deserializer)
} .map_or(Ok(None), |d| seed.deserialize(d).map(Some))
} }
fn next_value_seed<S: serde::de::DeserializeSeed<'de>>( fn next_value_seed<S: serde::de::DeserializeSeed<'de>>(

View File

@ -9,6 +9,9 @@ use std::fmt;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
#[cfg(feature = "decimal")]
use num_traits::FromPrimitive;
struct DynamicVisitor; struct DynamicVisitor;
impl<'de> Visitor<'de> for DynamicVisitor { impl<'de> Visitor<'de> for DynamicVisitor {
@ -38,14 +41,43 @@ impl<'de> Visitor<'de> for DynamicVisitor {
#[inline] #[inline]
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> { fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ return Ok(v.into());
Ok(v.into())
}
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
if v > i32::MAX as i64 { if v <= INT::MAX as i64 {
Ok(Dynamic::from(v)) return Ok(Dynamic::from(v as INT));
} else { }
self.visit_i32(v as i32)
#[allow(unreachable_code)]
{
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_i64(v) {
return Ok(Dynamic::from_decimal(n));
}
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
Err(Error::custom(format!("integer number too large: {v}")))
}
}
#[inline]
fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
if v <= i128::from(INT::MAX) {
return Ok(Dynamic::from(v as INT));
}
#[allow(unreachable_code)]
{
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_i128(v) {
return Ok(Dynamic::from_decimal(n));
}
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
Err(Error::custom(format!("integer number too large: {v}")))
} }
} }
#[inline(always)] #[inline(always)]
@ -59,57 +91,105 @@ impl<'de> Visitor<'de> for DynamicVisitor {
#[inline] #[inline]
fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> { fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ return Ok(Dynamic::from(v as INT));
Ok(INT::from(v).into())
}
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
if v > i32::MAX as u32 { if v <= INT::MAX as u32 {
Ok(Dynamic::from(v)) return Ok(Dynamic::from(v as INT));
} else { }
self.visit_i32(v as i32)
#[allow(unreachable_code)]
{
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_u32(v) {
return Ok(Dynamic::from_decimal(n));
}
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
Err(Error::custom(format!("integer number too large: {v}")))
} }
} }
#[inline] #[inline]
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> { fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
#[cfg(not(feature = "only_i32"))] if v <= INT::MAX as u64 {
if v > i64::MAX as u64 { return Ok(Dynamic::from(v as INT));
Ok(Dynamic::from(v))
} else {
self.visit_i64(v as i64)
} }
#[cfg(feature = "only_i32")]
if v > i32::MAX as u64 { #[cfg(feature = "decimal")]
Ok(Dynamic::from(v)) if let Some(n) = rust_decimal::Decimal::from_u64(v) {
} else { return Ok(Dynamic::from_decimal(n));
self.visit_i32(v as i32)
} }
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
#[allow(unreachable_code)]
Err(Error::custom(format!("integer number too large: {v}")))
}
#[inline]
fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
if v <= INT::MAX as u128 {
return Ok(Dynamic::from(v as INT));
}
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_u128(v) {
return Ok(Dynamic::from_decimal(n));
}
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
#[allow(unreachable_code)]
Err(Error::custom(format!("integer number too large: {v}")))
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[inline(always)] #[inline(always)]
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> { fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "no_float"))]
return self.visit_f64(v as f64); return Ok((v as crate::FLOAT).into());
#[cfg(feature = "f32_float")]
return Ok(v.into()); #[allow(unreachable_code)]
{
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_f32(v) {
return Ok(Dynamic::from_decimal(n));
}
Err(Error::custom(format!(
"floating-point number is not supported: {v}"
)))
}
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[inline(always)] #[inline(always)]
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> { fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "no_float"))]
return Ok(v.into()); return Ok((v as crate::FLOAT).into());
#[cfg(feature = "f32_float")]
return self.visit_f32(v as f32); #[allow(unreachable_code)]
{
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_f64(v) {
return Ok(Dynamic::from_decimal(n));
}
Err(Error::custom(format!(
"floating-point number is not supported: {v}"
)))
}
} }
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[inline] #[inline]
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> { fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
use rust_decimal::Decimal;
use std::convert::TryFrom; use std::convert::TryFrom;
Decimal::try_from(v) rust_decimal::Decimal::try_from(v)
.map(|v| v.into()) .map(|v| v.into())
.map_err(Error::custom) .map_err(Error::custom)
} }
@ -117,10 +197,9 @@ impl<'de> Visitor<'de> for DynamicVisitor {
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[inline] #[inline]
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> { fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
use rust_decimal::Decimal;
use std::convert::TryFrom; use std::convert::TryFrom;
Decimal::try_from(v) rust_decimal::Decimal::try_from(v)
.map(|v| v.into()) .map(|v| v.into())
.map_err(Error::custom) .map_err(Error::custom)
} }
@ -216,10 +295,9 @@ impl<'de> Deserialize<'de> for Scope<'de> {
where where
A: SeqAccess<'de>, A: SeqAccess<'de>,
{ {
let mut scope = match access.size_hint() { let mut scope = access
Some(size) => Scope::with_capacity(size), .size_hint()
None => Scope::new(), .map_or_else(Scope::new, Scope::with_capacity);
};
while let Some(ScopeEntry { while let Some(ScopeEntry {
name, name,

View File

@ -2,7 +2,7 @@
#![cfg(feature = "metadata")] #![cfg(feature = "metadata")]
use crate::api::type_names::format_type; use crate::api::type_names::format_type;
use crate::module::{calc_native_fn_hash, FuncInfo}; use crate::module::{calc_native_fn_hash, FuncInfo, ModuleFlags};
use crate::{calc_fn_hash, Engine, FnAccess, SmartString, StaticVec, AST}; use crate::{calc_fn_hash, Engine, FnAccess, SmartString, StaticVec, AST};
use serde::Serialize; use serde::Serialize;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -66,13 +66,13 @@ impl Ord for FnMetadata<'_> {
impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
fn from(info: &'a FuncInfo) -> Self { fn from(info: &'a FuncInfo) -> Self {
let base_hash = calc_fn_hash(None, &info.name, info.num_params); let base_hash = calc_fn_hash(None, &info.metadata.name, info.metadata.num_params);
let (typ, full_hash) = if info.func.is_script() { let (typ, full_hash) = if info.func.is_script() {
(FnType::Script, base_hash) (FnType::Script, base_hash)
} else { } else {
( (
FnType::Native, FnType::Native,
calc_native_fn_hash(None, &info.name, &info.param_types), calc_native_fn_hash(None, &info.metadata.name, &info.metadata.param_types),
) )
}; };
@ -80,12 +80,13 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
base_hash, base_hash,
full_hash, full_hash,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace: info.namespace, namespace: info.metadata.namespace,
access: info.access, access: info.metadata.access,
name: &info.name, name: &info.metadata.name,
typ, typ,
num_params: info.num_params, num_params: info.metadata.num_params,
params: info params: info
.metadata
.params_info .params_info
.iter() .iter()
.map(|s| { .map(|s| {
@ -99,7 +100,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
}) })
.collect(), .collect(),
_dummy: None, _dummy: None,
return_type: format_type(&info.return_type, true), return_type: format_type(&info.metadata.return_type, true),
signature: info.gen_signature().into(), signature: info.gen_signature().into(),
doc_comments: if info.func.is_script() { doc_comments: if info.func.is_script() {
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
@ -114,7 +115,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
.map(<_>::as_ref) .map(<_>::as_ref)
.collect() .collect()
} else { } else {
info.comments.iter().map(<_>::as_ref).collect() info.metadata.comments.iter().map(<_>::as_ref).collect()
}, },
} }
} }
@ -170,14 +171,20 @@ pub fn gen_metadata_to_json(
let mut global = ModuleMetadata::new(); let mut global = ModuleMetadata::new();
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
for (name, m) in &engine.global_sub_modules { for (name, m) in engine.global_sub_modules.iter().flat_map(|m| m.iter()) {
global.modules.insert(name, m.as_ref().into()); global.modules.insert(name, m.as_ref().into());
} }
let exclude_flags = if include_standard_packages {
ModuleFlags::empty()
} else {
ModuleFlags::STANDARD_LIB
};
engine engine
.global_modules .global_modules
.iter() .iter()
.filter(|m| include_standard_packages || !m.standard) .filter(|m| !m.flags.contains(exclude_flags))
.flat_map(|m| m.iter_fn()) .flat_map(|m| m.iter_fn())
.for_each(|f| { .for_each(|f| {
#[allow(unused_mut)] #[allow(unused_mut)]

View File

@ -1,6 +1,6 @@
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. //! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR}; use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR, INT};
use serde::ser::{ use serde::ser::{
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
}; };
@ -9,6 +9,9 @@ use std::fmt;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
#[cfg(feature = "decimal")]
use num_traits::FromPrimitive;
/// Serializer for [`Dynamic`][crate::Dynamic]. /// Serializer for [`Dynamic`][crate::Dynamic].
pub struct DynamicSerializer { pub struct DynamicSerializer {
/// Buffer to hold a temporary key. /// Buffer to hold a temporary key.
@ -37,8 +40,9 @@ impl DynamicSerializer {
/// # #[cfg(not(feature = "no_index"))] /// # #[cfg(not(feature = "no_index"))]
/// # #[cfg(not(feature = "no_object"))] /// # #[cfg(not(feature = "no_object"))]
/// # #[cfg(not(feature = "no_float"))] /// # #[cfg(not(feature = "no_float"))]
/// # #[cfg(not(feature = "f32_float"))]
/// # { /// # {
/// use rhai::{Dynamic, Array, Map, INT}; /// use rhai::{Dynamic, Array, Map};
/// use rhai::serde::to_dynamic; /// use rhai::serde::to_dynamic;
/// use serde::Serialize; /// use serde::Serialize;
/// ///
@ -112,151 +116,166 @@ impl Serializer for &mut DynamicSerializer {
#[inline(always)] #[inline(always)]
fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> { fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] Ok(INT::from(v).into())
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
} }
#[inline(always)] #[inline(always)]
fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> { fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] Ok(INT::from(v).into())
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
} }
#[inline(always)] #[inline(always)]
fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> { fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] Ok(INT::from(v).into())
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return Ok(v.into());
} }
#[inline] #[inline]
fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> { fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ return Ok(v.into());
Ok(v.into())
}
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
if v > i32::MAX as i64 { if v <= INT::MAX as i64 {
Ok(Dynamic::from(v)) return Ok(Dynamic::from(v as INT));
} else { }
self.serialize_i32(v as i32)
#[allow(unreachable_code)]
{
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_i64(v) {
return Ok(Dynamic::from_decimal(n));
}
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
Err(Error::custom(format!("integer number too large: {v}")))
} }
} }
#[inline] #[inline]
fn serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> { fn serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] if v <= i128::from(INT::MAX) {
if v > i64::MAX as i128 { return Ok(Dynamic::from(v as INT));
Ok(Dynamic::from(v))
} else {
self.serialize_i64(v as i64)
} }
#[cfg(feature = "only_i32")]
if v > i32::MAX as i128 { #[allow(unreachable_code)]
Ok(Dynamic::from(v)) {
} else { #[cfg(feature = "decimal")]
self.serialize_i32(v as i32) if let Some(n) = rust_decimal::Decimal::from_i128(v) {
return Ok(Dynamic::from_decimal(n));
}
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
Err(Error::custom(format!("integer number too large: {v}")))
} }
} }
#[inline(always)] #[inline(always)]
fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> { fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] Ok(INT::from(v).into())
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
} }
#[inline(always)] #[inline(always)]
fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> { fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] Ok(INT::from(v).into())
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
} }
#[inline] #[inline]
fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> { fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ return Ok(Dynamic::from(v as INT));
self.serialize_i64(i64::from(v))
}
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
if v > i32::MAX as u32 { if v <= INT::MAX as u32 {
Ok(Dynamic::from(v)) return Ok(Dynamic::from(v as INT));
} else { }
self.serialize_i32(v as i32)
#[allow(unreachable_code)]
{
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_u32(v) {
return Ok(Dynamic::from_decimal(n));
}
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
Err(Error::custom(format!("integer number too large: {v}")))
} }
} }
#[inline] #[inline]
fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> { fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] if v <= INT::MAX as u64 {
if v > i64::MAX as u64 { return Ok(Dynamic::from(v as INT));
Ok(Dynamic::from(v))
} else {
self.serialize_i64(v as i64)
} }
#[cfg(feature = "only_i32")]
if v > i32::MAX as u64 { #[cfg(feature = "decimal")]
Ok(Dynamic::from(v)) if let Some(n) = rust_decimal::Decimal::from_u64(v) {
} else { return Ok(Dynamic::from_decimal(n));
self.serialize_i32(v as i32)
} }
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
#[allow(unreachable_code)]
Err(Error::custom(format!("integer number too large: {v}")))
} }
#[inline] #[inline]
fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> { fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] if v <= INT::MAX as u128 {
if v > i64::MAX as u128 { return Ok(Dynamic::from(v as INT));
Ok(Dynamic::from(v))
} else {
self.serialize_i64(v as i64)
} }
#[cfg(feature = "only_i32")]
if v > i32::MAX as u128 { #[cfg(feature = "decimal")]
Ok(Dynamic::from(v)) if let Some(n) = rust_decimal::Decimal::from_u128(v) {
} else { return Ok(Dynamic::from_decimal(n));
self.serialize_i32(v as i32)
} }
#[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from_float(v as crate::FLOAT));
#[allow(unreachable_code)]
Err(Error::custom(format!("integer number too large: {v}")))
} }
#[inline] #[inline(always)]
fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> { fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] #[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from(v)); return Ok((v as crate::FLOAT).into());
#[cfg(feature = "no_float")] #[allow(unreachable_code)]
#[cfg(feature = "decimal")]
{ {
use rust_decimal::Decimal; #[cfg(feature = "decimal")]
use std::convert::TryFrom; if let Some(n) = rust_decimal::Decimal::from_f32(v) {
return Ok(Dynamic::from_decimal(n));
}
Decimal::try_from(v) Err(Error::custom(format!(
.map(|v| v.into()) "floating-point number is not supported: {v}"
.map_err(Error::custom) )))
} }
} }
#[inline] #[inline(always)]
fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> { fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] #[cfg(not(feature = "no_float"))]
return Ok(Dynamic::from(v)); return Ok((v as crate::FLOAT).into());
#[cfg(feature = "no_float")] #[allow(unreachable_code)]
#[cfg(feature = "decimal")]
{ {
use rust_decimal::Decimal; #[cfg(feature = "decimal")]
use std::convert::TryFrom; if let Some(n) = rust_decimal::Decimal::from_f64(v) {
return Ok(Dynamic::from_decimal(n));
}
Decimal::try_from(v) Err(Error::custom(format!(
.map(|v| v.into()) "floating-point number is not supported: {v}"
.map_err(Error::custom) )))
} }
} }
@ -332,10 +351,7 @@ impl Serializer for &mut DynamicSerializer {
_value: &T, _value: &T,
) -> RhaiResultOf<Self::Ok> { ) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ return Ok(make_variant(_variant, to_dynamic(_value)?));
let content = to_dynamic(_value)?;
make_variant(_variant, content)
}
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
@ -688,7 +704,7 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer {
#[inline] #[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
make_variant(self.variant, self.array.into()) Ok(make_variant(self.variant, self.array.into()))
} }
} }
@ -716,14 +732,14 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer {
#[inline] #[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
make_variant(self.variant, self.map.into()) Ok(make_variant(self.variant, self.map.into()))
} }
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline] #[inline]
fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult { fn make_variant(variant: &'static str, value: Dynamic) -> Dynamic {
let mut map = crate::Map::new(); let mut map = crate::Map::new();
map.insert(variant.into(), value); map.insert(variant.into(), value);
Ok(map.into()) map.into()
} }

View File

@ -58,7 +58,7 @@ impl Serialize for Dynamic {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref a, ..) => (**a).serialize(ser), Union::Array(ref a, ..) => (**a).serialize(ser),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref a, ..) => ser.serialize_bytes(&**a), Union::Blob(ref a, ..) => ser.serialize_bytes(a),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref m, ..) => { Union::Map(ref m, ..) => {
let mut map = ser.serialize_map(Some(m.len()))?; let mut map = ser.serialize_map(Some(m.len()))?;

View File

@ -9,7 +9,6 @@ use crate::{Engine, Identifier, LexError, SmartString, StaticVec, INT, UNSIGNED_
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
borrow::Cow,
cell::RefCell, cell::RefCell,
char, fmt, char, fmt,
iter::{FusedIterator, Peekable}, iter::{FusedIterator, Peekable},
@ -591,14 +590,67 @@ pub enum Token {
impl fmt::Display for Token { impl fmt::Display for Token {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.syntax()) #[allow(clippy::enum_glob_use)]
use Token::*;
match self {
IntegerConstant(i) => write!(f, "{i}"),
#[cfg(not(feature = "no_float"))]
FloatConstant(v) => write!(f, "{v}"),
#[cfg(feature = "decimal")]
DecimalConstant(d) => write!(f, "{d}"),
StringConstant(s) => write!(f, r#""{s}""#),
InterpolatedString(..) => f.write_str("string"),
CharConstant(c) => write!(f, "{c}"),
Identifier(s) => f.write_str(s),
Reserved(s) => f.write_str(s),
#[cfg(not(feature = "no_custom_syntax"))]
Custom(s) => f.write_str(s),
LexError(err) => write!(f, "{err}"),
Comment(s) => f.write_str(s),
EOF => f.write_str("{EOF}"),
token => f.write_str(token.literal_syntax()),
}
} }
} }
impl Token { impl Token {
/// Is the token a literal symbol?
#[must_use]
pub const fn is_literal(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*;
match self {
IntegerConstant(..) => false,
#[cfg(not(feature = "no_float"))]
FloatConstant(..) => false,
#[cfg(feature = "decimal")]
DecimalConstant(..) => false,
StringConstant(..)
| InterpolatedString(..)
| CharConstant(..)
| Identifier(..)
| Reserved(..) => false,
#[cfg(not(feature = "no_custom_syntax"))]
Custom(..) => false,
LexError(..) | Comment(..) => false,
EOF => false,
_ => true,
}
}
/// Get the literal syntax of the token. /// Get the literal syntax of the token.
///
/// # Panics
///
/// Panics if the token is not a literal symbol.
#[must_use] #[must_use]
pub const fn literal_syntax(&self) -> &'static str { pub const fn literal_syntax(&self) -> &'static str {
#[allow(clippy::enum_glob_use)]
use Token::*; use Token::*;
match self { match self {
@ -690,34 +742,7 @@ impl Token {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
As => "as", As => "as",
_ => "ERROR: NOT A KEYWORD", _ => panic!("token is not a literal symbol"),
}
}
/// Get the syntax of the token.
#[must_use]
pub fn syntax(&self) -> Cow<'static, str> {
use Token::*;
match self {
IntegerConstant(i) => i.to_string().into(),
#[cfg(not(feature = "no_float"))]
FloatConstant(f) => f.to_string().into(),
#[cfg(feature = "decimal")]
DecimalConstant(d) => d.to_string().into(),
StringConstant(s) => format!("\"{s}\"").into(),
InterpolatedString(..) => "string".into(),
CharConstant(c) => c.to_string().into(),
Identifier(s) => s.to_string().into(),
Reserved(s) => s.to_string().into(),
#[cfg(not(feature = "no_custom_syntax"))]
Custom(s) => s.to_string().into(),
LexError(err) => err.to_string().into(),
Comment(s) => s.to_string().into(),
EOF => "{EOF}".into(),
token => token.literal_syntax().into(),
} }
} }
@ -802,6 +827,7 @@ impl Token {
/// Reverse lookup a symbol token from a piece of syntax. /// Reverse lookup a symbol token from a piece of syntax.
#[must_use] #[must_use]
pub fn lookup_symbol_from_syntax(syntax: &str) -> Option<Self> { pub fn lookup_symbol_from_syntax(syntax: &str) -> Option<Self> {
#[allow(clippy::enum_glob_use)]
use Token::*; use Token::*;
Some(match syntax { Some(match syntax {
@ -941,21 +967,20 @@ impl Token {
/// (not sure about `fn` name). /// (not sure about `fn` name).
#[must_use] #[must_use]
pub const fn is_next_unary(&self) -> bool { pub const fn is_next_unary(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*; use Token::*;
match self { match self {
LexError(..) |
SemiColon | // ; - is unary SemiColon | // ; - is unary
Colon | // #{ foo: - is unary Colon | // #{ foo: - is unary
Comma | // ( ... , -expr ) - is unary Comma | // ( ... , -expr ) - is unary
//Period | //Period |
//Elvis | //Elvis |
//DoubleQuestion | DoubleQuestion | // ?? - is unary
//QuestionBracket | ExclusiveRange | // .. - is unary
ExclusiveRange | // .. - is unary
InclusiveRange | // ..= - is unary InclusiveRange | // ..= - is unary
LeftBrace | // { -expr } - is unary LeftBrace | // { -expr } - is unary
// RightBrace | { expr } - expr not unary & is closing // RightBrace | // { expr } - expr not unary & is closing
LeftParen | // ( -expr ) - is unary LeftParen | // ( -expr ) - is unary
// RightParen | // ( expr ) - expr not unary & is closing // RightParen | // ( expr ) - expr not unary & is closing
LeftBracket | // [ -expr ] - is unary LeftBracket | // [ -expr ] - is unary
@ -989,7 +1014,7 @@ impl Token {
Pipe | Pipe |
Ampersand | Ampersand |
If | If |
//Do | //Do |
While | While |
Until | Until |
In | In |
@ -1000,15 +1025,21 @@ impl Token {
XOr | XOr |
XOrAssign | XOrAssign |
Return | Return |
Throw => true, Throw => true,
_ => false, #[cfg(not(feature = "no_index"))]
QuestionBracket => true, // ?[ - is unary
LexError(..) => true,
_ => false,
} }
} }
/// Get the precedence number of the token. /// Get the precedence number of the token.
#[must_use] #[must_use]
pub const fn precedence(&self) -> Option<Precedence> { pub const fn precedence(&self) -> Option<Precedence> {
#[allow(clippy::enum_glob_use)]
use Token::*; use Token::*;
Precedence::new(match self { Precedence::new(match self {
@ -1041,6 +1072,7 @@ impl Token {
/// Does an expression bind to the right (instead of left)? /// Does an expression bind to the right (instead of left)?
#[must_use] #[must_use]
pub const fn is_bind_right(&self) -> bool { pub const fn is_bind_right(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*; use Token::*;
match self { match self {
@ -1054,6 +1086,7 @@ impl Token {
/// Is this token a standard symbol used in the language? /// Is this token a standard symbol used in the language?
#[must_use] #[must_use]
pub const fn is_standard_symbol(&self) -> bool { pub const fn is_standard_symbol(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*; use Token::*;
match self { match self {
@ -1080,6 +1113,7 @@ impl Token {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn is_standard_keyword(&self) -> bool { pub const fn is_standard_keyword(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*; use Token::*;
match self { match self {
@ -1127,7 +1161,7 @@ impl Token {
impl From<Token> for String { impl From<Token> for String {
#[inline(always)] #[inline(always)]
fn from(token: Token) -> Self { fn from(token: Token) -> Self {
token.syntax().into() token.to_string()
} }
} }
@ -1476,13 +1510,13 @@ pub fn get_next_token(
/// Test if the given character is a hex character. /// Test if the given character is a hex character.
#[inline(always)] #[inline(always)]
fn is_hex_digit(c: char) -> bool { const fn is_hex_digit(c: char) -> bool {
matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9') matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9')
} }
/// Test if the given character is a numeric digit. /// Test if the given character is a numeric digit.
#[inline(always)] #[inline(always)]
fn is_numeric_digit(c: char) -> bool { const fn is_numeric_digit(c: char) -> bool {
matches!(c, '0'..='9') matches!(c, '0'..='9')
} }
@ -1662,21 +1696,8 @@ fn get_next_token_inner(
}); });
// Parse number // Parse number
return Some(( let token = radix_base.map_or_else(
if let Some(radix) = radix_base { || {
let result = &result[2..];
UNSIGNED_INT::from_str_radix(&result, radix)
.map(|v| v as INT)
.map_or_else(
|_| {
Token::LexError(
LERR::MalformedNumber(result.to_string()).into(),
)
},
Token::IntegerConstant,
)
} else {
let num = INT::from_str(&result).map(Token::IntegerConstant); let num = INT::from_str(&result).map(Token::IntegerConstant);
// If integer parsing is unnecessary, try float instead // If integer parsing is unnecessary, try float instead
@ -1705,8 +1726,23 @@ fn get_next_token_inner(
Token::LexError(LERR::MalformedNumber(result.to_string()).into()) Token::LexError(LERR::MalformedNumber(result.to_string()).into())
}) })
}, },
num_pos, |radix| {
)); let result = &result[2..];
UNSIGNED_INT::from_str_radix(result, radix)
.map(|v| v as INT)
.map_or_else(
|_| {
Token::LexError(
LERR::MalformedNumber(result.to_string()).into(),
)
},
Token::IntegerConstant,
)
},
);
return Some((token, num_pos));
} }
// letter or underscore ... // letter or underscore ...
@ -1735,7 +1771,7 @@ fn get_next_token_inner(
Some('\r') => { Some('\r') => {
eat_next(stream, pos); eat_next(stream, pos);
// `\r\n // `\r\n
if let Some('\n') = stream.peek_next() { if stream.peek_next() == Some('\n') {
eat_next(stream, pos); eat_next(stream, pos);
} }
pos.new_line(); pos.new_line();
@ -1763,7 +1799,7 @@ fn get_next_token_inner(
// ' - character literal // ' - character literal
('\'', '\'') => { ('\'', '\'') => {
return Some(( return Some((
Token::LexError(LERR::MalformedChar("".to_string()).into()), Token::LexError(LERR::MalformedChar(String::new()).into()),
start_pos, start_pos,
)) ))
} }
@ -1916,7 +1952,7 @@ fn get_next_token_inner(
while let Some(c) = stream.get_next() { while let Some(c) = stream.get_next() {
if c == '\r' { if c == '\r' {
// \r\n // \r\n
if let Some('\n') = stream.peek_next() { if stream.peek_next() == Some('\n') {
eat_next(stream, pos); eat_next(stream, pos);
} }
pos.new_line(); pos.new_line();
@ -2399,7 +2435,7 @@ impl<'a> Iterator for TokenIterator<'a> {
Some((Token::Reserved(s), pos)) => (match Some((Token::Reserved(s), pos)) => (match
(s.as_str(), (s.as_str(),
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
(!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)), self.engine.custom_keywords.as_ref().map_or(false, |m| m.contains_key(&*s)),
#[cfg(feature = "no_custom_syntax")] #[cfg(feature = "no_custom_syntax")]
false false
) )
@ -2436,7 +2472,7 @@ impl<'a> Iterator for TokenIterator<'a> {
#[cfg(feature = "no_custom_syntax")] #[cfg(feature = "no_custom_syntax")]
(.., true) => unreachable!("no custom operators"), (.., true) => unreachable!("no custom operators"),
// Reserved keyword that is not custom and disabled. // Reserved keyword that is not custom and disabled.
(token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => { (token, false) if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token)) => {
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "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()) Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
}, },
@ -2445,13 +2481,13 @@ impl<'a> Iterator for TokenIterator<'a> {
}, pos), }, pos),
// Custom keyword // Custom keyword
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Some((Token::Identifier(s), pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s) => { Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(&*s)) => {
(Token::Custom(s), pos) (Token::Custom(s), pos)
} }
// Custom keyword/symbol - must be disabled // Custom keyword/symbol - must be disabled
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => { Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(token.literal_syntax())) => {
if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) { if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) {
// Disabled standard keyword/symbol // Disabled standard keyword/symbol
(Token::Custom(Box::new(token.literal_syntax().into())), pos) (Token::Custom(Box::new(token.literal_syntax().into())), pos)
} else { } else {
@ -2460,7 +2496,7 @@ impl<'a> Iterator for TokenIterator<'a> {
} }
} }
// Disabled symbol // Disabled symbol
Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => { Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) => {
(Token::Reserved(Box::new(token.literal_syntax().into())), pos) (Token::Reserved(Box::new(token.literal_syntax().into())), pos)
} }
// Normal symbol // Normal symbol

View File

@ -105,7 +105,7 @@ impl Add<BloomFilterU64> for &BloomFilterU64 {
impl AddAssign<Self> for BloomFilterU64 { impl AddAssign<Self> for BloomFilterU64 {
#[inline(always)] #[inline(always)]
fn add_assign(&mut self, rhs: Self) { fn add_assign(&mut self, rhs: Self) {
*self += &rhs *self += &rhs;
} }
} }

View File

@ -13,7 +13,7 @@ pub struct CustomTypeInfo {
/// _(internals)_ A collection of custom types. /// _(internals)_ A collection of custom types.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Clone, Hash, Default)] #[derive(Clone, Hash)]
pub struct CustomTypesCollection(BTreeMap<Identifier, CustomTypeInfo>); pub struct CustomTypesCollection(BTreeMap<Identifier, CustomTypeInfo>);
impl fmt::Debug for CustomTypesCollection { impl fmt::Debug for CustomTypesCollection {
@ -25,6 +25,13 @@ impl fmt::Debug for CustomTypesCollection {
} }
} }
impl Default for CustomTypesCollection {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl CustomTypesCollection { impl CustomTypesCollection {
/// Create a new [`CustomTypesCollection`]. /// Create a new [`CustomTypesCollection`].
#[inline(always)] #[inline(always)]

View File

@ -158,7 +158,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
#[inline] #[inline]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
match self.0 { match self.0 {
DynamicWriteLockInner::Reference(ref reference) => *reference, DynamicWriteLockInner::Reference(ref reference) => reference,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
DynamicWriteLockInner::Guard(ref guard) => guard.downcast_ref().expect(CHECKED), DynamicWriteLockInner::Guard(ref guard) => guard.downcast_ref().expect(CHECKED),
} }
@ -169,7 +169,7 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
match self.0 { match self.0 {
DynamicWriteLockInner::Reference(ref mut reference) => *reference, DynamicWriteLockInner::Reference(ref mut reference) => reference,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
DynamicWriteLockInner::Guard(ref mut guard) => guard.downcast_mut().expect(CHECKED), DynamicWriteLockInner::Guard(ref mut guard) => guard.downcast_mut().expect(CHECKED),
} }
@ -640,6 +640,7 @@ impl fmt::Debug for Dynamic {
} }
} }
#[allow(clippy::enum_glob_use)]
use AccessMode::*; use AccessMode::*;
impl Clone for Dynamic { impl Clone for Dynamic {
@ -1088,7 +1089,7 @@ impl Dynamic {
pub fn from<T: Variant + Clone>(value: T) -> Self { pub fn from<T: Variant + Clone>(value: T) -> Self {
// Coded this way in order to maximally leverage potentials for dead-code removal. // Coded this way in order to maximally leverage potentials for dead-code removal.
reify!(value, |v: Dynamic| return v); reify!(value, |v: Self| return v);
reify!(value, |v: INT| return v.into()); reify!(value, |v: INT| return v.into());
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -1187,7 +1188,7 @@ impl Dynamic {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
self.flatten_in_place(); self.flatten_in_place();
if TypeId::of::<T>() == TypeId::of::<Dynamic>() { if TypeId::of::<T>() == TypeId::of::<Self>() {
return Some(reify!(self => T)); return Some(reify!(self => T));
} }
if TypeId::of::<T>() == TypeId::of::<()>() { if TypeId::of::<T>() == TypeId::of::<()>() {
@ -1309,7 +1310,7 @@ impl Dynamic {
#[must_use] #[must_use]
pub fn cast<T: Any + Clone>(self) -> T { pub fn cast<T: Any + Clone>(self) -> T {
// Bail out early if the return type needs no cast // Bail out early if the return type needs no cast
if TypeId::of::<T>() == TypeId::of::<Dynamic>() { if TypeId::of::<T>() == TypeId::of::<Self>() {
return reify!(self => T); return reify!(self => T);
} }
@ -1710,10 +1711,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Unit(..) => true, Union::Unit(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Unit(..) => true, matches!(crate::func::locked_read(cell).0, Union::Unit(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1724,10 +1724,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Int(..) => true, Union::Int(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Int(..) => true, matches!(crate::func::locked_read(cell).0, Union::Int(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1741,10 +1740,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Float(..) => true, Union::Float(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Float(..) => true, matches!(crate::func::locked_read(cell).0, Union::Float(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1758,10 +1756,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Decimal(..) => true, Union::Decimal(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Decimal(..) => true, matches!(crate::func::locked_read(cell).0, Union::Decimal(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1772,10 +1769,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Bool(..) => true, Union::Bool(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Bool(..) => true, matches!(crate::func::locked_read(cell).0, Union::Bool(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1786,10 +1782,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Char(..) => true, Union::Char(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Char(..) => true, matches!(crate::func::locked_read(cell).0, Union::Char(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1800,10 +1795,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Str(..) => true, Union::Str(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Str(..) => true, matches!(crate::func::locked_read(cell).0, Union::Str(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1817,10 +1811,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Array(..) => true, Union::Array(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Array(..) => true, matches!(crate::func::locked_read(cell).0, Union::Array(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1834,10 +1827,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Blob(..) => true, Union::Blob(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Blob(..) => true, matches!(crate::func::locked_read(cell).0, Union::Blob(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1851,10 +1843,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::Map(..) => true, Union::Map(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::Map(..) => true, matches!(crate::func::locked_read(cell).0, Union::Map(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1865,10 +1856,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::FnPtr(..) => true, Union::FnPtr(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::FnPtr(..) => true, matches!(crate::func::locked_read(cell).0, Union::FnPtr(..))
_ => false, }
},
_ => false, _ => false,
} }
} }
@ -1882,10 +1872,9 @@ impl Dynamic {
match self.0 { match self.0 {
Union::TimeStamp(..) => true, Union::TimeStamp(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 { Union::Shared(ref cell, ..) => {
Union::TimeStamp(..) => true, matches!(crate::func::locked_read(cell).0, Union::TimeStamp(..))
_ => false, }
},
_ => false, _ => false,
} }
} }

View File

@ -299,10 +299,7 @@ impl EvalAltResult {
#[inline(never)] #[inline(never)]
#[must_use] #[must_use]
pub const fn is_pseudo_error(&self) -> bool { pub const fn is_pseudo_error(&self) -> bool {
match self { matches!(self, Self::LoopBreak(..) | Self::Return(..))
Self::LoopBreak(..) | Self::Return(..) => true,
_ => false,
}
} }
/// Can this error be caught? /// Can this error be caught?
#[cold] #[cold]
@ -357,20 +354,17 @@ impl EvalAltResult {
#[inline(never)] #[inline(never)]
#[must_use] #[must_use]
pub const fn is_system_exception(&self) -> bool { pub const fn is_system_exception(&self) -> bool {
match self { matches!(
Self::ErrorSystem(..) => true, self,
Self::ErrorParsing(..) => true, Self::ErrorSystem(..)
| Self::ErrorParsing(..)
Self::ErrorCustomSyntax(..) | Self::ErrorCustomSyntax(..)
| Self::ErrorTooManyOperations(..) | Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(..) | Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..) | Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(..) => true, | Self::ErrorDataTooLarge(..)
| Self::ErrorTerminated(..)
Self::ErrorTerminated(..) => true, )
_ => false,
}
} }
/// Get the [position][Position] of this error. /// Get the [position][Position] of this error.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -459,7 +453,6 @@ impl EvalAltResult {
/// Unwrap this error and get the very base error. /// Unwrap this error and get the very base error.
#[cold] #[cold]
#[inline(never)] #[inline(never)]
#[must_use]
pub fn unwrap_inner(&self) -> &Self { pub fn unwrap_inner(&self) -> &Self {
match self { match self {
Self::ErrorInFunctionCall(.., err, _) | Self::ErrorInModule(.., err, _) => { Self::ErrorInFunctionCall(.., err, _) | Self::ErrorInModule(.., err, _) => {

View File

@ -14,7 +14,7 @@ use num_traits::float::FloatCore as Float;
/// A type that wraps a floating-point number and implements [`Hash`]. /// A type that wraps a floating-point number and implements [`Hash`].
/// ///
/// Not available under `no_float`. /// Not available under `no_float`.
#[derive(Clone, Copy, PartialEq, PartialOrd)] #[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
pub struct FloatWrapper<F>(F); pub struct FloatWrapper<F>(F);
impl Hash for FloatWrapper<crate::FLOAT> { impl Hash for FloatWrapper<crate::FLOAT> {

View File

@ -147,7 +147,6 @@ impl FromStr for ImmutableString {
type Err = (); type Err = ();
#[inline(always)] #[inline(always)]
#[must_use]
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let s: SmartString = s.into(); let s: SmartString = s.into();
Ok(Self(s.into())) Ok(Self(s.into()))

View File

@ -12,7 +12,6 @@ use std::prelude::v1::*;
use std::{ use std::{
fmt, fmt,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
marker::PhantomData,
ops::AddAssign, ops::AddAssign,
}; };
@ -24,28 +23,23 @@ pub const MAX_STRING_LEN: usize = 24;
/// _(internals)_ A cache for interned strings. /// _(internals)_ A cache for interned strings.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
pub struct StringsInterner<'a> { #[derive(Clone)]
/// Maximum number of strings interned. #[must_use]
pub capacity: usize, pub struct StringsInterner {
/// Maximum string length.
pub max_string_len: usize,
/// Cached strings. /// Cached strings.
cache: StraightHashMap<ImmutableString>, cache: StraightHashMap<ImmutableString>,
/// Bloom filter to avoid caching "one-hit wonders". /// Bloom filter to avoid caching "one-hit wonders".
filter: BloomFilterU64, bloom_filter: BloomFilterU64,
/// Take care of the lifetime parameter.
dummy: PhantomData<&'a ()>,
} }
impl Default for StringsInterner<'_> { impl Default for StringsInterner {
#[inline(always)] #[inline(always)]
#[must_use]
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl fmt::Debug for StringsInterner<'_> { impl fmt::Debug for StringsInterner {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -53,17 +47,13 @@ impl fmt::Debug for StringsInterner<'_> {
} }
} }
impl StringsInterner<'_> { impl StringsInterner {
/// Create a new [`StringsInterner`]. /// Create a new [`StringsInterner`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
capacity: MAX_INTERNED_STRINGS,
max_string_len: MAX_STRING_LEN,
cache: StraightHashMap::default(), cache: StraightHashMap::default(),
filter: BloomFilterU64::new(), bloom_filter: BloomFilterU64::new(),
dummy: PhantomData,
} }
} }
@ -79,22 +69,26 @@ impl StringsInterner<'_> {
#[must_use] #[must_use]
pub fn get_with_mapper<S: AsRef<str>>( pub fn get_with_mapper<S: AsRef<str>>(
&mut self, &mut self,
id: &str, category: &str,
mapper: impl FnOnce(S) -> ImmutableString, mapper: impl FnOnce(S) -> ImmutableString,
text: S, text: S,
) -> ImmutableString { ) -> ImmutableString {
let key = text.as_ref(); let key = text.as_ref();
let hasher = &mut get_hasher(); let hasher = &mut get_hasher();
id.hash(hasher); category.hash(hasher);
key.hash(hasher); key.hash(hasher);
let hash = hasher.finish(); let hash = hasher.finish();
// Cache long strings only on the second try to avoid caching "one-hit wonders". // 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) { if key.len() > MAX_STRING_LEN && self.bloom_filter.is_absent_and_set(hash) {
return mapper(text); return mapper(text);
} }
if self.cache.is_empty() {
self.cache.reserve(MAX_INTERNED_STRINGS);
}
let result = match self.cache.entry(hash) { let result = match self.cache.entry(hash) {
Entry::Occupied(e) => return e.get().clone(), Entry::Occupied(e) => return e.get().clone(),
Entry::Vacant(e) => { Entry::Vacant(e) => {
@ -114,26 +108,22 @@ impl StringsInterner<'_> {
} }
/// If the interner is over capacity, remove the longest entry that has the lowest count /// If the interner is over capacity, remove the longest entry that has the lowest count
fn throttle_cache(&mut self, hash: u64) { #[inline]
if self.cache.len() <= self.capacity { fn throttle_cache(&mut self, skip_hash: u64) {
if self.cache.len() <= MAX_INTERNED_STRINGS {
return; return;
} }
// Leave some buffer to grow when shrinking the cache. // 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 // We leave at least two entries, one for the empty string, and one for the string
// that has just been inserted. // that has just been inserted.
let max = if self.capacity < 5 { while self.cache.len() > MAX_INTERNED_STRINGS - 3 {
2
} else {
self.capacity - 3
};
while self.cache.len() > max {
let (_, _, n) = self let (_, _, n) = self
.cache .cache
.iter() .iter()
.fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| { .fold((0, usize::MAX, 0), |(x, c, n), (&k, v)| {
if k != hash && (v.strong_count() < c || (v.strong_count() == c && v.len() > x)) if k != skip_hash
&& (v.strong_count() < c || (v.strong_count() == c && v.len() > x))
{ {
(v.len(), v.strong_count(), k) (v.len(), v.strong_count(), k)
} else { } else {
@ -169,14 +159,14 @@ impl StringsInterner<'_> {
} }
} }
impl AddAssign<Self> for StringsInterner<'_> { impl AddAssign<Self> for StringsInterner {
#[inline(always)] #[inline(always)]
fn add_assign(&mut self, rhs: Self) { fn add_assign(&mut self, rhs: Self) {
self.cache.extend(rhs.cache.into_iter()); self.cache.extend(rhs.cache.into_iter());
} }
} }
impl AddAssign<&Self> for StringsInterner<'_> { impl AddAssign<&Self> for StringsInterner {
#[inline(always)] #[inline(always)]
fn add_assign(&mut self, rhs: &Self) { fn add_assign(&mut self, rhs: &Self) {
self.cache self.cache

View File

@ -175,7 +175,6 @@ impl ParseErrorType {
/// Make a [`ParseError`] using the current type and position. /// Make a [`ParseError`] using the current type and position.
#[cold] #[cold]
#[inline(never)] #[inline(never)]
#[must_use]
pub(crate) fn into_err(self, pos: Position) -> ParseError { pub(crate) fn into_err(self, pos: Position) -> ParseError {
ParseError(self.into(), pos) ParseError(self.into(), pos)
} }
@ -299,7 +298,6 @@ impl ParseError {
/// Get the [type][ParseErrorType] of this parse error. /// Get the [type][ParseErrorType] of this parse error.
#[cold] #[cold]
#[inline(never)] #[inline(never)]
#[must_use]
pub const fn err_type(&self) -> &ParseErrorType { pub const fn err_type(&self) -> &ParseErrorType {
&self.0 &self.0
} }
@ -316,7 +314,7 @@ impl From<ParseErrorType> for RhaiError {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn from(err: ParseErrorType) -> Self { fn from(err: ParseErrorType) -> Self {
Box::new(err.into()) Self::new(err.into())
} }
} }
@ -332,7 +330,7 @@ impl From<ParseError> for RhaiError {
#[cold] #[cold]
#[inline(never)] #[inline(never)]
fn from(err: ParseError) -> Self { fn from(err: ParseError) -> Self {
Box::new(err.into()) Self::new(err.into())
} }
} }

View File

@ -6,12 +6,12 @@ use std::prelude::v1::*;
/// Run custom restoration logic upon the end of scope. /// Run custom restoration logic upon the end of scope.
#[must_use] #[must_use]
pub struct RestoreOnDrop<'a, T, R: FnOnce(&mut T)> { pub struct RestoreOnDrop<'a, T: ?Sized, R: FnOnce(&mut T)> {
value: &'a mut T, value: &'a mut T,
restore: Option<R>, restore: Option<R>,
} }
impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> { impl<'a, T: ?Sized, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
/// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at /// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at
/// the end of scope only when `need_restore` is `true`. /// the end of scope only when `need_restore` is `true`.
/// ///
@ -39,7 +39,7 @@ impl<'a, T, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
} }
} }
impl<'a, T, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> { impl<'a, T: ?Sized, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
#[inline(always)] #[inline(always)]
fn drop(&mut self) { fn drop(&mut self) {
if let Some(restore) = self.restore.take() { if let Some(restore) = self.restore.take() {
@ -48,7 +48,7 @@ impl<'a, T, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
} }
} }
impl<'a, T, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> { impl<'a, T: ?Sized, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> {
type Target = T; type Target = T;
#[inline(always)] #[inline(always)]
@ -57,7 +57,7 @@ impl<'a, T, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> {
} }
} }
impl<'a, T, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> { impl<'a, T: ?Sized, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> {
#[inline(always)] #[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.value self.value

View File

@ -353,6 +353,43 @@ impl Scope<'_> {
self.values.push(value); self.values.push(value);
self self
} }
/// Remove the last entry from the [`Scope`].
///
/// # Panics
///
/// Panics is the [`Scope`] is empty.
///
/// # Example
///
/// ```
/// use rhai::Scope;
///
/// let mut my_scope = Scope::new();
///
/// my_scope.push("x", 42_i64);
/// my_scope.push("y", 123_i64);
/// assert!(my_scope.contains("x"));
/// assert!(my_scope.contains("y"));
/// assert_eq!(my_scope.len(), 2);
///
/// my_scope.pop();
/// assert!(my_scope.contains("x"));
/// assert!(!my_scope.contains("y"));
/// assert_eq!(my_scope.len(), 1);
///
/// my_scope.pop();
/// assert!(!my_scope.contains("x"));
/// assert!(!my_scope.contains("y"));
/// assert_eq!(my_scope.len(), 0);
/// assert!(my_scope.is_empty());
/// ```
#[inline(always)]
pub fn pop(&mut self) -> &mut Self {
self.names.pop().expect("`Scope` must not be empty");
self.values.pop().expect("`Scope` must not be empty");
self.aliases.pop().expect("`Scope` must not be empty");
self
}
/// Truncate (rewind) the [`Scope`] to a previous size. /// Truncate (rewind) the [`Scope`] to a previous size.
/// ///
/// # Example /// # Example

View File

@ -14,7 +14,7 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
engine.register_raw_fn( engine.register_raw_fn(
"call_with_arg", "call_with_arg",
&[TypeId::of::<FnPtr>(), TypeId::of::<INT>()], [TypeId::of::<FnPtr>(), TypeId::of::<INT>()],
|context, args| { |context, args| {
let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>(); let fn_ptr = std::mem::take(args[0]).cast::<FnPtr>();
fn_ptr.call_raw(&context, None, [std::mem::take(args[1])]) fn_ptr.call_raw(&context, None, [std::mem::take(args[1])])
@ -165,7 +165,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
engine.register_raw_fn( engine.register_raw_fn(
"custom_call", "custom_call",
&[TypeId::of::<INT>(), TypeId::of::<FnPtr>()], [TypeId::of::<INT>(), TypeId::of::<FnPtr>()],
|context, args| { |context, args| {
let func = take(args[1]).cast::<FnPtr>(); let func = take(args[1]).cast::<FnPtr>();
@ -348,7 +348,7 @@ fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
let p1 = Rc::new(RefCell::new(41)); let p1 = Rc::new(RefCell::new(41));
let p2 = Rc::new(RefCell::new(1)); let p2 = Rc::new(RefCell::new(1));
f(p1.clone(), p2.clone())?; f(p1.clone(), p2)?;
assert_eq!(*p1.borrow(), 42); assert_eq!(*p1.borrow(), 42);

View File

@ -266,17 +266,18 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
*state = Dynamic::FALSE; *state = Dynamic::FALSE;
Ok(Some("$ident$".into())) Ok(Some("$ident$".into()))
} }
2 => match stream[1].as_str() { 2 => {
"world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())), match stream[1].as_str() {
"world" => Ok(Some("$$hello".into())), "world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())),
"kitty" => { "world" => Ok(Some("$$hello".into())),
*state = (42 as INT).into(); "kitty" => {
Ok(None) *state = (42 as INT).into();
Ok(None)
}
s => Err(LexError::ImproperSymbol(s.to_string(), String::new())
.into_err(Position::NONE)),
} }
s => Err(LexError::ImproperSymbol(s.to_string(), String::new()) }
.into_err(Position::NONE)
.into()),
},
_ => unreachable!(), _ => unreachable!(),
}, },
true, true,

View File

@ -59,13 +59,13 @@ fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
// Print debugger state - which is an object map // Print debugger state - which is an object map
println!( println!(
"Current state = {}", "Current state = {}",
context.global_runtime_state_mut().debugger.state() context.global_runtime_state_mut().debugger().state()
); );
// Modify state // Modify state
let mut state = context let mut state = context
.global_runtime_state_mut() .global_runtime_state_mut()
.debugger .debugger_mut()
.state_mut() .state_mut()
.write_lock::<Map>() .write_lock::<Map>()
.unwrap(); .unwrap();

View File

@ -13,22 +13,20 @@ use rhai::Array;
use rhai::Map; use rhai::Map;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use rhai::FLOAT; use rhai::FLOAT;
#[cfg(feature = "no_float")]
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
use rust_decimal::Decimal; use rust_decimal::Decimal;
#[test] #[test]
fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> { fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> {
assert!(to_dynamic(42_u64)?.is_int()); assert!(to_dynamic(42_u64)?.is_int());
assert!(to_dynamic(u64::MAX)?.is::<u64>());
assert!(to_dynamic(42 as INT)?.is_int()); assert!(to_dynamic(42 as INT)?.is_int());
assert!(to_dynamic(true)?.is_bool()); assert!(to_dynamic(true)?.is_bool());
assert!(to_dynamic(())?.is_unit()); assert!(to_dynamic(())?.is_unit());
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
{ {
assert!(to_dynamic(123.456_f64)?.is::<f64>()); assert!(to_dynamic(123.456_f64)?.is::<FLOAT>());
assert!(to_dynamic(123.456_f32)?.is::<f32>()); assert!(to_dynamic(123.456_f32)?.is::<FLOAT>());
} }
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]
@ -749,6 +747,31 @@ fn test_serde_json() -> serde_json::Result<()> {
Ok(()) Ok(())
} }
#[test]
#[cfg(feature = "metadata")]
#[cfg(feature = "decimal")]
#[cfg(not(feature = "no_float"))]
fn test_serde_json_numbers() -> serde_json::Result<()> {
use std::str::FromStr;
let d: Dynamic = serde_json::from_str("100000000000")?;
assert!(d.is::<INT>());
assert_eq!(d.as_int().unwrap(), 100000000000);
let d: Dynamic = serde_json::from_str("10000000000000000000")?;
assert!(d.is::<Decimal>());
assert_eq!(
d.as_decimal().unwrap(),
Decimal::from_str("10000000000000000000").unwrap()
);
let d: Dynamic = serde_json::from_str("10000000000000000000000000")?;
assert!(d.is::<FLOAT>());
assert_eq!(d.as_float().unwrap(), 10000000000000000000000000.0);
Ok(())
}
#[test] #[test]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn test_serde_optional() -> Result<(), Box<EvalAltResult>> { fn test_serde_optional() -> Result<(), Box<EvalAltResult>> {
@ -825,7 +848,14 @@ fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
fn test_serde_json_borrowed_string() { fn test_serde_json_borrowed_string() {
let value = json!({ "a": "b" }); let value = json!({ "a": "b" });
println!("value: {value:?}"); println!("value: {value:?}");
let _: Dynamic = serde_json::from_value(value).unwrap();
let result: Dynamic = serde_json::from_value(value.clone()).unwrap();
println!("result: {result:?}");
let value2 = serde_json::to_value(&result).unwrap();
println!("value2: {value2:?}");
assert_eq!(value, value2);
} }
#[test] #[test]