Merge pull request #658 from schungx/master
Allow global functions in unaliased imports.
This commit is contained in:
commit
56427e1dcd
25
.github/workflows/build.yml
vendored
25
.github/workflows/build.yml
vendored
@ -32,6 +32,7 @@ jobs:
|
|||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
|
|
||||||
# typical build with various feature combinations
|
# typical build with various feature combinations
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
@ -86,6 +87,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: ${{matrix.flags}}
|
args: ${{matrix.flags}}
|
||||||
|
|
||||||
# no-std builds are a bit more extensive to test
|
# no-std builds are a bit more extensive to test
|
||||||
no_std_build:
|
no_std_build:
|
||||||
name: NoStdBuild
|
name: NoStdBuild
|
||||||
@ -110,32 +112,40 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --manifest-path=no_std/no_std_test/Cargo.toml ${{matrix.flags}}
|
args: --manifest-path=no_std/no_std_test/Cargo.toml ${{matrix.flags}}
|
||||||
|
|
||||||
wasm:
|
wasm:
|
||||||
name: Check Wasm build
|
name: Check Wasm build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
flags:
|
flags:
|
||||||
|
- "--target wasm32-wasi"
|
||||||
# These fail currently, future PR should fix them
|
# These fail currently, future PR should fix them
|
||||||
# - ""
|
# - "--target wasm32-unknown-unknown"
|
||||||
# - "--features wasm-bindgen"
|
# - "--target wasm32-unknown-unknown --features wasm-bindgen"
|
||||||
- "--no-default-features"
|
- "--target wasm32-unknown-unknown --no-default-features"
|
||||||
- "--no-default-features --features wasm-bindgen"
|
- "--target wasm32-unknown-unknown --no-default-features --features wasm-bindgen"
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Setup Toolchain
|
- name: Setup Generic Wasm Toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.RUST_MSRV }}
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
target: wasm32-unknown-unknown
|
target: wasm32-unknown-unknown
|
||||||
|
- name: Setup Wasi Toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
target: wasm32-wasi
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --target wasm32-unknown-unknown ${{matrix.flags}}
|
args: ${{matrix.flags}}
|
||||||
|
|
||||||
rustfmt:
|
rustfmt:
|
||||||
name: Check Formatting
|
name: Check Formatting
|
||||||
@ -160,6 +170,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --all -- -Aclippy::all -Dclippy::perf
|
args: --all -- -Aclippy::all -Dclippy::perf
|
||||||
|
|
||||||
codegen_build:
|
codegen_build:
|
||||||
name: Codegen Build
|
name: Codegen Build
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
43
CHANGELOG.md
43
CHANGELOG.md
@ -4,28 +4,51 @@ Rhai Release Notes
|
|||||||
Version 1.11.0
|
Version 1.11.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications.
|
||||||
|
* `import` statements inside `eval` no longer cause errors in subsequent code.
|
||||||
|
* Functions marked `global` in `import`ed modules with no alias names now work properly.
|
||||||
|
|
||||||
|
Speed Improvements
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
### Stable hashing
|
### Stable hashing
|
||||||
|
|
||||||
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. This is necessary when using Rhai across shared-library boundaries.
|
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures.
|
||||||
* A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation.
|
* This is necessary when using Rhai across shared-library boundaries.
|
||||||
|
* A build script is used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation.
|
||||||
|
|
||||||
Bug fixes
|
### Serializable `Scope`
|
||||||
---------
|
|
||||||
|
|
||||||
* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications.
|
* `Scope` is now serializable and deserializable via `serde`.
|
||||||
|
|
||||||
|
### Call native Rust functions in `NativeCallContext`
|
||||||
|
|
||||||
|
* `NativeCallContext::call_native_fn` is added to call registered native Rust functions only.
|
||||||
|
* `NativeCallContext::call_native_fn_raw` is added as the advanced version.
|
||||||
|
* This is often desirable as Rust functions typically do not want a similar-named scripted function to hijack the process -- which will cause brittleness.
|
||||||
|
|
||||||
|
### Custom syntax improvements
|
||||||
|
|
||||||
|
* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`).
|
||||||
|
* This facilitates more accurate parsing by separating strings and identifiers.
|
||||||
|
|
||||||
|
### Limits API
|
||||||
|
|
||||||
|
* Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked`.
|
||||||
|
* This helps avoid the proliferation of unnecessary feature flags in third-party library code.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers.
|
|
||||||
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
|
||||||
* `Scope` is now serializable and deserializable via `serde`.
|
|
||||||
* `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline.
|
|
||||||
* `parse_json` function is added to parse a JSON string into an object map.
|
* `parse_json` function is added to parse a JSON string into an object map.
|
||||||
* Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code.
|
|
||||||
|
|
||||||
|
|
||||||
Version 1.10.1
|
Version 1.10.1
|
||||||
|
@ -233,6 +233,7 @@ impl Engine {
|
|||||||
arg_values,
|
arg_values,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
fn _call_fn(
|
fn _call_fn(
|
||||||
&self,
|
&self,
|
||||||
|
@ -181,7 +181,7 @@ impl FnCallHashes {
|
|||||||
|
|
||||||
/// _(internals)_ A function call.
|
/// _(internals)_ A function call.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Clone, Default, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct FnCallExpr {
|
pub struct FnCallExpr {
|
||||||
/// Namespace of the function, if any.
|
/// Namespace of the function, if any.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -196,6 +196,9 @@ pub struct FnCallExpr {
|
|||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
/// Is this function call a native operator?
|
/// Is this function call a native operator?
|
||||||
pub operator_token: Option<Token>,
|
pub operator_token: Option<Token>,
|
||||||
|
/// Can this function call be a scripted function?
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub can_be_script: bool,
|
||||||
/// [Position] of the function name.
|
/// [Position] of the function name.
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
@ -215,6 +218,10 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
if let Some(ref token) = self.operator_token {
|
if let Some(ref token) = self.operator_token {
|
||||||
ff.field("operator_token", token);
|
ff.field("operator_token", token);
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
if self.can_be_script {
|
||||||
|
ff.field("can_be_script", &self.can_be_script);
|
||||||
|
}
|
||||||
ff.field("hash", &self.hashes)
|
ff.field("hash", &self.hashes)
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
.field("args", &self.args);
|
.field("args", &self.args);
|
||||||
@ -493,6 +500,10 @@ impl fmt::Debug for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.write_str(&x.3)?;
|
f.write_str(&x.3)?;
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
if let Some(n) = x.1.index() {
|
||||||
|
write!(f, " #{}", n)?;
|
||||||
|
}
|
||||||
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
|
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
|
||||||
write!(f, " #{}", n)?;
|
write!(f, " #{}", n)?;
|
||||||
}
|
}
|
||||||
@ -680,6 +691,8 @@ impl Expr {
|
|||||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
operator_token: None,
|
operator_token: None,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: true,
|
||||||
pos,
|
pos,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -302,6 +302,10 @@ pub struct TryCatchBlock {
|
|||||||
pub catch_block: StmtBlock,
|
pub catch_block: StmtBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Number of items to keep inline for [`StmtBlockContainer`].
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
const STMT_BLOCK_INLINE_SIZE: usize = 8;
|
||||||
|
|
||||||
/// _(internals)_ The underlying container type for [`StmtBlock`].
|
/// _(internals)_ The underlying container type for [`StmtBlock`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
@ -309,7 +313,7 @@ pub struct TryCatchBlock {
|
|||||||
/// hold a statements block, with the assumption that most program blocks would container fewer than
|
/// hold a statements block, with the assumption that most program blocks would container fewer than
|
||||||
/// 8 statements, and those that do have a lot more statements.
|
/// 8 statements, and those that do have a lot more statements.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; 8]>;
|
pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
|
||||||
|
|
||||||
/// _(internals)_ The underlying container type for [`StmtBlock`].
|
/// _(internals)_ The underlying container type for [`StmtBlock`].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -491,9 +495,9 @@ impl From<Stmt> for StmtBlock {
|
|||||||
impl IntoIterator for StmtBlock {
|
impl IntoIterator for StmtBlock {
|
||||||
type Item = Stmt;
|
type Item = Stmt;
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
type IntoIter = smallvec::IntoIter<[Stmt; 8]>;
|
type IntoIter = smallvec::IntoIter<[Stmt; STMT_BLOCK_INLINE_SIZE]>;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
type IntoIter = smallvec::IntoIter<[Stmt; 3]>;
|
type IntoIter = smallvec::IntoIter<[Stmt; crate::STATIC_VEC_INLINE_SIZE]>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
@ -24,13 +24,13 @@ impl Engine {
|
|||||||
|
|
||||||
let root = namespace.root();
|
let root = namespace.root();
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
|
||||||
let index = if global.always_search_scope {
|
let index = if global.always_search_scope {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
namespace.index()
|
namespace.index()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Qualified - check if the root module is directly indexed
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
let offset = global.num_imports() - index.get();
|
let offset = global.num_imports() - index.get();
|
||||||
|
|
||||||
@ -223,9 +223,16 @@ impl Engine {
|
|||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
operator_token,
|
operator_token,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script,
|
||||||
..
|
..
|
||||||
} = expr;
|
} = expr;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let native = !can_be_script;
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
let native = true;
|
||||||
|
|
||||||
// Short-circuit native binary operator call if under Fast Operators mode
|
// Short-circuit native binary operator call if under Fast Operators mode
|
||||||
if operator_token.is_some() && self.fast_operators() && args.len() == 2 {
|
if operator_token.is_some() && self.fast_operators() && args.len() == 2 {
|
||||||
let mut lhs = self
|
let mut lhs = self
|
||||||
@ -251,7 +258,8 @@ impl Engine {
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
None, global, caches, lib, name, *hashes, operands, false, false, pos, level,
|
None, global, caches, lib, name, native, *hashes, operands, false, false, pos,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, ..)| v);
|
.map(|(v, ..)| v);
|
||||||
}
|
}
|
||||||
@ -280,6 +288,7 @@ impl Engine {
|
|||||||
lib,
|
lib,
|
||||||
this_ptr,
|
this_ptr,
|
||||||
name,
|
name,
|
||||||
|
native,
|
||||||
first_arg,
|
first_arg,
|
||||||
args,
|
args,
|
||||||
*hashes,
|
*hashes,
|
||||||
|
@ -23,12 +23,9 @@ pub type GlobalConstants =
|
|||||||
// corresponds to that key.
|
// corresponds to that key.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GlobalRuntimeState<'a> {
|
pub struct GlobalRuntimeState<'a> {
|
||||||
/// Stack of module names.
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
keys: crate::StaticVec<crate::ImmutableString>,
|
|
||||||
/// Stack of imported [modules][crate::Module].
|
/// Stack of imported [modules][crate::Module].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: crate::StaticVec<crate::Shared<crate::Module>>,
|
modules: crate::StaticVec<(crate::ImmutableString, crate::Shared<crate::Module>)>,
|
||||||
/// Source of the current context.
|
/// Source of the current context.
|
||||||
///
|
///
|
||||||
/// No source if the string is empty.
|
/// No source if the string is empty.
|
||||||
@ -80,8 +77,6 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(engine: &Engine) -> Self {
|
pub fn new(engine: &Engine) -> Self {
|
||||||
Self {
|
Self {
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
keys: crate::StaticVec::new_const(),
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: crate::StaticVec::new_const(),
|
modules: crate::StaticVec::new_const(),
|
||||||
source: Identifier::new_const(),
|
source: Identifier::new_const(),
|
||||||
@ -123,7 +118,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn num_imports(&self) -> usize {
|
pub fn num_imports(&self) -> usize {
|
||||||
self.keys.len()
|
self.modules.len()
|
||||||
}
|
}
|
||||||
/// Get the globally-imported [module][crate::Module] at a particular index.
|
/// Get the globally-imported [module][crate::Module] at a particular index.
|
||||||
///
|
///
|
||||||
@ -132,7 +127,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_shared_import(&self, index: usize) -> Option<crate::Shared<crate::Module>> {
|
pub fn get_shared_import(&self, index: usize) -> Option<crate::Shared<crate::Module>> {
|
||||||
self.modules.get(index).cloned()
|
self.modules.get(index).map(|(_, m)| m).cloned()
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to the globally-imported [module][crate::Module] at a
|
/// Get a mutable reference to the globally-imported [module][crate::Module] at a
|
||||||
/// particular index.
|
/// particular index.
|
||||||
@ -146,7 +141,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
) -> Option<&mut crate::Shared<crate::Module>> {
|
) -> Option<&mut crate::Shared<crate::Module>> {
|
||||||
self.modules.get_mut(index)
|
self.modules.get_mut(index).map(|(_, m)| m)
|
||||||
}
|
}
|
||||||
/// Get the index of a globally-imported [module][crate::Module] by name.
|
/// Get the index of a globally-imported [module][crate::Module] by name.
|
||||||
///
|
///
|
||||||
@ -155,12 +150,12 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn find_import(&self, name: &str) -> Option<usize> {
|
pub fn find_import(&self, name: &str) -> Option<usize> {
|
||||||
let len = self.keys.len();
|
let len = self.modules.len();
|
||||||
|
|
||||||
self.keys
|
self.modules
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.position(|key| key.as_str() == name)
|
.position(|(key, _)| key.as_str() == name)
|
||||||
.map(|i| len - 1 - i)
|
.map(|i| len - 1 - i)
|
||||||
}
|
}
|
||||||
/// Push an imported [module][crate::Module] onto the stack.
|
/// Push an imported [module][crate::Module] onto the stack.
|
||||||
@ -173,8 +168,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
name: impl Into<crate::ImmutableString>,
|
name: impl Into<crate::ImmutableString>,
|
||||||
module: impl Into<crate::Shared<crate::Module>>,
|
module: impl Into<crate::Shared<crate::Module>>,
|
||||||
) {
|
) {
|
||||||
self.keys.push(name.into());
|
self.modules.push((name.into(), module.into()));
|
||||||
self.modules.push(module.into());
|
|
||||||
}
|
}
|
||||||
/// Truncate the stack of globally-imported [modules][crate::Module] to a particular length.
|
/// Truncate the stack of globally-imported [modules][crate::Module] to a particular length.
|
||||||
///
|
///
|
||||||
@ -182,7 +176,6 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn truncate_imports(&mut self, size: usize) {
|
pub fn truncate_imports(&mut self, size: usize) {
|
||||||
self.keys.truncate(size);
|
|
||||||
self.modules.truncate(size);
|
self.modules.truncate(size);
|
||||||
}
|
}
|
||||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||||
@ -192,10 +185,9 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &crate::Module)> {
|
||||||
self.keys
|
self.modules
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.zip(self.modules.iter().rev())
|
|
||||||
.map(|(name, module)| (name.as_str(), &**module))
|
.map(|(name, module)| (name.as_str(), &**module))
|
||||||
}
|
}
|
||||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||||
@ -206,8 +198,8 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn iter_imports_raw(
|
pub(crate) fn iter_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
) -> impl Iterator<Item = &(crate::ImmutableString, crate::Shared<crate::Module>)> {
|
||||||
self.keys.iter().rev().zip(self.modules.iter().rev())
|
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.
|
||||||
///
|
///
|
||||||
@ -217,8 +209,8 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn scan_imports_raw(
|
pub fn scan_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
) -> impl Iterator<Item = &(crate::ImmutableString, crate::Shared<crate::Module>)> {
|
||||||
self.keys.iter().zip(self.modules.iter())
|
self.modules.iter()
|
||||||
}
|
}
|
||||||
/// Does the specified function hash key exist in the stack of globally-imported
|
/// Does the specified function hash key exist in the stack of globally-imported
|
||||||
/// [modules][crate::Module]?
|
/// [modules][crate::Module]?
|
||||||
@ -229,7 +221,9 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains_qualified_fn(&self, hash: u64) -> bool {
|
pub fn contains_qualified_fn(&self, hash: u64) -> bool {
|
||||||
self.modules.iter().any(|m| m.contains_qualified_fn(hash))
|
self.modules
|
||||||
|
.iter()
|
||||||
|
.any(|(_, m)| m.contains_qualified_fn(hash))
|
||||||
}
|
}
|
||||||
/// Get the specified function via its hash key from the stack of globally-imported
|
/// Get the specified function via its hash key from the stack of globally-imported
|
||||||
/// [modules][crate::Module].
|
/// [modules][crate::Module].
|
||||||
@ -245,7 +239,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
self.modules
|
self.modules
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id())))
|
.find_map(|(_, m)| m.get_qualified_fn(hash).map(|f| (f, m.id())))
|
||||||
}
|
}
|
||||||
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
|
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
|
||||||
/// globally-imported [modules][crate::Module]?
|
/// globally-imported [modules][crate::Module]?
|
||||||
@ -256,7 +250,9 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn contains_iter(&self, id: std::any::TypeId) -> bool {
|
pub fn contains_iter(&self, id: std::any::TypeId) -> bool {
|
||||||
self.modules.iter().any(|m| m.contains_qualified_iter(id))
|
self.modules
|
||||||
|
.iter()
|
||||||
|
.any(|(_, m)| m.contains_qualified_iter(id))
|
||||||
}
|
}
|
||||||
/// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported
|
/// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported
|
||||||
/// [modules][crate::Module].
|
/// [modules][crate::Module].
|
||||||
@ -269,7 +265,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
self.modules
|
self.modules
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|m| m.get_qualified_iter(id))
|
.find_map(|(_, m)| m.get_qualified_iter(id))
|
||||||
}
|
}
|
||||||
/// Get the current source.
|
/// Get the current source.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -312,29 +308,26 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl IntoIterator for GlobalRuntimeState<'_> {
|
impl IntoIterator for GlobalRuntimeState<'_> {
|
||||||
type Item = (crate::ImmutableString, crate::Shared<crate::Module>);
|
type Item = (crate::ImmutableString, crate::Shared<crate::Module>);
|
||||||
type IntoIter = std::iter::Zip<
|
type IntoIter = std::iter::Rev<
|
||||||
std::iter::Rev<smallvec::IntoIter<[crate::ImmutableString; 3]>>,
|
smallvec::IntoIter<
|
||||||
std::iter::Rev<smallvec::IntoIter<[crate::Shared<crate::Module>; 3]>>,
|
[(crate::ImmutableString, crate::Shared<crate::Module>); crate::STATIC_VEC_INLINE_SIZE],
|
||||||
|
>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.keys
|
self.modules.into_iter().rev()
|
||||||
.into_iter()
|
|
||||||
.rev()
|
|
||||||
.zip(self.modules.into_iter().rev())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
||||||
type Item = (&'a crate::ImmutableString, &'a crate::Shared<crate::Module>);
|
type Item = &'a (crate::ImmutableString, crate::Shared<crate::Module>);
|
||||||
type IntoIter = std::iter::Zip<
|
type IntoIter = std::iter::Rev<
|
||||||
std::iter::Rev<std::slice::Iter<'a, crate::ImmutableString>>,
|
std::slice::Iter<'a, (crate::ImmutableString, crate::Shared<crate::Module>)>,
|
||||||
std::iter::Rev<std::slice::Iter<'a, crate::Shared<crate::Module>>>,
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.keys.iter().rev().zip(self.modules.iter().rev())
|
self.modules.iter().rev()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,8 +338,7 @@ impl<K: Into<crate::ImmutableString>, M: Into<crate::Shared<crate::Module>>> Ext
|
|||||||
#[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 {
|
||||||
self.keys.push(k.into());
|
self.modules.push((k.into(), m.into()));
|
||||||
self.modules.push(m.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -358,7 +350,7 @@ impl fmt::Debug for GlobalRuntimeState<'_> {
|
|||||||
let mut f = f.debug_struct("GlobalRuntimeState");
|
let mut f = f.debug_struct("GlobalRuntimeState");
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
f.field("imports", &self.keys.iter().zip(self.modules.iter()));
|
f.field("imports", &self.modules);
|
||||||
|
|
||||||
f.field("source", &self.source)
|
f.field("source", &self.source)
|
||||||
.field("num_operations", &self.num_operations);
|
.field("num_operations", &self.num_operations);
|
||||||
|
@ -952,15 +952,19 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Ok(module) = module_result {
|
if let Ok(module) = module_result {
|
||||||
if !export.is_empty() {
|
let (export, must_be_indexed) = if !export.is_empty() {
|
||||||
if module.is_indexed() {
|
(export.name.clone(), true)
|
||||||
global.push_import(export.name.clone(), module);
|
} else {
|
||||||
} else {
|
(self.get_interned_string(""), false)
|
||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
};
|
||||||
let mut m = crate::func::shared_take_or_clone(module);
|
|
||||||
m.build_index();
|
if !must_be_indexed || module.is_indexed() {
|
||||||
global.push_import(export.name.clone(), m);
|
global.push_import(export, module);
|
||||||
}
|
} else {
|
||||||
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
|
let mut m = crate::func::shared_take_or_clone(module);
|
||||||
|
m.build_index();
|
||||||
|
global.push_import(export, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
global.num_modules_loaded += 1;
|
global.num_modules_loaded += 1;
|
||||||
|
174
src/func/call.rs
174
src/func/call.rs
@ -578,6 +578,7 @@ impl Engine {
|
|||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
|
_native_only: bool,
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -644,89 +645,90 @@ impl Engine {
|
|||||||
|
|
||||||
let level = level + 1;
|
let level = level + 1;
|
||||||
|
|
||||||
// Script-defined function call?
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let local_entry = &mut None;
|
if !_native_only {
|
||||||
|
// Script-defined function call?
|
||||||
|
let local_entry = &mut None;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
||||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
.resolve_fn(
|
||||||
.resolve_fn(
|
|
||||||
global,
|
|
||||||
caches,
|
|
||||||
local_entry,
|
|
||||||
lib,
|
|
||||||
fn_name,
|
|
||||||
hashes.script,
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.cloned()
|
|
||||||
{
|
|
||||||
// Script function call
|
|
||||||
assert!(func.is_script());
|
|
||||||
|
|
||||||
let func = func.get_script_fn_def().expect("script-defined function");
|
|
||||||
|
|
||||||
if func.body.is_empty() {
|
|
||||||
return Ok((Dynamic::UNIT, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut empty_scope;
|
|
||||||
let scope = match _scope {
|
|
||||||
Some(scope) => scope,
|
|
||||||
None => {
|
|
||||||
empty_scope = Scope::new();
|
|
||||||
&mut empty_scope
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let orig_source = mem::replace(
|
|
||||||
&mut global.source,
|
|
||||||
source
|
|
||||||
.as_ref()
|
|
||||||
.map_or(crate::Identifier::new_const(), |s| (**s).clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = if _is_method_call {
|
|
||||||
// Method call of script function - map first argument to `this`
|
|
||||||
let (first_arg, rest_args) = args.split_first_mut().unwrap();
|
|
||||||
|
|
||||||
self.call_script_fn(
|
|
||||||
scope,
|
|
||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
|
local_entry,
|
||||||
lib,
|
lib,
|
||||||
&mut Some(*first_arg),
|
fn_name,
|
||||||
func,
|
hashes.script,
|
||||||
rest_args,
|
None,
|
||||||
true,
|
false,
|
||||||
pos,
|
None,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
} else {
|
.cloned()
|
||||||
// Normal call of script function
|
{
|
||||||
let mut backup = ArgBackup::new();
|
// Script function call
|
||||||
|
assert!(func.is_script());
|
||||||
|
|
||||||
// The first argument is a reference?
|
let func = func.get_script_fn_def().expect("script-defined function");
|
||||||
if is_ref_mut && !args.is_empty() {
|
|
||||||
backup.change_first_arg_to_copy(args);
|
if func.body.is_empty() {
|
||||||
|
return Ok((Dynamic::UNIT, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = self.call_script_fn(
|
let mut empty_scope;
|
||||||
scope, global, caches, lib, &mut None, func, args, true, pos, level,
|
let scope = match _scope {
|
||||||
|
Some(scope) => scope,
|
||||||
|
None => {
|
||||||
|
empty_scope = Scope::new();
|
||||||
|
&mut empty_scope
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let orig_source = mem::replace(
|
||||||
|
&mut global.source,
|
||||||
|
source
|
||||||
|
.as_ref()
|
||||||
|
.map_or(crate::Identifier::new_const(), |s| (**s).clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Restore the original reference
|
let result = if _is_method_call {
|
||||||
backup.restore_first_arg(args);
|
// Method call of script function - map first argument to `this`
|
||||||
|
let (first_arg, rest_args) = args.split_first_mut().unwrap();
|
||||||
|
|
||||||
result
|
self.call_script_fn(
|
||||||
};
|
scope,
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
lib,
|
||||||
|
&mut Some(*first_arg),
|
||||||
|
func,
|
||||||
|
rest_args,
|
||||||
|
true,
|
||||||
|
pos,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Normal call of script function
|
||||||
|
let mut backup = ArgBackup::new();
|
||||||
|
|
||||||
// Restore the original source
|
// The first argument is a reference?
|
||||||
global.source = orig_source;
|
if is_ref_mut && !args.is_empty() {
|
||||||
|
backup.change_first_arg_to_copy(args);
|
||||||
|
}
|
||||||
|
|
||||||
return Ok((result?, false));
|
let result = self.call_script_fn(
|
||||||
|
scope, global, caches, lib, &mut None, func, args, true, pos, level,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restore the original reference
|
||||||
|
backup.restore_first_arg(args);
|
||||||
|
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
// Restore the original source
|
||||||
|
global.source = orig_source;
|
||||||
|
|
||||||
|
return Ok((result?, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native function call
|
// Native function call
|
||||||
@ -836,6 +838,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
lib,
|
lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
|
false,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
false,
|
false,
|
||||||
@ -881,6 +884,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
lib,
|
lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
|
false,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -968,6 +972,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
lib,
|
lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
|
false,
|
||||||
hash,
|
hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -995,6 +1000,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
|
native_only: bool,
|
||||||
first_arg: Option<&Expr>,
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
@ -1003,6 +1009,7 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
let native = native_only;
|
||||||
let mut first_arg = first_arg;
|
let mut first_arg = first_arg;
|
||||||
let mut a_expr = args_expr;
|
let mut a_expr = args_expr;
|
||||||
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len();
|
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len();
|
||||||
@ -1137,25 +1144,25 @@ impl Engine {
|
|||||||
KEYWORD_EVAL if total_args == 1 => {
|
KEYWORD_EVAL if total_args == 1 => {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
let orig_imports_len = global.num_imports();
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, pos) =
|
let (arg_value, pos) =
|
||||||
self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?;
|
self.get_arg_value(scope, global, caches, lib, this_ptr, arg, level)?;
|
||||||
let script = &arg_value
|
let s = &arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
||||||
let result = self.eval_script_expr_in_place(
|
let result =
|
||||||
scope,
|
self.eval_script_expr_in_place(scope, global, caches, lib, s, pos, level + 1);
|
||||||
global,
|
|
||||||
caches,
|
|
||||||
lib,
|
|
||||||
script,
|
|
||||||
pos,
|
|
||||||
level + 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||||
// all variable offsets from this point on will be mis-aligned.
|
// all variable offsets from this point on will be mis-aligned.
|
||||||
if scope.len() != orig_scope_len {
|
// The same is true for imports.
|
||||||
|
let scope_changed = scope.len() != orig_scope_len;
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
let scope_changed = scope_changed || global.num_imports() != orig_imports_len;
|
||||||
|
|
||||||
|
if scope_changed {
|
||||||
global.always_search_scope = true;
|
global.always_search_scope = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1199,8 +1206,8 @@ impl Engine {
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
scope, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos,
|
scope, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false,
|
||||||
level,
|
pos, level,
|
||||||
)
|
)
|
||||||
.map(|(v, ..)| v);
|
.map(|(v, ..)| v);
|
||||||
}
|
}
|
||||||
@ -1262,7 +1269,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
None, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level,
|
None, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, pos,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, ..)| v)
|
.map(|(v, ..)| v)
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ impl CallableFunction {
|
|||||||
Self::Script(..) => None,
|
Self::Script(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a new [`CallableFunction::Method`] from a [built-in function][`FnBuiltin`].
|
/// Create a new [`CallableFunction::Method`] from a built-in function.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_fn_builtin(func: FnBuiltin) -> Self {
|
pub fn from_fn_builtin(func: FnBuiltin) -> Self {
|
||||||
|
@ -135,7 +135,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
|
|||||||
|
|
||||||
impl<'a> NativeCallContext<'a> {
|
impl<'a> NativeCallContext<'a> {
|
||||||
/// _(internals)_ Create a new [`NativeCallContext`].
|
/// _(internals)_ Create a new [`NativeCallContext`].
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[deprecated(
|
#[deprecated(
|
||||||
since = "1.3.0",
|
since = "1.3.0",
|
||||||
note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly."
|
note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly."
|
||||||
@ -235,7 +235,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, &Shared<Module>)> {
|
) -> impl Iterator<Item = &(crate::ImmutableString, Shared<Module>)> {
|
||||||
self.global.iter().flat_map(|&g| g.iter_imports_raw())
|
self.global.iter().flat_map(|&g| g.iter_imports_raw())
|
||||||
}
|
}
|
||||||
/// _(internals)_ The current [`GlobalRuntimeState`], if any.
|
/// _(internals)_ The current [`GlobalRuntimeState`], if any.
|
||||||
@ -274,7 +274,7 @@ 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, &mut args)?;
|
let result = self._call_fn_raw(fn_name, false, false, false, &mut args)?;
|
||||||
|
|
||||||
let typ = self.engine().map_type_name(result.type_name());
|
let typ = self.engine().map_type_name(result.type_name());
|
||||||
|
|
||||||
@ -283,7 +283,32 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Call a function inside the call context.
|
/// Call a registered native Rust function inside the call context with the provided arguments.
|
||||||
|
///
|
||||||
|
/// This is often useful because Rust functions typically only want to cross-call other
|
||||||
|
/// registered Rust functions and not have to worry about scripted functions hijacking the
|
||||||
|
/// process unknowingly (or deliberately).
|
||||||
|
#[inline]
|
||||||
|
pub fn call_native_fn<T: Variant + Clone>(
|
||||||
|
&self,
|
||||||
|
fn_name: impl AsRef<str>,
|
||||||
|
args: impl FuncArgs,
|
||||||
|
) -> RhaiResultOf<T> {
|
||||||
|
let mut arg_values = StaticVec::new_const();
|
||||||
|
args.parse(&mut arg_values);
|
||||||
|
|
||||||
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||||
|
|
||||||
|
let result = self._call_fn_raw(fn_name, true, false, false, &mut args)?;
|
||||||
|
|
||||||
|
let typ = self.engine().map_type_name(result.type_name());
|
||||||
|
|
||||||
|
result.try_cast().ok_or_else(|| {
|
||||||
|
let t = self.engine().map_type_name(type_name::<T>()).into();
|
||||||
|
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Call a function (native Rust or scripted) inside the call context.
|
||||||
///
|
///
|
||||||
/// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for
|
/// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for
|
||||||
/// a script-defined function (or the object of a method call).
|
/// a script-defined function (or the object of a method call).
|
||||||
@ -302,6 +327,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
///
|
///
|
||||||
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
||||||
/// not consumed.
|
/// not consumed.
|
||||||
|
#[inline(always)]
|
||||||
pub fn call_fn_raw(
|
pub fn call_fn_raw(
|
||||||
&self,
|
&self,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: impl AsRef<str>,
|
||||||
@ -309,15 +335,76 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
is_method_call: bool,
|
is_method_call: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut global = self
|
self._call_fn_raw(fn_name, false, is_ref_mut, is_method_call, args)
|
||||||
|
}
|
||||||
|
/// Call a registered native Rust function inside the call context.
|
||||||
|
///
|
||||||
|
/// This is often useful because Rust functions typically only want to cross-call other
|
||||||
|
/// registered Rust functions and not have to worry about scripted functions hijacking the
|
||||||
|
/// process unknowingly (or deliberately).
|
||||||
|
///
|
||||||
|
/// # WARNING - Low Level API
|
||||||
|
///
|
||||||
|
/// This function is very low level.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid
|
||||||
|
/// unnecessarily cloning the arguments.
|
||||||
|
///
|
||||||
|
/// **DO NOT** reuse the arguments after this call. If they are needed afterwards, clone them
|
||||||
|
/// _before_ calling this function.
|
||||||
|
///
|
||||||
|
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
||||||
|
/// not consumed.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn call_native_fn_raw(
|
||||||
|
&self,
|
||||||
|
fn_name: impl AsRef<str>,
|
||||||
|
is_ref_mut: bool,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
|
) -> RhaiResult {
|
||||||
|
self._call_fn_raw(fn_name, true, is_ref_mut, false, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a function (native Rust or scripted) inside the call context.
|
||||||
|
fn _call_fn_raw(
|
||||||
|
&self,
|
||||||
|
fn_name: impl AsRef<str>,
|
||||||
|
native_only: bool,
|
||||||
|
is_ref_mut: bool,
|
||||||
|
is_method_call: bool,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
|
) -> RhaiResult {
|
||||||
|
let global = &mut self
|
||||||
.global
|
.global
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| GlobalRuntimeState::new(self.engine()));
|
.unwrap_or_else(|| GlobalRuntimeState::new(self.engine()));
|
||||||
let mut caches = Caches::new();
|
let caches = &mut Caches::new();
|
||||||
|
|
||||||
let fn_name = fn_name.as_ref();
|
let fn_name = fn_name.as_ref();
|
||||||
let args_len = args.len();
|
let args_len = args.len();
|
||||||
|
|
||||||
|
if native_only {
|
||||||
|
return self
|
||||||
|
.engine()
|
||||||
|
.call_native_fn(
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
self.lib,
|
||||||
|
fn_name,
|
||||||
|
calc_fn_hash(None, fn_name, args_len),
|
||||||
|
args,
|
||||||
|
is_ref_mut,
|
||||||
|
false,
|
||||||
|
Position::NONE,
|
||||||
|
self.level + 1,
|
||||||
|
)
|
||||||
|
.map(|(r, ..)| r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Native or script
|
||||||
|
|
||||||
let hash = if is_method_call {
|
let hash = if is_method_call {
|
||||||
FnCallHashes::from_all(
|
FnCallHashes::from_all(
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -331,10 +418,11 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
self.engine()
|
self.engine()
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
None,
|
None,
|
||||||
&mut global,
|
global,
|
||||||
&mut caches,
|
caches,
|
||||||
self.lib,
|
self.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
|
false,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
|
13
src/lib.rs
13
src/lib.rs
@ -341,6 +341,9 @@ pub use eval::{Caches, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeS
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub use api::definitions::Definitions;
|
pub use api::definitions::Definitions;
|
||||||
|
|
||||||
|
/// Number of items to keep inline for [`StaticVec`].
|
||||||
|
const STATIC_VEC_INLINE_SIZE: usize = 3;
|
||||||
|
|
||||||
/// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a
|
/// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a
|
||||||
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
||||||
///
|
///
|
||||||
@ -373,7 +376,7 @@ pub use api::definitions::Definitions;
|
|||||||
/// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels
|
/// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels
|
||||||
/// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get).
|
/// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get).
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
type StaticVec<T> = smallvec::SmallVec<[T; STATIC_VEC_INLINE_SIZE]>;
|
||||||
|
|
||||||
/// _(internals)_ Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec),
|
/// _(internals)_ Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec),
|
||||||
/// which is a [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
/// which is a [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
||||||
@ -408,7 +411,11 @@ type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
|||||||
/// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels
|
/// most scripts load fewer than 4 external modules; most module paths contain fewer than 4 levels
|
||||||
/// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get).
|
/// (e.g. `std::collections::map::HashMap` is 4 levels and it is just about as long as they get).
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
pub type StaticVec<T> = smallvec::SmallVec<[T; STATIC_VEC_INLINE_SIZE]>;
|
||||||
|
|
||||||
|
/// Number of items to keep inline for [`FnArgsVec`].
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
const FN_ARGS_VEC_INLINE_SIZE: usize = 5;
|
||||||
|
|
||||||
/// Inline arguments storage for function calls.
|
/// Inline arguments storage for function calls.
|
||||||
///
|
///
|
||||||
@ -423,7 +430,7 @@ pub type StaticVec<T> = smallvec::SmallVec<[T; 3]>;
|
|||||||
///
|
///
|
||||||
/// Under `no_closure`, this type aliases to [`StaticVec`][crate::StaticVec] instead.
|
/// Under `no_closure`, this type aliases to [`StaticVec`][crate::StaticVec] instead.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
type FnArgsVec<T> = smallvec::SmallVec<[T; 5]>;
|
type FnArgsVec<T> = smallvec::SmallVec<[T; FN_ARGS_VEC_INLINE_SIZE]>;
|
||||||
|
|
||||||
/// Inline arguments storage for function calls.
|
/// Inline arguments storage for function calls.
|
||||||
/// This type aliases to [`StaticVec`][crate::StaticVec].
|
/// This type aliases to [`StaticVec`][crate::StaticVec].
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use crate::{Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, ERR};
|
use crate::{
|
||||||
|
Engine, Module, ModuleResolver, Position, RhaiResultOf, Shared, StaticVec, ERR,
|
||||||
|
STATIC_VEC_INLINE_SIZE,
|
||||||
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{ops::AddAssign, slice::Iter, vec::IntoIter};
|
use std::{ops::AddAssign, slice::Iter};
|
||||||
|
|
||||||
/// [Module] resolution service that holds a collection of module resolvers,
|
/// [Module] resolution service that holds a collection of module resolvers,
|
||||||
/// to be searched in sequential order.
|
/// to be searched in sequential order.
|
||||||
@ -21,7 +24,7 @@ use std::{ops::AddAssign, slice::Iter, vec::IntoIter};
|
|||||||
/// engine.set_module_resolver(collection);
|
/// engine.set_module_resolver(collection);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ModuleResolversCollection(Vec<Box<dyn ModuleResolver>>);
|
pub struct ModuleResolversCollection(StaticVec<Box<dyn ModuleResolver>>);
|
||||||
|
|
||||||
impl ModuleResolversCollection {
|
impl ModuleResolversCollection {
|
||||||
/// Create a new [`ModuleResolversCollection`].
|
/// Create a new [`ModuleResolversCollection`].
|
||||||
@ -43,7 +46,7 @@ impl ModuleResolversCollection {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self(Vec::new())
|
Self(StaticVec::new_const())
|
||||||
}
|
}
|
||||||
/// Append a [module resolver][ModuleResolver] to the end.
|
/// Append a [module resolver][ModuleResolver] to the end.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -109,7 +112,7 @@ impl ModuleResolversCollection {
|
|||||||
|
|
||||||
impl IntoIterator for ModuleResolversCollection {
|
impl IntoIterator for ModuleResolversCollection {
|
||||||
type Item = Box<dyn ModuleResolver>;
|
type Item = Box<dyn ModuleResolver>;
|
||||||
type IntoIter = IntoIter<Box<dyn ModuleResolver>>;
|
type IntoIter = smallvec::IntoIter<[Box<dyn ModuleResolver>; STATIC_VEC_INLINE_SIZE]>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -897,24 +897,17 @@ 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);
|
||||||
|
|
||||||
match &mut **expr {
|
if matches!(**expr, Expr::FnCall(..) | Expr::Stmt(..)) {
|
||||||
// func(...)
|
state.set_dirty();
|
||||||
Expr::FnCall(x, pos) => {
|
*stmt = match *mem::take(expr) {
|
||||||
state.set_dirty();
|
// func(...);
|
||||||
*stmt = Stmt::FnCall(mem::take(x), *pos);
|
Expr::FnCall(x, pos) => Stmt::FnCall(x, pos),
|
||||||
}
|
// {};
|
||||||
// {...};
|
Expr::Stmt(x) if x.is_empty() => Stmt::Noop(x.position()),
|
||||||
Expr::Stmt(x) => {
|
// {...};
|
||||||
if x.is_empty() {
|
Expr::Stmt(x) => (*x).into(),
|
||||||
state.set_dirty();
|
_ => unreachable!(),
|
||||||
*stmt = Stmt::Noop(x.position());
|
};
|
||||||
} else {
|
|
||||||
state.set_dirty();
|
|
||||||
*stmt = mem::take(&mut **x).into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// expr;
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,7 +835,7 @@ pub mod array_functions {
|
|||||||
|
|
||||||
for item in array {
|
for item in array {
|
||||||
if ctx
|
if ctx
|
||||||
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
|
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||||
if item.type_id() == value.type_id() {
|
if item.type_id() == value.type_id() {
|
||||||
@ -927,7 +927,7 @@ pub mod array_functions {
|
|||||||
|
|
||||||
for (i, item) in array.iter_mut().enumerate().skip(start) {
|
for (i, item) in array.iter_mut().enumerate().skip(start) {
|
||||||
if ctx
|
if ctx
|
||||||
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
|
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||||
if item.type_id() == value.type_id() {
|
if item.type_id() == value.type_id() {
|
||||||
@ -2313,7 +2313,7 @@ pub mod array_functions {
|
|||||||
|
|
||||||
for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
|
for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
|
||||||
if !ctx
|
if !ctx
|
||||||
.call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2])
|
.call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||||
if a1.type_id() == a2.type_id() {
|
if a1.type_id() == a2.type_id() {
|
||||||
|
@ -213,7 +213,7 @@ mod map_functions {
|
|||||||
for (m1, v1) in map1 {
|
for (m1, v1) in map1 {
|
||||||
if let Some(v2) = map2.get_mut(m1) {
|
if let Some(v2) = map2.get_mut(m1) {
|
||||||
let equals = ctx
|
let equals = ctx
|
||||||
.call_fn_raw(OP_EQUALS, true, false, &mut [v1, v2])?
|
.call_native_fn_raw(OP_EQUALS, true, &mut [v1, v2])?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ pub fn print_with_func(
|
|||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
value: &mut Dynamic,
|
value: &mut Dynamic,
|
||||||
) -> crate::ImmutableString {
|
) -> crate::ImmutableString {
|
||||||
match ctx.call_fn_raw(fn_name, true, false, &mut [value]) {
|
match ctx.call_native_fn_raw(fn_name, true, &mut [value]) {
|
||||||
Ok(result) if result.is::<crate::ImmutableString>() => {
|
Ok(result) if result.is::<crate::ImmutableString>() => {
|
||||||
result.into_immutable_string().expect("`ImmutableString`")
|
result.into_immutable_string().expect("`ImmutableString`")
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,6 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_func_name = _lib.values().any(|f| f.name == name);
|
let is_func_name = _lib.values().any(|f| f.name == name);
|
||||||
|
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let is_func_name = false;
|
let is_func_name = false;
|
||||||
|
|
||||||
@ -600,7 +599,9 @@ impl Engine {
|
|||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
let hash = calc_fn_hash(None, &id, 0);
|
let hash = calc_fn_hash(None, &id, 0);
|
||||||
|
|
||||||
let hashes = if is_valid_function_name(&id) {
|
let is_valid_function_name = is_valid_function_name(&id);
|
||||||
|
|
||||||
|
let hashes = if is_valid_function_name {
|
||||||
hash.into()
|
hash.into()
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(hash)
|
FnCallHashes::from_native(hash)
|
||||||
@ -612,6 +613,8 @@ impl Engine {
|
|||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
operator_token: None,
|
operator_token: None,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: is_valid_function_name,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -668,7 +671,9 @@ impl Engine {
|
|||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
let hash = calc_fn_hash(None, &id, args.len());
|
let hash = calc_fn_hash(None, &id, args.len());
|
||||||
|
|
||||||
let hashes = if is_valid_function_name(&id) {
|
let is_valid_function_name = is_valid_function_name(&id);
|
||||||
|
|
||||||
|
let hashes = if is_valid_function_name {
|
||||||
hash.into()
|
hash.into()
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(hash)
|
FnCallHashes::from_native(hash)
|
||||||
@ -680,6 +685,8 @@ impl Engine {
|
|||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
operator_token: None,
|
operator_token: None,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: is_valid_function_name,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -1912,12 +1919,16 @@ impl Engine {
|
|||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
|
|
||||||
Ok(FnCallExpr {
|
Ok(FnCallExpr {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
namespace: Default::default(),
|
||||||
name: state.get_interned_string("-"),
|
name: state.get_interned_string("-"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
operator_token: Some(token),
|
operator_token: Some(token),
|
||||||
..Default::default()
|
capture_parent_scope: false,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
}
|
}
|
||||||
@ -1940,12 +1951,16 @@ impl Engine {
|
|||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
|
|
||||||
Ok(FnCallExpr {
|
Ok(FnCallExpr {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
namespace: Default::default(),
|
||||||
name: state.get_interned_string("+"),
|
name: state.get_interned_string("+"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
operator_token: Some(token),
|
operator_token: Some(token),
|
||||||
..Default::default()
|
capture_parent_scope: false,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
}
|
}
|
||||||
@ -1961,12 +1976,16 @@ impl Engine {
|
|||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
|
|
||||||
Ok(FnCallExpr {
|
Ok(FnCallExpr {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
namespace: Default::default(),
|
||||||
name: state.get_interned_string("!"),
|
name: state.get_interned_string("!"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
operator_token: Some(token),
|
operator_token: Some(token),
|
||||||
..Default::default()
|
capture_parent_scope: false,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
}
|
}
|
||||||
@ -2335,18 +2354,24 @@ impl Engine {
|
|||||||
|
|
||||||
let op = op_token.syntax();
|
let op = op_token.syntax();
|
||||||
let hash = calc_fn_hash(None, &op, 2);
|
let hash = calc_fn_hash(None, &op, 2);
|
||||||
let operator_token = if is_valid_function_name(&op) {
|
let is_function = is_valid_function_name(&op);
|
||||||
|
let operator_token = if is_function {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(op_token.clone())
|
Some(op_token.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let op_base = FnCallExpr {
|
let op_base = FnCallExpr {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
namespace: Default::default(),
|
||||||
name: state.get_interned_string(op.as_ref()),
|
name: state.get_interned_string(op.as_ref()),
|
||||||
hashes: FnCallHashes::from_native(hash),
|
hashes: FnCallHashes::from_native(hash),
|
||||||
|
args: StaticVec::new_const(),
|
||||||
pos,
|
pos,
|
||||||
operator_token,
|
operator_token,
|
||||||
..Default::default()
|
capture_parent_scope: false,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: is_function,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = StaticVec::new_const();
|
||||||
@ -2432,7 +2457,7 @@ impl Engine {
|
|||||||
let pos = args[0].start_position();
|
let pos = args[0].start_position();
|
||||||
|
|
||||||
FnCallExpr {
|
FnCallExpr {
|
||||||
hashes: if is_valid_function_name(&s) {
|
hashes: if is_function {
|
||||||
hash.into()
|
hash.into()
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(hash)
|
FnCallHashes::from_native(hash)
|
||||||
@ -2993,24 +3018,24 @@ impl Engine {
|
|||||||
// import expr ...
|
// import expr ...
|
||||||
let expr = self.parse_expr(input, state, lib, settings.level_up())?;
|
let expr = self.parse_expr(input, state, lib, settings.level_up())?;
|
||||||
|
|
||||||
// import expr;
|
let export = if !match_token(input, Token::As).0 {
|
||||||
if !match_token(input, Token::As).0 {
|
// import expr;
|
||||||
let empty = Ident {
|
Ident {
|
||||||
name: state.get_interned_string(""),
|
name: state.get_interned_string(""),
|
||||||
pos: Position::NONE,
|
pos: Position::NONE,
|
||||||
};
|
}
|
||||||
return Ok(Stmt::Import((expr, empty).into(), settings.pos));
|
} else {
|
||||||
}
|
// import expr as name ...
|
||||||
|
let (name, pos) = parse_var_name(input)?;
|
||||||
|
Ident {
|
||||||
|
name: state.get_interned_string(name),
|
||||||
|
pos,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// import expr as name ...
|
state.imports.push(export.name.clone());
|
||||||
let (name, pos) = parse_var_name(input)?;
|
|
||||||
let name = state.get_interned_string(name);
|
|
||||||
state.imports.push(name.clone());
|
|
||||||
|
|
||||||
Ok(Stmt::Import(
|
Ok(Stmt::Import((expr, export).into(), settings.pos))
|
||||||
(expr, Ident { name, pos }).into(),
|
|
||||||
settings.pos,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an export statement.
|
/// Parse an export statement.
|
||||||
@ -3659,6 +3684,8 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let expr = FnCallExpr {
|
let expr = FnCallExpr {
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
namespace: Default::default(),
|
||||||
name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY),
|
name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(
|
hashes: FnCallHashes::from_native(calc_fn_hash(
|
||||||
None,
|
None,
|
||||||
@ -3667,7 +3694,10 @@ impl Engine {
|
|||||||
)),
|
)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
..Default::default()
|
operator_token: None,
|
||||||
|
capture_parent_scope: false,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
can_be_script: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos);
|
.into_fn_call_expr(pos);
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ impl StringsInterner<'_> {
|
|||||||
|
|
||||||
// If the interner is over capacity, remove the longest entry that has the lowest count
|
// If the interner is over capacity, remove the longest entry that has the lowest count
|
||||||
if self.cache.len() > self.capacity {
|
if self.cache.len() > self.capacity {
|
||||||
// Leave some buffer to grow when shrinking the cache.
|
// Throttle: leave some buffer to grow when shrinking the cache.
|
||||||
// We leave at least two entries, one for the empty string, and one for the string
|
// We leave at least two entries, one for the empty string, and one for the string
|
||||||
// that has just been inserted.
|
// that has just been inserted.
|
||||||
let max = if self.capacity < 5 {
|
let max = if self.capacity < 5 {
|
||||||
|
Loading…
Reference in New Issue
Block a user