commit
2ffffb6fdc
@ -10,6 +10,12 @@ Bug fixes
|
|||||||
* Complex indexing/dotting chains now parse correctly, for example: `a[b][c[d]].e`
|
* Complex indexing/dotting chains now parse correctly, for example: `a[b][c[d]].e`
|
||||||
* `map` and `filter` for arrays are marked `pure`. Warnings are added to the documentation of pure array methods that take `this` closures.
|
* `map` and `filter` for arrays are marked `pure`. Warnings are added to the documentation of pure array methods that take `this` closures.
|
||||||
* Syntax such as `foo.bar::baz` no longer panics, but returns a proper parse error.
|
* Syntax such as `foo.bar::baz` no longer panics, but returns a proper parse error.
|
||||||
|
* `x += y` where `x` and `y` are `char` now works correctly.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* The functions `min` and `max` are added for numbers.
|
||||||
|
|
||||||
|
|
||||||
Version 1.12.0
|
Version 1.12.0
|
||||||
|
@ -3,8 +3,8 @@ use crate::packages::iter_basic::{BitRange, CharsStream, StepRange};
|
|||||||
use crate::parser::{ParseResult, ParseState};
|
use crate::parser::{ParseResult, ParseState};
|
||||||
use crate::types::StringsInterner;
|
use crate::types::StringsInterner;
|
||||||
use crate::{
|
use crate::{
|
||||||
Engine, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, OptimizationLevel, Position,
|
Engine, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, Position, RhaiError,
|
||||||
RhaiError, SmartString, ERR,
|
SmartString, ERR,
|
||||||
};
|
};
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -288,7 +288,7 @@ impl Engine {
|
|||||||
stream.peekable(),
|
stream.peekable(),
|
||||||
&mut state,
|
&mut state,
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
OptimizationLevel::None,
|
crate::OptimizationLevel::None,
|
||||||
#[cfg(feature = "no_optimize")]
|
#[cfg(feature = "no_optimize")]
|
||||||
(),
|
(),
|
||||||
)?;
|
)?;
|
||||||
|
@ -51,6 +51,7 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Initialize scripting engine
|
// Initialize scripting engine
|
||||||
|
#[allow(unused_mut)]
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
@ -98,7 +98,7 @@ pub struct Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
|
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
|
||||||
|
|
||||||
/// An empty [`ImmutableString`] for cloning purposes.
|
/// Strings interner.
|
||||||
pub(crate) interned_strings: Locked<StringsInterner>,
|
pub(crate) interned_strings: Locked<StringsInterner>,
|
||||||
|
|
||||||
/// A set of symbols to disable.
|
/// A set of symbols to disable.
|
||||||
|
@ -543,6 +543,7 @@ impl Engine {
|
|||||||
let op_pos = parent.position();
|
let op_pos = parent.position();
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
|
#[allow(unused_mut)]
|
||||||
let mut this_ptr = this_ptr;
|
let mut this_ptr = this_ptr;
|
||||||
|
|
||||||
match ChainType::from(parent) {
|
match ChainType::from(parent) {
|
||||||
|
@ -10,9 +10,9 @@ use crate::{
|
|||||||
Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult,
|
Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult,
|
||||||
SmartString, INT,
|
SmartString, INT,
|
||||||
};
|
};
|
||||||
|
use std::any::TypeId;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::TypeId, fmt::Write};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
@ -753,7 +753,7 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti
|
|||||||
let x = &mut *args[0].write_lock::<Dynamic>().unwrap();
|
let x = &mut *args[0].write_lock::<Dynamic>().unwrap();
|
||||||
|
|
||||||
let mut buf = SmartString::new_const();
|
let mut buf = SmartString::new_const();
|
||||||
write!(&mut buf, "{y}").unwrap();
|
buf.push(x.as_char().unwrap());
|
||||||
buf.push(y);
|
buf.push(y);
|
||||||
|
|
||||||
Ok((*x = buf.into()).into())
|
Ok((*x = buf.into()).into())
|
||||||
|
@ -51,12 +51,8 @@ impl Hasher for StraightHasher {
|
|||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write_u64(&mut self, i: u64) {
|
fn write_u64(&mut self, i: u64) {
|
||||||
if i == 0 {
|
|
||||||
self.0 = ALT_ZERO_HASH;
|
|
||||||
} else {
|
|
||||||
self.0 = i;
|
self.0 = i;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A hash builder for `StraightHasher`.
|
/// A hash builder for `StraightHasher`.
|
||||||
@ -69,7 +65,7 @@ impl BuildHasher for StraightHasherBuilder {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn build_hasher(&self) -> Self::Hasher {
|
fn build_hasher(&self) -> Self::Hasher {
|
||||||
StraightHasher(ALT_ZERO_HASH)
|
StraightHasher(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,16 +95,19 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_var_hash<'a>(
|
pub fn calc_var_hash<'a>(namespace: impl IntoIterator<Item = &'a str>, var_name: &str) -> u64 {
|
||||||
modules: impl IntoIterator<Item = &'a str, IntoIter = impl ExactSizeIterator<Item = &'a str>>,
|
|
||||||
var_name: &str,
|
|
||||||
) -> u64 {
|
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
let iter = modules.into_iter();
|
namespace.into_iter().for_each(|m| {
|
||||||
iter.len().hash(s);
|
// We always skip the first module
|
||||||
iter.skip(1).for_each(|m| m.hash(s));
|
if count > 0 {
|
||||||
|
m.hash(s);
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
});
|
||||||
|
count.hash(s);
|
||||||
var_name.hash(s);
|
var_name.hash(s);
|
||||||
|
|
||||||
match s.finish() {
|
match s.finish() {
|
||||||
@ -135,16 +134,21 @@ pub fn calc_var_hash<'a>(
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_fn_hash<'a>(
|
pub fn calc_fn_hash<'a>(
|
||||||
namespace: impl IntoIterator<Item = &'a str, IntoIter = impl ExactSizeIterator<Item = &'a str>>,
|
namespace: impl IntoIterator<Item = &'a str>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
num: usize,
|
num: usize,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
namespace.into_iter().for_each(|m| {
|
||||||
// We always skip the first module
|
// We always skip the first module
|
||||||
let iter = namespace.into_iter();
|
if count > 0 {
|
||||||
iter.len().hash(s);
|
m.hash(s);
|
||||||
iter.skip(1).for_each(|m| m.hash(s));
|
}
|
||||||
|
count += 1;
|
||||||
|
});
|
||||||
|
count.hash(s);
|
||||||
fn_name.hash(s);
|
fn_name.hash(s);
|
||||||
num.hash(s);
|
num.hash(s);
|
||||||
|
|
||||||
@ -163,17 +167,15 @@ pub fn calc_fn_hash<'a>(
|
|||||||
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_fn_hash_full(
|
pub fn calc_fn_hash_full(base: u64, params: impl IntoIterator<Item = TypeId>) -> u64 {
|
||||||
base: u64,
|
|
||||||
params: impl IntoIterator<Item = TypeId, IntoIter = impl ExactSizeIterator<Item = TypeId>>,
|
|
||||||
) -> u64 {
|
|
||||||
let s = &mut get_hasher();
|
let s = &mut get_hasher();
|
||||||
base.hash(s);
|
base.hash(s);
|
||||||
let iter = params.into_iter();
|
let mut count = 0;
|
||||||
iter.len().hash(s);
|
params.into_iter().for_each(|t| {
|
||||||
iter.for_each(|t| {
|
|
||||||
t.hash(s);
|
t.hash(s);
|
||||||
|
count += 1;
|
||||||
});
|
});
|
||||||
|
count.hash(s);
|
||||||
|
|
||||||
match s.finish() {
|
match s.finish() {
|
||||||
0 => ALT_ZERO_HASH,
|
0 => ALT_ZERO_HASH,
|
||||||
|
@ -462,7 +462,8 @@ impl Module {
|
|||||||
self.dynamic_functions_filter = None;
|
self.dynamic_functions_filter = None;
|
||||||
self.type_iterators = None;
|
self.type_iterators = None;
|
||||||
self.all_type_iterators = None;
|
self.all_type_iterators = None;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a custom type to a friendly display name.
|
/// Map a custom type to a friendly display name.
|
||||||
@ -746,7 +747,8 @@ impl Module {
|
|||||||
func: fn_def.into(),
|
func: fn_def.into(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
hash_script
|
hash_script
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,7 +787,8 @@ impl Module {
|
|||||||
self.all_functions = None;
|
self.all_functions = None;
|
||||||
self.all_variables = None;
|
self.all_variables = None;
|
||||||
self.all_type_iterators = None;
|
self.all_type_iterators = None;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
|
|
||||||
self.modules.get_or_insert_with(Default::default)
|
self.modules.get_or_insert_with(Default::default)
|
||||||
}
|
}
|
||||||
@ -851,7 +854,8 @@ impl Module {
|
|||||||
self.modules
|
self.modules
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
.insert(name.into(), sub_module.into());
|
.insert(name.into(), sub_module.into());
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -962,7 +966,8 @@ impl Module {
|
|||||||
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
|
||||||
if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
|
if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) {
|
||||||
f.metadata.namespace = namespace;
|
f.metadata.namespace = namespace;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -1078,7 +1083,8 @@ impl Module {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
|
|
||||||
hash_fn
|
hash_fn
|
||||||
}
|
}
|
||||||
@ -1676,7 +1682,8 @@ impl Module {
|
|||||||
self.all_functions = None;
|
self.all_functions = None;
|
||||||
self.all_variables = None;
|
self.all_variables = None;
|
||||||
self.all_type_iterators = None;
|
self.all_type_iterators = None;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
@ -1732,7 +1739,8 @@ impl Module {
|
|||||||
self.all_functions = None;
|
self.all_functions = None;
|
||||||
self.all_variables = None;
|
self.all_variables = None;
|
||||||
self.all_type_iterators = None;
|
self.all_type_iterators = None;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
@ -1797,7 +1805,8 @@ impl Module {
|
|||||||
self.all_functions = None;
|
self.all_functions = None;
|
||||||
self.all_variables = None;
|
self.all_variables = None;
|
||||||
self.all_type_iterators = None;
|
self.all_type_iterators = None;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
@ -1880,7 +1889,8 @@ impl Module {
|
|||||||
self.all_functions = None;
|
self.all_functions = None;
|
||||||
self.all_variables = None;
|
self.all_variables = None;
|
||||||
self.all_type_iterators = None;
|
self.all_type_iterators = None;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
@ -1923,7 +1933,8 @@ impl Module {
|
|||||||
self.all_functions = None;
|
self.all_functions = None;
|
||||||
self.all_variables = None;
|
self.all_variables = None;
|
||||||
self.all_type_iterators = None;
|
self.all_type_iterators = None;
|
||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags
|
||||||
|
.remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2306,7 +2317,7 @@ impl Module {
|
|||||||
|
|
||||||
path.push("");
|
path.push("");
|
||||||
|
|
||||||
let r = index_module(
|
let has_global_functions = index_module(
|
||||||
self,
|
self,
|
||||||
&mut path,
|
&mut path,
|
||||||
&mut variables,
|
&mut variables,
|
||||||
@ -2314,9 +2325,8 @@ impl Module {
|
|||||||
&mut type_iterators,
|
&mut type_iterators,
|
||||||
);
|
);
|
||||||
|
|
||||||
if r {
|
self.flags
|
||||||
self.flags |= ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
.set(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS, has_global_functions);
|
||||||
}
|
|
||||||
|
|
||||||
self.all_variables = if variables.is_empty() {
|
self.all_variables = if variables.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -229,7 +229,7 @@ fn optimize_stmt_block(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Optimize each statement in the block
|
// Optimize each statement in the block
|
||||||
for stmt in &mut statements {
|
statements.iter_mut().for_each(|stmt| {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Var(x, options, ..) => {
|
Stmt::Var(x, options, ..) => {
|
||||||
if options.contains(ASTFlags::CONSTANT) {
|
if options.contains(ASTFlags::CONSTANT) {
|
||||||
@ -252,7 +252,7 @@ fn optimize_stmt_block(
|
|||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
// Remove all pure statements except the last one
|
// Remove all pure statements except the last one
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
@ -626,11 +626,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
for r in &*ranges {
|
ranges.iter().for_each(|r| {
|
||||||
let b = &mut expressions[r.index()];
|
let b = &mut expressions[r.index()];
|
||||||
optimize_expr(&mut b.condition, state, false);
|
optimize_expr(&mut b.condition, state, false);
|
||||||
optimize_expr(&mut b.expr, state, false);
|
optimize_expr(&mut b.expr, state, false);
|
||||||
}
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -663,7 +663,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
optimize_expr(match_expr, state, false);
|
optimize_expr(match_expr, state, false);
|
||||||
|
|
||||||
// Optimize blocks
|
// Optimize blocks
|
||||||
for b in expressions.as_mut() {
|
expressions.iter_mut().for_each(|b| {
|
||||||
optimize_expr(&mut b.condition, state, false);
|
optimize_expr(&mut b.condition, state, false);
|
||||||
optimize_expr(&mut b.expr, state, false);
|
optimize_expr(&mut b.expr, state, false);
|
||||||
|
|
||||||
@ -671,7 +671,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
b.expr = Expr::Unit(b.expr.position());
|
b.expr = Expr::Unit(b.expr.position());
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
// Remove false cases
|
// Remove false cases
|
||||||
cases.retain(|_, list| {
|
cases.retain(|_, list| {
|
||||||
@ -718,12 +718,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove unused block statements
|
// Remove unused block statements
|
||||||
for index in 0..expressions.len() {
|
(0..expressions.len()).into_iter().for_each(|index| {
|
||||||
if *def_case == Some(index)
|
if *def_case == Some(index)
|
||||||
|| cases.values().flat_map(|c| c.iter()).any(|&n| n == index)
|
|| cases.values().flat_map(|c| c.iter()).any(|&n| n == index)
|
||||||
|| ranges.iter().any(|r| r.index() == index)
|
|| ranges.iter().any(|r| r.index() == index)
|
||||||
{
|
{
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let b = &mut expressions[index];
|
let b = &mut expressions[index];
|
||||||
@ -732,7 +732,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
b.expr = Expr::Unit(b.expr.position());
|
b.expr = Expr::Unit(b.expr.position());
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
@ -1159,8 +1159,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
||||||
|
|
||||||
// Move constant arguments
|
// Move constant arguments
|
||||||
for arg in &mut x.args {
|
x.args.iter_mut().for_each(|arg| match arg {
|
||||||
match arg {
|
|
||||||
Expr::DynamicConstant(..) | Expr::Unit(..)
|
Expr::DynamicConstant(..) | Expr::Unit(..)
|
||||||
| Expr::StringConstant(..) | Expr::CharConstant(..)
|
| Expr::StringConstant(..) | Expr::CharConstant(..)
|
||||||
| Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (),
|
| Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (),
|
||||||
@ -1172,8 +1171,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*arg = Expr::DynamicConstant(value.into(), arg.start_position());
|
*arg = Expr::DynamicConstant(value.into(), arg.start_position());
|
||||||
},
|
},
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
@ -1209,7 +1207,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) or xxx.id(args ..) -> optimize function call arguments
|
// id(args ..) or xxx.id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => for arg in &mut x.args {
|
Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => x.args.iter_mut().for_each(|arg| {
|
||||||
optimize_expr(arg, state, false);
|
optimize_expr(arg, state, false);
|
||||||
|
|
||||||
// Move constant arguments
|
// Move constant arguments
|
||||||
@ -1226,7 +1224,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
*arg = Expr::DynamicConstant(value.into(), arg.start_position());
|
*arg = Expr::DynamicConstant(value.into(), arg.start_position());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -1304,20 +1302,25 @@ impl Engine {
|
|||||||
let mut state = OptimizerState::new(self, lib, optimization_level);
|
let mut state = OptimizerState::new(self, lib, optimization_level);
|
||||||
|
|
||||||
// Add constants from global modules
|
// Add constants from global modules
|
||||||
for (name, value) in self.global_modules.iter().rev().flat_map(|m| m.iter_var()) {
|
self.global_modules
|
||||||
state.push_var(name, AccessMode::ReadOnly, Some(value.clone()));
|
.iter()
|
||||||
}
|
.rev()
|
||||||
|
.flat_map(|m| m.iter_var())
|
||||||
|
.for_each(|(name, value)| {
|
||||||
|
state.push_var(name, AccessMode::ReadOnly, Some(value.clone()))
|
||||||
|
});
|
||||||
|
|
||||||
// Add constants and variables from the scope
|
// Add constants and variables from the scope
|
||||||
if let Some(scope) = scope {
|
scope
|
||||||
for (name, constant, value) in scope.iter() {
|
.into_iter()
|
||||||
|
.flat_map(Scope::iter)
|
||||||
|
.for_each(|(name, constant, value)| {
|
||||||
if constant {
|
if constant {
|
||||||
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
||||||
} else {
|
} else {
|
||||||
state.push_var(name, AccessMode::ReadWrite, None);
|
state.push_var(name, AccessMode::ReadWrite, None);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
optimize_stmt_block(statements, &mut state, true, false, true)
|
optimize_stmt_block(statements, &mut state, true, false, true)
|
||||||
}
|
}
|
||||||
@ -1339,15 +1342,16 @@ impl Engine {
|
|||||||
let mut module = crate::Module::new();
|
let mut module = crate::Module::new();
|
||||||
|
|
||||||
if optimization_level == OptimizationLevel::None {
|
if optimization_level == OptimizationLevel::None {
|
||||||
for fn_def in functions {
|
functions.into_iter().for_each(|fn_def| {
|
||||||
module.set_script_fn(fn_def);
|
module.set_script_fn(fn_def);
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
// We only need the script library's signatures for optimization purposes
|
// We only need the script library's signatures for optimization purposes
|
||||||
let mut lib2 = crate::Module::new();
|
let mut lib2 = crate::Module::new();
|
||||||
|
|
||||||
for fn_def in &functions {
|
functions
|
||||||
lib2.set_script_fn(crate::ast::ScriptFnDef {
|
.iter()
|
||||||
|
.map(|fn_def| crate::ast::ScriptFnDef {
|
||||||
name: fn_def.name.clone(),
|
name: fn_def.name.clone(),
|
||||||
access: fn_def.access,
|
access: fn_def.access,
|
||||||
body: crate::ast::StmtBlock::NONE,
|
body: crate::ast::StmtBlock::NONE,
|
||||||
@ -1355,12 +1359,14 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: Box::default(),
|
comments: Box::default(),
|
||||||
|
})
|
||||||
|
.for_each(|script_def| {
|
||||||
|
lib2.set_script_fn(script_def);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
let lib2 = &[lib2.into()];
|
let lib2 = &[lib2.into()];
|
||||||
|
|
||||||
for fn_def in functions {
|
functions.into_iter().for_each(|fn_def| {
|
||||||
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
|
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
|
||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
@ -1369,7 +1375,7 @@ impl Engine {
|
|||||||
*fn_def.body = self.optimize_top_level(body, scope, lib2, optimization_level);
|
*fn_def.body = self.optimize_top_level(body, scope, lib2, optimization_level);
|
||||||
|
|
||||||
module.set_script_fn(fn_def);
|
module.set_script_fn(fn_def);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.into()
|
module.into()
|
||||||
|
@ -21,6 +21,8 @@ macro_rules! gen_cmp_functions {
|
|||||||
#[rhai_fn(name = ">=")] pub fn gte(x: $arg_type, y: $arg_type) -> bool { x >= y }
|
#[rhai_fn(name = ">=")] pub fn gte(x: $arg_type, y: $arg_type) -> bool { x >= y }
|
||||||
#[rhai_fn(name = "==")] pub fn eq(x: $arg_type, y: $arg_type) -> bool { x == y }
|
#[rhai_fn(name = "==")] pub fn eq(x: $arg_type, y: $arg_type) -> bool { x == y }
|
||||||
#[rhai_fn(name = "!=")] pub fn ne(x: $arg_type, y: $arg_type) -> bool { x != y }
|
#[rhai_fn(name = "!=")] pub fn ne(x: $arg_type, y: $arg_type) -> bool { x != y }
|
||||||
|
pub fn max(x: $arg_type, y: $arg_type) -> $arg_type { if x >= y { x } else { y } }
|
||||||
|
pub fn min(x: $arg_type, y: $arg_type) -> $arg_type { if x <= y { x } else { y } }
|
||||||
}
|
}
|
||||||
})* }
|
})* }
|
||||||
};
|
};
|
||||||
@ -53,14 +55,22 @@ def_package! {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
|
combine_with_exported_module!(lib, "float", float_functions);
|
||||||
|
|
||||||
#[cfg(not(feature = "f32_float"))]
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
{
|
||||||
reg_functions!(lib += float; f32);
|
reg_functions!(lib += float; f32);
|
||||||
combine_with_exported_module!(lib, "f32", f32_functions);
|
combine_with_exported_module!(lib, "f32", f32_functions);
|
||||||
|
}
|
||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
|
{
|
||||||
reg_functions!(lib += float; f64);
|
reg_functions!(lib += float; f64);
|
||||||
combine_with_exported_module!(lib, "f64", f64_functions);
|
combine_with_exported_module!(lib, "f64", f64_functions);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
combine_with_exported_module!(lib, "decimal", decimal_functions);
|
||||||
|
|
||||||
combine_with_exported_module!(lib, "logic", logic_functions);
|
combine_with_exported_module!(lib, "logic", logic_functions);
|
||||||
}
|
}
|
||||||
@ -95,9 +105,90 @@ mod logic_functions {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod f32_functions {
|
mod float_functions {
|
||||||
use crate::INT;
|
use crate::INT;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_if_32(x: INT, y: f32) -> f32 {
|
||||||
|
let (x, y) = (x as f32, y as f32);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_fi_32(x: f32, y: INT) -> f32 {
|
||||||
|
let (x, y) = (x as f32, y as f32);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_if_32(x: INT, y: f32) -> f32 {
|
||||||
|
let (x, y) = (x as f32, y as f32);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_fi_32(x: f32, y: INT) -> f32 {
|
||||||
|
let (x, y) = (x as f32, y as f32);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_if_64(x: INT, y: f64) -> f64 {
|
||||||
|
let (x, y) = (x as f64, y as f64);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_fi_64(x: f64, y: INT) -> f64 {
|
||||||
|
let (x, y) = (x as f64, y as f64);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_if_64(x: INT, y: f64) -> f64 {
|
||||||
|
let (x, y) = (x as f64, y as f64);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_fi_64(x: f64, y: INT) -> f64 {
|
||||||
|
let (x, y) = (x as f64, y as f64);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
#[export_module]
|
||||||
|
mod f32_functions {
|
||||||
|
use crate::{FLOAT, INT};
|
||||||
|
|
||||||
#[rhai_fn(name = "==")]
|
#[rhai_fn(name = "==")]
|
||||||
pub fn eq_if(x: INT, y: f32) -> bool {
|
pub fn eq_if(x: INT, y: f32) -> bool {
|
||||||
(x as f32) == (y as f32)
|
(x as f32) == (y as f32)
|
||||||
@ -146,13 +237,51 @@ mod f32_functions {
|
|||||||
pub fn lte_fi(x: f32, y: INT) -> bool {
|
pub fn lte_fi(x: f32, y: INT) -> bool {
|
||||||
(x as f32) <= (y as f32)
|
(x as f32) <= (y as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_64_32(x: FLOAT, y: f32) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_32_64(x: f32, y: FLOAT) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_64_32(x: FLOAT, y: f32) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_32_64(x: f32, y: FLOAT) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod f64_functions {
|
mod f64_functions {
|
||||||
use crate::INT;
|
use crate::{FLOAT, INT};
|
||||||
|
|
||||||
#[rhai_fn(name = "==")]
|
#[rhai_fn(name = "==")]
|
||||||
pub fn eq_if(x: INT, y: f64) -> bool {
|
pub fn eq_if(x: INT, y: f64) -> bool {
|
||||||
@ -202,4 +331,85 @@ mod f64_functions {
|
|||||||
pub fn lte_fi(x: f64, y: INT) -> bool {
|
pub fn lte_fi(x: f64, y: INT) -> bool {
|
||||||
(x as f64) <= (y as f64)
|
(x as f64) <= (y as f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_32_64(x: FLOAT, y: f64) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_64_32(x: f64, y: FLOAT) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_32_64(x: FLOAT, y: f64) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_64_32(x: f64, y: FLOAT) -> FLOAT {
|
||||||
|
let (x, y) = (x as FLOAT, y as FLOAT);
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
#[export_module]
|
||||||
|
mod decimal_functions {
|
||||||
|
use crate::INT;
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_id(x: INT, y: Decimal) -> Decimal {
|
||||||
|
let x = x.into();
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "max")]
|
||||||
|
pub fn max_di(x: Decimal, y: INT) -> Decimal {
|
||||||
|
let y = y.into();
|
||||||
|
if x >= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_id(x: INT, y: Decimal) -> Decimal {
|
||||||
|
let x = x.into();
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "min")]
|
||||||
|
pub fn min_di(x: Decimal, y: INT) -> Decimal {
|
||||||
|
let y = y.into();
|
||||||
|
if x <= y {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1244,14 +1244,14 @@ impl Engine {
|
|||||||
if !has_condition && ranges.is_empty() && r.len() <= SMALL_SWITCH_RANGE
|
if !has_condition && ranges.is_empty() && r.len() <= SMALL_SWITCH_RANGE
|
||||||
{
|
{
|
||||||
// Unroll small range
|
// Unroll small range
|
||||||
for n in r {
|
r.into_iter().for_each(|n| {
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
Dynamic::from_int(n).hash(hasher);
|
Dynamic::from_int(n).hash(hasher);
|
||||||
cases
|
cases
|
||||||
.entry(hasher.finish())
|
.entry(hasher.finish())
|
||||||
.and_modify(|cases| cases.push(index))
|
.and_modify(|cases| cases.push(index))
|
||||||
.or_insert_with(|| [index].into());
|
.or_insert_with(|| [index].into());
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
// Other range
|
// Other range
|
||||||
r.set_index(index);
|
r.set_index(index);
|
||||||
@ -2769,7 +2769,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !orig_breakable {
|
if !orig_breakable {
|
||||||
settings.flags &= !ParseSettingFlags::BREAKABLE;
|
settings.flags.remove(ParseSettingFlags::BREAKABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
@ -3146,7 +3146,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse statements inside the block
|
// Parse statements inside the block
|
||||||
settings.flags &= !ParseSettingFlags::GLOBAL_LEVEL;
|
settings.flags.remove(ParseSettingFlags::GLOBAL_LEVEL);
|
||||||
|
|
||||||
let stmt = self.parse_stmt(input, state, lib, settings)?;
|
let stmt = self.parse_stmt(input, state, lib, settings)?;
|
||||||
|
|
||||||
@ -3983,9 +3983,9 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
let mut m = crate::Module::new();
|
let mut m = crate::Module::new();
|
||||||
|
|
||||||
for fn_def in _lib {
|
_lib.into_iter().for_each(|fn_def| {
|
||||||
m.set_script_fn(fn_def);
|
m.set_script_fn(fn_def);
|
||||||
}
|
});
|
||||||
|
|
||||||
return Ok(AST::new(statements, m));
|
return Ok(AST::new(statements, m));
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ impl FnPtr {
|
|||||||
/// Create a new function pointer without checking its parameters.
|
/// Create a new function pointer without checking its parameters.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn new_unchecked(
|
pub(crate) fn new_unchecked(
|
||||||
name: impl Into<ImmutableString>,
|
name: impl Into<ImmutableString>,
|
||||||
curry: StaticVec<Dynamic>,
|
curry: StaticVec<Dynamic>,
|
||||||
|
Loading…
Reference in New Issue
Block a user