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
==============
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
------------
@ -20,6 +25,7 @@ Enhancements
* The `TypeBuilder` type and `CustomType` trait are no longer marked as volatile.
* `FuncArgs` is also implemented for arrays.
* `Engine::set_XXX` API can now be chained.
* `EvalContext::scope_mut` now returns `&mut Scope` instead of `&mut &mut Scope`.
Version 1.11.0

View File

@ -1,3 +1,3 @@
//! 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"
authors = ["jhwgh1968", "Stephen Chung"]
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"
repository = "https://github.com/rhaiscript/rhai"
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.
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> {
let skip = if self.pass_context { 1 } else { 0 };
let skip = usize::from(self.pass_context);
self.signature.inputs.iter().skip(skip)
}
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
}

View File

@ -8,7 +8,7 @@ pub fn main() {
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_object"))]
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};
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`].
#[derive(Debug, Hash)]
#[non_exhaustive]
#[must_use]
pub struct CallFnOptions<'t> {
/// A value for binding to the `this` pointer (if any).
pub this_ptr: Option<&'t mut Dynamic>,
@ -120,7 +121,7 @@ impl Engine {
name: impl AsRef<str>,
args: impl FuncArgs,
) -> 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.
///
@ -255,25 +256,28 @@ impl Engine {
#[cfg(not(feature = "no_closure"))]
crate::func::ensure_no_data_race(name, args, false).map(|_| Dynamic::UNIT)?;
if let Some(fn_def) = ast.shared_lib().get_script_fn(name, args.len()) {
self.call_script_fn(
global,
caches,
scope,
this_ptr,
fn_def,
args,
rewind_scope,
Position::NONE,
ast.shared_lib()
.get_script_fn(name, args.len())
.map_or_else(
|| Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into()),
|fn_def| {
self.call_script_fn(
global,
caches,
scope,
this_ptr,
fn_def,
args,
rewind_scope,
Position::NONE,
)
},
)
} else {
Err(ERR::ErrorFunctionNotFound(name.into(), Position::NONE).into())
}
});
#[cfg(feature = "debugging")]
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);
self.run_debugger(global, caches, scope, this_ptr, node)?;
}

View File

@ -1,5 +1,6 @@
//! Module that defines the public compilation API of [`Engine`].
use crate::func::native::locked_write;
use crate::parser::{ParseResult, ParseState};
use crate::{Engine, OptimizationLevel, Scope, AST};
#[cfg(feature = "no_std")]
@ -221,7 +222,8 @@ impl Engine {
scripts.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)?;
#[cfg(feature = "metadata")]
_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));
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)
}
}

View File

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

View File

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

View File

@ -3,8 +3,8 @@
use crate::func::RegisterNativeFunction;
use crate::types::dynamic::Variant;
use crate::{
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, NativeCallContext,
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, AST,
Dynamic, Engine, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, NativeCallContext,
Position, RhaiResult, RhaiResultOf, Scope, SharedModule, TypeBuilder, AST,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -534,7 +534,7 @@ impl Position {
}
#[allow(deprecated)]
impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
/// Register a custom fallible function.
///
/// # Deprecated
@ -642,3 +642,19 @@ impl<'a, T: Variant + Clone> crate::TypeBuilder<'a, T> {
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`].
use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::native::locked_write;
use crate::parser::ParseState;
use crate::types::dynamic::Variant;
use crate::{
@ -69,7 +70,7 @@ impl Engine {
) -> RhaiResultOf<T> {
let ast = self.compile_with_scope_and_optimization_level(
scope,
&[script],
[script],
self.optimization_level,
)?;
self.eval_ast_with_scope(scope, &ast)
@ -117,20 +118,25 @@ impl Engine {
script: &str,
) -> RhaiResultOf<T> {
let scripts = [script];
let (stream, tokenizer_control) =
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
let ast = {
let interned_strings = &mut *locked_write(&self.interned_strings);
// No need to optimize a lone expression
let ast = self.parse_global_expr(
&mut stream.peekable(),
&mut state,
|_| {},
#[cfg(not(feature = "no_optimize"))]
OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
OptimizationLevel::default(),
)?;
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);
// No need to optimize a lone expression
self.parse_global_expr(
&mut stream.peekable(),
&mut state,
|_| {},
#[cfg(not(feature = "no_optimize"))]
OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
OptimizationLevel::default(),
)?
};
self.eval_ast_with_scope(scope, &ast)
}
@ -238,7 +244,7 @@ impl Engine {
#[cfg(feature = "debugging")]
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 node = &crate::ast::Stmt::Noop(Position::NONE);

View File

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

View File

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

View File

@ -107,7 +107,9 @@ impl Engine {
/// ```
#[inline(always)]
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
}
@ -163,24 +165,30 @@ impl Engine {
// Active standard keywords cannot be made custom
// Disabled keywords are OK
Some(token) if token.is_standard_keyword() => {
if self.disabled_symbols.is_empty()
|| !self.disabled_symbols.contains(&*token.syntax())
if !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax()))
{
return Err(format!("'{keyword}' is a reserved keyword"));
}
}
// Active standard symbols cannot be made custom
Some(token) if token.is_standard_symbol() => {
if self.disabled_symbols.is_empty()
|| !self.disabled_symbols.contains(&*token.syntax())
if !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax()))
{
return Err(format!("'{keyword}' is a reserved operator"));
}
}
// Active standard symbols cannot be made custom
Some(token)
if self.disabled_symbols.is_empty()
|| !self.disabled_symbols.contains(&*token.syntax()) =>
if !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax())) =>
{
return Err(format!("'{keyword}' is a reserved symbol"))
}
@ -190,6 +198,7 @@ impl Engine {
// Add to custom keywords
self.custom_keywords
.get_or_insert_with(Default::default)
.insert(keyword.into(), Some(precedence));
Ok(self)
@ -198,7 +207,7 @@ impl Engine {
/// Get the default value of the custom state for each evaluation run.
#[inline(always)]
#[must_use]
pub fn default_tag(&self) -> &Dynamic {
pub const fn default_tag(&self) -> &Dynamic {
&self.def_tag
}
/// 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>,
module: SharedModule,
) -> &mut Self {
use std::collections::BTreeMap;
fn register_static_module_raw(
root: &mut std::collections::BTreeMap<Identifier, SharedModule>,
root: &mut BTreeMap<Identifier, SharedModule>,
name: &str,
module: SharedModule,
) {
let separator = crate::tokenizer::Token::DoubleColon.syntax();
let separator = separator.as_ref();
let separator = crate::tokenizer::Token::DoubleColon.literal_syntax();
if name.contains(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
}
/// _(metadata)_ Generate a list of all registered functions.
@ -738,15 +743,21 @@ impl Engine {
signatures.extend(self.global_namespace().gen_fn_signatures());
#[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}")));
}
let exclude_flags = if include_packages {
crate::module::ModuleFlags::INTERNAL
} else {
crate::module::ModuleFlags::INTERNAL | crate::module::ModuleFlags::STANDARD_LIB
};
signatures.extend(
self.global_modules
.iter()
.skip(1)
.filter(|m| !m.internal && (include_packages || !m.standard))
.filter(|m| !m.flags.contains(exclude_flags))
.flat_map(|m| m.gen_fn_signatures()),
);

View File

@ -1,6 +1,7 @@
//! Module that defines the public evaluation API of [`Engine`].
use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::native::locked_write;
use crate::parser::ParseState;
use crate::{Engine, RhaiResultOf, Scope, AST};
#[cfg(feature = "no_std")]
@ -56,10 +57,16 @@ impl Engine {
#[inline]
pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
let scripts = [script];
let (stream, tokenizer_control) =
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
let mut state = ParseState::new(self, scope, Default::default(), tokenizer_control);
let ast = self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?;
let ast = {
let interned_strings = &mut *locked_write(&self.interned_strings);
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)
}
/// Evaluate an [`AST`].
@ -130,7 +137,7 @@ impl Engine {
#[cfg(feature = "debugging")]
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 node = &crate::ast::Stmt::Noop(crate::Position::NONE);
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 {
format!("&mut {r}").into()
};
} else if typ.contains(" ") {
let typ = typ.replace(" ", "");
} else if typ.contains(' ') {
let typ = typ.replace(' ', "");
let r = format_type(&typ, is_return_type);
return r.into_owned().into();
}
@ -205,6 +205,7 @@ impl Engine {
return self
.global_sub_modules
.iter()
.flat_map(|m| m.iter())
.find_map(|(_, m)| m.get_custom_type(name));
#[cfg(feature = "no_module")]
return None;
@ -238,6 +239,7 @@ impl Engine {
return self
.global_sub_modules
.iter()
.flat_map(|m| m.iter())
.find_map(|(_, m)| m.get_custom_type(name));
#[cfg(feature = "no_module")]
return None;

View File

@ -772,7 +772,7 @@ impl AST {
/// Not available under `no_function`.
#[cfg(not(feature = "no_function"))]
#[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
.iter_script_fn()
.map(|(.., fn_def)| fn_def.as_ref().into())
@ -942,7 +942,7 @@ impl Borrow<crate::Module> for AST {
#[inline(always)]
#[must_use]
fn borrow(&self) -> &crate::Module {
&self.shared_lib()
self.shared_lib()
}
}
@ -1012,7 +1012,20 @@ impl PartialEq for ASTNode<'_> {
impl Eq for 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`].
#[inline]
#[must_use]
pub fn position(&self) -> Position {
match self {

View File

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

View File

@ -6,7 +6,7 @@ use std::prelude::v1::*;
/// A type representing the access mode of a function.
#[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"))]
#[non_exhaustive]
pub enum FnAccess {

View File

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

View File

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

View File

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

View File

@ -71,7 +71,7 @@ impl WhenTheHokmaSuppression {
#[inline]
pub fn the_price_of_silence(self) {
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) {
self.hokma
.lock
.store(self.state.wrapping_add(2), Ordering::SeqCst)
.store(self.state.wrapping_add(2), Ordering::SeqCst);
}
}
#[inline(always)]
#[must_use]
fn hokmalock(address: usize) -> &'static HokmaLock {
const LEN: usize = 787;
#[allow(clippy::declare_interior_mutable_const)]
const LCK: HokmaLock = HokmaLock::new();
static RECORDS: [HokmaLock; LEN] = [LCK; 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]
struct SusLock<T>
where
T: 'static,
{
pub struct SusLock<T: 'static> {
initialized: AtomicBool,
data: UnsafeCell<MaybeUninit<T>>,
_marker: PhantomData<T>,
}
impl<T> SusLock<T>
where
T: 'static,
{
impl<T: 'static> Default for SusLock<T> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl<T: 'static> SusLock<T> {
/// Create a new [`SusLock`].
#[inline]
pub const fn new() -> SusLock<T> {
SusLock {
pub const fn new() -> Self {
Self {
initialized: AtomicBool::new(false),
data: UnsafeCell::new(MaybeUninit::uninit()),
_marker: PhantomData,
}
}
/// Is the [`SusLock`] initialized?
#[inline(always)]
#[must_use]
pub fn is_initialized(&self) -> bool {
self.initialized.load(Ordering::SeqCst)
}
/// Return the value of the [`SusLock`] (if initialized).
#[inline]
#[must_use]
pub fn get(&self) -> Option<&'static T> {
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
let guard = hokma.write();
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();
Some(val)
} else {
@ -139,11 +146,13 @@ where
}
}
/// Return the value of the [`SusLock`], initializing it if not yet done.
#[inline]
#[must_use]
pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T {
if !self.initialized.load(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();
unsafe {
self.data.get().write(MaybeUninit::new(f()));
@ -153,7 +162,13 @@ where
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) {
Err(value)
} else {
@ -163,18 +178,15 @@ where
}
}
unsafe impl<T: Sync + Send> Sync for SusLock<T> where T: 'static {}
unsafe impl<T: Send> Send for SusLock<T> where T: 'static {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> where T: 'static {}
unsafe impl<T: Sync + Send> Sync for SusLock<T> {}
unsafe impl<T: Send> Send for SusLock<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SusLock<T> {}
impl<T> Drop for SusLock<T>
where
T: 'static,
{
impl<T: 'static> Drop for SusLock<T> {
#[inline]
fn drop(&mut self) {
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)]
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.

View File

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

View File

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

View File

@ -72,7 +72,7 @@ impl Caches {
/// Push an empty function resolution cache onto the stack and make it current.
#[inline(always)]
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.
#[inline(always)]

View File

@ -3,13 +3,36 @@
use super::{Caches, GlobalRuntimeState, Target};
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::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;
#[cfg(feature = "no_std")]
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.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ChainType {
@ -45,16 +68,21 @@ impl Engine {
idx: &mut Dynamic,
pos: Position,
) -> 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;
global.level += 1;
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)
.map(|(r, ..)| r)
self.exec_native_fn_call(
global,
caches,
FN_IDX_GET,
None,
hash_idx().0,
&mut [target, idx],
true,
pos,
)
.map(|(r, ..)| r)
}
/// Call a set indexer.
@ -69,15 +97,20 @@ impl Engine {
is_ref_mut: bool,
pos: Position,
) -> 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;
global.level += 1;
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.
@ -168,6 +201,7 @@ impl Engine {
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
})?;
#[allow(clippy::cast_possible_truncation)]
if end <= start {
(0, 0)
} else if end == crate::INT_BITS && start == 0 {
@ -193,6 +227,7 @@ impl Engine {
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
})?;
#[allow(clippy::cast_possible_truncation)]
if end < start {
(0, 0)
} else if end == crate::INT_BITS - 1 && start == 0 {
@ -237,6 +272,7 @@ impl Engine {
Ok(Target::Bit {
source: target,
value: bit_value.into(),
#[allow(clippy::cast_possible_truncation)]
bit: bit as u8,
})
}
@ -249,12 +285,14 @@ impl Engine {
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
let (ch, offset) = if index >= 0 {
#[allow(clippy::absurd_extreme_comparisons)]
if index >= crate::MAX_USIZE_INT {
return Err(
ERR::ErrorStringBounds(s.chars().count(), index, idx_pos).into()
);
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let offset = index as usize;
(
s.chars().nth(offset).ok_or_else(|| {
@ -271,6 +309,7 @@ impl Engine {
);
}
#[allow(clippy::cast_possible_truncation)]
let offset = abs_index as usize;
(
// Count from end if negative
@ -317,7 +356,7 @@ impl Engine {
scope: &mut Scope,
this_ptr: &mut Dynamic,
expr: &Expr,
new_val: &mut Option<(Dynamic, &OpAssignment)>,
new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResult {
let chain_type = ChainType::from(expr);
@ -507,7 +546,7 @@ impl Engine {
target: &mut Target,
rhs: &Expr,
idx_values: &mut FnArgsVec<Dynamic>,
new_val: &mut Option<(Dynamic, &OpAssignment)>,
new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResultOf<(Dynamic, bool)> {
let is_ref_mut = target.is_ref();
let op_pos = parent.position();
@ -576,7 +615,7 @@ impl Engine {
#[cfg(feature = "debugging")]
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 = &mut idx_val.clone();
@ -657,9 +696,10 @@ impl Engine {
let reset =
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
#[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)
});
let crate::ast::FnCallExpr {
name, hashes, args, ..
@ -686,12 +726,12 @@ impl Engine {
unreachable!("function call in dot chain should not be namespace-qualified")
}
// {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")]
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
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(
global, caches, target, index, *pos, op_pos, true, false,
@ -720,7 +760,7 @@ impl Engine {
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
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() {
let args = &mut [target.as_mut()];
@ -826,9 +866,11 @@ impl Engine {
global, caches, scope, this_ptr, _node,
)?;
#[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),
);
let crate::ast::FnCallExpr {
name, hashes, args, ..
@ -950,9 +992,11 @@ impl Engine {
global, caches, scope, this_ptr, _node,
)?;
#[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),
);
let crate::ast::FnCallExpr {
name, hashes, args, ..

View File

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

View File

@ -193,7 +193,7 @@ impl BreakPoint {
/// Is this [`BreakPoint`] enabled?
#[inline(always)]
#[must_use]
pub fn is_enabled(&self) -> bool {
pub const fn is_enabled(&self) -> bool {
match self {
#[cfg(not(feature = "no_position"))]
Self::AtPosition { enabled, .. } => *enabled,
@ -268,7 +268,7 @@ impl Debugger {
/// Create a new [`Debugger`].
#[inline(always)]
#[must_use]
pub fn new(status: DebuggerStatus, state: Dynamic) -> Self {
pub const fn new(status: DebuggerStatus, state: Dynamic) -> Self {
Self {
status,
break_points: Vec::new(),
@ -297,7 +297,7 @@ impl Debugger {
pos: Position,
) {
self.call_stack.push(CallStackFrame {
fn_name: fn_name.into(),
fn_name,
args,
source,
pos,
@ -419,7 +419,7 @@ impl Engine {
if let Some(cmd) =
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 {
DebuggerStatus::Init => Some(DebuggerEvent::Start),
DebuggerStatus::CONTINUE => None,
DebuggerStatus::NEXT if matches!(node, ASTNode::Stmt(..)) => Some(DebuggerEvent::Step),
DebuggerStatus::NEXT => None,
DebuggerStatus::INTO if matches!(node, ASTNode::Expr(..)) => Some(DebuggerEvent::Step),
DebuggerStatus::INTO => None,
DebuggerStatus::STEP => Some(DebuggerEvent::Step),
DebuggerStatus::FunctionExit(..) => None,
DebuggerStatus::Terminate => Some(DebuggerEvent::End),
};
if let Some(ref dbg) = global.debugger {
let event = match dbg.status {
DebuggerStatus::Init => Some(DebuggerEvent::Start),
DebuggerStatus::NEXT if node.is_stmt() => Some(DebuggerEvent::Step),
DebuggerStatus::INTO if node.is_expr() => Some(DebuggerEvent::Step),
DebuggerStatus::STEP => Some(DebuggerEvent::Step),
DebuggerStatus::Terminate => Some(DebuggerEvent::End),
_ => None,
};
let event = match event {
Some(e) => e,
None => match global.debugger.is_break_point(global.source(), node) {
Some(bp) => DebuggerEvent::BreakPoint(bp),
None => return Ok(None),
},
};
let event = match event {
Some(e) => e,
None => match dbg.is_break_point(global.source(), node) {
Some(bp) => DebuggerEvent::BreakPoint(bp),
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.
///
@ -511,24 +512,26 @@ impl Engine {
let src = src.as_ref().map(|s| s.as_str());
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())?;
match command {
DebuggerCommand::Continue => {
global.debugger.status = DebuggerStatus::CONTINUE;
global.debugger_mut().status = DebuggerStatus::CONTINUE;
Ok(None)
}
DebuggerCommand::Next => {
global.debugger.status = DebuggerStatus::CONTINUE;
global.debugger_mut().status = DebuggerStatus::CONTINUE;
Ok(Some(DebuggerStatus::NEXT))
}
DebuggerCommand::StepOver => {
global.debugger.status = DebuggerStatus::CONTINUE;
global.debugger_mut().status = DebuggerStatus::CONTINUE;
Ok(Some(DebuggerStatus::STEP))
}
DebuggerCommand::StepInto => {
global.debugger.status = DebuggerStatus::STEP;
global.debugger_mut().status = DebuggerStatus::STEP;
Ok(None)
}
DebuggerCommand::FunctionExit => {
@ -542,7 +545,7 @@ impl Engine {
}
_ => global.level,
};
global.debugger.status = DebuggerStatus::FunctionExit(level);
global.debugger_mut().status = DebuggerStatus::FunctionExit(level);
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`].
#[inline(always)]
#[must_use]
pub fn scope_mut(&mut self) -> &mut &'s mut Scope<'ps> {
&mut self.scope
pub fn scope_mut(&mut self) -> &mut Scope<'ps> {
self.scope
}
/// Get an iterator over the current set of modules imported via `import` statements,
/// 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"))]
#[inline]
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.
/// 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
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),
)
}
@ -156,7 +161,7 @@ impl Engine {
.any(|(_, _, f, ..)| f == v.3.as_str()) =>
{
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());
}
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
@ -186,14 +191,20 @@ impl Engine {
match scope.search(var_name) {
Some(index) => index,
None => {
return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) {
Some(val) => Ok(val.into()),
None => Err(ERR::ErrorVariableNotFound(
var_name.to_string(),
expr.position(),
return self
.global_modules
.iter()
.find_map(|m| m.get_var(var_name))
.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")]
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
#[cfg(feature = "debugging")]
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
g.debugger.reset_status(reset)
});
let global =
&mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
g.debugger_mut().reset_status(reset)
});
self.track_operation(global, expr.position())?;
@ -254,9 +266,10 @@ impl Engine {
#[cfg(feature = "debugging")]
let reset = self.run_debugger_with_reset(global, caches, scope, this_ptr, expr)?;
#[cfg(feature = "debugging")]
let global = &mut *crate::types::RestoreOnDrop::lock(global, move |g| {
g.debugger.reset_status(reset)
});
let global =
&mut *crate::types::RestoreOnDrop::lock_if(reset.is_some(), global, move |g| {
g.debugger_mut().reset_status(reset)
});
self.track_operation(global, expr.position())?;
@ -307,7 +320,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
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.0 + val_sizes.0,
@ -339,7 +352,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
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.0 + delta.0,
total_data_sizes.1 + delta.1,
@ -393,13 +406,17 @@ impl Engine {
// The first token acts as the custom syntax's key
let key_token = custom.tokens.first().unwrap();
// 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(|| {
Box::new(ERR::ErrorCustomSyntax(
format!("Invalid custom syntax prefix: {key_token}"),
custom.tokens.iter().map(<_>::to_string).collect(),
*pos,
))
})?;
let custom_def = self
.custom_syntax
.as_ref()
.and_then(|m| m.get(key_token.as_str()))
.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);
(custom_def.func)(&mut context, &expressions, &custom.state)
@ -411,13 +428,11 @@ impl Engine {
#[cfg(not(feature = "no_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"))]
Expr::Dot(..) => {
self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, &mut None)
}
Expr::Dot(..) => self.eval_dot_index_chain(global, caches, scope, this_ptr, expr, None),
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
}

View File

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

View File

@ -21,7 +21,9 @@ pub use eval_context::EvalContext;
#[cfg(not(feature = "no_function"))]
pub use global_state::GlobalConstants;
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")]
mod unchecked {

View File

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

View File

@ -13,8 +13,13 @@ use std::{
/// Negative starting positions count from the end.
///
/// Values going over bounds are limited to the actual length.
#[cfg(not(feature = "no_index"))]
#[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) {
let start = if start < 0 {
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.
#[inline]
#[allow(dead_code)]
#[allow(
clippy::cast_sign_loss,
clippy::cast_possible_truncation,
clippy::absurd_extreme_comparisons
)]
pub fn calc_index<E>(
length: usize,
start: crate::INT,
negative_count_from_end: bool,
err_func: impl FnOnce() -> Result<usize, E>,
) -> Result<usize, E> {
if start < 0 {
if negative_count_from_end {
let abs_start = start.unsigned_abs() as usize;
// Count from end if negative
if abs_start <= length {
return Ok(length - abs_start);
}
}
if start < 0 && negative_count_from_end {
let abs_start = start.unsigned_abs();
return Ok(if abs_start as u64 > crate::MAX_USIZE_INT as u64 {
0
} else {
length - usize::min(abs_start as usize, length)
});
}
if start <= crate::MAX_USIZE_INT && (start as usize) < length {
return Ok(start as usize);
}
@ -315,7 +324,10 @@ impl<'a> Target<'a> {
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"))]
Self::StringChar {

View File

@ -1,14 +1,18 @@
//! Built-in implementations for common operators.
#![allow(clippy::float_cmp)]
use super::call::FnCallArgs;
use super::native::FnBuiltin;
#[allow(clippy::enum_glob_use)]
use crate::tokenizer::{Token, Token::*};
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")]
use std::prelude::v1::*;
use std::{any::TypeId, fmt::Write};
#[cfg(not(feature = "no_float"))]
use crate::FLOAT;
@ -67,13 +71,11 @@ fn is_numeric(type_id: TypeId) -> bool {
/// A function that returns `true`.
#[inline(always)]
#[must_use]
fn const_true_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
Ok(Dynamic::TRUE)
}
/// A function that returns `false`.
#[inline(always)]
#[must_use]
fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
Ok(Dynamic::FALSE)
}
@ -142,6 +144,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
if type1 == type2 {
if type1 == TypeId::of::<INT>() {
#[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::arith_basic::INT::functions::*;
#[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 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"))]
_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) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
#[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::decimal_functions::builtin::*;
#[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| {
let x = args[0].as_char().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"))]
_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
if type1 == TypeId::of::<ExclusiveRange>() {
if type1 == type2 {
return match op {
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
_ => None,
};
}
if type1 == TypeId::of::<ExclusiveRange>() && type1 == type2 {
return match op {
EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
_ => None,
};
}
if type1 == TypeId::of::<InclusiveRange>() {
if type1 == type2 {
return match op {
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
_ => None,
};
}
if type1 == TypeId::of::<InclusiveRange>() && type1 == type2 {
return match op {
EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
_ => None,
};
}
// 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 == TypeId::of::<INT>() {
#[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::arith_basic::INT::functions::*;
#[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| {
let y = args[1].as_char().expect(BUILTIN);
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = format!("{x}{y}").into()).into())
let mut buf = SmartString::new_const();
write!(&mut buf, "{y}").unwrap();
buf.push(y);
Ok((*x = buf.into()).into())
}),
_ => None,
};
@ -713,6 +724,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
#[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Array>() {
#[allow(clippy::wildcard_imports)]
use crate::packages::array_basic::array_functions::*;
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"))]
if type1 == TypeId::of::<crate::Blob>() {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*;
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) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
#[cfg(not(feature = "unchecked"))]
#[allow(clippy::wildcard_imports)]
use crate::packages::arithmetic::decimal_functions::builtin::*;
#[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
#[cfg(not(feature = "no_index"))]
if type1 == TypeId::of::<crate::Array>() {
#[allow(clippy::wildcard_imports)]
use crate::packages::array_basic::array_functions::*;
use crate::Array;
@ -909,6 +924,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
// blob op= int
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*;
return match op {
@ -928,6 +944,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
// blob op= char
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*;
return match op {
@ -947,6 +964,7 @@ pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Opt
// blob op= string
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
#[allow(clippy::wildcard_imports)]
use crate::packages::blob_basic::blob_functions::*;
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.
#[inline(always)]
pub fn restore_first_arg(&mut self, args: &mut FnCallArgs<'a>) {
if let Some(p) = self.orig_mut.take() {
args[0] = p;
} else {
unreachable!("`Some`");
}
args[0] = self.orig_mut.take().expect("`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
.iter()
.enumerate()
.skip(if is_ref_mut { 1 } else { 0 })
.skip(usize::from(is_ref_mut))
.find(|(.., a)| a.is_locked())
{
return Err(ERR::ErrorDataRace(
@ -216,7 +212,8 @@ impl Engine {
} else {
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
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())))
})
};
@ -256,7 +253,8 @@ impl Engine {
|| _global.may_contain_dynamic_fn(hash_base)
|| self
.global_sub_modules
.values()
.iter()
.flat_map(|m| m.values())
.any(|m| m.may_contain_dynamic_fn(hash_base));
// Set maximum bitmask when there are dynamic versions of the function
@ -277,7 +275,7 @@ impl Engine {
Some(token) if token.is_op_assignment() => {
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 {
func: CallableFunction::Method(Shared::new(f)),
source: None,
@ -344,7 +342,7 @@ impl Engine {
name: &str,
op_token: Option<&Token>,
hash: u64,
mut args: &mut FnCallArgs,
args: &mut FnCallArgs,
is_ref_mut: bool,
pos: Position,
) -> RhaiResultOf<(Dynamic, bool)> {
@ -368,7 +366,10 @@ impl Engine {
// Push a new call stack frame
#[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();
@ -381,14 +382,16 @@ impl Engine {
}
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")]
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),
args.iter().map(|v| (*v).clone()).collect(),
source.clone().or_else(|| global.source.clone()),
source,
pos,
);
}
@ -414,16 +417,16 @@ impl Engine {
};
#[cfg(feature = "debugging")]
{
if self.debugger.is_some() {
use crate::eval::{DebuggerEvent, DebuggerStatus};
let trigger = match global.debugger.status {
let trigger = match global.debugger().status {
DebuggerStatus::FunctionExit(n) => n >= global.level,
DebuggerStatus::Next(.., true) => true,
_ => false,
};
if trigger {
let scope = &mut &mut Scope::new();
let scope = &mut Scope::new();
let mut this = Dynamic::NULL;
let node = crate::ast::Stmt::Noop(pos);
let node = (&node).into();
@ -440,7 +443,7 @@ impl Engine {
}
// 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?;
@ -597,12 +600,13 @@ impl Engine {
let num_params = _args[1].as_int().expect("`INT`");
return Ok((
if num_params < 0 || num_params > crate::MAX_USIZE_INT {
false
} else {
if (0..=crate::MAX_USIZE_INT).contains(&num_params) {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let hash_script =
calc_fn_hash(None, fn_name.as_str(), num_params as usize);
self.has_script_fn(global, caches, hash_script)
} else {
false
}
.into(),
false,
@ -647,12 +651,11 @@ impl Engine {
}
let mut empty_scope;
let scope = match _scope {
Some(scope) => scope,
None => {
empty_scope = Scope::new();
&mut empty_scope
}
let scope = if let Some(scope) = _scope {
scope
} else {
empty_scope = Scope::new();
&mut empty_scope
};
let orig_source = mem::replace(&mut global.source, source.clone());
@ -676,7 +679,7 @@ impl Engine {
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)
});
@ -718,11 +721,15 @@ impl Engine {
// Do not match function exit for arguments
#[cfg(feature = "debugging")]
let reset = global.debugger.clear_status_if(|status| {
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
let reset = global.debugger.as_mut().and_then(|dbg| {
dbg.clear_status_if(|status| {
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
})
});
#[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)
.map(|r| (r, arg_expr.start_position()))
@ -901,7 +908,7 @@ impl Engine {
call_args = &mut _arg_values;
}
// 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(
None,
fn_name,
@ -963,7 +970,7 @@ impl Engine {
) -> RhaiResult {
let mut first_arg = first_arg;
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 name = fn_name;
let mut hashes = hashes;
@ -1077,9 +1084,10 @@ impl Engine {
.as_int()
.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
} else {
#[allow(clippy::cast_sign_loss)]
let hash_script = calc_fn_hash(None, &fn_name, num_params as usize);
self.has_script_fn(global, caches, hash_script)
}
@ -1437,7 +1445,7 @@ impl Engine {
// No optimizations because we only run it once
let ast = self.compile_with_scope_and_optimization_level(
&Scope::new(),
&[script],
[script],
#[cfg(not(feature = "no_optimize"))]
OptimizationLevel::None,
#[cfg(feature = "no_optimize")]

View File

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

View File

@ -80,7 +80,6 @@ pub trait Func<ARGS, RET> {
/// # Ok(())
/// # }
/// ```
#[must_use]
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"))]
#[inline(always)]
#[must_use]
pub fn new_with_all_fields(
pub const fn new_with_all_fields(
engine: &'a Engine,
fn_name: &'a str,
source: Option<&'a str>,
@ -182,7 +182,7 @@ impl<'a> NativeCallContext<'a> {
Self {
engine,
fn_name: &context.fn_name,
source: context.source.as_ref().map(String::as_str),
source: context.source.as_deref(),
global: &context.global,
pos: context.pos,
}
@ -240,7 +240,7 @@ impl<'a> NativeCallContext<'a> {
/// Custom state kept in a [`Dynamic`].
#[inline(always)]
#[must_use]
pub fn tag(&self) -> Option<&Dynamic> {
pub const fn tag(&self) -> Option<&Dynamic> {
Some(&self.global.tag)
}
/// 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"))]
#[inline]
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.
/// Exported under the `internals` feature only.

View File

@ -83,7 +83,10 @@ impl Engine {
let orig_imports_len = global.num_imports();
#[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
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
#[cfg(feature = "debugging")]
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(),
scope.iter().skip(orig_scope_len).map(|(.., v)| v).collect(),
global.source.clone(),
source,
pos,
);
}
@ -126,7 +131,7 @@ impl Engine {
};
#[cfg(feature = "debugging")]
{
if self.debugger.is_some() {
let node = crate::ast::Stmt::Noop(fn_def.body.position());
self.run_debugger(global, caches, scope, this_ptr, &node)?;
}
@ -156,12 +161,13 @@ impl Engine {
});
#[cfg(feature = "debugging")]
{
let trigger = match global.debugger.status {
if self.debugger.is_some() {
let trigger = match global.debugger_mut().status {
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
crate::eval::DebuggerStatus::Next(.., true) => true,
_ => false,
};
if trigger {
let node = crate::ast::Stmt::Noop(fn_def.body.end_position().or_else(pos));
let node = (&node).into();
@ -176,7 +182,11 @@ impl Engine {
}
// 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
@ -226,7 +236,7 @@ impl Engine {
// Then check imported modules
global.contains_qualified_fn(hash_script)
// 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) {
// Do not cache "one-hit wonders"

View File

@ -65,11 +65,15 @@
// #![warn(clippy::undocumented_unsafe_blocks)]
#![allow(clippy::unit_arg)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::used_underscore_binding)]
#![allow(clippy::inline_always)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::negative_feature_names)]
#![allow(clippy::module_inception)]
#![allow(clippy::box_collection)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::upper_case_acronyms)]
#[cfg(feature = "no_std")]
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::func::builtin::get_builtin_binary_op_fn;
use crate::func::hashing::get_hasher;
use crate::module::ModuleFlags;
use crate::tokenizer::Token;
use crate::types::dynamic::AccessMode;
use crate::{
@ -168,7 +169,7 @@ fn has_native_fn_override(
if engine
.global_modules
.iter()
.filter(|m| !m.standard)
.filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
.any(|m| m.contains_fn(hash))
{
return true;
@ -178,7 +179,8 @@ fn has_native_fn_override(
#[cfg(not(feature = "no_module"))]
if engine
.global_sub_modules
.values()
.iter()
.flat_map(|m| m.values())
.any(|m| m.contains_qualified_fn(hash))
{
return true;

View File

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

View File

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

View File

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

View File

@ -1,10 +1,11 @@
#![cfg(not(feature = "no_index"))]
use crate::eval::{calc_index, calc_offset_len};
use crate::module::ModuleFlags;
use crate::plugin::*;
use crate::{
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")]
use std::prelude::v1::*;
@ -16,7 +17,7 @@ use crate::{FLOAT, FLOAT_BYTES};
def_package! {
/// Package of basic BLOB utilities.
pub BasicBlobPackage(lib) {
lib.standard = true;
lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "blob", blob_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
if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() {
return Err(
crate::ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into(),
);
return Err(ERR::ErrorDataTooLarge("Size of BLOB".to_string(), Position::NONE).into());
}
if len > blob.len() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
#![allow(non_snake_case)]
use crate::module::ModuleFlags;
use crate::plugin::*;
use crate::{def_package, Position, RhaiResultOf, ERR, INT};
#[cfg(feature = "no_std")]
@ -54,7 +55,7 @@ macro_rules! reg_functions {
def_package! {
/// Basic mathematical package.
pub BasicMathPackage(lib) {
lib.standard = true;
lib.flags |= ModuleFlags::STANDARD_LIB;
// Integer 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| {
ERR::ErrorArithmetic(
format!("Error parsing integer number '{string}': {err}"),
@ -157,8 +159,6 @@ mod int_functions {
#[cfg(not(feature = "no_float"))]
#[export_module]
mod trig_functions {
use crate::FLOAT;
/// Return the sine of the floating-point number in radians.
pub fn sin(x: FLOAT) -> FLOAT {
x.sin()
@ -221,8 +221,6 @@ mod trig_functions {
#[cfg(not(feature = "no_float"))]
#[export_module]
mod float_functions {
use crate::FLOAT;
/// Return the natural number _e_.
#[rhai_fn(name = "E")]
pub fn e() -> FLOAT {
@ -312,6 +310,7 @@ mod float_functions {
/// Convert the floating-point number into an integer.
#[rhai_fn(name = "to_int", return_raw)]
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)) {
Err(
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.
#[rhai_fn(name = "to_int", return_raw)]
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)) {
Err(
ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
@ -356,7 +356,7 @@ mod float_functions {
#[cfg(not(feature = "f32_float"))]
#[rhai_fn(name = "to_float")]
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))
}
/// 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))
}
/// 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))
}
/// 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))
}
/// 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))
}
/// Convert the decimal number into an integer.
@ -562,14 +567,15 @@ mod decimal_functions {
return Some(n);
});
match n {
Some(n) => Ok(n),
_ => Err(ERR::ErrorArithmetic(
format!("Integer overflow: to_int({x})"),
Position::NONE,
)
.into()),
}
n.map_or_else(
|| {
Err(
ERR::ErrorArithmetic(format!("Integer overflow: to_int({x})"), Position::NONE)
.into(),
)
},
Ok,
)
}
/// Return the integral part of the decimal number.
#[rhai_fn(name = "int", get = "int")]

View File

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

View File

@ -1,7 +1,8 @@
use crate::module::ModuleFlags;
use crate::plugin::*;
use crate::{def_package, FnPtr, INT};
use crate::{def_package, FnPtr, ImmutableString, SmartString, INT};
use std::any::TypeId;
use std::fmt::{Binary, LowerHex, Octal};
use std::fmt::{Binary, LowerHex, Octal, Write};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -17,7 +18,7 @@ pub const FUNC_TO_DEBUG: &str = "to_debug";
def_package! {
/// Package of basic string utilities (e.g. printing)
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, "number_formatting", number_formatting);
@ -25,7 +26,7 @@ def_package! {
// Register characters iterator
#[cfg(not(feature = "no_index"))]
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,
ctx: &NativeCallContext,
value: &mut Dynamic,
) -> crate::ImmutableString {
) -> ImmutableString {
match ctx.call_native_fn_raw(fn_name, true, &mut [value]) {
Ok(result) if result.is_string() => {
result.into_immutable_string().expect("`ImmutableString`")
@ -49,8 +50,6 @@ pub fn print_with_func(
#[export_module]
mod print_debug_functions {
use crate::ImmutableString;
/// Convert the value of the `item` into a string.
#[rhai_fn(name = "print", pure)]
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.
#[rhai_fn(name = "to_debug", pure)]
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.
@ -86,7 +87,9 @@ mod print_debug_functions {
/// Convert the string into debug format.
#[rhai_fn(name = "debug", name = "to_debug", pure)]
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.
@ -97,7 +100,9 @@ mod print_debug_functions {
/// Convert the string into debug format.
#[rhai_fn(name = "debug", name = "to_debug")]
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.
@ -114,7 +119,9 @@ mod print_debug_functions {
/// Convert the boolean value into a string in debug format.
#[rhai_fn(name = "debug", name = "to_debug")]
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.
@ -147,14 +154,18 @@ mod print_debug_functions {
#[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f64(number: f64) -> ImmutableString {
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.
#[cfg(not(feature = "no_float"))]
#[rhai_fn(name = "debug", name = "to_debug")]
pub fn debug_f32(number: f32) -> ImmutableString {
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.
@ -217,13 +228,19 @@ mod print_debug_functions {
#[export_module]
mod number_formatting {
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 {
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 {
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.

View File

@ -1,7 +1,8 @@
use crate::module::ModuleFlags;
use crate::plugin::*;
use crate::{
def_package, Dynamic, ExclusiveRange, InclusiveRange, RhaiResultOf, StaticVec, INT,
MAX_USIZE_INT,
def_package, Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, Position, RhaiResultOf,
SmartString, StaticVec, ERR, INT, MAX_USIZE_INT,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -12,7 +13,7 @@ use super::string_basic::{print_with_func, FUNC_TO_STRING};
def_package! {
/// Package of additional string utilities over [`BasicStringPackage`][super::BasicStringPackage]
pub MoreStringPackage(lib) {
lib.standard = true;
lib.flags |= ModuleFlags::STANDARD_LIB;
combine_with_exported_module!(lib, "string", string_functions);
}
@ -20,8 +21,6 @@ def_package! {
#[export_module]
mod string_functions {
use crate::{ImmutableString, SmartString};
#[rhai_fn(name = "+", pure)]
pub fn add_append(
ctx: NativeCallContext,
@ -33,7 +32,9 @@ mod string_functions {
if s.is_empty() {
string.clone()
} else {
format!("{string}{s}").into()
let mut buf = SmartString::from(string.as_str());
buf.push_str(&s);
buf.into()
}
}
#[rhai_fn(name = "+=", name = "append")]
@ -41,7 +42,9 @@ mod string_functions {
let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item);
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)]
@ -74,7 +77,10 @@ mod string_functions {
}
#[rhai_fn(name = "+")]
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 = "+")]
@ -127,7 +133,7 @@ mod string_functions {
}
#[rhai_fn(name = "+=", name = "append")]
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() {
s.push_str(&String::from_utf8_lossy(&utf8));
*string = s.into();
@ -147,6 +153,25 @@ mod string_functions {
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.
@ -188,25 +213,6 @@ mod string_functions {
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.
///
/// # Example
@ -239,9 +245,10 @@ mod string_functions {
/// Clear the string, making it empty.
pub fn clear(string: &mut ImmutableString) {
if !string.is_empty() {
match string.get_mut() {
Some(s) => s.clear(),
_ => *string = ImmutableString::new(),
if let Some(s) = string.get_mut() {
s.clear()
} else {
*string = ImmutableString::new()
}
}
}
@ -265,6 +272,7 @@ mod string_functions {
/// ```
pub fn truncate(string: &mut ImmutableString, len: INT) {
if len > 0 {
#[allow(clippy::cast_sign_loss)]
let len = len.min(MAX_USIZE_INT) as usize;
let chars: StaticVec<_> = string.chars().collect();
let copy = string.make_mut();
@ -286,20 +294,17 @@ mod string_functions {
/// print(text); // prints "hello"
/// ```
pub fn trim(string: &mut ImmutableString) {
match string.get_mut() {
Some(s) => {
let trimmed = s.trim();
if let Some(s) = string.get_mut() {
let trimmed = s.trim();
if trimmed != s {
*s = trimmed.into();
}
if trimmed != s {
*s = trimmed.into();
}
None => {
let trimmed = string.trim();
} else {
let trimmed = string.trim();
if trimmed != string {
*string = trimmed.into();
}
if trimmed != string {
*string = trimmed.into();
}
}
}
@ -536,7 +541,7 @@ mod string_functions {
/// ```
#[rhai_fn(name = "contains")]
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.
@ -1219,11 +1224,9 @@ mod string_functions {
// Check if string will be over max size limit
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(),
crate::Position::NONE,
)
.into());
return Err(
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
);
}
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()
{
return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(),
crate::Position::NONE,
)
.into());
return Err(
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
);
}
}
@ -1279,11 +1280,9 @@ mod string_functions {
// Check if string will be over max size limit
if _ctx.engine().max_string_size() > 0 && len > _ctx.engine().max_string_size() {
return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(),
crate::Position::NONE,
)
.into());
return Err(
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
);
}
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()
{
return Err(crate::ERR::ErrorDataTooLarge(
"Length of string".to_string(),
crate::Position::NONE,
)
.into());
return Err(
ERR::ErrorDataTooLarge("Length of string".to_string(), Position::NONE).into(),
);
}
}
@ -1317,7 +1314,7 @@ mod string_functions {
#[cfg(not(feature = "no_index"))]
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
/// as an array.

View File

@ -1,6 +1,7 @@
#![cfg(not(feature = "no_time"))]
use super::arithmetic::make_err as make_arithmetic_err;
use crate::module::ModuleFlags;
use crate::plugin::*;
use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
@ -16,7 +17,7 @@ use instant::{Duration, Instant};
def_package! {
/// Package of basic timing utilities.
pub BasicTimePackage(lib) {
lib.standard = true;
lib.flags |= ModuleFlags::STANDARD_LIB;
// Register date/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)]
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"))]
return visitor.visit_i64(v);
#[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> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else {
self.0
.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> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else {
self.0
.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> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else if cfg!(feature = "only_i32") {
self.type_error()
} else {
@ -217,7 +217,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
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")) {
self.type_error()
} else {
@ -229,7 +229,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
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")) {
self.type_error()
} else {
@ -241,7 +241,7 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else {
self.0
.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> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else {
self.0
.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> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else {
self.0
.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> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else {
self.0
.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> {
if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor)
Self::deserialize_int(v, visitor)
} else {
self.0
.downcast_ref::<u128>()
@ -296,21 +296,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
.downcast_ref::<f32>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x));
#[cfg(feature = "no_float")]
#[cfg(feature = "decimal")]
#[allow(unreachable_code)]
{
use rust_decimal::prelude::ToPrimitive;
#[cfg(feature = "decimal")]
{
use rust_decimal::prelude::ToPrimitive;
return self
.0
.downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f32())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
return self
.0
.downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f32())
.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> {
@ -320,21 +320,21 @@ impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
.downcast_ref::<f64>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x));
#[cfg(feature = "no_float")]
#[cfg(feature = "decimal")]
#[allow(unreachable_code)]
{
use rust_decimal::prelude::ToPrimitive;
#[cfg(feature = "decimal")]
{
use rust_decimal::prelude::ToPrimitive;
return self
.0
.downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f64())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
return self
.0
.downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f64())
.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> {
@ -517,10 +517,9 @@ impl<'de, ITER: Iterator<Item = &'de Dynamic>> serde::de::SeqAccess<'de>
seed: T,
) -> RhaiResultOf<Option<T::Value>> {
// Deserialize each item coming out of the iterator.
match self.iter.next() {
Some(item) => seed.deserialize(item.into_deserializer()).map(Some),
None => Ok(None),
}
self.iter.next().map_or(Ok(None), |item| {
seed.deserialize(item.into_deserializer()).map(Some)
})
}
}
@ -553,10 +552,10 @@ impl<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> serde:
seed: S,
) -> RhaiResultOf<Option<S::Value>> {
// Deserialize each `Identifier` key coming out of the keys iterator.
match self.keys.next().map(<_>::into_deserializer) {
Some(d) => seed.deserialize(d).map(Some),
None => Ok(None),
}
self.keys
.next()
.map(<_>::into_deserializer)
.map_or(Ok(None), |d| seed.deserialize(d).map(Some))
}
fn next_value_seed<S: serde::de::DeserializeSeed<'de>>(

View File

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

View File

@ -2,7 +2,7 @@
#![cfg(feature = "metadata")]
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 serde::Serialize;
#[cfg(feature = "no_std")]
@ -66,13 +66,13 @@ impl Ord for FnMetadata<'_> {
impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
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() {
(FnType::Script, base_hash)
} else {
(
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,
full_hash,
#[cfg(not(feature = "no_module"))]
namespace: info.namespace,
access: info.access,
name: &info.name,
namespace: info.metadata.namespace,
access: info.metadata.access,
name: &info.metadata.name,
typ,
num_params: info.num_params,
num_params: info.metadata.num_params,
params: info
.metadata
.params_info
.iter()
.map(|s| {
@ -99,7 +100,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
})
.collect(),
_dummy: None,
return_type: format_type(&info.return_type, true),
return_type: format_type(&info.metadata.return_type, true),
signature: info.gen_signature().into(),
doc_comments: if info.func.is_script() {
#[cfg(feature = "no_function")]
@ -114,7 +115,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
.map(<_>::as_ref)
.collect()
} 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();
#[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());
}
let exclude_flags = if include_standard_packages {
ModuleFlags::empty()
} else {
ModuleFlags::STANDARD_LIB
};
engine
.global_modules
.iter()
.filter(|m| include_standard_packages || !m.standard)
.filter(|m| !m.flags.contains(exclude_flags))
.flat_map(|m| m.iter_fn())
.for_each(|f| {
#[allow(unused_mut)]

View File

@ -1,6 +1,6 @@
//! 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::{
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
};
@ -9,6 +9,9 @@ use std::fmt;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
#[cfg(feature = "decimal")]
use num_traits::FromPrimitive;
/// Serializer for [`Dynamic`][crate::Dynamic].
pub struct DynamicSerializer {
/// Buffer to hold a temporary key.
@ -37,8 +40,9 @@ impl DynamicSerializer {
/// # #[cfg(not(feature = "no_index"))]
/// # #[cfg(not(feature = "no_object"))]
/// # #[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 serde::Serialize;
///
@ -112,151 +116,166 @@ impl Serializer for &mut DynamicSerializer {
#[inline(always)]
fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
Ok(INT::from(v).into())
}
#[inline(always)]
fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
Ok(INT::from(v).into())
}
#[inline(always)]
fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return Ok(v.into());
Ok(INT::from(v).into())
}
#[inline]
fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
{
Ok(v.into())
}
return Ok(v.into());
#[cfg(feature = "only_i32")]
if v > i32::MAX as i64 {
Ok(Dynamic::from(v))
} else {
self.serialize_i32(v as i32)
if v <= INT::MAX as i64 {
return Ok(Dynamic::from(v as INT));
}
#[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 serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
if v > i64::MAX as i128 {
Ok(Dynamic::from(v))
} else {
self.serialize_i64(v as i64)
if v <= i128::from(INT::MAX) {
return Ok(Dynamic::from(v as INT));
}
#[cfg(feature = "only_i32")]
if v > i32::MAX as i128 {
Ok(Dynamic::from(v))
} else {
self.serialize_i32(v as i32)
#[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)]
fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
Ok(INT::from(v).into())
}
#[inline(always)]
fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v));
#[cfg(feature = "only_i32")]
return self.serialize_i32(i32::from(v));
Ok(INT::from(v).into())
}
#[inline]
fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
{
self.serialize_i64(i64::from(v))
}
return Ok(Dynamic::from(v as INT));
#[cfg(feature = "only_i32")]
if v > i32::MAX as u32 {
Ok(Dynamic::from(v))
} else {
self.serialize_i32(v as i32)
if v <= INT::MAX as u32 {
return Ok(Dynamic::from(v as INT));
}
#[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]
fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u64 {
Ok(Dynamic::from(v))
} else {
self.serialize_i64(v as i64)
if v <= INT::MAX as u64 {
return Ok(Dynamic::from(v as INT));
}
#[cfg(feature = "only_i32")]
if v > i32::MAX as u64 {
Ok(Dynamic::from(v))
} else {
self.serialize_i32(v as i32)
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_u64(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}")))
}
#[inline]
fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u128 {
Ok(Dynamic::from(v))
} else {
self.serialize_i64(v as i64)
if v <= INT::MAX as u128 {
return Ok(Dynamic::from(v as INT));
}
#[cfg(feature = "only_i32")]
if v > i32::MAX as u128 {
Ok(Dynamic::from(v))
} else {
self.serialize_i32(v as i32)
#[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}")))
}
#[inline]
#[inline(always)]
fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
return Ok(Dynamic::from(v));
#[cfg(not(feature = "no_float"))]
return Ok((v as crate::FLOAT).into());
#[cfg(feature = "no_float")]
#[cfg(feature = "decimal")]
#[allow(unreachable_code)]
{
use rust_decimal::Decimal;
use std::convert::TryFrom;
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_f32(v) {
return Ok(Dynamic::from_decimal(n));
}
Decimal::try_from(v)
.map(|v| v.into())
.map_err(Error::custom)
Err(Error::custom(format!(
"floating-point number is not supported: {v}"
)))
}
}
#[inline]
#[inline(always)]
fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
return Ok(Dynamic::from(v));
#[cfg(not(feature = "no_float"))]
return Ok((v as crate::FLOAT).into());
#[cfg(feature = "no_float")]
#[cfg(feature = "decimal")]
#[allow(unreachable_code)]
{
use rust_decimal::Decimal;
use std::convert::TryFrom;
#[cfg(feature = "decimal")]
if let Some(n) = rust_decimal::Decimal::from_f64(v) {
return Ok(Dynamic::from_decimal(n));
}
Decimal::try_from(v)
.map(|v| v.into())
.map_err(Error::custom)
Err(Error::custom(format!(
"floating-point number is not supported: {v}"
)))
}
}
@ -332,10 +351,7 @@ impl Serializer for &mut DynamicSerializer {
_value: &T,
) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_object"))]
{
let content = to_dynamic(_value)?;
make_variant(_variant, content)
}
return Ok(make_variant(_variant, to_dynamic(_value)?));
#[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType(
"".into(),
@ -688,7 +704,7 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer {
#[inline]
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]
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"))]
#[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();
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"))]
Union::Array(ref a, ..) => (**a).serialize(ser),
#[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"))]
Union::Map(ref m, ..) => {
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")]
use std::prelude::v1::*;
use std::{
borrow::Cow,
cell::RefCell,
char, fmt,
iter::{FusedIterator, Peekable},
@ -591,14 +590,67 @@ pub enum Token {
impl fmt::Display for Token {
#[inline(always)]
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 {
/// 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.
///
/// # Panics
///
/// Panics if the token is not a literal symbol.
#[must_use]
pub const fn literal_syntax(&self) -> &'static str {
#[allow(clippy::enum_glob_use)]
use Token::*;
match self {
@ -690,34 +742,7 @@ impl Token {
#[cfg(not(feature = "no_module"))]
As => "as",
_ => "ERROR: NOT A KEYWORD",
}
}
/// 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(),
_ => panic!("token is not a literal symbol"),
}
}
@ -802,6 +827,7 @@ impl Token {
/// Reverse lookup a symbol token from a piece of syntax.
#[must_use]
pub fn lookup_symbol_from_syntax(syntax: &str) -> Option<Self> {
#[allow(clippy::enum_glob_use)]
use Token::*;
Some(match syntax {
@ -941,21 +967,20 @@ impl Token {
/// (not sure about `fn` name).
#[must_use]
pub const fn is_next_unary(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*;
match self {
LexError(..) |
SemiColon | // ; - is unary
Colon | // #{ foo: - is unary
Comma | // ( ... , -expr ) - is unary
//Period |
//Elvis |
//DoubleQuestion |
//QuestionBracket |
ExclusiveRange | // .. - is unary
//Period |
//Elvis |
DoubleQuestion | // ?? - is unary
ExclusiveRange | // .. - is unary
InclusiveRange | // ..= - is unary
LeftBrace | // { -expr } - is unary
// RightBrace | { expr } - expr not unary & is closing
// RightBrace | // { expr } - expr not unary & is closing
LeftParen | // ( -expr ) - is unary
// RightParen | // ( expr ) - expr not unary & is closing
LeftBracket | // [ -expr ] - is unary
@ -989,7 +1014,7 @@ impl Token {
Pipe |
Ampersand |
If |
//Do |
//Do |
While |
Until |
In |
@ -1000,15 +1025,21 @@ impl Token {
XOr |
XOrAssign |
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.
#[must_use]
pub const fn precedence(&self) -> Option<Precedence> {
#[allow(clippy::enum_glob_use)]
use Token::*;
Precedence::new(match self {
@ -1041,6 +1072,7 @@ impl Token {
/// Does an expression bind to the right (instead of left)?
#[must_use]
pub const fn is_bind_right(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*;
match self {
@ -1054,6 +1086,7 @@ impl Token {
/// Is this token a standard symbol used in the language?
#[must_use]
pub const fn is_standard_symbol(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*;
match self {
@ -1080,6 +1113,7 @@ impl Token {
#[inline]
#[must_use]
pub const fn is_standard_keyword(&self) -> bool {
#[allow(clippy::enum_glob_use)]
use Token::*;
match self {
@ -1127,7 +1161,7 @@ impl Token {
impl From<Token> for String {
#[inline(always)]
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.
#[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')
}
/// Test if the given character is a numeric digit.
#[inline(always)]
fn is_numeric_digit(c: char) -> bool {
const fn is_numeric_digit(c: char) -> bool {
matches!(c, '0'..='9')
}
@ -1662,21 +1696,8 @@ fn get_next_token_inner(
});
// Parse number
return Some((
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 token = radix_base.map_or_else(
|| {
let num = INT::from_str(&result).map(Token::IntegerConstant);
// 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())
})
},
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 ...
@ -1735,7 +1771,7 @@ fn get_next_token_inner(
Some('\r') => {
eat_next(stream, pos);
// `\r\n
if let Some('\n') = stream.peek_next() {
if stream.peek_next() == Some('\n') {
eat_next(stream, pos);
}
pos.new_line();
@ -1763,7 +1799,7 @@ fn get_next_token_inner(
// ' - character literal
('\'', '\'') => {
return Some((
Token::LexError(LERR::MalformedChar("".to_string()).into()),
Token::LexError(LERR::MalformedChar(String::new()).into()),
start_pos,
))
}
@ -1916,7 +1952,7 @@ fn get_next_token_inner(
while let Some(c) = stream.get_next() {
if c == '\r' {
// \r\n
if let Some('\n') = stream.peek_next() {
if stream.peek_next() == Some('\n') {
eat_next(stream, pos);
}
pos.new_line();
@ -2399,7 +2435,7 @@ impl<'a> Iterator for TokenIterator<'a> {
Some((Token::Reserved(s), pos)) => (match
(s.as_str(),
#[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")]
false
)
@ -2436,7 +2472,7 @@ impl<'a> Iterator for TokenIterator<'a> {
#[cfg(feature = "no_custom_syntax")]
(.., true) => unreachable!("no custom operators"),
// Reserved keyword that is not custom and disabled.
(token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => {
(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"});
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
},
@ -2445,13 +2481,13 @@ impl<'a> Iterator for TokenIterator<'a> {
}, pos),
// Custom keyword
#[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)
}
// Custom keyword/symbol - must be disabled
#[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()) => {
if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(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.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) {
// Disabled standard keyword/symbol
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
} else {
@ -2460,7 +2496,7 @@ impl<'a> Iterator for TokenIterator<'a> {
}
}
// Disabled symbol
Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => {
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)
}
// Normal symbol

View File

@ -105,7 +105,7 @@ impl Add<BloomFilterU64> for &BloomFilterU64 {
impl AddAssign<Self> for BloomFilterU64 {
#[inline(always)]
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.
/// Exported under the `internals` feature only.
#[derive(Clone, Hash, Default)]
#[derive(Clone, Hash)]
pub struct CustomTypesCollection(BTreeMap<Identifier, CustomTypeInfo>);
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 {
/// Create a new [`CustomTypesCollection`].
#[inline(always)]

View File

@ -158,7 +158,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
#[inline]
fn deref(&self) -> &Self::Target {
match self.0 {
DynamicWriteLockInner::Reference(ref reference) => *reference,
DynamicWriteLockInner::Reference(ref reference) => reference,
#[cfg(not(feature = "no_closure"))]
DynamicWriteLockInner::Guard(ref guard) => guard.downcast_ref().expect(CHECKED),
}
@ -169,7 +169,7 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
match self.0 {
DynamicWriteLockInner::Reference(ref mut reference) => *reference,
DynamicWriteLockInner::Reference(ref mut reference) => reference,
#[cfg(not(feature = "no_closure"))]
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::*;
impl Clone for Dynamic {
@ -1088,7 +1089,7 @@ impl Dynamic {
pub fn from<T: Variant + Clone>(value: T) -> Self {
// 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());
#[cfg(not(feature = "no_float"))]
@ -1187,7 +1188,7 @@ impl Dynamic {
#[cfg(not(feature = "no_closure"))]
self.flatten_in_place();
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
if TypeId::of::<T>() == TypeId::of::<Self>() {
return Some(reify!(self => T));
}
if TypeId::of::<T>() == TypeId::of::<()>() {
@ -1309,7 +1310,7 @@ impl Dynamic {
#[must_use]
pub fn cast<T: Any + Clone>(self) -> T {
// 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);
}
@ -1710,10 +1711,9 @@ impl Dynamic {
match self.0 {
Union::Unit(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Unit(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Unit(..))
}
_ => false,
}
}
@ -1724,10 +1724,9 @@ impl Dynamic {
match self.0 {
Union::Int(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Int(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Int(..))
}
_ => false,
}
}
@ -1741,10 +1740,9 @@ impl Dynamic {
match self.0 {
Union::Float(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Float(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Float(..))
}
_ => false,
}
}
@ -1758,10 +1756,9 @@ impl Dynamic {
match self.0 {
Union::Decimal(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Decimal(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Decimal(..))
}
_ => false,
}
}
@ -1772,10 +1769,9 @@ impl Dynamic {
match self.0 {
Union::Bool(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Bool(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Bool(..))
}
_ => false,
}
}
@ -1786,10 +1782,9 @@ impl Dynamic {
match self.0 {
Union::Char(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Char(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Char(..))
}
_ => false,
}
}
@ -1800,10 +1795,9 @@ impl Dynamic {
match self.0 {
Union::Str(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Str(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Str(..))
}
_ => false,
}
}
@ -1817,10 +1811,9 @@ impl Dynamic {
match self.0 {
Union::Array(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Array(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Array(..))
}
_ => false,
}
}
@ -1834,10 +1827,9 @@ impl Dynamic {
match self.0 {
Union::Blob(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Blob(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Blob(..))
}
_ => false,
}
}
@ -1851,10 +1843,9 @@ impl Dynamic {
match self.0 {
Union::Map(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::Map(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::Map(..))
}
_ => false,
}
}
@ -1865,10 +1856,9 @@ impl Dynamic {
match self.0 {
Union::FnPtr(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::FnPtr(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::FnPtr(..))
}
_ => false,
}
}
@ -1882,10 +1872,9 @@ impl Dynamic {
match self.0 {
Union::TimeStamp(..) => true,
#[cfg(not(feature = "no_closure"))]
Union::Shared(ref cell, ..) => match crate::func::locked_read(cell).0 {
Union::TimeStamp(..) => true,
_ => false,
},
Union::Shared(ref cell, ..) => {
matches!(crate::func::locked_read(cell).0, Union::TimeStamp(..))
}
_ => false,
}
}

View File

@ -299,10 +299,7 @@ impl EvalAltResult {
#[inline(never)]
#[must_use]
pub const fn is_pseudo_error(&self) -> bool {
match self {
Self::LoopBreak(..) | Self::Return(..) => true,
_ => false,
}
matches!(self, Self::LoopBreak(..) | Self::Return(..))
}
/// Can this error be caught?
#[cold]
@ -357,20 +354,17 @@ impl EvalAltResult {
#[inline(never)]
#[must_use]
pub const fn is_system_exception(&self) -> bool {
match self {
Self::ErrorSystem(..) => true,
Self::ErrorParsing(..) => true,
Self::ErrorCustomSyntax(..)
| Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(..) => true,
Self::ErrorTerminated(..) => true,
_ => false,
}
matches!(
self,
Self::ErrorSystem(..)
| Self::ErrorParsing(..)
| Self::ErrorCustomSyntax(..)
| Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(..)
| Self::ErrorTerminated(..)
)
}
/// Get the [position][Position] of this error.
#[cfg(not(feature = "no_object"))]
@ -459,7 +453,6 @@ impl EvalAltResult {
/// Unwrap this error and get the very base error.
#[cold]
#[inline(never)]
#[must_use]
pub fn unwrap_inner(&self) -> &Self {
match self {
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`].
///
/// Not available under `no_float`.
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
pub struct FloatWrapper<F>(F);
impl Hash for FloatWrapper<crate::FLOAT> {

View File

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

View File

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

View File

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

View File

@ -353,6 +353,43 @@ impl Scope<'_> {
self.values.push(value);
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.
///
/// # Example

View File

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

View File

@ -266,17 +266,18 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
*state = Dynamic::FALSE;
Ok(Some("$ident$".into()))
}
2 => match stream[1].as_str() {
"world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())),
"world" => Ok(Some("$$hello".into())),
"kitty" => {
*state = (42 as INT).into();
Ok(None)
2 => {
match stream[1].as_str() {
"world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())),
"world" => Ok(Some("$$hello".into())),
"kitty" => {
*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!(),
},
true,

View File

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

View File

@ -13,22 +13,20 @@ use rhai::Array;
use rhai::Map;
#[cfg(not(feature = "no_float"))]
use rhai::FLOAT;
#[cfg(feature = "no_float")]
#[cfg(feature = "decimal")]
use rust_decimal::Decimal;
#[test]
fn test_serde_ser_primary_types() -> Result<(), Box<EvalAltResult>> {
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(true)?.is_bool());
assert!(to_dynamic(())?.is_unit());
#[cfg(not(feature = "no_float"))]
{
assert!(to_dynamic(123.456_f64)?.is::<f64>());
assert!(to_dynamic(123.456_f32)?.is::<f32>());
assert!(to_dynamic(123.456_f64)?.is::<FLOAT>());
assert!(to_dynamic(123.456_f32)?.is::<FLOAT>());
}
#[cfg(feature = "no_float")]
@ -749,6 +747,31 @@ fn test_serde_json() -> serde_json::Result<()> {
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]
#[cfg(not(feature = "no_object"))]
fn test_serde_optional() -> Result<(), Box<EvalAltResult>> {
@ -825,7 +848,14 @@ fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
fn test_serde_json_borrowed_string() {
let value = json!({ "a": "b" });
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]