Implement closures.
This commit is contained in:
parent
747c0345f2
commit
4079164bfd
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -30,8 +30,7 @@ jobs:
|
|||||||
- "--features no_object"
|
- "--features no_object"
|
||||||
- "--features no_function"
|
- "--features no_function"
|
||||||
- "--features no_module"
|
- "--features no_module"
|
||||||
- "--features no_capture"
|
- "--features no_closure"
|
||||||
- "--features no_shared"
|
|
||||||
- "--features unicode-xid-ident"
|
- "--features unicode-xid-ident"
|
||||||
toolchain: [stable]
|
toolchain: [stable]
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
|
@ -33,14 +33,13 @@ only_i64 = [] # set INT=i64 (default) and disable support for all other in
|
|||||||
no_index = [] # no arrays and indexing
|
no_index = [] # no arrays and indexing
|
||||||
no_object = [] # no custom objects
|
no_object = [] # no custom objects
|
||||||
no_function = [] # no script-defined functions
|
no_function = [] # no script-defined functions
|
||||||
no_capture = [] # no automatic read/write binding of anonymous function's local variables to it's external context
|
no_closure = [] # no automatic sharing and capture of anonymous functions to external variables
|
||||||
no_shared = [] # no shared values
|
|
||||||
no_module = [] # no modules
|
no_module = [] # no modules
|
||||||
internals = [] # expose internal data structures
|
internals = [] # expose internal data structures
|
||||||
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
||||||
|
|
||||||
# compiling for no-std
|
# compiling for no-std
|
||||||
no_std = [ "no_shared", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ]
|
no_std = [ "no_closure", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash" ]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
@ -25,6 +25,7 @@ New features
|
|||||||
* Capturing of the calling scope for function call via the `func!(...)` syntax.
|
* Capturing of the calling scope for function call via the `func!(...)` syntax.
|
||||||
* `Module::set_indexer_get_set_fn` is added as a shorthand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`.
|
* `Module::set_indexer_get_set_fn` is added as a shorthand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`.
|
||||||
* New `unicode-xid-ident` feature to allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) for identifiers.
|
* New `unicode-xid-ident` feature to allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) for identifiers.
|
||||||
|
* `Scope::iter_raw` returns an iterator with a reference to the underlying `Dynamic` value (which may be shared).
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
@ -33,6 +34,7 @@ Breaking changes
|
|||||||
* Function signature for defining custom syntax is simplified.
|
* Function signature for defining custom syntax is simplified.
|
||||||
* `Engine::register_raw_fn_XXX` API shortcuts are removed.
|
* `Engine::register_raw_fn_XXX` API shortcuts are removed.
|
||||||
* `PackagesCollection::get_fn`, `PackagesCollection::contains_fn`, `Module::get_fn` and `Module::contains_fn` now take an additional `public_only` parameter indicating whether only public functions are accepted.
|
* `PackagesCollection::get_fn`, `PackagesCollection::contains_fn`, `Module::get_fn` and `Module::contains_fn` now take an additional `public_only` parameter indicating whether only public functions are accepted.
|
||||||
|
* The iterator returned by `Scope::iter` now contains a clone of the `Dynamic` value (unshared).
|
||||||
|
|
||||||
Housekeeping
|
Housekeeping
|
||||||
------------
|
------------
|
||||||
|
@ -10,8 +10,6 @@ Keywords List
|
|||||||
| `let` | Variable declaration | | No |
|
| `let` | Variable declaration | | No |
|
||||||
| `const` | Constant declaration | | No |
|
| `const` | Constant declaration | | No |
|
||||||
| `is_shared` | Is a value shared? | | No |
|
| `is_shared` | Is a value shared? | | No |
|
||||||
| `shared` | Share value | [`no_shared`] | No |
|
|
||||||
| `take` | Un-share value | [`no_shared`] | No |
|
|
||||||
| `if` | If statement | | No |
|
| `if` | If statement | | No |
|
||||||
| `else` | else block of if statement | | No |
|
| `else` | else block of if statement | | No |
|
||||||
| `while` | While loop | | No |
|
| `while` | While loop | | No |
|
||||||
@ -44,6 +42,7 @@ Reserved Keywords
|
|||||||
| --------- | --------------------- |
|
| --------- | --------------------- |
|
||||||
| `var` | Variable declaration |
|
| `var` | Variable declaration |
|
||||||
| `static` | Variable declaration |
|
| `static` | Variable declaration |
|
||||||
|
| `shared` | Share value |
|
||||||
| `do` | Looping |
|
| `do` | Looping |
|
||||||
| `each` | Looping |
|
| `each` | Looping |
|
||||||
| `then` | Control flow |
|
| `then` | Control flow |
|
||||||
|
@ -22,7 +22,7 @@ fn print_obj() { print(this.data); }
|
|||||||
```
|
```
|
||||||
|
|
||||||
The above can be replaced by using _anonymous functions_ which have the same syntax as Rust's closures
|
The above can be replaced by using _anonymous functions_ which have the same syntax as Rust's closures
|
||||||
(but they are **NOT** closures, merely syntactic sugar):
|
(but they are **NOT** real closures, merely syntactic sugar):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let obj = #{
|
let obj = #{
|
||||||
@ -50,12 +50,10 @@ fn anon_fn_1002() { print this.data; }
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
WARNING - NOT Closures
|
WARNING - NOT Real Closures
|
||||||
----------------------
|
--------------------------
|
||||||
|
|
||||||
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
|
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
|
||||||
**not** closures. In particular, they do not capture their execution environment. They are more like
|
**not** real closures.
|
||||||
Rust's function pointers.
|
In particular, they capture their execution environment via [automatic currying][capture],
|
||||||
|
unless the [`no_closure`] feature is turned on.
|
||||||
They do, however, _capture_ variable _values_ from their execution environment, unless the [`no_capture`]
|
|
||||||
feature is turned on. This is accomplished via [automatic currying][capture].
|
|
||||||
|
@ -15,7 +15,7 @@ is created.
|
|||||||
|
|
||||||
Variables that are accessible during the time the [anonymous function] is created can be captured,
|
Variables that are accessible during the time the [anonymous function] is created can be captured,
|
||||||
as long as they are not shadowed by local variables defined within the function's scope.
|
as long as they are not shadowed by local variables defined within the function's scope.
|
||||||
The values captured are the values of those variables at the time of the [anonymous function]'s creation.
|
The captured variables are automatically converted into reference-counted shared values.
|
||||||
|
|
||||||
|
|
||||||
New Parameters For Captured Variables
|
New Parameters For Captured Variables
|
||||||
@ -29,28 +29,32 @@ In actual implementation, this de-sugars to:
|
|||||||
|
|
||||||
3. The variable is added to the parameters list of the anonymous function, at the front.
|
3. The variable is added to the parameters list of the anonymous function, at the front.
|
||||||
|
|
||||||
4. The current value of the variable is then [curried][currying] into the [function pointer] itself, essentially carrying that value and inserting it into future calls of the function.
|
4. The variable is then turned into a reference-counted shared value.
|
||||||
|
|
||||||
Automatic currying can be turned off via the [`no_capture`] feature.
|
5. The shared value is then [curried][currying] into the [function pointer] itself, essentially carrying a reference to that shared value and inserting it into future calls of the function.
|
||||||
|
|
||||||
|
Automatic currying can be turned off via the [`no_closure`] feature.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = 40;
|
let x = 1;
|
||||||
|
|
||||||
let f = |y| x + y; // current value of variable 'x' is auto-curried
|
let f = |y| x + y; // variable 'x' is auto-curried (captured) into 'f'
|
||||||
// the value 40 is curried into 'f'
|
// 'x' is converted into a shared value
|
||||||
|
|
||||||
x = 1; // 'x' can be changed but the curried value is not
|
x = 40; // 'x' can be changed
|
||||||
|
|
||||||
f.call(2) == 42; // the value of 'x' is still 40
|
f.call(2) == 42; // the value of 'x' is 40 because 'x' is shared
|
||||||
|
|
||||||
// The above de-sugars into this:
|
// The above de-sugars into this:
|
||||||
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
|
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
|
||||||
|
|
||||||
let f = Fn("anon$1001").curry(x); // current value of 'x' is curried
|
make_shared(x); // convert 'x' into a shared value
|
||||||
|
|
||||||
|
let f = Fn("anon$1001").curry(x); // shared 'x' is curried
|
||||||
|
|
||||||
f.call(2) == 42;
|
f.call(2) == 42;
|
||||||
```
|
```
|
||||||
|
@ -9,7 +9,7 @@ The following are reserved keywords in Rhai:
|
|||||||
| ------------------------------------------------- | ------------------------------------------------ | --------------------- | :--------------------: |
|
| ------------------------------------------------- | ------------------------------------------------ | --------------------- | :--------------------: |
|
||||||
| `true`, `false` | | Boolean constants | |
|
| `true`, `false` | | Boolean constants | |
|
||||||
| `let`, `const` | `var`, `static` | Variable declarations | |
|
| `let`, `const` | `var`, `static` | Variable declarations | |
|
||||||
| `shared`, `take`, `is_shared` | | Shared values | [`no_shared`] |
|
| `is_shared` | | Shared values | [`no_closure`] |
|
||||||
| `if`, `else` | `then`, `goto`, `exit` | Control flow | |
|
| `if`, `else` | `then`, `goto`, `exit` | Control flow | |
|
||||||
| | `switch`, `match`, `case` | Matching | |
|
| | `switch`, `match`, `case` | Matching | |
|
||||||
| `while`, `loop`, `for`, `in`, `continue`, `break` | `do`, `each` | Looping | |
|
| `while`, `loop`, `for`, `in`, `continue`, `break` | `do`, `each` | Looping | |
|
||||||
|
@ -30,20 +30,22 @@ that resembles very closely that of class methods in an OOP language.
|
|||||||
|
|
||||||
Anonymous functions can also _capture_ variables from the defining environment, which is a very
|
Anonymous functions can also _capture_ variables from the defining environment, which is a very
|
||||||
common OOP pattern. Capturing is accomplished via a feature called _[automatic currying]_ and
|
common OOP pattern. Capturing is accomplished via a feature called _[automatic currying]_ and
|
||||||
can be turned off via the [`no_capture`] feature.
|
can be turned off via the [`no_closure`] feature.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
let factor = 1;
|
||||||
|
|
||||||
// Define the object
|
// Define the object
|
||||||
let obj =
|
let obj =
|
||||||
#{
|
#{
|
||||||
data: 0,
|
data: 0,
|
||||||
increment: |x| this.data += x, // when called, 'this' binds to 'obj'
|
increment: |x| this.data += x, // 'this' binds to 'obj'
|
||||||
update: |x| this.data = x, // when called, 'this' binds to 'obj'
|
update: |x| this.data = x * factor, // 'this' binds to 'obj', 'factor' is captured
|
||||||
action: || print(this.data) // when called, 'this' binds to 'obj'
|
action: || print(this.data) // 'this' binds to 'obj'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use the object
|
// Use the object
|
||||||
@ -52,4 +54,9 @@ obj.action(); // prints 1
|
|||||||
|
|
||||||
obj.update(42);
|
obj.update(42);
|
||||||
obj.action(); // prints 42
|
obj.action(); // prints 42
|
||||||
|
|
||||||
|
factor = 2;
|
||||||
|
|
||||||
|
obj.update(42);
|
||||||
|
obj.action(); // prints 84
|
||||||
```
|
```
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
[`no_object`]: {{rootUrl}}/start/features.md
|
[`no_object`]: {{rootUrl}}/start/features.md
|
||||||
[`no_function`]: {{rootUrl}}/start/features.md
|
[`no_function`]: {{rootUrl}}/start/features.md
|
||||||
[`no_module`]: {{rootUrl}}/start/features.md
|
[`no_module`]: {{rootUrl}}/start/features.md
|
||||||
[`no_capture`]: {{rootUrl}}/start/features.md
|
[`no_closure`]: {{rootUrl}}/start/features.md
|
||||||
[`no_shared`]: {{rootUrl}}/start/features.md
|
|
||||||
[`no_std`]: {{rootUrl}}/start/features.md
|
[`no_std`]: {{rootUrl}}/start/features.md
|
||||||
[`no-std`]: {{rootUrl}}/start/features.md
|
[`no-std`]: {{rootUrl}}/start/features.md
|
||||||
[`internals`]: {{rootUrl}}/start/features.md
|
[`internals`]: {{rootUrl}}/start/features.md
|
||||||
|
@ -23,9 +23,8 @@ more control over what a script can (or cannot) do.
|
|||||||
| `no_object` | Disable support for [custom types] and [object maps]. |
|
| `no_object` | Disable support for [custom types] and [object maps]. |
|
||||||
| `no_function` | Disable script-defined [functions]. |
|
| `no_function` | Disable script-defined [functions]. |
|
||||||
| `no_module` | Disable loading external [modules]. |
|
| `no_module` | Disable loading external [modules]. |
|
||||||
| `no_capture` | Disable [capturing][capture] external variables in [anonymous functions] and [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
|
| `no_closure` | Disable [capturing][capture] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
|
||||||
| `no_shared` | Disable sharing of data values. |
|
| `no_std` | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||||
| `no_std` | Build for `no-std` (implies `no_shared`). Notice that additional dependencies will be pulled in to replace `std` features. |
|
|
||||||
| `serde` | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
|
| `serde` | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
|
||||||
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
|
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
|
||||||
| `unicode-xid-ident` | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. |
|
| `unicode-xid-ident` | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. |
|
||||||
|
@ -114,10 +114,15 @@ fn main() {
|
|||||||
}
|
}
|
||||||
"exit" | "quit" => break, // quit
|
"exit" | "quit" => break, // quit
|
||||||
"scope" => {
|
"scope" => {
|
||||||
scope
|
scope.iter_raw().enumerate().for_each(|(i, (name, value))| {
|
||||||
.iter()
|
println!(
|
||||||
.enumerate()
|
"[{}] {}{} = {:?}",
|
||||||
.for_each(|(i, (name, value))| println!("[{}] {} = {:?}", i + 1, name, value));
|
i + 1,
|
||||||
|
name,
|
||||||
|
if value.is_shared() { " (shared)" } else { "" },
|
||||||
|
*value.read_lock::<Dynamic>().unwrap(),
|
||||||
|
)
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
"astu" => {
|
"astu" => {
|
||||||
|
146
src/any.rs
146
src/any.rs
@ -4,7 +4,7 @@ use crate::fn_native::{FnPtr, SendSync};
|
|||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use crate::fn_native::SharedMut;
|
use crate::fn_native::SharedMut;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -24,14 +24,14 @@ use crate::stdlib::{
|
|||||||
string::String,
|
string::String,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
cell::{Ref, RefCell, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use crate::stdlib::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use crate::stdlib::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ pub enum Union {
|
|||||||
Map(Box<Map>),
|
Map(Box<Map>),
|
||||||
FnPtr(Box<FnPtr>),
|
FnPtr(Box<FnPtr>),
|
||||||
Variant(Box<Box<dyn Variant>>),
|
Variant(Box<Box<dyn Variant>>),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Shared(SharedMut<Dynamic>),
|
Shared(SharedMut<Dynamic>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,11 +176,11 @@ enum DynamicReadLockInner<'d, T: Variant + Clone> {
|
|||||||
/// A simple reference to a non-shared value.
|
/// A simple reference to a non-shared value.
|
||||||
Reference(&'d T),
|
Reference(&'d T),
|
||||||
/// A read guard to a shared `RefCell`.
|
/// A read guard to a shared `RefCell`.
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Guard(Ref<'d, Dynamic>),
|
Guard(Ref<'d, Dynamic>),
|
||||||
/// A read guard to a shared `RwLock`.
|
/// A read guard to a shared `RwLock`.
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Guard(RwLockReadGuard<'d, Dynamic>),
|
Guard(RwLockReadGuard<'d, Dynamic>),
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ impl<'d, T: Variant + Clone> Deref for DynamicReadLock<'d, T> {
|
|||||||
match &self.0 {
|
match &self.0 {
|
||||||
DynamicReadLockInner::Reference(reference) => *reference,
|
DynamicReadLockInner::Reference(reference) => *reference,
|
||||||
// Unwrapping is safe because all checking is already done in its constructor
|
// Unwrapping is safe because all checking is already done in its constructor
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,11 +212,11 @@ enum DynamicWriteLockInner<'d, T: Variant + Clone> {
|
|||||||
/// A simple mutable reference to a non-shared value.
|
/// A simple mutable reference to a non-shared value.
|
||||||
Reference(&'d mut T),
|
Reference(&'d mut T),
|
||||||
/// A write guard to a shared `RefCell`.
|
/// A write guard to a shared `RefCell`.
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Guard(RefMut<'d, Dynamic>),
|
Guard(RefMut<'d, Dynamic>),
|
||||||
/// A write guard to a shared `RwLock`.
|
/// A write guard to a shared `RwLock`.
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Guard(RwLockWriteGuard<'d, Dynamic>),
|
Guard(RwLockWriteGuard<'d, Dynamic>),
|
||||||
}
|
}
|
||||||
@ -229,7 +229,7 @@ impl<'d, T: Variant + Clone> Deref for DynamicWriteLock<'d, T> {
|
|||||||
match &self.0 {
|
match &self.0 {
|
||||||
DynamicWriteLockInner::Reference(reference) => *reference,
|
DynamicWriteLockInner::Reference(reference) => *reference,
|
||||||
// Unwrapping is safe because all checking is already done in its constructor
|
// Unwrapping is safe because all checking is already done in its constructor
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,7 +241,7 @@ impl<'d, T: Variant + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
|||||||
match &mut self.0 {
|
match &mut self.0 {
|
||||||
DynamicWriteLockInner::Reference(reference) => *reference,
|
DynamicWriteLockInner::Reference(reference) => *reference,
|
||||||
// Unwrapping is safe because all checking is already done in its constructor
|
// Unwrapping is safe because all checking is already done in its constructor
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(),
|
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,7 +261,7 @@ impl Dynamic {
|
|||||||
/// instead of one of the supported system primitive types?
|
/// instead of one of the supported system primitive types?
|
||||||
pub fn is_shared(&self) -> bool {
|
pub fn is_shared(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => true,
|
Union::Shared(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -302,10 +302,10 @@ impl Dynamic {
|
|||||||
Union::Map(_) => TypeId::of::<Map>(),
|
Union::Map(_) => TypeId::of::<Map>(),
|
||||||
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
|
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
|
||||||
Union::Variant(value) => (***value).type_id(),
|
Union::Variant(value) => (***value).type_id(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => (*cell.borrow()).type_id(),
|
Union::Shared(cell) => (*cell.borrow()).type_id(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => (*cell.read().unwrap()).type_id(),
|
Union::Shared(cell) => (*cell.read().unwrap()).type_id(),
|
||||||
}
|
}
|
||||||
@ -335,10 +335,10 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::Variant(value) if value.is::<Instant>() => "timestamp",
|
Union::Variant(value) if value.is::<Instant>() => "timestamp",
|
||||||
Union::Variant(value) => (***value).type_name(),
|
Union::Variant(value) => (***value).type_name(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => (*cell.borrow()).type_name(),
|
Union::Shared(cell) => (*cell.borrow()).type_name(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => (*cell.read().unwrap()).type_name(),
|
Union::Shared(cell) => (*cell.read().unwrap()).type_name(),
|
||||||
}
|
}
|
||||||
@ -396,7 +396,7 @@ impl fmt::Display for Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||||
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => f.write_str("<shared>"),
|
Union::Shared(_) => f.write_str("<shared>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,7 +424,7 @@ impl fmt::Debug for Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
Union::Variant(value) if value.is::<Instant>() => write!(f, "<timestamp>"),
|
||||||
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => f.write_str("<shared>"),
|
Union::Shared(_) => f.write_str("<shared>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -446,7 +446,7 @@ impl Clone for Dynamic {
|
|||||||
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
||||||
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
|
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
|
||||||
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell) => Self(Union::Shared(cell.clone())),
|
Union::Shared(ref cell) => Self(Union::Shared(cell.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -571,9 +571,9 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics under the `no_shared` feature.
|
/// Panics under the `no_closure` feature.
|
||||||
pub fn into_shared(self) -> Self {
|
pub fn into_shared(self) -> Self {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Shared(..) => self,
|
Union::Shared(..) => self,
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -582,7 +582,7 @@ impl Dynamic {
|
|||||||
_ => Self(Union::Shared(Arc::new(RwLock::new(self)))),
|
_ => Self(Union::Shared(Arc::new(RwLock::new(self)))),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "no_shared")]
|
#[cfg(feature = "no_closure")]
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,11 +614,11 @@ impl Dynamic {
|
|||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => return cell.borrow().clone().try_cast(),
|
Union::Shared(cell) => return cell.borrow().clone().try_cast(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => return cell.read().unwrap().clone().try_cast(),
|
Union::Shared(cell) => return cell.read().unwrap().clone().try_cast(),
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -703,7 +703,7 @@ impl Dynamic {
|
|||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => unreachable!(),
|
Union::Shared(_) => unreachable!(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -748,17 +748,17 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clone_inner_data<T: Variant + Clone>(&self) -> Option<T> {
|
pub fn clone_inner_data<T: Variant + Clone>(self) -> Option<T> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell) => {
|
Union::Shared(cell) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return Some(cell.borrow().downcast_ref::<T>().unwrap().clone());
|
return Some(cell.borrow().downcast_ref::<T>().unwrap().clone());
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return Some(cell.read().unwrap().downcast_ref::<T>().unwrap().clone());
|
return Some(cell.read().unwrap().downcast_ref::<T>().unwrap().clone());
|
||||||
}
|
}
|
||||||
_ => self.downcast_ref().cloned(),
|
_ => self.try_cast(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,7 +772,7 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_locked(&self) -> bool {
|
pub fn is_locked(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref _cell) => {
|
Union::Shared(ref _cell) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return _cell.try_borrow().is_err();
|
return _cell.try_borrow().is_err();
|
||||||
@ -788,22 +788,33 @@ impl Dynamic {
|
|||||||
/// Casting to `Dynamic` just returns a reference to it.
|
/// Casting to `Dynamic` just returns a reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
|
///
|
||||||
|
/// # Panics and Deadlocks When Value is Shared
|
||||||
|
///
|
||||||
|
/// Under the `sync` feature, this call may deadlock.
|
||||||
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell) => {
|
Union::Shared(ref cell) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return Some(DynamicReadLock(DynamicReadLockInner::Guard(cell.borrow())));
|
let data = cell.borrow();
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return Some(DynamicReadLock(DynamicReadLockInner::Guard(
|
let data = cell.read().unwrap();
|
||||||
cell.read().unwrap(),
|
|
||||||
)));
|
let type_id = (*data).type_id();
|
||||||
|
|
||||||
|
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(DynamicReadLock(DynamicReadLockInner::Guard(data)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => self
|
_ => self
|
||||||
.downcast_ref()
|
.downcast_ref()
|
||||||
.map(|reference| DynamicReadLock(DynamicReadLockInner::Reference(reference))),
|
.map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -811,24 +822,33 @@ impl Dynamic {
|
|||||||
/// Casting to `Dynamic` just returns a mutable reference to it.
|
/// Casting to `Dynamic` just returns a mutable reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
|
///
|
||||||
|
/// # Panics and Deadlocks When Value is Shared
|
||||||
|
///
|
||||||
|
/// Under the `sync` feature, this call may deadlock.
|
||||||
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell) => {
|
Union::Shared(ref cell) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(
|
let data = cell.borrow_mut();
|
||||||
cell.borrow_mut(),
|
|
||||||
)));
|
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(
|
let data = cell.write().unwrap();
|
||||||
cell.write().unwrap(),
|
|
||||||
)));
|
let type_id = (*data).type_id();
|
||||||
|
|
||||||
|
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(DynamicWriteLock(DynamicWriteLockInner::Guard(data)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => self
|
_ => self
|
||||||
.downcast_mut()
|
.downcast_mut()
|
||||||
.map(|reference| DynamicWriteLock(DynamicWriteLockInner::Reference(reference))),
|
.map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -836,6 +856,10 @@ impl Dynamic {
|
|||||||
/// Casting to `Dynamic` just returns a reference to it.
|
/// Casting to `Dynamic` just returns a reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
fn downcast_ref<T: Variant + Clone>(&self) -> Option<&T> {
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
@ -909,7 +933,7 @@ impl Dynamic {
|
|||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => unreachable!(),
|
Union::Shared(_) => unreachable!(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -919,6 +943,10 @@ impl Dynamic {
|
|||||||
/// Casting to `Dynamic` just returns a mutable reference to it.
|
/// Casting to `Dynamic` just returns a mutable reference to it.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
fn downcast_mut<T: Variant + Clone>(&mut self) -> Option<&mut T> {
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
@ -986,7 +1014,7 @@ impl Dynamic {
|
|||||||
|
|
||||||
match &mut self.0 {
|
match &mut self.0 {
|
||||||
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => unreachable!(),
|
Union::Shared(_) => unreachable!(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -997,10 +1025,8 @@ impl Dynamic {
|
|||||||
pub fn as_int(&self) -> Result<INT, &'static str> {
|
pub fn as_int(&self) -> Result<INT, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Int(n) => Ok(n),
|
Union::Int(n) => Ok(n),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self
|
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
.clone_inner_data::<INT>()
|
|
||||||
.ok_or_else(|| self.type_name()),
|
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1011,10 +1037,8 @@ impl Dynamic {
|
|||||||
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Float(n) => Ok(n),
|
Union::Float(n) => Ok(n),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self
|
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
.clone_inner_data::<FLOAT>()
|
|
||||||
.ok_or_else(|| self.type_name()),
|
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1024,10 +1048,8 @@ impl Dynamic {
|
|||||||
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Bool(b) => Ok(b),
|
Union::Bool(b) => Ok(b),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self
|
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
.clone_inner_data::<bool>()
|
|
||||||
.ok_or_else(|| self.type_name()),
|
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1037,10 +1059,8 @@ impl Dynamic {
|
|||||||
pub fn as_char(&self) -> Result<char, &'static str> {
|
pub fn as_char(&self) -> Result<char, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Char(n) => Ok(n),
|
Union::Char(n) => Ok(n),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self
|
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
.clone_inner_data::<char>()
|
|
||||||
.ok_or_else(|| self.type_name()),
|
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1070,7 +1090,7 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Str(s) => Ok(s),
|
Union::Str(s) => Ok(s),
|
||||||
Union::FnPtr(f) => Ok(f.take_data().0),
|
Union::FnPtr(f) => Ok(f.take_data().0),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell) => {
|
Union::Shared(cell) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
{
|
{
|
||||||
|
@ -1284,7 +1284,7 @@ impl Engine {
|
|||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
if cfg!(not(feature = "no_shared")) {
|
if cfg!(not(feature = "no_closure")) {
|
||||||
ensure_no_data_race(name, args, false)?;
|
ensure_no_data_race(name, args, false)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ use crate::module::resolvers;
|
|||||||
#[cfg(any(not(feature = "no_object"), not(feature = "no_module")))]
|
#[cfg(any(not(feature = "no_object"), not(feature = "no_module")))]
|
||||||
use crate::utils::ImmutableString;
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::any::DynamicWriteLock;
|
use crate::any::DynamicWriteLock;
|
||||||
|
|
||||||
@ -49,6 +49,9 @@ use crate::stdlib::{
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::stdlib::any::TypeId;
|
use crate::stdlib::any::TypeId;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
use crate::stdlib::mem;
|
||||||
|
|
||||||
/// Variable-sized array of `Dynamic` values.
|
/// Variable-sized array of `Dynamic` values.
|
||||||
///
|
///
|
||||||
/// Not available under the `no_index` feature.
|
/// Not available under the `no_index` feature.
|
||||||
@ -96,8 +99,6 @@ pub const KEYWORD_EVAL: &str = "eval";
|
|||||||
pub const KEYWORD_FN_PTR: &str = "Fn";
|
pub const KEYWORD_FN_PTR: &str = "Fn";
|
||||||
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
||||||
pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
|
pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
|
||||||
pub const KEYWORD_SHARED: &str = "shared";
|
|
||||||
pub const KEYWORD_TAKE: &str = "take";
|
|
||||||
pub const KEYWORD_IS_SHARED: &str = "is_shared";
|
pub const KEYWORD_IS_SHARED: &str = "is_shared";
|
||||||
pub const KEYWORD_THIS: &str = "this";
|
pub const KEYWORD_THIS: &str = "this";
|
||||||
pub const FN_TO_STRING: &str = "to_string";
|
pub const FN_TO_STRING: &str = "to_string";
|
||||||
@ -132,7 +133,7 @@ pub enum Target<'a> {
|
|||||||
Ref(&'a mut Dynamic),
|
Ref(&'a mut Dynamic),
|
||||||
/// The target is a mutable reference to a Shared `Dynamic` value.
|
/// The target is a mutable reference to a Shared `Dynamic` value.
|
||||||
/// It holds both the access guard and the original shared value.
|
/// It holds both the access guard and the original shared value.
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
LockGuard((DynamicWriteLock<'a, Dynamic>, Dynamic)),
|
LockGuard((DynamicWriteLock<'a, Dynamic>, Dynamic)),
|
||||||
/// The target is a temporary `Dynamic` value (i.e. the mutation can cause no side effects).
|
/// The target is a temporary `Dynamic` value (i.e. the mutation can cause no side effects).
|
||||||
@ -149,7 +150,7 @@ impl Target<'_> {
|
|||||||
pub fn is_ref(&self) -> bool {
|
pub fn is_ref(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(_) => true,
|
Self::Ref(_) => true,
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::LockGuard(_) => true,
|
Self::LockGuard(_) => true,
|
||||||
Self::Value(_) => false,
|
Self::Value(_) => false,
|
||||||
@ -161,7 +162,7 @@ impl Target<'_> {
|
|||||||
pub fn is_value(&self) -> bool {
|
pub fn is_value(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(_) => false,
|
Self::Ref(_) => false,
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::LockGuard(_) => false,
|
Self::LockGuard(_) => false,
|
||||||
Self::Value(_) => true,
|
Self::Value(_) => true,
|
||||||
@ -173,7 +174,7 @@ impl Target<'_> {
|
|||||||
pub fn is_shared(&self) -> bool {
|
pub fn is_shared(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(r) => r.is_shared(),
|
Self::Ref(r) => r.is_shared(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::LockGuard(_) => true,
|
Self::LockGuard(_) => true,
|
||||||
Self::Value(r) => r.is_shared(),
|
Self::Value(r) => r.is_shared(),
|
||||||
@ -186,7 +187,7 @@ impl Target<'_> {
|
|||||||
pub fn is<T: Variant + Clone>(&self) -> bool {
|
pub fn is<T: Variant + Clone>(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Target::Ref(r) => r.is::<T>(),
|
Target::Ref(r) => r.is::<T>(),
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Target::LockGuard((r, _)) => r.is::<T>(),
|
Target::LockGuard((r, _)) => r.is::<T>(),
|
||||||
Target::Value(r) => r.is::<T>(),
|
Target::Value(r) => r.is::<T>(),
|
||||||
@ -198,7 +199,7 @@ impl Target<'_> {
|
|||||||
pub fn clone_into_dynamic(self) -> Dynamic {
|
pub fn clone_into_dynamic(self) -> Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(r) => r.clone(), // Referenced value is cloned
|
Self::Ref(r) => r.clone(), // Referenced value is cloned
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::LockGuard((_, orig)) => orig, // Original value is simply taken
|
Self::LockGuard((_, orig)) => orig, // Original value is simply taken
|
||||||
Self::Value(v) => v, // Owned value is simply taken
|
Self::Value(v) => v, // Owned value is simply taken
|
||||||
@ -210,7 +211,7 @@ impl Target<'_> {
|
|||||||
pub fn as_mut(&mut self) -> &mut Dynamic {
|
pub fn as_mut(&mut self) -> &mut Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(r) => *r,
|
Self::Ref(r) => *r,
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::LockGuard((r, _)) => r.deref_mut(),
|
Self::LockGuard((r, _)) => r.deref_mut(),
|
||||||
Self::Value(ref mut r) => r,
|
Self::Value(ref mut r) => r,
|
||||||
@ -223,7 +224,7 @@ impl Target<'_> {
|
|||||||
pub fn set_value(&mut self, new_val: Dynamic) -> Result<(), Box<EvalAltResult>> {
|
pub fn set_value(&mut self, new_val: Dynamic) -> Result<(), Box<EvalAltResult>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Ref(r) => **r = new_val,
|
Self::Ref(r) => **r = new_val,
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Self::LockGuard((r, _)) => **r = new_val,
|
Self::LockGuard((r, _)) => **r = new_val,
|
||||||
Self::Value(_) => {
|
Self::Value(_) => {
|
||||||
@ -260,7 +261,7 @@ impl Target<'_> {
|
|||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
||||||
fn from(value: &'a mut Dynamic) -> Self {
|
fn from(value: &'a mut Dynamic) -> Self {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if value.is_shared() {
|
if value.is_shared() {
|
||||||
// Cloning is cheap for a shared value
|
// Cloning is cheap for a shared value
|
||||||
@ -632,7 +633,7 @@ pub fn search_scope_only<'s, 'a>(
|
|||||||
|
|
||||||
// Check for data race - probably not necessary because the only place it should conflict is in a method call
|
// Check for data race - probably not necessary because the only place it should conflict is in a method call
|
||||||
// when the object variable is also used as a parameter.
|
// when the object variable is also used as a parameter.
|
||||||
// if cfg!(not(feature = "no_shared")) && val.is_locked() {
|
// if cfg!(not(feature = "no_closure")) && val.is_locked() {
|
||||||
// return Err(Box::new(EvalAltResult::ErrorDataRace(name.into(), *pos)));
|
// return Err(Box::new(EvalAltResult::ErrorDataRace(name.into(), *pos)));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@ -1351,12 +1352,9 @@ impl Engine {
|
|||||||
)),
|
)),
|
||||||
// Normal assignment
|
// Normal assignment
|
||||||
ScopeEntryType::Normal if op.is_empty() => {
|
ScopeEntryType::Normal if op.is_empty() => {
|
||||||
if cfg!(not(feature = "no_shared")) && lhs_ptr.is_shared() {
|
let rhs_val = rhs_val.clone_inner_data().unwrap();
|
||||||
*lhs_ptr.write_lock::<Dynamic>().unwrap() = if rhs_val.is_shared() {
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
rhs_val.clone_inner_data().unwrap()
|
*lhs_ptr.write_lock::<Dynamic>().unwrap() = rhs_val;
|
||||||
} else {
|
|
||||||
rhs_val
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
*lhs_ptr = rhs_val;
|
*lhs_ptr = rhs_val;
|
||||||
}
|
}
|
||||||
@ -1377,7 +1375,7 @@ impl Engine {
|
|||||||
.get_fn(hash_fn, false)
|
.get_fn(hash_fn, false)
|
||||||
.or_else(|| self.packages.get_fn(hash_fn, false))
|
.or_else(|| self.packages.get_fn(hash_fn, false))
|
||||||
{
|
{
|
||||||
if cfg!(not(feature = "no_shared")) && lhs_ptr.is_shared() {
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
let mut lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
let mut lock_guard = lhs_ptr.write_lock::<Dynamic>().unwrap();
|
||||||
let lhs_ptr_inner = lock_guard.deref_mut();
|
let lhs_ptr_inner = lock_guard.deref_mut();
|
||||||
|
|
||||||
@ -1401,12 +1399,9 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
.map_err(|err| err.new_position(*op_pos))?;
|
.map_err(|err| err.new_position(*op_pos))?;
|
||||||
|
|
||||||
if cfg!(not(feature = "no_shared")) && lhs_ptr.is_shared() {
|
let value = value.clone_inner_data().unwrap();
|
||||||
*lhs_ptr.write_lock::<Dynamic>().unwrap() = if value.is_shared() {
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
value.clone_inner_data().unwrap()
|
*lhs_ptr.write_lock::<Dynamic>().unwrap() = value;
|
||||||
} else {
|
|
||||||
value
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
*lhs_ptr = value;
|
*lhs_ptr = value;
|
||||||
}
|
}
|
||||||
@ -1845,6 +1840,25 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Share statement
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Stmt::Share(x) => {
|
||||||
|
let (var_name, _) = x.as_ref();
|
||||||
|
|
||||||
|
match scope.get_index(var_name) {
|
||||||
|
Some((index, ScopeEntryType::Normal)) => {
|
||||||
|
let (val, _) = scope.get_mut(index);
|
||||||
|
|
||||||
|
if !val.is_shared() {
|
||||||
|
// Replace the variable with a shared value.
|
||||||
|
*val = mem::take(val).into_shared();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(Default::default())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.check_data_size(result)
|
self.check_data_size(result)
|
||||||
|
@ -93,7 +93,7 @@ pub enum ParseErrorType {
|
|||||||
MalformedInExpr(String),
|
MalformedInExpr(String),
|
||||||
/// A capturing has syntax error. Wrapped value is the error description (if any).
|
/// A capturing has syntax error. Wrapped value is the error description (if any).
|
||||||
///
|
///
|
||||||
/// Never appears under the `no_capture` feature.
|
/// Never appears under the `no_closure` feature.
|
||||||
MalformedCapture(String),
|
MalformedCapture(String),
|
||||||
/// A map definition has duplicated property names. Wrapped value is the property name.
|
/// A map definition has duplicated property names. Wrapped value is the property name.
|
||||||
///
|
///
|
||||||
|
@ -5,7 +5,7 @@ use crate::calc_fn_hash;
|
|||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG,
|
search_imports, search_namespace, search_scope_only, Engine, Imports, State, KEYWORD_DEBUG,
|
||||||
KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_SHARED,
|
KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_SHARED,
|
||||||
KEYWORD_PRINT, KEYWORD_SHARED, KEYWORD_TAKE, KEYWORD_TYPE_OF,
|
KEYWORD_PRINT, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::error::ParseErrorType;
|
use crate::error::ParseErrorType;
|
||||||
use crate::fn_native::{FnCallArgs, FnPtr};
|
use crate::fn_native::{FnCallArgs, FnPtr};
|
||||||
@ -33,7 +33,7 @@ use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::engine::{Map, Target, FN_GET, FN_SET};
|
use crate::engine::{Map, Target, FN_GET, FN_SET};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use crate::scope::Entry as ScopeEntry;
|
use crate::scope::Entry as ScopeEntry;
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
@ -47,7 +47,7 @@ use crate::stdlib::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use crate::stdlib::{collections::HashSet, string::String};
|
use crate::stdlib::{collections::HashSet, string::String};
|
||||||
|
|
||||||
/// Extract the property name from a getter function name.
|
/// Extract the property name from a getter function name.
|
||||||
@ -139,7 +139,7 @@ impl Drop for ArgBackup<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add captured variables into scope
|
// Add captured variables into scope
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
fn add_captured_variables_into_scope<'s>(
|
fn add_captured_variables_into_scope<'s>(
|
||||||
externals: &HashSet<String>,
|
externals: &HashSet<String>,
|
||||||
captured: Scope<'s>,
|
captured: Scope<'s>,
|
||||||
@ -166,7 +166,7 @@ pub fn ensure_no_data_race(
|
|||||||
args: &FnCallArgs,
|
args: &FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
if cfg!(not(feature = "no_shared")) {
|
if cfg!(not(feature = "no_closure")) {
|
||||||
let skip = if is_ref { 1 } else { 0 };
|
let skip = if is_ref { 1 } else { 0 };
|
||||||
|
|
||||||
if let Some((n, _)) = args
|
if let Some((n, _)) = args
|
||||||
@ -456,7 +456,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
if cfg!(not(feature = "no_shared")) {
|
if cfg!(not(feature = "no_closure")) {
|
||||||
ensure_no_data_race(fn_name, args, is_ref)?;
|
ensure_no_data_race(fn_name, args, is_ref)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,7 +505,7 @@ impl Engine {
|
|||||||
let mods = &mut Imports::new();
|
let mods = &mut Imports::new();
|
||||||
|
|
||||||
// Add captured variables into scope
|
// Add captured variables into scope
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if let Some(captured) = _capture {
|
if let Some(captured) = _capture {
|
||||||
add_captured_variables_into_scope(&func.externals, captured, scope);
|
add_captured_variables_into_scope(&func.externals, captured, scope);
|
||||||
}
|
}
|
||||||
@ -690,18 +690,12 @@ impl Engine {
|
|||||||
.into(),
|
.into(),
|
||||||
false,
|
false,
|
||||||
))
|
))
|
||||||
} else if cfg!(not(feature = "no_shared"))
|
} else if cfg!(not(feature = "no_closure"))
|
||||||
&& _fn_name == KEYWORD_IS_SHARED
|
&& _fn_name == KEYWORD_IS_SHARED
|
||||||
&& idx.is_empty()
|
&& idx.is_empty()
|
||||||
{
|
{
|
||||||
// take call
|
// take call
|
||||||
Ok((target.is_shared().into(), false))
|
Ok((target.is_shared().into(), false))
|
||||||
} else if cfg!(not(feature = "no_shared")) && _fn_name == KEYWORD_SHARED && idx.is_empty() {
|
|
||||||
// take call
|
|
||||||
Ok((obj.clone().into_shared(), false))
|
|
||||||
} else if cfg!(not(feature = "no_shared")) && _fn_name == KEYWORD_TAKE && idx.is_empty() {
|
|
||||||
// take call
|
|
||||||
Ok((obj.clone_inner_data::<Dynamic>().unwrap(), false))
|
|
||||||
} else {
|
} else {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
let redirected;
|
let redirected;
|
||||||
@ -814,29 +808,13 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_shared()
|
// Handle is_shared()
|
||||||
if cfg!(not(feature = "no_shared")) && name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
|
if cfg!(not(feature = "no_closure")) && name == KEYWORD_IS_SHARED && args_expr.len() == 1 {
|
||||||
let expr = args_expr.get(0).unwrap();
|
let expr = args_expr.get(0).unwrap();
|
||||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
return Ok(value.is_shared().into());
|
return Ok(value.is_shared().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle shared()
|
|
||||||
if cfg!(not(feature = "no_shared")) && name == KEYWORD_SHARED && args_expr.len() == 1 {
|
|
||||||
let expr = args_expr.get(0).unwrap();
|
|
||||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
return Ok(value.into_shared());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle take()
|
|
||||||
if cfg!(not(feature = "no_shared")) && name == KEYWORD_TAKE && args_expr.len() == 1 {
|
|
||||||
let expr = args_expr.get(0).unwrap();
|
|
||||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
|
||||||
|
|
||||||
return Ok(value.clone_inner_data::<Dynamic>().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle call() - Redirect function call
|
// Handle call() - Redirect function call
|
||||||
let redirected;
|
let redirected;
|
||||||
let mut args_expr = args_expr.as_ref();
|
let mut args_expr = args_expr.as_ref();
|
||||||
@ -896,7 +874,7 @@ impl Engine {
|
|||||||
let mut arg_values: StaticVec<_>;
|
let mut arg_values: StaticVec<_>;
|
||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
let mut is_ref = false;
|
let mut is_ref = false;
|
||||||
let capture = if cfg!(not(feature = "no_capture")) && capture && !scope.is_empty() {
|
let capture = if cfg!(not(feature = "no_closure")) && capture && !scope.is_empty() {
|
||||||
Some(scope.flatten_clone())
|
Some(scope.flatten_clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -924,7 +902,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Turn it into a method call only if the object is not shared
|
// Turn it into a method call only if the object is not shared
|
||||||
args = if target.is_shared() {
|
args = if target.is_shared() {
|
||||||
arg_values.insert(0, target.clone_inner_data().unwrap());
|
arg_values.insert(0, target.clone().clone_inner_data().unwrap());
|
||||||
arg_values.iter_mut().collect()
|
arg_values.iter_mut().collect()
|
||||||
} else {
|
} else {
|
||||||
is_ref = true;
|
is_ref = true;
|
||||||
@ -1041,7 +1019,7 @@ impl Engine {
|
|||||||
let mods = &mut Imports::new();
|
let mods = &mut Imports::new();
|
||||||
|
|
||||||
// Add captured variables into scope
|
// Add captured variables into scope
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if _capture && !scope.is_empty() {
|
if _capture && !scope.is_empty() {
|
||||||
add_captured_variables_into_scope(
|
add_captured_variables_into_scope(
|
||||||
&func.externals,
|
&func.externals,
|
||||||
|
@ -22,10 +22,10 @@ use crate::stdlib::rc::Rc;
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use crate::stdlib::sync::Arc;
|
use crate::stdlib::sync::Arc;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
use crate::stdlib::cell::RefCell;
|
use crate::stdlib::cell::RefCell;
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
use crate::stdlib::sync::RwLock;
|
use crate::stdlib::sync::RwLock;
|
||||||
|
|
||||||
@ -51,11 +51,11 @@ pub type Shared<T> = Rc<T>;
|
|||||||
pub type Shared<T> = Arc<T>;
|
pub type Shared<T> = Arc<T>;
|
||||||
|
|
||||||
/// Mutable reference-counted container (read-write lock)
|
/// Mutable reference-counted container (read-write lock)
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type SharedMut<T> = Shared<RefCell<T>>;
|
pub type SharedMut<T> = Shared<RefCell<T>>;
|
||||||
/// Mutable reference-counted container (read-write lock)
|
/// Mutable reference-counted container (read-write lock)
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type SharedMut<T> = Shared<RwLock<T>>;
|
pub type SharedMut<T> = Shared<RwLock<T>>;
|
||||||
|
|
||||||
|
@ -765,7 +765,7 @@ pub fn optimize_into_ast(
|
|||||||
access: fn_def.access,
|
access: fn_def.access,
|
||||||
body: Default::default(),
|
body: Default::default(),
|
||||||
params: fn_def.params.clone(),
|
params: fn_def.params.clone(),
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals: fn_def.externals.clone(),
|
externals: fn_def.externals.clone(),
|
||||||
pos: fn_def.pos,
|
pos: fn_def.pos,
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ use crate::stdlib::{
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::stdlib::collections::hash_map::DefaultHasher;
|
use crate::stdlib::collections::hash_map::DefaultHasher;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
use crate::stdlib::collections::HashSet;
|
use crate::stdlib::collections::HashSet;
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -366,7 +366,7 @@ pub struct ScriptFnDef {
|
|||||||
/// Names of function parameters.
|
/// Names of function parameters.
|
||||||
pub params: StaticVec<String>,
|
pub params: StaticVec<String>,
|
||||||
/// Access to external variables.
|
/// Access to external variables.
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub externals: HashSet<String>,
|
pub externals: HashSet<String>,
|
||||||
/// Function body.
|
/// Function body.
|
||||||
pub body: Stmt,
|
pub body: Stmt,
|
||||||
@ -414,13 +414,13 @@ struct ParseState<'e> {
|
|||||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
stack: Vec<(String, ScopeEntryType)>,
|
stack: Vec<(String, ScopeEntryType)>,
|
||||||
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals: HashMap<String, Position>,
|
externals: HashMap<String, Position>,
|
||||||
/// An indicator that prevents variables capturing into externals one time.
|
/// An indicator that disables variable capturing into externals one single time.
|
||||||
/// If set to true the next call of `access_var` will not capture the variable.
|
/// If set to false the next call to `access_var` will not capture the variable.
|
||||||
/// All consequent calls to `access_var` will not be affected
|
/// All consequent calls to `access_var` will not be affected
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
capture: bool,
|
allow_capture: bool,
|
||||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
modules: Vec<String>,
|
modules: Vec<String>,
|
||||||
/// Maximum levels of expression nesting.
|
/// Maximum levels of expression nesting.
|
||||||
@ -444,10 +444,10 @@ impl<'e> ParseState<'e> {
|
|||||||
max_expr_depth,
|
max_expr_depth,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
max_function_expr_depth,
|
max_function_expr_depth,
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals: Default::default(),
|
externals: Default::default(),
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
capture: true,
|
allow_capture: true,
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
modules: Default::default(),
|
modules: Default::default(),
|
||||||
}
|
}
|
||||||
@ -469,13 +469,13 @@ impl<'e> ParseState<'e> {
|
|||||||
.find(|(_, (n, _))| *n == name)
|
.find(|(_, (n, _))| *n == name)
|
||||||
.and_then(|(i, _)| NonZeroUsize::new(i + 1));
|
.and_then(|(i, _)| NonZeroUsize::new(i + 1));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if self.capture {
|
if self.allow_capture {
|
||||||
if index.is_none() && !self.externals.contains_key(name) {
|
if index.is_none() && !self.externals.contains_key(name) {
|
||||||
self.externals.insert(name.to_string(), _pos);
|
self.externals.insert(name.to_string(), _pos);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.capture = true
|
self.allow_capture = true
|
||||||
}
|
}
|
||||||
|
|
||||||
index
|
index
|
||||||
@ -579,6 +579,9 @@ pub enum Stmt {
|
|||||||
Position,
|
Position,
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
|
/// Convert a variable to shared.
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Share(Box<(String, Position)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
@ -606,6 +609,9 @@ impl Stmt {
|
|||||||
Stmt::Import(x) => x.2,
|
Stmt::Import(x) => x.2,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Export(x) => x.1,
|
Stmt::Export(x) => x.1,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Stmt::Share(x) => x.1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,6 +635,9 @@ impl Stmt {
|
|||||||
Stmt::Import(x) => x.2 = new_pos,
|
Stmt::Import(x) => x.2 = new_pos,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Export(x) => x.1 = new_pos,
|
Stmt::Export(x) => x.1 = new_pos,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Stmt::Share(x) => x.1 = new_pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -655,6 +664,9 @@ impl Stmt {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(_) | Stmt::Export(_) => false,
|
Stmt::Import(_) | Stmt::Export(_) => false,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Stmt::Share(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,6 +690,9 @@ impl Stmt {
|
|||||||
Stmt::Import(_) => false,
|
Stmt::Import(_) => false,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Export(_) => false,
|
Stmt::Export(_) => false,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Stmt::Share(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1671,7 +1686,7 @@ fn parse_primary(
|
|||||||
|
|
||||||
root_expr = match (root_expr, token) {
|
root_expr = match (root_expr, token) {
|
||||||
// Function call
|
// Function call
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
(Expr::Variable(x), Token::Bang) => {
|
(Expr::Variable(x), Token::Bang) => {
|
||||||
if !match_token(input, Token::LeftParen)? {
|
if !match_token(input, Token::LeftParen)? {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
@ -1855,7 +1870,7 @@ fn parse_unary(
|
|||||||
|
|
||||||
let (expr, func) = parse_anon_fn(input, &mut new_state, lib, settings)?;
|
let (expr, func) = parse_anon_fn(input, &mut new_state, lib, settings)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
new_state.externals.iter().for_each(|(closure, pos)| {
|
new_state.externals.iter().for_each(|(closure, pos)| {
|
||||||
state.access_var(closure, *pos);
|
state.access_var(closure, *pos);
|
||||||
});
|
});
|
||||||
@ -2216,9 +2231,9 @@ fn parse_binary_op(
|
|||||||
if cfg!(not(feature = "no_object")) && op_token == Token::Period {
|
if cfg!(not(feature = "no_object")) && op_token == Token::Period {
|
||||||
if let (Token::Identifier(_), _) = input.peek().unwrap() {
|
if let (Token::Identifier(_), _) = input.peek().unwrap() {
|
||||||
// prevents capturing of the object properties as vars: xxx.<var>
|
// prevents capturing of the object properties as vars: xxx.<var>
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
{
|
{
|
||||||
state.capture = false;
|
state.allow_capture = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3095,7 +3110,7 @@ fn parse_fn(
|
|||||||
|
|
||||||
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
|
let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let externals = state
|
let externals = state
|
||||||
.externals
|
.externals
|
||||||
.iter()
|
.iter()
|
||||||
@ -3108,7 +3123,7 @@ fn parse_fn(
|
|||||||
name: name.into(),
|
name: name.into(),
|
||||||
access,
|
access,
|
||||||
params,
|
params,
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals,
|
externals,
|
||||||
body,
|
body,
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
@ -3128,6 +3143,17 @@ fn make_curry_from_externals(
|
|||||||
let num_externals = externals.len();
|
let num_externals = externals.len();
|
||||||
let mut args: StaticVec<_> = Default::default();
|
let mut args: StaticVec<_> = Default::default();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
externals.iter().for_each(|(var_name, pos)| {
|
||||||
|
args.push(Expr::Variable(Box::new((
|
||||||
|
(var_name.into(), *pos),
|
||||||
|
None,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
))));
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
externals.into_iter().for_each(|(var_name, pos)| {
|
externals.into_iter().for_each(|(var_name, pos)| {
|
||||||
args.push(Expr::Variable(Box::new(((var_name, pos), None, 0, None))));
|
args.push(Expr::Variable(Box::new(((var_name, pos), None, 0, None))));
|
||||||
});
|
});
|
||||||
@ -3142,7 +3168,23 @@ fn make_curry_from_externals(
|
|||||||
None,
|
None,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Expr::Dot(Box::new((fn_expr, fn_call, pos)))
|
let expr = Expr::Dot(Box::new((fn_expr, fn_call, pos)));
|
||||||
|
|
||||||
|
// If there are captured variables, convert the entire expression into a statement block,
|
||||||
|
// then insert the relevant `Share` statements.
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
{
|
||||||
|
// Statement block
|
||||||
|
let mut statements: StaticVec<_> = Default::default();
|
||||||
|
// Insert `Share` statements
|
||||||
|
statements.extend(externals.into_iter().map(|x| Stmt::Share(Box::new(x))));
|
||||||
|
// Final expression
|
||||||
|
statements.push(Stmt::Expr(Box::new(expr)));
|
||||||
|
Expr::Stmt(Box::new((Stmt::Block(Box::new((statements, pos))), pos)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_closure")]
|
||||||
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an anonymous function definition.
|
/// Parse an anonymous function definition.
|
||||||
@ -3215,7 +3257,7 @@ fn parse_anon_fn(
|
|||||||
// External variables may need to be processed in a consistent order,
|
// External variables may need to be processed in a consistent order,
|
||||||
// so extract them into a list.
|
// so extract them into a list.
|
||||||
let externals: StaticVec<_> = {
|
let externals: StaticVec<_> = {
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
{
|
{
|
||||||
state
|
state
|
||||||
.externals
|
.externals
|
||||||
@ -3223,11 +3265,11 @@ fn parse_anon_fn(
|
|||||||
.map(|(k, &v)| (k.clone(), v))
|
.map(|(k, &v)| (k.clone(), v))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_capture")]
|
#[cfg(feature = "no_closure")]
|
||||||
Default::default()
|
Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let params: StaticVec<_> = if cfg!(not(feature = "no_capture")) {
|
let params: StaticVec<_> = if cfg!(not(feature = "no_closure")) {
|
||||||
externals
|
externals
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, _)| k)
|
.map(|(k, _)| k)
|
||||||
@ -3257,7 +3299,7 @@ fn parse_anon_fn(
|
|||||||
name: fn_name.clone(),
|
name: fn_name.clone(),
|
||||||
access: FnAccess::Public,
|
access: FnAccess::Public,
|
||||||
params,
|
params,
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
externals: Default::default(),
|
externals: Default::default(),
|
||||||
body,
|
body,
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
@ -3265,7 +3307,7 @@ fn parse_anon_fn(
|
|||||||
|
|
||||||
let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
|
let expr = Expr::FnPointer(Box::new((fn_name, settings.pos)));
|
||||||
|
|
||||||
let expr = if cfg!(not(feature = "no_capture")) {
|
let expr = if cfg!(not(feature = "no_closure")) {
|
||||||
make_curry_from_externals(expr, externals, settings.pos)
|
make_curry_from_externals(expr, externals, settings.pos)
|
||||||
} else {
|
} else {
|
||||||
expr
|
expr
|
||||||
|
69
src/scope.rs
69
src/scope.rs
@ -158,32 +158,6 @@ impl<'a> Scope<'a> {
|
|||||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false)
|
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new shared entry to the Scope.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Scope;
|
|
||||||
///
|
|
||||||
/// let mut my_scope = Scope::new();
|
|
||||||
///
|
|
||||||
/// my_scope.push_shared("x", 42_i64);
|
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
|
||||||
/// ```
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
|
||||||
pub fn push_shared<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
|
||||||
&mut self,
|
|
||||||
name: K,
|
|
||||||
value: T,
|
|
||||||
) -> &mut Self {
|
|
||||||
self.push_dynamic_value(
|
|
||||||
name,
|
|
||||||
EntryType::Normal,
|
|
||||||
Dynamic::from(value).into_shared(),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add (push) a new `Dynamic` entry to the Scope.
|
/// Add (push) a new `Dynamic` entry to the Scope.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@ -226,34 +200,6 @@ impl<'a> Scope<'a> {
|
|||||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true)
|
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new shared constant to the Scope.
|
|
||||||
///
|
|
||||||
/// Shared constants are immutable and cannot be assigned to, but their shared values can change.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Scope;
|
|
||||||
///
|
|
||||||
/// let mut my_scope = Scope::new();
|
|
||||||
///
|
|
||||||
/// my_scope.push_constant_shared("x", 42_i64);
|
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
|
||||||
/// ```
|
|
||||||
#[cfg(not(feature = "no_shared"))]
|
|
||||||
pub fn push_constant_shared<K: Into<Cow<'a, str>>, T: Variant + Clone>(
|
|
||||||
&mut self,
|
|
||||||
name: K,
|
|
||||||
value: T,
|
|
||||||
) -> &mut Self {
|
|
||||||
self.push_dynamic_value(
|
|
||||||
name,
|
|
||||||
EntryType::Constant,
|
|
||||||
Dynamic::from(value).into_shared(),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
/// Add (push) a new constant with a `Dynamic` value to the Scope.
|
||||||
///
|
///
|
||||||
/// Constants are immutable and cannot be assigned to. Their values never change.
|
/// Constants are immutable and cannot be assigned to. Their values never change.
|
||||||
@ -394,7 +340,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||||
self.get_entry(name)
|
self.get_entry(name)
|
||||||
.and_then(|Entry { value, .. }| value.clone_inner_data::<T>())
|
.and_then(|Entry { value, .. }| value.clone().clone_inner_data::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the value of the named entry.
|
/// Update the value of the named entry.
|
||||||
@ -485,13 +431,20 @@ impl<'a> Scope<'a> {
|
|||||||
///
|
///
|
||||||
/// let (name, value) = iter.next().unwrap();
|
/// let (name, value) = iter.next().unwrap();
|
||||||
/// assert_eq!(name, "x");
|
/// assert_eq!(name, "x");
|
||||||
/// assert_eq!(value.clone().cast::<i64>(), 42);
|
/// assert_eq!(value.cast::<i64>(), 42);
|
||||||
///
|
///
|
||||||
/// let (name, value) = iter.next().unwrap();
|
/// let (name, value) = iter.next().unwrap();
|
||||||
/// assert_eq!(name, "foo");
|
/// assert_eq!(name, "foo");
|
||||||
/// assert_eq!(value.clone().cast::<String>(), "hello");
|
/// assert_eq!(value.cast::<String>(), "hello");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, Dynamic)> {
|
||||||
|
self.iter_raw()
|
||||||
|
.map(|(name, value)| (name, value.clone().clone_inner_data().unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an iterator to entries in the Scope.
|
||||||
|
/// Shared values are not expanded.
|
||||||
|
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(|Entry { name, value, .. }| (name.as_ref(), value))
|
.map(|Entry { name, value, .. }| (name.as_ref(), value))
|
||||||
|
19
src/token.rs
19
src/token.rs
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY,
|
Engine, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY,
|
||||||
KEYWORD_IS_SHARED, KEYWORD_PRINT, KEYWORD_SHARED, KEYWORD_TAKE, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
KEYWORD_IS_SHARED, KEYWORD_PRINT, KEYWORD_THIS, KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::error::LexError;
|
use crate::error::LexError;
|
||||||
@ -501,14 +501,15 @@ impl Token {
|
|||||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||||
|
|
||||||
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" | "public"
|
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" | "public"
|
||||||
| "new" | "use" | "module" | "package" | "var" | "static" | "with" | "do" | "each"
|
| "new" | "use" | "module" | "package" | "var" | "static" | "shared" | "with"
|
||||||
| "then" | "goto" | "exit" | "switch" | "match" | "case" | "try" | "catch"
|
| "do" | "each" | "then" | "goto" | "exit" | "switch" | "match" | "case" | "try"
|
||||||
| "default" | "void" | "null" | "nil" | "spawn" | "go" | "sync" | "async" | "await"
|
| "catch" | "default" | "void" | "null" | "nil" | "spawn" | "go" | "sync" | "async"
|
||||||
| "yield" => Reserved(syntax.into()),
|
| "await" | "yield" => Reserved(syntax.into()),
|
||||||
|
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_SHARED | KEYWORD_SHARED
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_SHARED | KEYWORD_THIS => {
|
||||||
| KEYWORD_TAKE | KEYWORD_THIS => Reserved(syntax.into()),
|
Reserved(syntax.into())
|
||||||
|
}
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
@ -1432,8 +1433,8 @@ fn get_identifier(
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_keyword_function(name: &str) -> bool {
|
pub fn is_keyword_function(name: &str) -> bool {
|
||||||
match name {
|
match name {
|
||||||
#[cfg(not(feature = "no_shared"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
KEYWORD_SHARED | KEYWORD_TAKE | KEYWORD_IS_SHARED => true,
|
KEYWORD_IS_SHARED => true,
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => true,
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -35,7 +35,8 @@ fn test_fn_ptr_curry_call() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(all(not(feature = "no_capture"), not(feature = "no_object")))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
@ -56,5 +57,61 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
|||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let a = 41;
|
||||||
|
let foo = |x| { a += x };
|
||||||
|
foo.call(1);
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
let a = 41;
|
||||||
|
let foo = |x| { a += x };
|
||||||
|
a.is_shared()
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let a = 1;
|
||||||
|
let b = 40;
|
||||||
|
let foo = |x| { this += a + x };
|
||||||
|
b.call(foo, 1);
|
||||||
|
b
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let a = 20;
|
||||||
|
let foo = |x| { this += a + x };
|
||||||
|
a.call(foo, 1);
|
||||||
|
a
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorDataRace(_, _)
|
||||||
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(feature = "no_capture"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
fn test_function_captures() -> Result<(), Box<EvalAltResult>> {
|
fn test_function_captures() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
324
tests/shared.rs
324
tests/shared.rs
@ -1,324 +0,0 @@
|
|||||||
#![cfg(not(feature = "no_shared"))]
|
|
||||||
|
|
||||||
use rhai::{Array, Dynamic, Engine, EvalAltResult, FnPtr, Module, RegisterFn, INT};
|
|
||||||
use std::any::TypeId;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_shared() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("shared(42)")?, 42);
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<bool>("shared(true)")?, true);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<String>("let x = shared(true); type_of(x)")?,
|
|
||||||
"bool"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<String>("let x = shared(true); x = (); type_of(x)")?,
|
|
||||||
"()"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
assert_eq!(engine.eval::<f64>("shared(4.2)")?, 4.2);
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<String>(r#"shared("test")"#)?, "test");
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<String>(r#"shared("test")"#)?, "test");
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<char>("shared('x')")?, 'x');
|
|
||||||
|
|
||||||
assert!(engine.eval::<bool>("is_shared(shared(42))")?);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert!(engine.eval::<bool>("shared(42).is_shared()")?);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
{
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<String>(
|
|
||||||
r#"
|
|
||||||
let s = shared("test");
|
|
||||||
let i = shared(0);
|
|
||||||
i = 2;
|
|
||||||
s[i] = 'S';
|
|
||||||
|
|
||||||
s
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
"teSt"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine
|
|
||||||
.eval::<Array>(
|
|
||||||
r#"
|
|
||||||
let x = shared([1, 2, 3]);
|
|
||||||
let y = shared([4, 5]);
|
|
||||||
x + y
|
|
||||||
"#
|
|
||||||
)?
|
|
||||||
.len(),
|
|
||||||
5
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r"
|
|
||||||
let x = shared([2, 9]);
|
|
||||||
x.insert(-1, 1);
|
|
||||||
x.insert(999, 3);
|
|
||||||
|
|
||||||
let r = x.remove(2);
|
|
||||||
|
|
||||||
let y = shared([4, 5]);
|
|
||||||
x.append(y);
|
|
||||||
|
|
||||||
x.len + r
|
|
||||||
"
|
|
||||||
)?,
|
|
||||||
14
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<bool>(
|
|
||||||
r#"
|
|
||||||
let x = shared([1, 2, 3]);
|
|
||||||
|
|
||||||
if x[0] + x[2] == 4 {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
let x = shared([1, 2, 3]);
|
|
||||||
let y = shared(());
|
|
||||||
|
|
||||||
(|| {
|
|
||||||
for i in x {
|
|
||||||
y = i * 10;
|
|
||||||
}
|
|
||||||
}).call();
|
|
||||||
|
|
||||||
y
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
30
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
let y = shared(#{a: 1, b: 2, c: 3});
|
|
||||||
y.c = shared(5);
|
|
||||||
y.c
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
5
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
let y = shared(#{a: 1, b: 2, c: shared(3)});
|
|
||||||
let c = y.c;
|
|
||||||
c = 5;// "c" holds Dynamic Shared
|
|
||||||
y.c
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
5
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
let x = shared(1);
|
|
||||||
(|| x = x + 41).call();
|
|
||||||
x
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
#[cfg(not(feature = "no_capture"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
let x = shared(#{a: 1, b: shared(2), c: 3});
|
|
||||||
let a = x.a;
|
|
||||||
let b = x.b;
|
|
||||||
a = 100; // does not hold reference to x.a
|
|
||||||
b = 20; // does hold reference to x.b
|
|
||||||
|
|
||||||
let f = |a| {
|
|
||||||
x.c = x.a + x.b + a;
|
|
||||||
};
|
|
||||||
|
|
||||||
f.call(21);
|
|
||||||
|
|
||||||
x.c
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
// Register a binary function named `foo`
|
|
||||||
engine.register_fn("custom_addition", |x: INT, y: INT| x + y);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>("custom_addition(shared(20), shared(22))")?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
{
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct TestStruct {
|
|
||||||
x: INT,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestStruct {
|
|
||||||
fn update(&mut self) {
|
|
||||||
self.x += 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge(&mut self, other: Self) {
|
|
||||||
self.x += other.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_x(&mut self) -> INT {
|
|
||||||
self.x
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_x(&mut self, new_x: INT) {
|
|
||||||
self.x = new_x;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new() -> Self {
|
|
||||||
TestStruct { x: 1 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
engine
|
|
||||||
.register_type::<TestStruct>()
|
|
||||||
.register_get_set("x", TestStruct::get_x, TestStruct::set_x)
|
|
||||||
.register_fn("update", TestStruct::update)
|
|
||||||
.register_fn("merge", TestStruct::merge)
|
|
||||||
.register_fn("new_ts", TestStruct::new)
|
|
||||||
.register_raw_fn(
|
|
||||||
"mutate_with_cb",
|
|
||||||
&[
|
|
||||||
TypeId::of::<TestStruct>(),
|
|
||||||
TypeId::of::<INT>(),
|
|
||||||
TypeId::of::<FnPtr>(),
|
|
||||||
],
|
|
||||||
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
|
|
||||||
let fp = std::mem::take(args[2]).cast::<FnPtr>();
|
|
||||||
let mut value = args[1].clone();
|
|
||||||
{
|
|
||||||
let mut lock = value.write_lock::<INT>().unwrap();
|
|
||||||
*lock = *lock + 1;
|
|
||||||
}
|
|
||||||
let this_ptr = args.get_mut(0).unwrap();
|
|
||||||
|
|
||||||
fp.call_dynamic(engine, lib, Some(this_ptr), [value])
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r"
|
|
||||||
let a = shared(new_ts());
|
|
||||||
|
|
||||||
a.x = 100;
|
|
||||||
a.update();
|
|
||||||
a.merge(a.take()); // take is important to prevent a deadlock
|
|
||||||
|
|
||||||
a.x
|
|
||||||
"
|
|
||||||
)?,
|
|
||||||
2200
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r"
|
|
||||||
let a = shared(new_ts());
|
|
||||||
let b = shared(100);
|
|
||||||
|
|
||||||
a.mutate_with_cb(b, |param| {
|
|
||||||
this.x = param;
|
|
||||||
param = 50;
|
|
||||||
this.update();
|
|
||||||
});
|
|
||||||
|
|
||||||
a.update();
|
|
||||||
a.x += b;
|
|
||||||
|
|
||||||
a.x
|
|
||||||
"
|
|
||||||
)?,
|
|
||||||
2151
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_shared_data_race() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
{
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { this += x };
|
|
||||||
|
|
||||||
let a = shared(41);
|
|
||||||
a.foo(1);
|
|
||||||
a
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(matches!(
|
|
||||||
*engine
|
|
||||||
.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { this += x };
|
|
||||||
|
|
||||||
let a = shared(42);
|
|
||||||
a.foo(a);
|
|
||||||
a
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorDataRace(_, _)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user