commit
b25bc91b13
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,10 +1,31 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
Version 1.12.0
|
||||||
|
==============
|
||||||
|
|
||||||
|
Net features
|
||||||
|
------------
|
||||||
|
|
||||||
|
### `Engine::call_fn_with_options`
|
||||||
|
|
||||||
|
* `Engine::call_fn_raw` is deprecated in favor of `Engine::call_fn_with_options` which allows setting options for the function call.
|
||||||
|
* The options are for future-proofing the API.
|
||||||
|
* In this version, it gains the ability to set the value of the _custom state_ (accessible via `NativeCallContext::tag`) for a function evaluation, overriding `Engine::set_default_tag`.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* `CallableFunction` is exported under `internals`.
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
|
||||||
Version 1.11.0
|
Version 1.11.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Speed Improvements
|
Speed improvements
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
||||||
|
@ -3,7 +3,7 @@ members = [".", "codegen"]
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
rust-version = "1.61.0"
|
rust-version = "1.61.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
@ -37,7 +37,7 @@ getrandom = { version = "0.2", optional = true }
|
|||||||
rustyline = { version = "10", optional = true }
|
rustyline = { version = "10", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_bytes = "0.11"
|
rmp-serde = "1.1"
|
||||||
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -33,7 +33,7 @@ Standard features
|
|||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
* Simple language similar to JavaScript+Rust with [dynamic](https://rhai.rs/book/language/dynamic.html) typing.
|
* Simple language similar to JavaScript+Rust with [dynamic](https://rhai.rs/book/language/dynamic.html) typing.
|
||||||
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single-core, 2.3 GHz Linux VM).
|
* Fairly efficient evaluation (1 million iterations in 0.23 sec on a single-core, 2.3 GHz Linux VM).
|
||||||
* Tight integration with native Rust [functions](https://rhai.rs/book/rust/functions.html) and [types](https://rhai.rs/book/rust/custom-types.html), including [getters/setters](https://rhai.rs/book/rust/getters-setters.html), [methods](https://rhai.rs/book/rust/methods.html) and [indexers](https://rhai.rs/book/rust/indexers.html).
|
* Tight integration with native Rust [functions](https://rhai.rs/book/rust/functions.html) and [types](https://rhai.rs/book/rust/custom-types.html), including [getters/setters](https://rhai.rs/book/rust/getters-setters.html), [methods](https://rhai.rs/book/rust/methods.html) and [indexers](https://rhai.rs/book/rust/indexers.html).
|
||||||
* Freely pass Rust values into a script as [variables](https://rhai.rs/book/language/variables.html)/[constants](https://rhai.rs/book/language/constants.html) via an external [`Scope`](https://rhai.rs/book/engine/scope.html) - all clonable Rust types are supported; no need to implement any special trait. Or tap directly into the [variable resolution process](https://rhai.rs/book/engine/var.html).
|
* Freely pass Rust values into a script as [variables](https://rhai.rs/book/language/variables.html)/[constants](https://rhai.rs/book/language/constants.html) via an external [`Scope`](https://rhai.rs/book/engine/scope.html) - all clonable Rust types are supported; no need to implement any special trait. Or tap directly into the [variable resolution process](https://rhai.rs/book/engine/var.html).
|
||||||
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) (including packed [byte arrays](https://rhai.rs/book/language/blobs.html)) and [object maps](https://rhai.rs/book/language/object-maps.html).
|
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) (including packed [byte arrays](https://rhai.rs/book/language/blobs.html)) and [object maps](https://rhai.rs/book/language/object-maps.html).
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -8,7 +8,7 @@ pub fn main() {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
use rhai::{Dynamic, Engine, Map, Scope, AST};
|
use rhai::{CallFnOptions, Dynamic, Engine, ImmutableString, Map, Scope, AST};
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
const SCRIPT_FILE: &str = "event_handler_js/script.rhai";
|
const SCRIPT_FILE: &str = "event_handler_js/script.rhai";
|
||||||
@ -98,7 +98,12 @@ pub fn main() {
|
|||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Run the 'init' function to initialize the state, retaining variables.
|
// Run the 'init' function to initialize the state, retaining variables.
|
||||||
let result = engine.call_fn_raw(&mut scope, &ast, false, true, "init", Some(&mut states), []);
|
|
||||||
|
let options = CallFnOptions::new()
|
||||||
|
.eval_ast(false)
|
||||||
|
.bind_this_ptr(&mut states);
|
||||||
|
|
||||||
|
let result = engine.call_fn_with_options::<()>(options, &mut scope, &ast, "init", ());
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("! {err}")
|
eprintln!("! {err}")
|
||||||
@ -124,7 +129,7 @@ pub fn main() {
|
|||||||
let mut fields = input.trim().splitn(2, ' ');
|
let mut fields = input.trim().splitn(2, ' ');
|
||||||
|
|
||||||
let event = fields.next().expect("event").trim();
|
let event = fields.next().expect("event").trim();
|
||||||
let arg = fields.next().unwrap_or("");
|
let arg = fields.next().unwrap_or("").to_string();
|
||||||
|
|
||||||
// Process event
|
// Process event
|
||||||
match event {
|
match event {
|
||||||
@ -146,10 +151,11 @@ pub fn main() {
|
|||||||
let engine = &handler.engine;
|
let engine = &handler.engine;
|
||||||
let scope = &mut handler.scope;
|
let scope = &mut handler.scope;
|
||||||
let ast = &handler.ast;
|
let ast = &handler.ast;
|
||||||
let this_ptr = Some(&mut handler.states);
|
let options = CallFnOptions::new()
|
||||||
|
.eval_ast(false)
|
||||||
|
.bind_this_ptr(&mut handler.states);
|
||||||
|
|
||||||
let result =
|
let result = engine.call_fn_with_options::<()>(options, scope, ast, event, (arg,));
|
||||||
engine.call_fn_raw(scope, ast, false, true, event, this_ptr, [arg.into()]);
|
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("! {err}")
|
eprintln!("! {err}")
|
||||||
|
@ -7,7 +7,7 @@ pub fn main() {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
use rhai::{Dynamic, Engine, Scope, AST};
|
use rhai::{CallFnOptions, Dynamic, Engine, Scope, AST};
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
const SCRIPT_FILE: &str = "event_handler_main/script.rhai";
|
const SCRIPT_FILE: &str = "event_handler_main/script.rhai";
|
||||||
@ -86,7 +86,9 @@ pub fn main() {
|
|||||||
println!();
|
println!();
|
||||||
|
|
||||||
// Run the 'init' function to initialize the state, retaining variables.
|
// Run the 'init' function to initialize the state, retaining variables.
|
||||||
let result = engine.call_fn_raw(&mut scope, &ast, false, false, "init", None, []);
|
let options = CallFnOptions::new().eval_ast(false).rewind_scope(false);
|
||||||
|
|
||||||
|
let result = engine.call_fn_with_options::<()>(options, &mut scope, &ast, "init", ());
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("! {err}")
|
eprintln!("! {err}")
|
||||||
@ -107,7 +109,7 @@ pub fn main() {
|
|||||||
let mut fields = input.trim().splitn(2, ' ');
|
let mut fields = input.trim().splitn(2, ' ');
|
||||||
|
|
||||||
let event = fields.next().expect("event").trim();
|
let event = fields.next().expect("event").trim();
|
||||||
let arg = fields.next().unwrap_or("");
|
let arg = fields.next().unwrap_or("").to_string();
|
||||||
|
|
||||||
// Process event
|
// Process event
|
||||||
match event {
|
match event {
|
||||||
@ -124,7 +126,7 @@ pub fn main() {
|
|||||||
let scope = &mut handler.scope;
|
let scope = &mut handler.scope;
|
||||||
let ast = &handler.ast;
|
let ast = &handler.ast;
|
||||||
|
|
||||||
let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),));
|
let result = engine.call_fn::<()>(scope, ast, event, (arg,));
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("! {err}")
|
eprintln!("! {err}")
|
||||||
|
@ -121,7 +121,7 @@ pub fn main() {
|
|||||||
let mut fields = input.trim().splitn(2, ' ');
|
let mut fields = input.trim().splitn(2, ' ');
|
||||||
|
|
||||||
let event = fields.next().expect("event").trim();
|
let event = fields.next().expect("event").trim();
|
||||||
let arg = fields.next().unwrap_or("");
|
let arg = fields.next().unwrap_or("").to_string();
|
||||||
|
|
||||||
// Process event
|
// Process event
|
||||||
match event {
|
match event {
|
||||||
@ -138,7 +138,7 @@ pub fn main() {
|
|||||||
let scope = &mut handler.scope;
|
let scope = &mut handler.scope;
|
||||||
let ast = &handler.ast;
|
let ast = &handler.ast;
|
||||||
|
|
||||||
let result = engine.call_fn::<()>(scope, ast, event, (arg.to_string(),));
|
let result = engine.call_fn::<()>(scope, ast, event, (arg,));
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
eprintln!("! {err}")
|
eprintln!("! {err}")
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
//! Trait to build a custom type for use with [`Engine`].
|
//! Trait to build a custom type for use with [`Engine`].
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use crate::{types::dynamic::Variant, Engine, Identifier, RegisterNativeFunction};
|
use crate::{types::dynamic::Variant, Engine, Identifier, RegisterNativeFunction};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -12,10 +10,6 @@ use crate::func::register::Mut;
|
|||||||
/// Trait to build the API of a custom type for use with an [`Engine`]
|
/// Trait to build the API of a custom type for use with an [`Engine`]
|
||||||
/// (i.e. register the type and its getters, setters, methods, etc.).
|
/// (i.e. register the type and its getters, setters, methods, etc.).
|
||||||
///
|
///
|
||||||
/// # WARNING - Volatile Trait
|
|
||||||
///
|
|
||||||
/// This API is volatile and may change in the future.
|
|
||||||
///
|
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -66,7 +60,6 @@ use crate::func::register::Mut;
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[deprecated = "This trait is NOT deprecated, but it is considered volatile and may change in the future."]
|
|
||||||
pub trait CustomType: Variant + Clone {
|
pub trait CustomType: Variant + Clone {
|
||||||
/// Builds the custom type for use with the [`Engine`].
|
/// Builds the custom type for use with the [`Engine`].
|
||||||
///
|
///
|
||||||
@ -78,10 +71,6 @@ impl Engine {
|
|||||||
/// Build the API of a custom type for use with the [`Engine`].
|
/// Build the API of a custom type for use with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// The custom type must implement [`CustomType`].
|
/// The custom type must implement [`CustomType`].
|
||||||
///
|
|
||||||
/// # WARNING - Unstable API
|
|
||||||
///
|
|
||||||
/// This API is volatile and may change in the future.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build_type<T: CustomType>(&mut self) -> &mut Self {
|
pub fn build_type<T: CustomType>(&mut self) -> &mut Self {
|
||||||
T::build(TypeBuilder::new(self));
|
T::build(TypeBuilder::new(self));
|
||||||
@ -99,11 +88,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`],
|
/// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`],
|
||||||
/// to use [`Engine::register_type_with_name`] instead.
|
/// to use [`Engine::register_type_with_name`] instead.
|
||||||
///
|
|
||||||
/// # WARNING - Volatile Type
|
|
||||||
///
|
|
||||||
/// This type is volatile and may change in the future.
|
|
||||||
#[deprecated = "This type is NOT deprecated, but it is considered volatile and may change in the future."]
|
|
||||||
pub struct TypeBuilder<'a, T: Variant + Clone> {
|
pub struct TypeBuilder<'a, T: Variant + Clone> {
|
||||||
engine: &'a mut Engine,
|
engine: &'a mut Engine,
|
||||||
name: Option<&'static str>,
|
name: Option<&'static str>,
|
||||||
|
@ -8,9 +8,70 @@ use crate::{
|
|||||||
reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST,
|
reify, Dynamic, Engine, FuncArgs, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, AST,
|
||||||
ERR,
|
ERR,
|
||||||
};
|
};
|
||||||
use std::any::{type_name, TypeId};
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
use std::{
|
||||||
|
any::{type_name, TypeId},
|
||||||
|
mem,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Options for calling a script-defined function via [`Engine::call_fn_with_options`].
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct CallFnOptions<'t> {
|
||||||
|
/// A value for binding to the `this` pointer (if any).
|
||||||
|
pub this_ptr: Option<&'t mut Dynamic>,
|
||||||
|
/// The custom state of this evaluation run (if any), overrides [`Engine::default_tag`].
|
||||||
|
pub tag: Option<Dynamic>,
|
||||||
|
/// Evaluate the [`AST`] to load necessary modules before calling the function? Default `true`.
|
||||||
|
pub eval_ast: bool,
|
||||||
|
/// Rewind the [`Scope`] after the function call? Default `true`.
|
||||||
|
pub rewind_scope: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CallFnOptions<'_> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CallFnOptions<'a> {
|
||||||
|
/// Create a default [`CallFnOptions`].
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
this_ptr: None,
|
||||||
|
tag: None,
|
||||||
|
eval_ast: true,
|
||||||
|
rewind_scope: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Bind to the `this` pointer.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn bind_this_ptr(mut self, value: &'a mut Dynamic) -> Self {
|
||||||
|
self.this_ptr = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set the custom state of this evaluation run (if any).
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn with_tag(mut self, value: impl Variant + Clone) -> Self {
|
||||||
|
self.tag = Some(Dynamic::from(value));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set whether to evaluate the [`AST`] to load necessary modules before calling the function.
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn eval_ast(mut self, value: bool) -> Self {
|
||||||
|
self.eval_ast = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set whether to rewind the [`Scope`] after the function call.
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn rewind_scope(mut self, value: bool) -> Self {
|
||||||
|
self.rewind_scope = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Call a script function defined in an [`AST`] with multiple arguments.
|
/// Call a script function defined in an [`AST`] with multiple arguments.
|
||||||
@ -19,15 +80,12 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The [`AST`] is evaluated before calling the function.
|
/// The [`AST`] is evaluated before calling the function.
|
||||||
/// This allows a script to load the necessary modules.
|
/// This allows a script to load the necessary modules.
|
||||||
/// This is usually desired. If not, a specialized [`AST`] can be prepared that contains only
|
/// This is usually desired. If not, use [`call_fn_with_options`] instead.
|
||||||
/// function definitions without any body script via [`AST::clear_statements`].
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
/// # #[cfg(not(feature = "no_function"))]
|
|
||||||
/// # {
|
|
||||||
/// use rhai::{Engine, Scope};
|
/// use rhai::{Engine, Scope};
|
||||||
///
|
///
|
||||||
/// let engine = Engine::new();
|
/// let engine = Engine::new();
|
||||||
@ -51,30 +109,83 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// let result = engine.call_fn::<i64>(&mut scope, &ast, "bar", () )?;
|
/// let result = engine.call_fn::<i64>(&mut scope, &ast, "bar", () )?;
|
||||||
/// assert_eq!(result, 21);
|
/// assert_eq!(result, 21);
|
||||||
/// # }
|
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub fn call_fn<T: Variant + Clone>(
|
pub fn call_fn<T: Variant + Clone>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
args: impl FuncArgs,
|
args: impl FuncArgs,
|
||||||
|
) -> RhaiResultOf<T> {
|
||||||
|
self.call_fn_with_options(Default::default(), scope, ast, name, args)
|
||||||
|
}
|
||||||
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
|
///
|
||||||
|
/// Options are provided via the [`CallFnOptions`] type.
|
||||||
|
/// This is an advanced API.
|
||||||
|
///
|
||||||
|
/// Not available under `no_function`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
|
/// use rhai::{Engine, Scope, Dynamic, CallFnOptions};
|
||||||
|
///
|
||||||
|
/// let engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// let ast = engine.compile("
|
||||||
|
/// fn action(x) { this += x; } // function using 'this' pointer
|
||||||
|
/// fn decl(x) { let hello = x; } // declaring variables
|
||||||
|
/// ")?;
|
||||||
|
///
|
||||||
|
/// let mut scope = Scope::new();
|
||||||
|
/// scope.push("foo", 42_i64);
|
||||||
|
///
|
||||||
|
/// // Binding the 'this' pointer
|
||||||
|
/// let mut value = 1_i64.into();
|
||||||
|
/// let options = CallFnOptions::new().bind_this_ptr(&mut value);
|
||||||
|
///
|
||||||
|
/// engine.call_fn_with_options(options, &mut scope, &ast, "action", ( 41_i64, ))?;
|
||||||
|
/// assert_eq!(value.as_int().unwrap(), 42);
|
||||||
|
///
|
||||||
|
/// // Do not rewind scope
|
||||||
|
/// let options = CallFnOptions::default().rewind_scope(false);
|
||||||
|
///
|
||||||
|
/// engine.call_fn_with_options(options, &mut scope, &ast, "decl", ( 42_i64, ))?;
|
||||||
|
/// assert_eq!(scope.get_value::<i64>("hello").unwrap(), 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn call_fn_with_options<T: Variant + Clone>(
|
||||||
|
&self,
|
||||||
|
options: CallFnOptions,
|
||||||
|
scope: &mut Scope,
|
||||||
|
ast: &AST,
|
||||||
|
name: impl AsRef<str>,
|
||||||
|
args: impl FuncArgs,
|
||||||
) -> RhaiResultOf<T> {
|
) -> RhaiResultOf<T> {
|
||||||
let mut arg_values = StaticVec::new_const();
|
let mut arg_values = StaticVec::new_const();
|
||||||
args.parse(&mut arg_values);
|
args.parse(&mut arg_values);
|
||||||
|
|
||||||
let result = self.call_fn_raw(scope, ast, true, true, name, None, arg_values)?;
|
let result = self._call_fn(
|
||||||
|
options,
|
||||||
|
scope,
|
||||||
|
&mut GlobalRuntimeState::new(self),
|
||||||
|
&mut Caches::new(),
|
||||||
|
ast,
|
||||||
|
name.as_ref(),
|
||||||
|
arg_values.as_mut(),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Bail out early if the return type needs no cast
|
// Bail out early if the return type needs no cast
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
return Ok(reify!(result => T));
|
return Ok(reify!(result => T));
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
|
||||||
return Ok(reify!(() => T));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cast return type
|
// Cast return type
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
@ -86,120 +197,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
///
|
///
|
||||||
/// The following options are available:
|
|
||||||
///
|
|
||||||
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
|
||||||
/// * whether to rewind the [`Scope`] after the function call
|
|
||||||
/// * a value for binding to the `this` pointer (if any)
|
|
||||||
///
|
|
||||||
/// Not available under `no_function`.
|
|
||||||
///
|
|
||||||
/// # WARNING - Low Level API
|
|
||||||
///
|
|
||||||
/// This function is very low level.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
|
||||||
/// This is to avoid unnecessarily cloning the arguments.
|
|
||||||
///
|
|
||||||
/// Do not use the arguments after this call. If they are needed afterwards, clone them _before_
|
|
||||||
/// calling this function.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
|
||||||
/// # #[cfg(not(feature = "no_function"))]
|
|
||||||
/// # {
|
|
||||||
/// use rhai::{Engine, Scope, Dynamic};
|
|
||||||
///
|
|
||||||
/// let engine = Engine::new();
|
|
||||||
///
|
|
||||||
/// let ast = engine.compile("
|
|
||||||
/// fn add(x, y) { len(x) + y + foo }
|
|
||||||
/// fn add1(x) { len(x) + 1 + foo }
|
|
||||||
/// fn bar() { foo/2 }
|
|
||||||
/// fn action(x) { this += x; } // function using 'this' pointer
|
|
||||||
/// fn decl(x) { let hello = x; } // declaring variables
|
|
||||||
/// ")?;
|
|
||||||
///
|
|
||||||
/// let mut scope = Scope::new();
|
|
||||||
/// scope.push("foo", 42_i64);
|
|
||||||
///
|
|
||||||
/// // Call the script-defined function
|
|
||||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "add", None, [ "abc".into(), 123_i64.into() ])?;
|
|
||||||
/// // ^^^^ no 'this' pointer
|
|
||||||
/// assert_eq!(result.cast::<i64>(), 168);
|
|
||||||
///
|
|
||||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "add1", None, [ "abc".into() ])?;
|
|
||||||
/// assert_eq!(result.cast::<i64>(), 46);
|
|
||||||
///
|
|
||||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "bar", None, [])?;
|
|
||||||
/// assert_eq!(result.cast::<i64>(), 21);
|
|
||||||
///
|
|
||||||
/// let mut value = 1_i64.into();
|
|
||||||
/// let result = engine.call_fn_raw(&mut scope, &ast, true, true, "action", Some(&mut value), [ 41_i64.into() ])?;
|
|
||||||
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
|
||||||
/// assert_eq!(value.as_int().unwrap(), 42);
|
|
||||||
///
|
|
||||||
/// engine.call_fn_raw(&mut scope, &ast, true, false, "decl", None, [ 42_i64.into() ])?;
|
|
||||||
/// // ^^^^^ do not rewind scope
|
|
||||||
/// assert_eq!(scope.get_value::<i64>("hello").unwrap(), 42);
|
|
||||||
/// # }
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn call_fn_raw(
|
|
||||||
&self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
ast: &AST,
|
|
||||||
eval_ast: bool,
|
|
||||||
rewind_scope: bool,
|
|
||||||
name: impl AsRef<str>,
|
|
||||||
this_ptr: Option<&mut Dynamic>,
|
|
||||||
arg_values: impl AsMut<[Dynamic]>,
|
|
||||||
) -> RhaiResult {
|
|
||||||
let mut arg_values = arg_values;
|
|
||||||
|
|
||||||
self._call_fn(
|
|
||||||
scope,
|
|
||||||
&mut GlobalRuntimeState::new(self),
|
|
||||||
&mut Caches::new(),
|
|
||||||
ast,
|
|
||||||
eval_ast,
|
|
||||||
rewind_scope,
|
|
||||||
name.as_ref(),
|
|
||||||
this_ptr,
|
|
||||||
arg_values.as_mut(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/// _(internals)_ Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
///
|
|
||||||
/// The following options are available:
|
|
||||||
///
|
|
||||||
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
|
||||||
/// * whether to rewind the [`Scope`] after the function call
|
|
||||||
/// * a value for binding to the `this` pointer (if any)
|
|
||||||
///
|
|
||||||
/// Not available under `no_function`.
|
|
||||||
///
|
|
||||||
/// # WARNING - Unstable API
|
|
||||||
///
|
|
||||||
/// This API is volatile and may change in the future.
|
|
||||||
///
|
|
||||||
/// # WARNING - Low Level API
|
|
||||||
///
|
|
||||||
/// This function is _extremely_ low level.
|
|
||||||
///
|
|
||||||
/// A [`GlobalRuntimeState`] and [`Caches`] need to be passed into the function, which can be
|
|
||||||
/// created via [`GlobalRuntimeState::new`] and [`Caches::new`].
|
|
||||||
///
|
|
||||||
/// This makes repeatedly calling particular functions more efficient as the functions
|
|
||||||
/// resolution cache is kept intact.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`. This is to avoid
|
/// All the arguments are _consumed_, meaning that they're replaced by `()`. This is to avoid
|
||||||
@ -207,56 +204,31 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// Do not use the arguments after this call. If they are needed afterwards, clone them _before_
|
/// Do not use the arguments after this call. If they are needed afterwards, clone them _before_
|
||||||
/// calling this function.
|
/// calling this function.
|
||||||
#[cfg(feature = "internals")]
|
|
||||||
#[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn call_fn_raw_raw(
|
pub(crate) fn _call_fn(
|
||||||
&self,
|
&self,
|
||||||
|
options: CallFnOptions,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
eval_ast: bool,
|
|
||||||
rewind_scope: bool,
|
|
||||||
name: &str,
|
name: &str,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
|
||||||
arg_values: &mut [Dynamic],
|
|
||||||
) -> RhaiResult {
|
|
||||||
self._call_fn(
|
|
||||||
scope,
|
|
||||||
global,
|
|
||||||
caches,
|
|
||||||
ast,
|
|
||||||
eval_ast,
|
|
||||||
rewind_scope,
|
|
||||||
name,
|
|
||||||
this_ptr,
|
|
||||||
arg_values,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
|
||||||
fn _call_fn(
|
|
||||||
&self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
global: &mut GlobalRuntimeState,
|
|
||||||
caches: &mut Caches,
|
|
||||||
ast: &AST,
|
|
||||||
eval_ast: bool,
|
|
||||||
rewind_scope: bool,
|
|
||||||
name: &str,
|
|
||||||
this_ptr: Option<&mut Dynamic>,
|
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let statements = ast.statements();
|
let statements = ast.statements();
|
||||||
|
|
||||||
let orig_lib_len = global.lib.len();
|
let orig_lib_len = global.lib.len();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
let mut orig_tag = None;
|
||||||
|
|
||||||
|
if let Some(value) = options.tag {
|
||||||
|
orig_tag = Some(mem::replace(&mut global.tag, value));
|
||||||
|
}
|
||||||
|
|
||||||
global.lib.push(ast.shared_lib().clone());
|
global.lib.push(ast.shared_lib().clone());
|
||||||
|
|
||||||
let mut no_this_ptr = Dynamic::NULL;
|
let mut no_this_ptr = Dynamic::NULL;
|
||||||
let this_ptr = this_ptr.unwrap_or(&mut no_this_ptr);
|
let this_ptr = options.this_ptr.unwrap_or(&mut no_this_ptr);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let orig_embedded_module_resolver = std::mem::replace(
|
let orig_embedded_module_resolver = std::mem::replace(
|
||||||
@ -264,7 +236,9 @@ impl Engine {
|
|||||||
ast.resolver().cloned(),
|
ast.resolver().cloned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = if eval_ast && !statements.is_empty() {
|
let rewind_scope = options.rewind_scope;
|
||||||
|
|
||||||
|
let result = if options.eval_ast && !statements.is_empty() {
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
let scope = &mut *RestoreOnDrop::lock_if(rewind_scope, scope, move |s| {
|
let scope = &mut *RestoreOnDrop::lock_if(rewind_scope, scope, move |s| {
|
||||||
s.rewind(orig_scope_len);
|
s.rewind(orig_scope_len);
|
||||||
@ -308,6 +282,11 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
global.embedded_module_resolver = orig_embedded_module_resolver;
|
global.embedded_module_resolver = orig_embedded_module_resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(value) = orig_tag {
|
||||||
|
global.tag = value;
|
||||||
|
}
|
||||||
|
|
||||||
global.lib.truncate(orig_lib_len);
|
global.lib.truncate(orig_lib_len);
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -117,10 +117,10 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// # Deprecated
|
/// # Deprecated
|
||||||
///
|
///
|
||||||
/// This method is deprecated. Use [`run_ast_with_scope`][Engine::run_ast_with_scope] instead.
|
/// This method is deprecated. Use [`call_fn_with_options`][Engine::call_fn_with_options] instead.
|
||||||
///
|
///
|
||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
#[deprecated(since = "1.1.0", note = "use `call_fn_raw` instead")]
|
#[deprecated(since = "1.1.0", note = "use `call_fn_with_options` instead")]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn call_fn_dynamic(
|
pub fn call_fn_dynamic(
|
||||||
@ -132,8 +132,56 @@ impl Engine {
|
|||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
arg_values: impl AsMut<[Dynamic]>,
|
arg_values: impl AsMut<[Dynamic]>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
#[allow(deprecated)]
|
||||||
self.call_fn_raw(scope, ast, eval_ast, true, name, this_ptr, arg_values)
|
self.call_fn_raw(scope, ast, eval_ast, true, name, this_ptr, arg_values)
|
||||||
}
|
}
|
||||||
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
|
///
|
||||||
|
/// The following options are available:
|
||||||
|
///
|
||||||
|
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
||||||
|
/// * whether to rewind the [`Scope`] after the function call
|
||||||
|
/// * a value for binding to the `this` pointer (if any)
|
||||||
|
///
|
||||||
|
/// Not available under `no_function`.
|
||||||
|
///
|
||||||
|
/// # Deprecated
|
||||||
|
///
|
||||||
|
/// This method is deprecated. Use [`call_fn_with_options`][Engine::call_fn_with_options] instead.
|
||||||
|
///
|
||||||
|
/// This method will be removed in the next major version.
|
||||||
|
#[deprecated(since = "1.12.0", note = "use `call_fn_with_options` instead")]
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn call_fn_raw(
|
||||||
|
&self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
ast: &AST,
|
||||||
|
eval_ast: bool,
|
||||||
|
rewind_scope: bool,
|
||||||
|
name: impl AsRef<str>,
|
||||||
|
this_ptr: Option<&mut Dynamic>,
|
||||||
|
arg_values: impl AsMut<[Dynamic]>,
|
||||||
|
) -> RhaiResult {
|
||||||
|
let mut arg_values = arg_values;
|
||||||
|
|
||||||
|
let options = crate::CallFnOptions {
|
||||||
|
this_ptr,
|
||||||
|
eval_ast,
|
||||||
|
rewind_scope,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
self._call_fn(
|
||||||
|
options,
|
||||||
|
scope,
|
||||||
|
&mut crate::eval::GlobalRuntimeState::new(self),
|
||||||
|
&mut crate::eval::Caches::new(),
|
||||||
|
ast,
|
||||||
|
name.as_ref(),
|
||||||
|
arg_values.as_mut(),
|
||||||
|
)
|
||||||
|
}
|
||||||
/// Register a custom fallible function with the [`Engine`].
|
/// Register a custom fallible function with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// # Deprecated
|
/// # Deprecated
|
||||||
|
@ -4,11 +4,14 @@ use crate::eval::{Caches, GlobalRuntimeState};
|
|||||||
use crate::parser::ParseState;
|
use crate::parser::ParseState;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
reify, Dynamic, Engine, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::type_name, mem};
|
use std::{
|
||||||
|
any::{type_name, TypeId},
|
||||||
|
mem,
|
||||||
|
};
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Evaluate a string as a script, returning the result value or an error.
|
/// Evaluate a string as a script, returning the result value or an error.
|
||||||
@ -190,6 +193,11 @@ impl Engine {
|
|||||||
|
|
||||||
let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?;
|
let result = self.eval_ast_with_scope_raw(global, caches, scope, ast)?;
|
||||||
|
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify!(result => T));
|
||||||
|
}
|
||||||
|
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
result.try_cast::<T>().ok_or_else(|| {
|
result.try_cast::<T>().ok_or_else(|| {
|
||||||
|
@ -102,7 +102,7 @@ impl Default for Limits {
|
|||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Is there a data size limit set?
|
/// Is there a data size limit set?
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub(crate) const fn has_data_size_limit(&self) -> bool {
|
pub(crate) const fn has_data_size_limit(&self) -> bool {
|
||||||
self.limits.max_string_size.is_some()
|
self.limits.max_string_size.is_some()
|
||||||
|| {
|
|| {
|
||||||
|
@ -68,8 +68,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether `if`-expression is allowed.
|
/// Set whether `if`-expression is allowed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_if_expression(&mut self, enable: bool) {
|
pub fn set_allow_if_expression(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::IF_EXPR, enable);
|
self.options.set(LangOptions::IF_EXPR, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Is `switch` expression allowed?
|
/// Is `switch` expression allowed?
|
||||||
/// Default is `true`.
|
/// Default is `true`.
|
||||||
@ -80,8 +81,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether `switch` expression is allowed.
|
/// Set whether `switch` expression is allowed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_switch_expression(&mut self, enable: bool) {
|
pub fn set_allow_switch_expression(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::SWITCH_EXPR, enable);
|
self.options.set(LangOptions::SWITCH_EXPR, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Are loop expressions allowed?
|
/// Are loop expressions allowed?
|
||||||
/// Default is `true`.
|
/// Default is `true`.
|
||||||
@ -92,8 +94,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether loop expressions are allowed.
|
/// Set whether loop expressions are allowed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_loop_expressions(&mut self, enable: bool) {
|
pub fn set_allow_loop_expressions(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::LOOP_EXPR, enable);
|
self.options.set(LangOptions::LOOP_EXPR, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Is statement-expression allowed?
|
/// Is statement-expression allowed?
|
||||||
/// Default is `true`.
|
/// Default is `true`.
|
||||||
@ -104,8 +107,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether statement-expression is allowed.
|
/// Set whether statement-expression is allowed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_statement_expression(&mut self, enable: bool) {
|
pub fn set_allow_statement_expression(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::STMT_EXPR, enable);
|
self.options.set(LangOptions::STMT_EXPR, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Is anonymous function allowed?
|
/// Is anonymous function allowed?
|
||||||
/// Default is `true`.
|
/// Default is `true`.
|
||||||
@ -122,8 +126,9 @@ impl Engine {
|
|||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_anonymous_fn(&mut self, enable: bool) {
|
pub fn set_allow_anonymous_fn(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::ANON_FN, enable);
|
self.options.set(LangOptions::ANON_FN, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Is looping allowed?
|
/// Is looping allowed?
|
||||||
/// Default is `true`.
|
/// Default is `true`.
|
||||||
@ -134,8 +139,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether looping is allowed.
|
/// Set whether looping is allowed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_looping(&mut self, enable: bool) {
|
pub fn set_allow_looping(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::LOOPING, enable);
|
self.options.set(LangOptions::LOOPING, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Is variables shadowing allowed?
|
/// Is variables shadowing allowed?
|
||||||
/// Default is `true`.
|
/// Default is `true`.
|
||||||
@ -146,8 +152,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether variables shadowing is allowed.
|
/// Set whether variables shadowing is allowed.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_allow_shadowing(&mut self, enable: bool) {
|
pub fn set_allow_shadowing(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::SHADOW, enable);
|
self.options.set(LangOptions::SHADOW, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Is strict variables mode enabled?
|
/// Is strict variables mode enabled?
|
||||||
/// Default is `false`.
|
/// Default is `false`.
|
||||||
@ -158,8 +165,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether strict variables mode is enabled.
|
/// Set whether strict variables mode is enabled.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_strict_variables(&mut self, enable: bool) {
|
pub fn set_strict_variables(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::STRICT_VAR, enable);
|
self.options.set(LangOptions::STRICT_VAR, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Raise error if an object map property does not exist?
|
/// Raise error if an object map property does not exist?
|
||||||
/// Default is `false`.
|
/// Default is `false`.
|
||||||
@ -177,9 +185,10 @@ impl Engine {
|
|||||||
/// Not available under `no_object`.
|
/// Not available under `no_object`.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_fail_on_invalid_map_property(&mut self, enable: bool) {
|
pub fn set_fail_on_invalid_map_property(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options
|
self.options
|
||||||
.set(LangOptions::FAIL_ON_INVALID_MAP_PROPERTY, enable);
|
.set(LangOptions::FAIL_ON_INVALID_MAP_PROPERTY, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
/// Is fast operators mode enabled?
|
/// Is fast operators mode enabled?
|
||||||
/// Default is `false`.
|
/// Default is `false`.
|
||||||
@ -190,7 +199,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Set whether fast operators mode is enabled.
|
/// Set whether fast operators mode is enabled.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_fast_operators(&mut self, enable: bool) {
|
pub fn set_fast_operators(&mut self, enable: bool) -> &mut Self {
|
||||||
self.options.set(LangOptions::FAST_OPS, enable);
|
self.options.set(LangOptions::FAST_OPS, enable);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -624,6 +624,38 @@ impl Expr {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Get the [options][ASTFlags] of the expression.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn options(&self) -> ASTFlags {
|
||||||
|
match self {
|
||||||
|
Self::Index(_, options, _) | Self::Dot(_, options, _) => *options,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Self::FloatConstant(..) => ASTFlags::NONE,
|
||||||
|
|
||||||
|
Self::DynamicConstant(..)
|
||||||
|
| Self::BoolConstant(..)
|
||||||
|
| Self::IntegerConstant(..)
|
||||||
|
| Self::CharConstant(..)
|
||||||
|
| Self::Unit(..)
|
||||||
|
| Self::StringConstant(..)
|
||||||
|
| Self::Array(..)
|
||||||
|
| Self::Map(..)
|
||||||
|
| Self::Variable(..)
|
||||||
|
| Self::And(..)
|
||||||
|
| Self::Or(..)
|
||||||
|
| Self::Coalesce(..)
|
||||||
|
| Self::FnCall(..)
|
||||||
|
| Self::MethodCall(..)
|
||||||
|
| Self::InterpolatedString(..)
|
||||||
|
| Self::Property(..)
|
||||||
|
| Self::Stmt(..) => ASTFlags::NONE,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
|
Self::Custom(..) => ASTFlags::NONE,
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Get the [position][Position] of the expression.
|
/// Get the [position][Position] of the expression.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -656,6 +656,34 @@ impl Stmt {
|
|||||||
pub const fn is_noop(&self) -> bool {
|
pub const fn is_noop(&self) -> bool {
|
||||||
matches!(self, Self::Noop(..))
|
matches!(self, Self::Noop(..))
|
||||||
}
|
}
|
||||||
|
/// Get the [options][ASTFlags] of this statement.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn options(&self) -> ASTFlags {
|
||||||
|
match self {
|
||||||
|
Self::Do(_, options, _)
|
||||||
|
| Self::Var(_, options, _)
|
||||||
|
| Self::BreakLoop(_, options, _)
|
||||||
|
| Self::Return(_, options, _) => *options,
|
||||||
|
|
||||||
|
Self::Noop(..)
|
||||||
|
| Self::If(..)
|
||||||
|
| Self::Switch(..)
|
||||||
|
| Self::Block(..)
|
||||||
|
| Self::Expr(..)
|
||||||
|
| Self::FnCall(..)
|
||||||
|
| Self::While(..)
|
||||||
|
| Self::For(..)
|
||||||
|
| Self::TryCatch(..)
|
||||||
|
| Self::Assignment(..) => ASTFlags::NONE,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
Self::Import(..) | Self::Export(..) => ASTFlags::NONE,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Self::Share(..) => ASTFlags::NONE,
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Get the [position][Position] of this statement.
|
/// Get the [position][Position] of this statement.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Set to [`None`] to disable stable hashing.
|
//! Set to [`None`] to disable stable hashing.
|
||||||
//!
|
//!
|
||||||
//! See [`set_rhai_ahash_seed`].
|
//! See [`rhai::config::hashing::set_ahash_seed`][set_ahash_seed].
|
||||||
//!
|
//!
|
||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
@ -189,7 +189,7 @@ static AHASH_SEED: SusLock<Option<[u64; 4]>> = SusLock::new();
|
|||||||
/// # Warning
|
/// # Warning
|
||||||
///
|
///
|
||||||
/// * You can only call this function **ONCE** for the entire duration of program execution.
|
/// * You can only call this function **ONCE** for the entire duration of program execution.
|
||||||
/// * You **MUST** call this before performing **ANY** Rhai operation (e.g. creating an [`Engine`]).
|
/// * You **MUST** call this before performing **ANY** Rhai operation (e.g. creating an [`Engine`][crate::Engine]).
|
||||||
///
|
///
|
||||||
/// # Error
|
/// # Error
|
||||||
///
|
///
|
||||||
@ -216,7 +216,7 @@ pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>
|
|||||||
///
|
///
|
||||||
/// Otherwise, the hashing seed is randomized to protect against DOS attacks.
|
/// Otherwise, the hashing seed is randomized to protect against DOS attacks.
|
||||||
///
|
///
|
||||||
/// See [`set_rhai_ahash_seed`] for more.
|
/// See [`rhai::config::hashing::set_ahash_seed`][set_ahash_seed] for more.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_ahash_seed() -> &'static Option<[u64; 4]> {
|
pub fn get_ahash_seed() -> &'static Option<[u64; 4]> {
|
||||||
|
1396
src/eval/chaining.rs
1396
src/eval/chaining.rs
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
use super::GlobalRuntimeState;
|
use super::GlobalRuntimeState;
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, ERR};
|
use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR};
|
||||||
|
use std::borrow::Borrow;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -70,6 +71,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Raise an error if any data size exceeds limit.
|
/// Raise an error if any data size exceeds limit.
|
||||||
|
///
|
||||||
|
/// [`Position`] in [`EvalAltResult`][crate::EvalAltResult] is always [`NONE`][Position::NONE]
|
||||||
|
/// and should be set afterwards.
|
||||||
pub(crate) fn raise_err_if_over_data_size_limit(
|
pub(crate) fn raise_err_if_over_data_size_limit(
|
||||||
&self,
|
&self,
|
||||||
(_arr, _map, s): (usize, usize, usize),
|
(_arr, _map, s): (usize, usize, usize),
|
||||||
@ -111,15 +115,20 @@ impl Engine {
|
|||||||
|
|
||||||
/// Check whether the size of a [`Dynamic`] is within limits.
|
/// Check whether the size of a [`Dynamic`] is within limits.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> {
|
pub(crate) fn check_data_size<T: Borrow<Dynamic>>(
|
||||||
|
&self,
|
||||||
|
value: T,
|
||||||
|
pos: Position,
|
||||||
|
) -> RhaiResultOf<T> {
|
||||||
// If no data size limits, just return
|
// If no data size limits, just return
|
||||||
if !self.has_data_size_limit() {
|
if !self.has_data_size_limit() {
|
||||||
return Ok(());
|
return Ok(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sizes = Self::calc_data_sizes(value, true);
|
let sizes = Self::calc_data_sizes(value.borrow(), true);
|
||||||
|
|
||||||
self.raise_err_if_over_data_size_limit(sizes)
|
self.raise_err_if_over_data_size_limit(sizes)
|
||||||
|
.map(|_| value)
|
||||||
.map_err(|err| err.fill_position(pos))
|
.map_err(|err| err.fill_position(pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +137,7 @@ impl Engine {
|
|||||||
/// Not available under `unchecked`.
|
/// Not available under `unchecked`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> {
|
pub fn ensure_data_size_within_limits(&self, value: &Dynamic) -> RhaiResultOf<()> {
|
||||||
self.check_data_size(value, Position::NONE)
|
self.check_data_size(value, Position::NONE).map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
@ -141,26 +150,15 @@ impl Engine {
|
|||||||
|
|
||||||
// Guard against too many operations
|
// Guard against too many operations
|
||||||
let max = self.max_operations();
|
let max = self.max_operations();
|
||||||
let num_operations = global.num_operations;
|
|
||||||
|
|
||||||
if max > 0 && num_operations > max {
|
if max > 0 && global.num_operations > max {
|
||||||
return Err(ERR::ErrorTooManyOperations(pos).into());
|
return Err(ERR::ErrorTooManyOperations(pos).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report progress
|
// Report progress
|
||||||
self.progress
|
self.progress
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|p| p(num_operations))
|
.and_then(|p| p(global.num_operations))
|
||||||
.map_or(Ok(()), |token| Err(ERR::ErrorTerminated(token, pos).into()))
|
.map_or(Ok(()), |token| Err(ERR::ErrorTerminated(token, pos).into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that it is valid.
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn check_return_value(&self, result: RhaiResult, pos: Position) -> RhaiResult {
|
|
||||||
if let Ok(ref r) = result {
|
|
||||||
self.check_data_size(r, pos)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Evaluation context.
|
//! Evaluation context.
|
||||||
|
|
||||||
use super::{Caches, GlobalRuntimeState};
|
use super::{Caches, GlobalRuntimeState};
|
||||||
use crate::{Dynamic, Engine, Module, Scope};
|
use crate::{Dynamic, Engine, Scope};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
|||||||
/// in reverse order (i.e. modules imported last come first).
|
/// in reverse order (i.e. modules imported last come first).
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
||||||
self.global.iter_imports()
|
self.global.iter_imports()
|
||||||
}
|
}
|
||||||
/// Custom state kept in a [`Dynamic`].
|
/// Custom state kept in a [`Dynamic`].
|
||||||
@ -104,7 +104,7 @@ impl<'a, 's, 'ps, 'g, 'c, 't> EvalContext<'a, 's, 'ps, 'g, 'c, 't> {
|
|||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
|
||||||
self.global.lib.iter().map(|m| m.as_ref())
|
self.global.lib.iter().map(|m| m.as_ref())
|
||||||
}
|
}
|
||||||
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
|
/// _(internals)_ The current set of namespaces containing definitions of all script-defined functions.
|
||||||
|
@ -4,7 +4,7 @@ use super::{Caches, EvalContext, GlobalRuntimeState, Target};
|
|||||||
use crate::ast::{Expr, OpAssignment};
|
use crate::ast::{Expr, OpAssignment};
|
||||||
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
use crate::engine::{KEYWORD_THIS, OP_CONCAT};
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, SharedModule, ERR};
|
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, ERR};
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -18,25 +18,21 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
global: &GlobalRuntimeState,
|
global: &GlobalRuntimeState,
|
||||||
namespace: &crate::ast::Namespace,
|
namespace: &crate::ast::Namespace,
|
||||||
) -> Option<SharedModule> {
|
) -> Option<crate::SharedModule> {
|
||||||
assert!(!namespace.is_empty());
|
assert!(!namespace.is_empty());
|
||||||
|
|
||||||
let root = namespace.root();
|
let root = namespace.root();
|
||||||
|
|
||||||
let index = if global.always_search_scope {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
namespace.index()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
// Qualified - check if the root module is directly indexed
|
||||||
if let Some(index) = index {
|
if !global.always_search_scope {
|
||||||
|
if let Some(index) = namespace.index() {
|
||||||
let offset = global.num_imports() - index.get();
|
let offset = global.num_imports() - index.get();
|
||||||
|
|
||||||
if let m @ Some(_) = global.get_shared_import(offset) {
|
if let m @ Some(_) = global.get_shared_import(offset) {
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Do a text-match search if the index doesn't work
|
// Do a text-match search if the index doesn't work
|
||||||
global.find_import(root).map_or_else(
|
global.find_import(root).map_or_else(
|
||||||
@ -54,12 +50,12 @@ impl Engine {
|
|||||||
scope: &'s mut Scope,
|
scope: &'s mut Scope,
|
||||||
this_ptr: &'s mut Dynamic,
|
this_ptr: &'s mut Dynamic,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
) -> RhaiResultOf<(Target<'s>, Position)> {
|
) -> RhaiResultOf<Target<'s>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Variable(_, Some(_), _) => {
|
Expr::Variable(_, Some(_), _) => {
|
||||||
self.search_scope_only(global, caches, scope, this_ptr, expr)
|
self.search_scope_only(global, caches, scope, this_ptr, expr)
|
||||||
}
|
}
|
||||||
Expr::Variable(v, None, _var_pos) => match &**v {
|
Expr::Variable(v, None, ..) => match &**v {
|
||||||
// Normal variable access
|
// Normal variable access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(_, ns, ..) if ns.is_empty() => {
|
(_, ns, ..) if ns.is_empty() => {
|
||||||
@ -86,7 +82,7 @@ impl Engine {
|
|||||||
|mut target| {
|
|mut target| {
|
||||||
// Module variables are constant
|
// Module variables are constant
|
||||||
target.set_access_mode(AccessMode::ReadOnly);
|
target.set_access_mode(AccessMode::ReadOnly);
|
||||||
Ok((target.into(), *_var_pos))
|
Ok(target.into())
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -101,7 +97,7 @@ impl Engine {
|
|||||||
let mut target: Target = value.clone().into();
|
let mut target: Target = value.clone().into();
|
||||||
// Module variables are constant
|
// Module variables are constant
|
||||||
target.set_access_mode(AccessMode::ReadOnly);
|
target.set_access_mode(AccessMode::ReadOnly);
|
||||||
return Ok((target, *_var_pos));
|
return Ok(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,23 +132,23 @@ impl Engine {
|
|||||||
scope: &'s mut Scope,
|
scope: &'s mut Scope,
|
||||||
this_ptr: &'s mut Dynamic,
|
this_ptr: &'s mut Dynamic,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
) -> RhaiResultOf<(Target<'s>, Position)> {
|
) -> RhaiResultOf<Target<'s>> {
|
||||||
// Make sure that the pointer indirection is taken only when absolutely necessary.
|
// Make sure that the pointer indirection is taken only when absolutely necessary.
|
||||||
|
|
||||||
let (index, var_pos) = match expr {
|
let index = match expr {
|
||||||
// Check if the variable is `this`
|
// Check if the variable is `this`
|
||||||
Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => {
|
Expr::Variable(v, None, ..) if v.0.is_none() && v.3 == KEYWORD_THIS => {
|
||||||
return if this_ptr.is_null() {
|
return if this_ptr.is_null() {
|
||||||
Err(ERR::ErrorUnboundThis(*pos).into())
|
Err(ERR::ErrorUnboundThis(expr.position()).into())
|
||||||
} else {
|
} else {
|
||||||
Ok((this_ptr.into(), *pos))
|
Ok(this_ptr.into())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ if global.always_search_scope => (0, expr.start_position()),
|
_ if global.always_search_scope => 0,
|
||||||
Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos),
|
Expr::Variable(_, Some(i), ..) => i.get() as usize,
|
||||||
// Scripted function with the same name
|
// Scripted function with the same name
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Expr::Variable(v, None, pos)
|
Expr::Variable(v, None, ..)
|
||||||
if global
|
if global
|
||||||
.lib
|
.lib
|
||||||
.iter()
|
.iter()
|
||||||
@ -161,9 +157,9 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
let val: Dynamic =
|
let val: Dynamic =
|
||||||
crate::FnPtr::new_unchecked(v.3.as_str(), Default::default()).into();
|
crate::FnPtr::new_unchecked(v.3.as_str(), Default::default()).into();
|
||||||
return Ok((val.into(), *pos));
|
return Ok(val.into());
|
||||||
}
|
}
|
||||||
Expr::Variable(v, None, pos) => (v.0.map_or(0, NonZeroUsize::get), *pos),
|
Expr::Variable(v, None, ..) => v.0.map_or(0, NonZeroUsize::get),
|
||||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -174,10 +170,10 @@ impl Engine {
|
|||||||
match resolve_var(var_name, index, context) {
|
match resolve_var(var_name, index, context) {
|
||||||
Ok(Some(mut result)) => {
|
Ok(Some(mut result)) => {
|
||||||
result.set_access_mode(AccessMode::ReadOnly);
|
result.set_access_mode(AccessMode::ReadOnly);
|
||||||
return Ok((result.into(), var_pos));
|
return Ok(result.into());
|
||||||
}
|
}
|
||||||
Ok(None) => (),
|
Ok(None) => (),
|
||||||
Err(err) => return Err(err.fill_position(var_pos)),
|
Err(err) => return Err(err.fill_position(expr.position())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,10 +187,12 @@ impl Engine {
|
|||||||
Some(index) => index,
|
Some(index) => index,
|
||||||
None => {
|
None => {
|
||||||
return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) {
|
return match self.global_modules.iter().find_map(|m| m.get_var(var_name)) {
|
||||||
Some(val) => Ok((val.into(), var_pos)),
|
Some(val) => Ok(val.into()),
|
||||||
None => {
|
None => Err(ERR::ErrorVariableNotFound(
|
||||||
Err(ERR::ErrorVariableNotFound(var_name.to_string(), var_pos).into())
|
var_name.to_string(),
|
||||||
}
|
expr.position(),
|
||||||
|
)
|
||||||
|
.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,17 +200,10 @@ impl Engine {
|
|||||||
|
|
||||||
let val = scope.get_mut_by_index(index);
|
let val = scope.get_mut_by_index(index);
|
||||||
|
|
||||||
Ok((val.into(), var_pos))
|
Ok(val.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an expression.
|
/// Evaluate an expression.
|
||||||
//
|
|
||||||
// # Implementation Notes
|
|
||||||
//
|
|
||||||
// Do not use the `?` operator within the main body as it makes this function return early,
|
|
||||||
// possibly by-passing important cleanup tasks at the end.
|
|
||||||
//
|
|
||||||
// Errors that are not recoverable, such as system errors or safety errors, can use `?`.
|
|
||||||
pub(crate) fn eval_expr(
|
pub(crate) fn eval_expr(
|
||||||
&self,
|
&self,
|
||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
@ -256,7 +247,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.search_namespace(global, caches, scope, this_ptr, expr)
|
self.search_namespace(global, caches, scope, this_ptr, expr)
|
||||||
.map(|(val, ..)| val.take_or_clone())
|
.map(Target::take_or_clone)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,10 +277,8 @@ impl Engine {
|
|||||||
let target = &mut concat;
|
let target = &mut concat;
|
||||||
|
|
||||||
let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE);
|
let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE);
|
||||||
let root = ("", Position::NONE);
|
|
||||||
|
|
||||||
let result = x
|
x.iter()
|
||||||
.iter()
|
|
||||||
.try_for_each(|expr| {
|
.try_for_each(|expr| {
|
||||||
let item = self
|
let item = self
|
||||||
.eval_expr(global, caches, scope, this_ptr, expr)?
|
.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||||
@ -297,11 +286,10 @@ impl Engine {
|
|||||||
|
|
||||||
op_info.pos = expr.start_position();
|
op_info.pos = expr.start_position();
|
||||||
|
|
||||||
self.eval_op_assignment(global, caches, &op_info, target, root, item)
|
self.eval_op_assignment(global, caches, &op_info, expr, target, item)
|
||||||
})
|
})
|
||||||
.map(|_| concat.take_or_clone());
|
.map(|_| concat.take_or_clone())
|
||||||
|
.and_then(|r| self.check_data_size(r, expr.start_position()))
|
||||||
self.check_return_value(result, expr.start_position())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -414,9 +402,8 @@ impl Engine {
|
|||||||
})?;
|
})?;
|
||||||
let mut context = EvalContext::new(self, global, caches, scope, this_ptr);
|
let mut context = EvalContext::new(self, global, caches, scope, this_ptr);
|
||||||
|
|
||||||
let result = (custom_def.func)(&mut context, &expressions, &custom.state);
|
(custom_def.func)(&mut context, &expressions, &custom.state)
|
||||||
|
.and_then(|r| self.check_data_size(r, expr.start_position()))
|
||||||
self.check_return_value(result, expr.start_position())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
Expr::Stmt(x) if x.is_empty() => Ok(Dynamic::UNIT),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Global runtime state.
|
//! Global runtime state.
|
||||||
|
|
||||||
use crate::{Dynamic, Engine, ImmutableString, SharedModule, StaticVec};
|
use crate::{Dynamic, Engine, ImmutableString};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -25,13 +25,13 @@ pub type GlobalConstants =
|
|||||||
pub struct GlobalRuntimeState {
|
pub struct GlobalRuntimeState {
|
||||||
/// Names of imported [modules][crate::Module].
|
/// Names of imported [modules][crate::Module].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
imports: StaticVec<ImmutableString>,
|
imports: crate::StaticVec<ImmutableString>,
|
||||||
/// Stack of imported [modules][crate::Module].
|
/// Stack of imported [modules][crate::Module].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: StaticVec<SharedModule>,
|
modules: crate::StaticVec<crate::SharedModule>,
|
||||||
/// The current stack of loaded [modules][crate::Module] containing script-defined functions.
|
/// The current stack of loaded [modules][crate::Module] containing script-defined functions.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub lib: StaticVec<SharedModule>,
|
pub lib: crate::StaticVec<crate::SharedModule>,
|
||||||
/// Source of the current context.
|
/// Source of the current context.
|
||||||
///
|
///
|
||||||
/// No source if the string is empty.
|
/// No source if the string is empty.
|
||||||
@ -84,11 +84,11 @@ impl GlobalRuntimeState {
|
|||||||
pub fn new(engine: &Engine) -> Self {
|
pub fn new(engine: &Engine) -> Self {
|
||||||
Self {
|
Self {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
imports: StaticVec::new_const(),
|
imports: crate::StaticVec::new_const(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: StaticVec::new_const(),
|
modules: crate::StaticVec::new_const(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
lib: StaticVec::new_const(),
|
lib: crate::StaticVec::new_const(),
|
||||||
source: None,
|
source: None,
|
||||||
num_operations: 0,
|
num_operations: 0,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -99,7 +99,10 @@ impl GlobalRuntimeState {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
embedded_module_resolver: None,
|
embedded_module_resolver: None,
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
fn_hash_indexing: (0, 0),
|
fn_hash_indexing: (
|
||||||
|
crate::calc_fn_hash(None, crate::engine::FN_IDX_GET, 2),
|
||||||
|
crate::calc_fn_hash(None, crate::engine::FN_IDX_SET, 3),
|
||||||
|
),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
constants: None,
|
constants: None,
|
||||||
@ -135,7 +138,7 @@ impl GlobalRuntimeState {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_shared_import(&self, index: usize) -> Option<SharedModule> {
|
pub fn get_shared_import(&self, index: usize) -> Option<crate::SharedModule> {
|
||||||
self.modules.get(index).cloned()
|
self.modules.get(index).cloned()
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to the globally-imported [module][crate::Module] at a
|
/// Get a mutable reference to the globally-imported [module][crate::Module] at a
|
||||||
@ -146,7 +149,10 @@ impl GlobalRuntimeState {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn get_shared_import_mut(&mut self, index: usize) -> Option<&mut SharedModule> {
|
pub(crate) fn get_shared_import_mut(
|
||||||
|
&mut self,
|
||||||
|
index: usize,
|
||||||
|
) -> Option<&mut crate::SharedModule> {
|
||||||
self.modules.get_mut(index)
|
self.modules.get_mut(index)
|
||||||
}
|
}
|
||||||
/// Get the index of a globally-imported [module][crate::Module] by name.
|
/// Get the index of a globally-imported [module][crate::Module] by name.
|
||||||
@ -170,7 +176,7 @@ impl GlobalRuntimeState {
|
|||||||
pub fn push_import(
|
pub fn push_import(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<ImmutableString>,
|
name: impl Into<ImmutableString>,
|
||||||
module: impl Into<SharedModule>,
|
module: impl Into<crate::SharedModule>,
|
||||||
) {
|
) {
|
||||||
self.imports.push(name.into());
|
self.imports.push(name.into());
|
||||||
self.modules.push(module.into());
|
self.modules.push(module.into());
|
||||||
@ -203,7 +209,7 @@ impl GlobalRuntimeState {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn iter_imports_raw(
|
pub(crate) fn iter_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&ImmutableString, &SharedModule)> {
|
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||||
self.imports.iter().zip(self.modules.iter()).rev()
|
self.imports.iter().zip(self.modules.iter()).rev()
|
||||||
}
|
}
|
||||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
||||||
@ -211,7 +217,9 @@ impl GlobalRuntimeState {
|
|||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn scan_imports_raw(&self) -> impl Iterator<Item = (&ImmutableString, &SharedModule)> {
|
pub fn scan_imports_raw(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&ImmutableString, &crate::SharedModule)> {
|
||||||
self.imports.iter().zip(self.modules.iter())
|
self.imports.iter().zip(self.modules.iter())
|
||||||
}
|
}
|
||||||
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
||||||
@ -291,34 +299,22 @@ impl GlobalRuntimeState {
|
|||||||
}
|
}
|
||||||
/// Get the pre-calculated index getter hash.
|
/// Get the pre-calculated index getter hash.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn hash_idx_get(&mut self) -> u64 {
|
pub(crate) fn hash_idx_get(&mut self) -> u64 {
|
||||||
if self.fn_hash_indexing == (0, 0) {
|
|
||||||
let n1 = crate::calc_fn_hash(None, crate::engine::FN_IDX_GET, 2);
|
|
||||||
let n2 = crate::calc_fn_hash(None, crate::engine::FN_IDX_SET, 3);
|
|
||||||
self.fn_hash_indexing = (n1, n2);
|
|
||||||
n1
|
|
||||||
} else {
|
|
||||||
self.fn_hash_indexing.0
|
self.fn_hash_indexing.0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/// Get the pre-calculated index setter hash.
|
/// Get the pre-calculated index setter hash.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn hash_idx_set(&mut self) -> u64 {
|
pub(crate) fn hash_idx_set(&mut self) -> u64 {
|
||||||
if self.fn_hash_indexing == (0, 0) {
|
|
||||||
let n1 = crate::calc_fn_hash(None, crate::engine::FN_IDX_GET, 2);
|
|
||||||
let n2 = crate::calc_fn_hash(None, crate::engine::FN_IDX_SET, 3);
|
|
||||||
self.fn_hash_indexing = (n1, n2);
|
|
||||||
n2
|
|
||||||
} else {
|
|
||||||
self.fn_hash_indexing.1
|
self.fn_hash_indexing.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl<K: Into<ImmutableString>, M: Into<SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
|
impl<K: Into<ImmutableString>, M: Into<crate::SharedModule>> Extend<(K, M)> for GlobalRuntimeState {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, M)>>(&mut self, iter: T) {
|
||||||
for (k, m) in iter {
|
for (k, m) in iter {
|
||||||
|
@ -25,7 +25,10 @@ pub use target::{calc_index, calc_offset_len, Target};
|
|||||||
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
mod unchecked {
|
mod unchecked {
|
||||||
use crate::{eval::GlobalRuntimeState, Dynamic, Engine, Position, RhaiResult, RhaiResultOf};
|
use crate::{eval::GlobalRuntimeState, Dynamic, Engine, Position, RhaiResultOf};
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
@ -40,18 +43,12 @@ mod unchecked {
|
|||||||
|
|
||||||
/// Check whether the size of a [`Dynamic`] is within limits.
|
/// Check whether the size of a [`Dynamic`] is within limits.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) const fn check_data_size(&self, _: &Dynamic, _: Position) -> RhaiResultOf<()> {
|
pub(crate) const fn check_data_size<T: Borrow<Dynamic>>(
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check a result to ensure that it is valid.
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) const fn check_return_value(
|
|
||||||
&self,
|
&self,
|
||||||
result: RhaiResult,
|
value: T,
|
||||||
_: Position,
|
_: Position,
|
||||||
) -> RhaiResult {
|
) -> RhaiResultOf<T> {
|
||||||
result
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,22 +8,13 @@ use crate::ast::{
|
|||||||
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
use crate::func::{get_builtin_op_assignment_fn, get_hasher};
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::types::RestoreOnDrop;
|
use crate::types::RestoreOnDrop;
|
||||||
use crate::{
|
use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, ERR, INT};
|
||||||
Dynamic, Engine, ImmutableString, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT,
|
|
||||||
};
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Evaluate a statements block.
|
/// Evaluate a statements block.
|
||||||
//
|
|
||||||
// # Implementation Notes
|
|
||||||
//
|
|
||||||
// Do not use the `?` operator within the main body as it makes this function return early,
|
|
||||||
// possibly by-passing important cleanup tasks at the end.
|
|
||||||
//
|
|
||||||
// Errors that are not recoverable, such as system errors or safety errors, can use `?`.
|
|
||||||
pub(crate) fn eval_stmt_block(
|
pub(crate) fn eval_stmt_block(
|
||||||
&self,
|
&self,
|
||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
@ -112,13 +103,15 @@ impl Engine {
|
|||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
op_info: &OpAssignment,
|
op_info: &OpAssignment,
|
||||||
|
root: &Expr,
|
||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
root: (&str, Position),
|
|
||||||
mut new_val: Dynamic,
|
mut new_val: Dynamic,
|
||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
// Assignment to constant variable?
|
// Assignment to constant variable?
|
||||||
if target.is_read_only() {
|
if target.is_read_only() {
|
||||||
return Err(ERR::ErrorAssignmentToConstant(root.0.to_string(), root.1).into());
|
let name = root.get_variable_name(false).unwrap_or_default();
|
||||||
|
let pos = root.start_position();
|
||||||
|
return Err(ERR::ErrorAssignmentToConstant(name.to_string(), pos).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if op_info.is_op_assignment() {
|
if op_info.is_op_assignment() {
|
||||||
@ -166,14 +159,13 @@ impl Engine {
|
|||||||
*args[0] = self
|
*args[0] = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
global, caches, op, token, *hash_op, args, true, *op_pos,
|
global, caches, op, token, *hash_op, args, true, *op_pos,
|
||||||
)
|
)?
|
||||||
.map_err(|err| err.fill_position(op_info.pos))?
|
|
||||||
.0;
|
.0;
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_data_size(args[0], root.1)?;
|
self.check_data_size(&*args[0], root.position())?;
|
||||||
} else {
|
} else {
|
||||||
// Normal assignment
|
// Normal assignment
|
||||||
|
|
||||||
@ -190,13 +182,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a statement.
|
/// Evaluate a statement.
|
||||||
//
|
|
||||||
// # Implementation Notes
|
|
||||||
//
|
|
||||||
// Do not use the `?` operator within the main body as it makes this function return early,
|
|
||||||
// possibly by-passing important cleanup tasks at the end.
|
|
||||||
//
|
|
||||||
// Errors that are not recoverable, such as system errors or safety errors, can use `?`.
|
|
||||||
pub(crate) fn eval_stmt(
|
pub(crate) fn eval_stmt(
|
||||||
&self,
|
&self,
|
||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
@ -234,29 +219,29 @@ impl Engine {
|
|||||||
.eval_expr(global, caches, scope, this_ptr, rhs)?
|
.eval_expr(global, caches, scope, this_ptr, rhs)?
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
let (mut lhs_ptr, pos) =
|
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||||
self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
|
||||||
|
|
||||||
let var_name = x.3.as_str();
|
let var_name = x.3.as_str();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
// Also handle case where target is a `Dynamic` shared value
|
// Also handle case where target is a `Dynamic` shared value
|
||||||
// (returned by a variable resolver, for example)
|
// (returned by a variable resolver, for example)
|
||||||
let is_temp_result = !lhs_ptr.is_ref() && !lhs_ptr.is_shared();
|
let is_temp_result = !target.is_ref() && !target.is_shared();
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
let is_temp_result = !lhs_ptr.is_ref();
|
let is_temp_result = !target.is_ref();
|
||||||
|
|
||||||
// Cannot assign to temp result from expression
|
// Cannot assign to temp result from expression
|
||||||
if is_temp_result {
|
if is_temp_result {
|
||||||
return Err(ERR::ErrorAssignmentToConstant(var_name.to_string(), pos).into());
|
return Err(ERR::ErrorAssignmentToConstant(
|
||||||
|
var_name.to_string(),
|
||||||
|
lhs.position(),
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.track_operation(global, pos)?;
|
self.track_operation(global, lhs.position())?;
|
||||||
|
|
||||||
let root = (var_name, pos);
|
self.eval_op_assignment(global, caches, op_info, lhs, &mut target, rhs_val)?;
|
||||||
let lhs_ptr = &mut lhs_ptr;
|
|
||||||
|
|
||||||
self.eval_op_assignment(global, caches, op_info, lhs_ptr, root, rhs_val)?;
|
|
||||||
|
|
||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
@ -788,8 +773,8 @@ impl Engine {
|
|||||||
|
|
||||||
let v = self.eval_expr(global, caches, scope, this_ptr, expr)?;
|
let v = self.eval_expr(global, caches, scope, this_ptr, expr)?;
|
||||||
let typ = v.type_name();
|
let typ = v.type_name();
|
||||||
let path = v.try_cast::<ImmutableString>().ok_or_else(|| {
|
let path = v.try_cast::<crate::ImmutableString>().ok_or_else(|| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, expr.position())
|
self.make_type_mismatch_err::<crate::ImmutableString>(typ, expr.position())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
use crate::ModuleResolver;
|
use crate::ModuleResolver;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! Type to hold a mutable reference to the target of an evaluation.
|
//! Type to hold a mutable reference to the target of an evaluation.
|
||||||
|
|
||||||
use crate::types::dynamic::Variant;
|
|
||||||
use crate::{Dynamic, Position, RhaiResultOf};
|
use crate::{Dynamic, Position, RhaiResultOf};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -52,26 +51,23 @@ pub fn calc_index<E>(
|
|||||||
length: usize,
|
length: usize,
|
||||||
start: crate::INT,
|
start: crate::INT,
|
||||||
negative_count_from_end: bool,
|
negative_count_from_end: bool,
|
||||||
err: impl FnOnce() -> Result<usize, E>,
|
err_func: impl FnOnce() -> Result<usize, E>,
|
||||||
) -> Result<usize, E> {
|
) -> Result<usize, E> {
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
if negative_count_from_end {
|
if negative_count_from_end {
|
||||||
let abs_start = start.unsigned_abs() as usize;
|
let abs_start = start.unsigned_abs() as usize;
|
||||||
|
|
||||||
// Count from end if negative
|
// Count from end if negative
|
||||||
if abs_start > length {
|
if abs_start <= length {
|
||||||
err()
|
return Ok(length - abs_start);
|
||||||
} else {
|
|
||||||
Ok(length - abs_start)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err()
|
|
||||||
}
|
}
|
||||||
} else if start > crate::MAX_USIZE_INT || start as usize >= length {
|
|
||||||
err()
|
|
||||||
} else {
|
|
||||||
Ok(start as usize)
|
|
||||||
}
|
}
|
||||||
|
if start <= crate::MAX_USIZE_INT && (start as usize) < length {
|
||||||
|
return Ok(start as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
err_func()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that encapsulates a mutation target for an expression with side effects.
|
/// A type that encapsulates a mutation target for an expression with side effects.
|
||||||
@ -180,7 +176,7 @@ impl<'a> Target<'a> {
|
|||||||
Self::RefMut(r) => r.is_shared(),
|
Self::RefMut(r) => r.is_shared(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { .. } => true,
|
Self::SharedValue { .. } => true,
|
||||||
Self::TempValue(r) => r.is_shared(),
|
Self::TempValue(value) => value.is_shared(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { .. }
|
Self::Bit { .. }
|
||||||
| Self::BitField { .. }
|
| Self::BitField { .. }
|
||||||
@ -188,29 +184,6 @@ impl<'a> Target<'a> {
|
|||||||
| Self::StringChar { .. } => false,
|
| Self::StringChar { .. } => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the [`Target`] a specific type?
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is<T: Variant + Clone>(&self) -> bool {
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::RefMut(r) => r.is::<T>(),
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
Self::SharedValue { source, .. } => source.is::<T>(),
|
|
||||||
Self::TempValue(r) => r.is::<T>(),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::Bit { .. } => TypeId::of::<T>() == TypeId::of::<bool>(),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::BitField { .. } => TypeId::of::<T>() == TypeId::of::<crate::INT>(),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::BlobByte { .. } => TypeId::of::<T>() == TypeId::of::<crate::Blob>(),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Self::StringChar { .. } => TypeId::of::<T>() == TypeId::of::<char>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get the value of the [`Target`] as a [`Dynamic`], cloning a referenced value if necessary.
|
/// Get the value of the [`Target`] as a [`Dynamic`], cloning a referenced value if necessary.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -219,7 +192,7 @@ impl<'a> Target<'a> {
|
|||||||
Self::RefMut(r) => r.clone(), // Referenced value is cloned
|
Self::RefMut(r) => r.clone(), // Referenced value is cloned
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { value, .. } => value, // Original shared value is simply taken
|
Self::SharedValue { value, .. } => value, // Original shared value is simply taken
|
||||||
Self::TempValue(v) => v, // Owned value is simply taken
|
Self::TempValue(value) => value, // Owned value is simply taken
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { value, .. } => value, // boolean is taken
|
Self::Bit { value, .. } => value, // boolean is taken
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -259,7 +232,7 @@ impl<'a> Target<'a> {
|
|||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { source, .. } => source,
|
Self::SharedValue { source, .. } => source,
|
||||||
Self::TempValue(v) => v,
|
Self::TempValue(value) => value,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { source, .. } => source,
|
Self::Bit { source, .. } => source,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -342,13 +315,7 @@ impl<'a> Target<'a> {
|
|||||||
|
|
||||||
let value = &mut *source.write_lock::<crate::Blob>().expect("`Blob`");
|
let value = &mut *source.write_lock::<crate::Blob>().expect("`Blob`");
|
||||||
|
|
||||||
let index = *index;
|
value[*index] = (new_byte & 0x00ff) as u8;
|
||||||
|
|
||||||
if index < value.len() {
|
|
||||||
value[index] = (new_byte & 0x00ff) as u8;
|
|
||||||
} else {
|
|
||||||
unreachable!("blob index out of bounds: {}", index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::StringChar {
|
Self::StringChar {
|
||||||
@ -369,12 +336,10 @@ impl<'a> Target<'a> {
|
|||||||
.write_lock::<crate::ImmutableString>()
|
.write_lock::<crate::ImmutableString>()
|
||||||
.expect("`ImmutableString`");
|
.expect("`ImmutableString`");
|
||||||
|
|
||||||
let index = *index;
|
|
||||||
|
|
||||||
*s = s
|
*s = s
|
||||||
.chars()
|
.chars()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, ch)| if i == index { new_ch } else { ch })
|
.map(|(i, ch)| if i == *index { new_ch } else { ch })
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -407,7 +372,7 @@ impl Deref for Target<'_> {
|
|||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { source, .. } => source,
|
Self::SharedValue { source, .. } => source,
|
||||||
Self::TempValue(ref r) => r,
|
Self::TempValue(ref value) => value,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { ref value, .. }
|
Self::Bit { ref value, .. }
|
||||||
| Self::BitField { ref value, .. }
|
| Self::BitField { ref value, .. }
|
||||||
@ -440,7 +405,7 @@ impl DerefMut for Target<'_> {
|
|||||||
Self::RefMut(r) => r,
|
Self::RefMut(r) => r,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::SharedValue { source, .. } => &mut *source,
|
Self::SharedValue { source, .. } => &mut *source,
|
||||||
Self::TempValue(ref mut r) => r,
|
Self::TempValue(ref mut value) => value,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::Bit { ref mut value, .. }
|
Self::Bit { ref mut value, .. }
|
||||||
| Self::BitField { ref mut value, .. }
|
| Self::BitField { ref mut value, .. }
|
||||||
|
@ -66,6 +66,13 @@ impl<T: Variant + Clone> FuncArgs for Vec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Variant + Clone, const N: usize> FuncArgs for [T; N] {
|
||||||
|
#[inline]
|
||||||
|
fn parse<ARGS: Extend<Dynamic>>(self, args: &mut ARGS) {
|
||||||
|
args.extend(IntoIterator::into_iter(self).map(Dynamic::from));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Macro to implement [`FuncArgs`] for tuples of standard types (each can be converted into a [`Dynamic`]).
|
/// Macro to implement [`FuncArgs`] for tuples of standard types (each can be converted into a [`Dynamic`]).
|
||||||
macro_rules! impl_args {
|
macro_rules! impl_args {
|
||||||
($($p:ident),*) => {
|
($($p:ident),*) => {
|
||||||
|
@ -12,7 +12,7 @@ use crate::tokenizer::{is_valid_function_name, Token};
|
|||||||
use crate::types::RestoreOnDrop;
|
use crate::types::RestoreOnDrop;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString,
|
||||||
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, ERR,
|
OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
@ -166,7 +166,7 @@ impl Engine {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
fn resolve_fn<'s>(
|
fn resolve_fn<'s>(
|
||||||
&self,
|
&self,
|
||||||
global: &GlobalRuntimeState,
|
_global: &GlobalRuntimeState,
|
||||||
caches: &'s mut Caches,
|
caches: &'s mut Caches,
|
||||||
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
||||||
op_token: Option<&Token>,
|
op_token: Option<&Token>,
|
||||||
@ -194,7 +194,7 @@ impl Engine {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let func = global
|
let func = _global
|
||||||
.lib
|
.lib
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
@ -214,7 +214,7 @@ impl Engine {
|
|||||||
// Scripted functions are not exposed globally
|
// Scripted functions are not exposed globally
|
||||||
func
|
func
|
||||||
} else {
|
} else {
|
||||||
func.or_else(|| global.get_qualified_fn(hash)).or_else(|| {
|
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
|
||||||
self.global_sub_modules
|
self.global_sub_modules
|
||||||
.values()
|
.values()
|
||||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||||
@ -223,17 +223,17 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some((f, s)) = func {
|
if let Some((f, s)) = func {
|
||||||
// Specific version found
|
// Specific version found
|
||||||
let new_entry = Some(FnResolutionCacheEntry {
|
let new_entry = FnResolutionCacheEntry {
|
||||||
func: f.clone(),
|
func: f.clone(),
|
||||||
source: s.cloned(),
|
source: s.cloned(),
|
||||||
});
|
};
|
||||||
return if cache.filter.is_absent_and_set(hash) {
|
return if cache.filter.is_absent_and_set(hash) {
|
||||||
// Do not cache "one-hit wonders"
|
// Do not cache "one-hit wonders"
|
||||||
*local_entry = new_entry;
|
*local_entry = Some(new_entry);
|
||||||
local_entry.as_ref()
|
local_entry.as_ref()
|
||||||
} else {
|
} else {
|
||||||
// Cache entry
|
// Cache entry
|
||||||
entry.insert(new_entry).as_ref()
|
entry.insert(Some(new_entry)).as_ref()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,14 +246,14 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_dynamic = is_dynamic
|
let is_dynamic = is_dynamic
|
||||||
|| global
|
|| _global
|
||||||
.lib
|
.lib
|
||||||
.iter()
|
.iter()
|
||||||
.any(|m| m.may_contain_dynamic_fn(hash_base));
|
.any(|m| m.may_contain_dynamic_fn(hash_base));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let is_dynamic = is_dynamic
|
let is_dynamic = is_dynamic
|
||||||
|| global.may_contain_dynamic_fn(hash_base)
|
|| _global.may_contain_dynamic_fn(hash_base)
|
||||||
|| self
|
|| self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.values()
|
.values()
|
||||||
@ -279,13 +279,13 @@ impl Engine {
|
|||||||
|
|
||||||
get_builtin_op_assignment_fn(token, *first_arg, rest_args[0])
|
get_builtin_op_assignment_fn(token, *first_arg, rest_args[0])
|
||||||
.map(|f| FnResolutionCacheEntry {
|
.map(|f| FnResolutionCacheEntry {
|
||||||
func: CallableFunction::from_fn_builtin(f),
|
func: CallableFunction::Method(Shared::new(f)),
|
||||||
source: None,
|
source: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some(token) => get_builtin_binary_op_fn(token, args[0], args[1])
|
Some(token) => get_builtin_binary_op_fn(token, args[0], args[1])
|
||||||
.map(|f| FnResolutionCacheEntry {
|
.map(|f| FnResolutionCacheEntry {
|
||||||
func: CallableFunction::from_fn_builtin(f),
|
func: CallableFunction::Method(Shared::new(f)),
|
||||||
source: None,
|
source: None,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -363,16 +363,13 @@ impl Engine {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
if func.is_some() {
|
if let Some(FnResolutionCacheEntry { func, source }) = func {
|
||||||
let is_method = func.map_or(false, |f| f.func.is_method());
|
assert!(func.is_native());
|
||||||
|
|
||||||
// Push a new call stack frame
|
// Push a new call stack frame
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let orig_call_stack_len = global.debugger.call_stack().len();
|
let orig_call_stack_len = global.debugger.call_stack().len();
|
||||||
|
|
||||||
let FnResolutionCacheEntry { func, source } = func.unwrap();
|
|
||||||
assert!(func.is_native());
|
|
||||||
|
|
||||||
let backup = &mut ArgBackup::new();
|
let backup = &mut ArgBackup::new();
|
||||||
|
|
||||||
// Calling pure function but the first argument is a reference?
|
// Calling pure function but the first argument is a reference?
|
||||||
@ -397,6 +394,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run external function
|
// Run external function
|
||||||
|
let is_method = func.is_method();
|
||||||
let src = source.as_ref().map(|s| s.as_str());
|
let src = source.as_ref().map(|s| s.as_str());
|
||||||
let context = (self, name, src, &*global, pos).into();
|
let context = (self, name, src, &*global, pos).into();
|
||||||
|
|
||||||
@ -406,16 +404,22 @@ impl Engine {
|
|||||||
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
|
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
|
||||||
} else {
|
} else {
|
||||||
f.call(context, args)
|
f.call(context, args)
|
||||||
|
.and_then(|r| self.check_data_size(r, pos))
|
||||||
|
.map_err(|err| err.fill_position(pos))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
func.get_native_fn().unwrap()(context, args)
|
func.get_native_fn().unwrap()(context, args)
|
||||||
|
.and_then(|r| self.check_data_size(r, pos))
|
||||||
|
.map_err(|err| err.fill_position(pos))
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
{
|
{
|
||||||
|
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
||||||
|
|
||||||
let trigger = match global.debugger.status {
|
let trigger = match global.debugger.status {
|
||||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
DebuggerStatus::FunctionExit(n) => n >= global.level,
|
||||||
crate::eval::DebuggerStatus::Next(.., true) => true,
|
DebuggerStatus::Next(.., true) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if trigger {
|
if trigger {
|
||||||
@ -424,8 +428,8 @@ impl Engine {
|
|||||||
let node = crate::ast::Stmt::Noop(pos);
|
let node = crate::ast::Stmt::Noop(pos);
|
||||||
let node = (&node).into();
|
let node = (&node).into();
|
||||||
let event = match _result {
|
let event = match _result {
|
||||||
Ok(ref r) => crate::eval::DebuggerEvent::FunctionExitWithValue(r),
|
Ok(ref r) => DebuggerEvent::FunctionExitWithValue(r),
|
||||||
Err(ref err) => crate::eval::DebuggerEvent::FunctionExitWithError(err),
|
Err(ref err) => DebuggerEvent::FunctionExitWithError(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) =
|
if let Err(err) =
|
||||||
@ -439,13 +443,12 @@ impl Engine {
|
|||||||
global.debugger.rewind_call_stack(orig_call_stack_len);
|
global.debugger.rewind_call_stack(orig_call_stack_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the return value (including data sizes)
|
let result = _result?;
|
||||||
let result = self.check_return_value(_result, pos)?;
|
|
||||||
|
|
||||||
// Check the data size of any `&mut` object, which may be changed.
|
// Check the data size of any `&mut` object, which may be changed.
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if is_ref_mut && !args.is_empty() {
|
if is_ref_mut && !args.is_empty() {
|
||||||
self.check_data_size(args[0], pos)?;
|
self.check_data_size(&*args[0], pos)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if the function match print/debug (which requires special processing)
|
// See if the function match print/debug (which requires special processing)
|
||||||
@ -1188,14 +1191,14 @@ impl Engine {
|
|||||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let (mut target, _pos) =
|
let mut target =
|
||||||
self.search_namespace(global, caches, scope, this_ptr, first_expr)?;
|
self.search_namespace(global, caches, scope, this_ptr, first_expr)?;
|
||||||
|
|
||||||
if target.is_read_only() {
|
if target.is_read_only() {
|
||||||
target = target.into_owned();
|
target = target.into_owned();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.track_operation(global, _pos)?;
|
self.track_operation(global, first_expr.position())?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1269,10 +1272,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
let first_arg = &args_expr[0];
|
let first_arg = &args_expr[0];
|
||||||
let (target, _pos) =
|
let target = self.search_scope_only(global, caches, scope, this_ptr, first_arg)?;
|
||||||
self.search_scope_only(global, caches, scope, this_ptr, first_arg)?;
|
|
||||||
|
|
||||||
self.track_operation(global, _pos)?;
|
self.track_operation(global, first_arg.position())?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1382,19 +1384,18 @@ impl Engine {
|
|||||||
Some(f) if f.is_plugin_fn() => {
|
Some(f) if f.is_plugin_fn() => {
|
||||||
let context = (self, fn_name, module.id(), &*global, pos).into();
|
let context = (self, fn_name, module.id(), &*global, pos).into();
|
||||||
let f = f.get_plugin_fn().expect("plugin function");
|
let f = f.get_plugin_fn().expect("plugin function");
|
||||||
let result = if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
||||||
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
||||||
} else {
|
} else {
|
||||||
f.call(context, &mut args)
|
f.call(context, &mut args)
|
||||||
};
|
.and_then(|r| self.check_data_size(r, pos))
|
||||||
self.check_return_value(result, pos)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(f) if f.is_native() => {
|
Some(f) if f.is_native() => {
|
||||||
let func = f.get_native_fn().expect("native function");
|
let func = f.get_native_fn().expect("native function");
|
||||||
let context = (self, fn_name, module.id(), &*global, pos).into();
|
let context = (self, fn_name, module.id(), &*global, pos).into();
|
||||||
let result = func(context, &mut args);
|
func(context, &mut args).and_then(|r| self.check_data_size(r, pos))
|
||||||
self.check_return_value(result, pos)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(f) => unreachable!("unknown function type: {:?}", f),
|
Some(f) => unreachable!("unknown function type: {:?}", f),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Module defining the standard Rhai function type.
|
//! Module defining the standard Rhai function type.
|
||||||
|
|
||||||
use super::native::{FnAny, FnBuiltin, FnPlugin, IteratorFn, SendSync};
|
use super::native::{FnAny, FnPlugin, IteratorFn, SendSync};
|
||||||
use crate::ast::FnAccess;
|
use crate::ast::FnAccess;
|
||||||
use crate::plugin::PluginFunction;
|
use crate::plugin::PluginFunction;
|
||||||
use crate::Shared;
|
use crate::Shared;
|
||||||
@ -8,7 +8,8 @@ use std::fmt;
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
/// A type encapsulating a function callable by Rhai.
|
/// _(internals)_ A type encapsulating a function callable by Rhai.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum CallableFunction {
|
pub enum CallableFunction {
|
||||||
@ -199,18 +200,6 @@ impl CallableFunction {
|
|||||||
Self::Script(..) => None,
|
Self::Script(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a new [`CallableFunction::Method`] from a built-in function.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_fn_builtin(func: FnBuiltin) -> Self {
|
|
||||||
Self::Method(Shared::new(func))
|
|
||||||
}
|
|
||||||
/// Create a new [`CallableFunction::Plugin`].
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
|
|
||||||
Self::Plugin(Shared::new(func))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -232,7 +221,7 @@ impl From<Shared<crate::ast::ScriptFnDef>> for CallableFunction {
|
|||||||
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
|
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(func: T) -> Self {
|
fn from(func: T) -> Self {
|
||||||
Self::from_plugin(func)
|
Self::Plugin(Shared::new(func))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
///
|
///
|
||||||
/// # Zeros
|
/// # Zeros
|
||||||
///
|
///
|
||||||
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
|
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
@ -107,9 +107,8 @@ pub fn calc_var_hash<'a>(
|
|||||||
|
|
||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
let iter = modules.into_iter();
|
let iter = modules.into_iter();
|
||||||
let len = iter.len();
|
iter.len().hash(s);
|
||||||
iter.skip(1).for_each(|m| m.hash(s));
|
iter.skip(1).for_each(|m| m.hash(s));
|
||||||
len.hash(s);
|
|
||||||
var_name.hash(s);
|
var_name.hash(s);
|
||||||
|
|
||||||
match s.finish() {
|
match s.finish() {
|
||||||
@ -128,7 +127,7 @@ pub fn calc_var_hash<'a>(
|
|||||||
///
|
///
|
||||||
/// # Zeros
|
/// # Zeros
|
||||||
///
|
///
|
||||||
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
|
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
||||||
///
|
///
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
@ -144,9 +143,8 @@ pub fn calc_fn_hash<'a>(
|
|||||||
|
|
||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
let iter = namespace.into_iter();
|
let iter = namespace.into_iter();
|
||||||
let len = iter.len();
|
iter.len().hash(s);
|
||||||
iter.skip(1).for_each(|m| m.hash(s));
|
iter.skip(1).for_each(|m| m.hash(s));
|
||||||
len.hash(s);
|
|
||||||
fn_name.hash(s);
|
fn_name.hash(s);
|
||||||
num.hash(s);
|
num.hash(s);
|
||||||
|
|
||||||
@ -162,7 +160,7 @@ pub fn calc_fn_hash<'a>(
|
|||||||
///
|
///
|
||||||
/// # Zeros
|
/// # Zeros
|
||||||
///
|
///
|
||||||
/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`.
|
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_fn_hash_full(
|
pub fn calc_fn_hash_full(
|
||||||
@ -172,11 +170,10 @@ pub fn calc_fn_hash_full(
|
|||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
base.hash(s);
|
base.hash(s);
|
||||||
let iter = params.into_iter();
|
let iter = params.into_iter();
|
||||||
let len = iter.len();
|
iter.len().hash(s);
|
||||||
iter.for_each(|t| {
|
iter.for_each(|t| {
|
||||||
t.hash(s);
|
t.hash(s);
|
||||||
});
|
});
|
||||||
len.hash(s);
|
|
||||||
|
|
||||||
match s.finish() {
|
match s.finish() {
|
||||||
0 => ALT_ZERO_HASH,
|
0 => ALT_ZERO_HASH,
|
||||||
|
@ -7,10 +7,10 @@ use crate::plugin::PluginFunction;
|
|||||||
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
use crate::tokenizer::{is_valid_function_name, Token, TokenizeState};
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Module, Position, RhaiResult,
|
calc_fn_hash, reify, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult,
|
||||||
RhaiResultOf, SharedModule, StaticVec, VarDefInfo, ERR,
|
RhaiResultOf, StaticVec, VarDefInfo, ERR,
|
||||||
};
|
};
|
||||||
use std::any::type_name;
|
use std::any::{type_name, TypeId};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ pub struct NativeCallContextStore {
|
|||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
impl NativeCallContextStore {
|
impl NativeCallContextStore {
|
||||||
/// Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
|
/// Create a [`NativeCallContext`] from a [`NativeCallContextStore`].
|
||||||
///
|
///
|
||||||
/// # WARNING - Unstable API
|
/// # WARNING - Unstable API
|
||||||
///
|
///
|
||||||
@ -167,7 +167,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
|
/// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextStore`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # WARNING - Unstable API
|
/// # WARNING - Unstable API
|
||||||
@ -187,7 +187,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
pos: context.pos,
|
pos: context.pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`].
|
/// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextStore`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # WARNING - Unstable API
|
/// # WARNING - Unstable API
|
||||||
@ -249,7 +249,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
||||||
self.global.iter_imports()
|
self.global.iter_imports()
|
||||||
}
|
}
|
||||||
/// Get an iterator over the current set of modules imported via `import` statements in reverse order.
|
/// Get an iterator over the current set of modules imported via `import` statements in reverse order.
|
||||||
@ -258,7 +258,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn iter_imports_raw(
|
pub(crate) fn iter_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&crate::ImmutableString, &SharedModule)> {
|
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::SharedModule)> {
|
||||||
self.global.iter_imports_raw()
|
self.global.iter_imports_raw()
|
||||||
}
|
}
|
||||||
/// _(internals)_ The current [`GlobalRuntimeState`], if any.
|
/// _(internals)_ The current [`GlobalRuntimeState`], if any.
|
||||||
@ -277,7 +277,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
|
pub fn iter_namespaces(&self) -> impl Iterator<Item = &crate::Module> {
|
||||||
self.global.lib.iter().map(|m| m.as_ref())
|
self.global.lib.iter().map(|m| m.as_ref())
|
||||||
}
|
}
|
||||||
/// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
|
/// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
|
||||||
@ -288,7 +288,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn namespaces(&self) -> &[SharedModule] {
|
pub fn namespaces(&self) -> &[crate::SharedModule] {
|
||||||
&self.global.lib
|
&self.global.lib
|
||||||
}
|
}
|
||||||
/// Call a function inside the call context with the provided arguments.
|
/// Call a function inside the call context with the provided arguments.
|
||||||
@ -303,7 +303,12 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
let result = self._call_fn_raw(fn_name, false, false, false, &mut args)?;
|
let result = self._call_fn_raw(fn_name, &mut args, false, false, false)?;
|
||||||
|
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify!(result => T));
|
||||||
|
}
|
||||||
|
|
||||||
let typ = self.engine().map_type_name(result.type_name());
|
let typ = self.engine().map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -328,7 +333,12 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
let result = self._call_fn_raw(fn_name, true, false, false, &mut args)?;
|
let result = self._call_fn_raw(fn_name, &mut args, true, false, false)?;
|
||||||
|
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify!(result => T));
|
||||||
|
}
|
||||||
|
|
||||||
let typ = self.engine().map_type_name(result.type_name());
|
let typ = self.engine().map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -369,7 +379,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let native_only = native_only && !crate::parser::is_anonymous_fn(name);
|
let native_only = native_only && !crate::parser::is_anonymous_fn(name);
|
||||||
|
|
||||||
self._call_fn_raw(fn_name, native_only, is_ref_mut, is_method_call, args)
|
self._call_fn_raw(fn_name, args, native_only, is_ref_mut, is_method_call)
|
||||||
}
|
}
|
||||||
/// Call a registered native Rust function inside the call context.
|
/// Call a registered native Rust function inside the call context.
|
||||||
///
|
///
|
||||||
@ -398,17 +408,17 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
self._call_fn_raw(fn_name, true, is_ref_mut, false, args)
|
self._call_fn_raw(fn_name, args, true, is_ref_mut, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function (native Rust or scripted) inside the call context.
|
/// Call a function (native Rust or scripted) inside the call context.
|
||||||
fn _call_fn_raw(
|
fn _call_fn_raw(
|
||||||
&self,
|
&self,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: impl AsRef<str>,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
native_only: bool,
|
native_only: bool,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
is_method_call: bool,
|
is_method_call: bool,
|
||||||
args: &mut [&mut Dynamic],
|
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut global = &mut self.global.clone();
|
let mut global = &mut self.global.clone();
|
||||||
let caches = &mut Caches::new();
|
let caches = &mut Caches::new();
|
||||||
@ -509,7 +519,8 @@ pub fn shared_take<T>(value: Shared<T>) -> T {
|
|||||||
shared_try_take(value).ok().expect("not shared")
|
shared_try_take(value).ok().expect("not shared")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lock a [`Locked`] resource for mutable access.
|
/// _(internals)_ Lock a [`Locked`] resource for mutable access.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -521,7 +532,8 @@ pub fn locked_read<T>(value: &Locked<T>) -> LockGuard<T> {
|
|||||||
return value.read().unwrap();
|
return value.read().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lock a [`Locked`] resource for mutable access.
|
/// _(internals)_ Lock a [`Locked`] resource for mutable access.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -201,8 +201,6 @@ pub use api::{eval::eval, events::VarDefInfo, run::run};
|
|||||||
pub use ast::{FnAccess, AST};
|
pub use ast::{FnAccess, AST};
|
||||||
pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
||||||
pub use eval::EvalContext;
|
pub use eval::EvalContext;
|
||||||
#[cfg(feature = "internals")]
|
|
||||||
pub use func::native::{locked_read, locked_write};
|
|
||||||
pub use func::{NativeCallContext, RegisterNativeFunction};
|
pub use func::{NativeCallContext, RegisterNativeFunction};
|
||||||
pub use module::{FnNamespace, Module};
|
pub use module::{FnNamespace, Module};
|
||||||
pub use tokenizer::Position;
|
pub use tokenizer::Position;
|
||||||
@ -255,6 +253,9 @@ pub use func::Func;
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use ast::ScriptFnMetadata;
|
pub use ast::ScriptFnMetadata;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub use api::call_fn::CallFnOptions;
|
||||||
|
|
||||||
/// Variable-sized array of [`Dynamic`] values.
|
/// Variable-sized array of [`Dynamic`] values.
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under `no_index`.
|
||||||
@ -342,7 +343,7 @@ pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeS
|
|||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
pub use func::NativeCallContextStore;
|
pub use func::{locked_read, locked_write, CallableFunction, NativeCallContextStore};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{ops::AddAssign, slice::Iter};
|
use std::{ops::AddAssign, slice::Iter};
|
||||||
|
|
||||||
/// [Module] resolution service that holds a collection of module resolvers,
|
/// [Module][crate::Module] resolution service that holds a collection of module resolvers,
|
||||||
/// to be searched in sequential order.
|
/// to be searched in sequential order.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -2,7 +2,7 @@ use crate::{Engine, ModuleResolver, Position, RhaiResultOf, SharedModule, ERR};
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
/// Empty/disabled [module][Module] resolution service that acts as a dummy.
|
/// Empty/disabled [module][crate::Module] resolution service that acts as a dummy.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -848,8 +848,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
Stmt::Expr(expr) => {
|
Stmt::Expr(expr) => {
|
||||||
optimize_expr(expr, state, false);
|
optimize_expr(expr, state, false);
|
||||||
|
|
||||||
if matches!(**expr, Expr::FnCall(..) | Expr::Stmt(..)) {
|
// Do not promote until the expression is fully optimized
|
||||||
state.set_dirty();
|
if !state.is_dirty() && matches!(**expr, Expr::FnCall(..) | Expr::Stmt(..)) {
|
||||||
*stmt = match *mem::take(expr) {
|
*stmt = match *mem::take(expr) {
|
||||||
// func(...);
|
// func(...);
|
||||||
Expr::FnCall(x, pos) => Stmt::FnCall(x, pos),
|
Expr::FnCall(x, pos) => Stmt::FnCall(x, pos),
|
||||||
@ -859,6 +859,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
Expr::Stmt(x) => (*x).into(),
|
Expr::Stmt(x) => (*x).into(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,6 +923,22 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
// var.rhs
|
// var.rhs
|
||||||
(Expr::Variable(..), rhs) => optimize_expr(rhs, state, true),
|
(Expr::Variable(..), rhs) => optimize_expr(rhs, state, true),
|
||||||
|
// const.type_of()
|
||||||
|
(lhs, Expr::MethodCall(x, pos)) if lhs.is_constant() && x.name == KEYWORD_TYPE_OF && x.args.is_empty() => {
|
||||||
|
if let Some(value) = lhs.get_literal_value() {
|
||||||
|
state.set_dirty();
|
||||||
|
let typ = state.engine.map_type_name(value.type_name()).into();
|
||||||
|
*expr = Expr::from_dynamic(typ, *pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// const.is_shared()
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
(lhs, Expr::MethodCall(x, pos)) if lhs.is_constant() && x.name == crate::engine::KEYWORD_IS_SHARED && x.args.is_empty() => {
|
||||||
|
if let Some(..) = lhs.get_literal_value() {
|
||||||
|
state.set_dirty();
|
||||||
|
*expr = Expr::from_dynamic(Dynamic::FALSE, *pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
|
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ use rust_decimal::Decimal;
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn std_add<T>(x: T, y: T) -> Option<T>
|
fn std_add<T>(x: T, y: T) -> Option<T>
|
||||||
where
|
where
|
||||||
T: Debug + Copy + PartialOrd + num_traits::CheckedAdd<Output = T>,
|
T: num_traits::CheckedAdd<Output = T>,
|
||||||
{
|
{
|
||||||
x.checked_add(&y)
|
x.checked_add(&y)
|
||||||
}
|
}
|
||||||
@ -31,14 +31,14 @@ where
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn regular_add<T>(x: T, y: T) -> Option<T>
|
fn regular_add<T>(x: T, y: T) -> Option<T>
|
||||||
where
|
where
|
||||||
T: Debug + Copy + PartialOrd + std::ops::Add<Output = T>,
|
T: std::ops::Add<Output = T>,
|
||||||
{
|
{
|
||||||
Some(x + y)
|
Some(x + y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range iterator with step
|
// Range iterator with step
|
||||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||||
pub struct StepRange<T: Debug + Copy + PartialOrd> {
|
pub struct StepRange<T> {
|
||||||
pub from: T,
|
pub from: T,
|
||||||
pub to: T,
|
pub to: T,
|
||||||
pub step: T,
|
pub step: T,
|
||||||
@ -46,7 +46,7 @@ pub struct StepRange<T: Debug + Copy + PartialOrd> {
|
|||||||
pub dir: i8,
|
pub dir: i8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Debug + Copy + PartialOrd> Debug for StepRange<T> {
|
impl<T: Debug> Debug for StepRange<T> {
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
@ -58,7 +58,7 @@ impl<T: Debug + Copy + PartialOrd> Debug for StepRange<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Debug + Copy + PartialOrd> StepRange<T> {
|
impl<T: Copy + PartialOrd> StepRange<T> {
|
||||||
pub fn new(from: T, to: T, step: T, add: fn(T, T) -> Option<T>) -> RhaiResultOf<Self> {
|
pub fn new(from: T, to: T, step: T, add: fn(T, T) -> Option<T>) -> RhaiResultOf<Self> {
|
||||||
let mut dir = 0;
|
let mut dir = 0;
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ impl<T: Debug + Copy + PartialOrd> StepRange<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Debug + Copy + PartialOrd> Iterator for StepRange<T> {
|
impl<T: Copy + PartialOrd> Iterator for StepRange<T> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<T> {
|
fn next(&mut self) -> Option<T> {
|
||||||
@ -118,7 +118,7 @@ impl<T: Debug + Copy + PartialOrd> Iterator for StepRange<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Debug + Copy + PartialOrd> FusedIterator for StepRange<T> {}
|
impl<T: Copy + PartialOrd> FusedIterator for StepRange<T> {}
|
||||||
|
|
||||||
// Bit-field iterator with step
|
// Bit-field iterator with step
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
|
@ -3818,7 +3818,7 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
process_settings: impl Fn(&mut ParseSettings),
|
process_settings: impl FnOnce(&mut ParseSettings),
|
||||||
_optimization_level: OptimizationLevel,
|
_optimization_level: OptimizationLevel,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let mut functions = StraightHashMap::default();
|
let mut functions = StraightHashMap::default();
|
||||||
@ -3882,7 +3882,7 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
process_settings: impl Fn(&mut ParseSettings),
|
process_settings: impl FnOnce(&mut ParseSettings),
|
||||||
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
|
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
|
||||||
let mut statements = StmtBlockContainer::new_const();
|
let mut statements = StmtBlockContainer::new_const();
|
||||||
let mut functions = StraightHashMap::default();
|
let mut functions = StraightHashMap::default();
|
||||||
|
@ -127,20 +127,21 @@ impl<'de> Visitor<'de> for DynamicVisitor {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
|
fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
|
||||||
self.visit_string(v.to_string())
|
Ok(v.into())
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||||
Ok(v.into())
|
Ok(v.into())
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn visit_borrowed_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
|
|
||||||
self.visit_str(v)
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
|
||||||
Ok(v.into())
|
Ok(v.into())
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[inline(always)]
|
||||||
|
fn visit_bytes<E: Error>(self, v: &[u8]) -> Result<Self::Value, E> {
|
||||||
|
Ok(Dynamic::from_blob(v.to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
|
fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
|
||||||
|
@ -20,7 +20,7 @@ impl Serialize for Dynamic {
|
|||||||
Union::Unit(..) => ser.serialize_unit(),
|
Union::Unit(..) => ser.serialize_unit(),
|
||||||
Union::Bool(x, ..) => ser.serialize_bool(x),
|
Union::Bool(x, ..) => ser.serialize_bool(x),
|
||||||
Union::Str(ref s, ..) => ser.serialize_str(s.as_str()),
|
Union::Str(ref s, ..) => ser.serialize_str(s.as_str()),
|
||||||
Union::Char(c, ..) => ser.serialize_str(&c.to_string()),
|
Union::Char(c, ..) => ser.serialize_char(c),
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
Union::Int(x, ..) => ser.serialize_i64(x),
|
Union::Int(x, ..) => ser.serialize_i64(x),
|
||||||
@ -58,7 +58,7 @@ impl Serialize for Dynamic {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(ref a, ..) => (**a).serialize(ser),
|
Union::Array(ref a, ..) => (**a).serialize(ser),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Blob(ref a, ..) => (**a).serialize(ser),
|
Union::Blob(ref a, ..) => ser.serialize_bytes(&**a),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(ref m, ..) => {
|
Union::Map(ref m, ..) => {
|
||||||
let mut map = ser.serialize_map(Some(m.len()))?;
|
let mut map = ser.serialize_map(Some(m.len()))?;
|
||||||
|
@ -1181,42 +1181,105 @@ impl Dynamic {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn try_cast<T: Any>(self) -> Option<T> {
|
pub fn try_cast<T: Any>(mut self) -> Option<T> {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if let Union::Shared(..) = self.0 {
|
self.flatten_in_place();
|
||||||
return self.flatten().try_cast::<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
reify!(self, |v: T| return Some(v));
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Some(reify!(self => T));
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Unit(..) => Some(reify!(() => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Int(n, ..) => Some(reify!(n => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Float(v, ..) => Some(reify!(*v => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<rust_decimal::Decimal>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Decimal(v, ..) => Some(reify!(*v => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Bool(b, ..) => Some(reify!(b => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Str(s, ..) => Some(reify!(s => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Str(s, ..) => Some(reify!(s.to_string() => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Char(c, ..) => Some(reify!(c => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<crate::Array>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Array(a, ..) => Some(reify!(*a => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<crate::Blob>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Blob(b, ..) => Some(reify!(*b => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<crate::Map>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::Map(m, ..) => Some(reify!(*m => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::FnPtr(f, ..) => Some(reify!(*f => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_time"))]
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
|
return match self.0 {
|
||||||
|
Union::TimeStamp(t, ..) => Some(reify!(*t => T)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Null => unreachable!(),
|
|
||||||
|
|
||||||
Union::Int(v, ..) => reify!(v => Option<T>),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
Union::Float(v, ..) => reify!(*v => Option<T>),
|
|
||||||
#[cfg(feature = "decimal")]
|
|
||||||
Union::Decimal(v, ..) => reify!(*v => Option<T>),
|
|
||||||
Union::Bool(v, ..) => reify!(v => Option<T>),
|
|
||||||
Union::Str(v, ..) => {
|
|
||||||
reify!(v, |v: T| Some(v), || reify!(v.to_string() => Option<T>))
|
|
||||||
}
|
|
||||||
Union::Char(v, ..) => reify!(v => Option<T>),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Union::Array(v, ..) => reify!(*v => Option<T>),
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
Union::Blob(v, ..) => reify!(*v => Option<T>),
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Union::Map(v, ..) => reify!(*v => Option<T>),
|
|
||||||
Union::FnPtr(v, ..) => reify!(*v => Option<T>),
|
|
||||||
#[cfg(not(feature = "no_time"))]
|
|
||||||
Union::TimeStamp(v, ..) => reify!(*v => Option<T>),
|
|
||||||
Union::Unit(v, ..) => reify!(v => Option<T>),
|
|
||||||
Union::Variant(v, ..) => (*v).as_boxed_any().downcast().ok().map(|x| *x),
|
Union::Variant(v, ..) => (*v).as_boxed_any().downcast().ok().map(|x| *x),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(..) => unreachable!("Union::Shared case should be already handled"),
|
Union::Shared(..) => unreachable!("Union::Shared case should be already handled"),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Convert the [`Dynamic`] value into a specific type.
|
/// Convert the [`Dynamic`] value into a specific type.
|
||||||
@ -1245,6 +1308,11 @@ impl Dynamic {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn cast<T: Any + Clone>(self) -> T {
|
pub fn cast<T: Any + Clone>(self) -> T {
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return reify!(self => T);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let self_type_name = if self.is_shared() {
|
let self_type_name = if self.is_shared() {
|
||||||
// Avoid panics/deadlocks with shared values
|
// Avoid panics/deadlocks with shared values
|
||||||
|
@ -198,7 +198,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
if s.starts_with(crate::engine::FN_SET) =>
|
if s.starts_with(crate::engine::FN_SET) =>
|
||||||
{
|
{
|
||||||
let prop = &s[crate::engine::FN_SET.len()..];
|
let prop = &s[crate::engine::FN_SET.len()..];
|
||||||
write!(f, "Cannot modify property {prop} of constant")?
|
write!(f, "Cannot modify property '{prop}' of a constant")?
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => {
|
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_GET => {
|
||||||
@ -209,10 +209,10 @@ impl fmt::Display for EvalAltResult {
|
|||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_SET => {
|
Self::ErrorNonPureMethodCallOnConstant(s, ..) if s == crate::engine::FN_IDX_SET => {
|
||||||
write!(f, "Cannot assign to indexer of constant")?
|
write!(f, "Cannot assign to the indexer of a constant")?
|
||||||
}
|
}
|
||||||
Self::ErrorNonPureMethodCallOnConstant(s, ..) => {
|
Self::ErrorNonPureMethodCallOnConstant(s, ..) => {
|
||||||
write!(f, "Non-pure method {s} cannot be called on constant")?
|
write!(f, "Non-pure method '{s}' cannot be called on a constant")?
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant {s}")?,
|
Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant {s}")?,
|
||||||
@ -230,8 +230,8 @@ impl fmt::Display for EvalAltResult {
|
|||||||
Self::ErrorArithmetic(s, ..) if s.is_empty() => f.write_str("Arithmetic error")?,
|
Self::ErrorArithmetic(s, ..) if s.is_empty() => f.write_str("Arithmetic error")?,
|
||||||
Self::ErrorArithmetic(s, ..) => f.write_str(s)?,
|
Self::ErrorArithmetic(s, ..) => f.write_str(s)?,
|
||||||
|
|
||||||
Self::LoopBreak(true, ..) => f.write_str("'break' not inside a loop")?,
|
Self::LoopBreak(true, ..) => f.write_str("'break' must be inside a loop")?,
|
||||||
Self::LoopBreak(false, ..) => f.write_str("'continue' not inside a loop")?,
|
Self::LoopBreak(false, ..) => f.write_str("'continue' must be inside a loop")?,
|
||||||
|
|
||||||
Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?,
|
Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?,
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ use crate::eval::GlobalRuntimeState;
|
|||||||
use crate::tokenizer::is_valid_function_name;
|
use crate::tokenizer::is_valid_function_name;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, RhaiResult,
|
reify, Dynamic, Engine, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError,
|
||||||
RhaiResultOf, StaticVec, AST, ERR,
|
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
any::type_name,
|
any::{type_name, TypeId},
|
||||||
convert::{TryFrom, TryInto},
|
convert::{TryFrom, TryInto},
|
||||||
fmt, mem,
|
fmt, mem,
|
||||||
};
|
};
|
||||||
@ -160,6 +160,11 @@ impl FnPtr {
|
|||||||
|
|
||||||
let result = self.call_raw(&ctx, None, arg_values)?;
|
let result = self.call_raw(&ctx, None, arg_values)?;
|
||||||
|
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify!(result => T));
|
||||||
|
}
|
||||||
|
|
||||||
let typ = engine.map_type_name(result.type_name());
|
let typ = engine.map_type_name(result.type_name());
|
||||||
|
|
||||||
result.try_cast().ok_or_else(|| {
|
result.try_cast().ok_or_else(|| {
|
||||||
@ -184,6 +189,11 @@ impl FnPtr {
|
|||||||
|
|
||||||
let result = self.call_raw(context, None, arg_values)?;
|
let result = self.call_raw(context, None, arg_values)?;
|
||||||
|
|
||||||
|
// Bail out early if the return type needs no cast
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
|
return Ok(reify!(result => T));
|
||||||
|
}
|
||||||
|
|
||||||
let typ = context.engine().map_type_name(result.type_name());
|
let typ = context.engine().map_type_name(result.type_name());
|
||||||
|
|
||||||
result.try_cast().ok_or_else(|| {
|
result.try_cast().ok_or_else(|| {
|
||||||
|
@ -80,7 +80,7 @@ impl StringsInterner<'_> {
|
|||||||
pub fn get_with_mapper<S: AsRef<str>>(
|
pub fn get_with_mapper<S: AsRef<str>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &str,
|
id: &str,
|
||||||
mapper: impl Fn(S) -> ImmutableString,
|
mapper: impl FnOnce(S) -> ImmutableString,
|
||||||
text: S,
|
text: S,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
let key = text.as_ref();
|
let key = text.as_ref();
|
||||||
|
@ -445,7 +445,8 @@ impl Scope<'_> {
|
|||||||
.rev()
|
.rev()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(.., key)| &name == key)
|
.find(|(.., key)| &name == key)
|
||||||
.and_then(|(index, ..)| self.values[len - 1 - index].flatten_clone().try_cast())
|
.map(|(index, ..)| self.values[len - 1 - index].flatten_clone())
|
||||||
|
.and_then(Dynamic::try_cast)
|
||||||
}
|
}
|
||||||
/// Check if the named entry in the [`Scope`] is constant.
|
/// Check if the named entry in the [`Scope`] is constant.
|
||||||
///
|
///
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, Scope, AST, INT};
|
use rhai::{CallFnOptions, Dynamic, Engine, EvalAltResult, FnPtr, Func, FuncArgs, Scope, AST, INT};
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -50,11 +50,10 @@ fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(!scope.contains("bar"));
|
assert!(!scope.contains("bar"));
|
||||||
|
|
||||||
let args = [(2 as INT).into()];
|
let options = CallFnOptions::new().eval_ast(false).rewind_scope(false);
|
||||||
let r = engine
|
|
||||||
.call_fn_raw(&mut scope, &ast, false, false, "define_var", None, args)?
|
let r =
|
||||||
.as_int()
|
engine.call_fn_with_options::<INT>(options, &mut scope, &ast, "define_var", (2 as INT,))?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(r, 42);
|
assert_eq!(r, 42);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -87,9 +86,13 @@ fn test_call_fn_scope() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
for _ in 0..50 {
|
for _ in 0..50 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine
|
engine.call_fn_with_options::<INT>(
|
||||||
.call_fn_raw(&mut scope, &ast, true, false, "foo", None, [Dynamic::THREE])?
|
CallFnOptions::new().rewind_scope(false),
|
||||||
.as_int()?,
|
&mut scope,
|
||||||
|
&ast,
|
||||||
|
"foo",
|
||||||
|
[Dynamic::THREE],
|
||||||
|
)?,
|
||||||
168
|
168
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -798,6 +798,7 @@ fn test_serde_optional() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
|
fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
@ -805,13 +806,16 @@ fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"
|
"
|
||||||
let x = blob(10);
|
let x = blob(10);
|
||||||
for i in 0..10 { x[i] = i; }
|
for i in 0..10 { x[i] = i; }
|
||||||
x
|
#{ x: x }
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let r = from_dynamic::<serde_bytes::ByteBuf>(&r)?;
|
let data = format!("{r:?}");
|
||||||
|
|
||||||
assert_eq!(r.to_vec(), vec![0_u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
let encoded = rmp_serde::to_vec(&r).unwrap();
|
||||||
|
let decoded: Dynamic = rmp_serde::from_slice(&encoded).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(format!("{decoded:?}"), data);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user