Merge pull request #415 from schungx/master

Minor updates.
This commit is contained in:
Stephen Chung 2021-05-25 17:12:29 +08:00 committed by GitHub
commit 9a54fd6fae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1241 additions and 1059 deletions

View File

@ -4,10 +4,16 @@ Rhai Release Notes
Version 0.20.2
==============
This version adds a number of convenience features:
* Ability for a `Dynamic` to hold an `i32` _tag_ of arbitrary data
* Simplifies dynamic properties access by falling back to an indexer (passing the name of the property as a string) when a property is not found.
Bug fixes
---------
* Constant propagation during optimization for constants held in a custom scope now works properly instead of always replacing by `()`.
* Propagation of constants held in a custom scope now works properly instead of always replacing by `()`.
Breaking changes
----------------
@ -15,14 +21,17 @@ Breaking changes
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
* Assigning to a property of a constant is now allowed and no longer raise an `EvalAltResult::ErrorAssignmentToConstant` error. This is to facilitate the Singleton pattern. Registered setter functions are automatically guarded against setters calling on constants and will continue to raise errors unless the `pure` attribute is present (for plugins).
* If a property getter/setter is not found, an indexer with string index, if any, is tried.
* The indexers API (`Engine::register_indexer_XXX` and `Module::set_indexer_XXX`) are now also exposed under `no_index`.
New features
------------
* Each `Dynamic` value can now contain arbitrary data (type `i16`) in the form of a _tag_. This is to use up otherwise wasted space in the `Dynamic` type.
* Each `Dynamic` value can now contain arbitrary data (type `i32`) in the form of a _tag_. This is to use up otherwise wasted space in the `Dynamic` type.
* A new internal feature `no_smartstring` to turn off `SmartString` for those rare cases that it is needed.
* `DynamicReadLock` and `DynamicWriteLoc` are exposed under `internals`.
* `From<Shared<Locked<Dynamic>>>` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`.
* An indexer with string index acts as a _fallback_ to a property getter/setter.
Enhancements
------------

View File

@ -92,7 +92,7 @@ default_features = false
optional = true
[dependencies.rust_decimal]
version = "1.13"
version = "1.14"
default_features = false
features = ["maths"]
optional = true

View File

@ -6,7 +6,7 @@ const REPEAT = 5;
fn fib(n) {
if n < 2 {
n
n
} else {
fib(n-1) + fib(n-2)
}
@ -19,7 +19,7 @@ let result;
let now = timestamp();
for n in range(0, REPEAT) {
result = fib(TARGET);
result = fib(TARGET);
}
print(`Finished. Run time = ${now.elapsed} seconds.`);

View File

@ -8,7 +8,7 @@ loop {
x -= 1;
if x <= 0 { break; }
if x <= 0 { break; }
}
export x as foo;

View File

@ -1,13 +1,13 @@
const SIZE = 50;
fn new_mat(x, y) {
let row = [];
row.pad(y, 0.0);
let matrix = [];
matrix.pad(x, row);
matrix
let row = [];
row.pad(y, 0.0);
let matrix = [];
matrix.pad(x, row);
matrix
}
fn mat_gen() {
@ -20,13 +20,13 @@ fn mat_gen() {
m[i][j] = tmp * (i - j) * (i + j);
}
}
m
}
fn mat_mul(a, b) {
let b2 = new_mat(a[0].len, b[0].len);
for i in range(0, a[0].len) {
for j in range(0, b[0].len) {
b2[j][i] = b[i][j];
@ -35,13 +35,13 @@ fn mat_mul(a, b) {
let c = new_mat(a.len, b[0].len);
for i in range(0, c.len) {
for j in range(0, c[i].len) {
c[i][j] = 0.0;
for z in range(0, a[i].len) {
c[i][j] += a[i][z] * b2[j][z];
}
for i in range(0, c.len) {
for j in range(0, c[i].len) {
c[i][j] = 0.0;
for z in range(0, a[i].len) {
c[i][j] += a[i][z] * b2[j][z];
}
}
}
@ -56,7 +56,7 @@ const c = mat_mul(a, b);
/*
for i in range(0, SIZE) {
print(c[i]);
print(c[i]);
}
*/

View File

@ -5,79 +5,79 @@ let now = timestamp();
let adverbs = [ "moderately", "really", "slightly", "very" ];
let adjectives = [
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
"common", "compassionate", "competent", "complete", "complex", "complicated",
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
"constant", "content", "conventional", "cooked", "cool", "cooperative",
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
"crafty"
"abandoned", "able", "absolute", "academic", "acceptable", "acclaimed",
"accomplished", "accurate", "aching", "acidic", "acrobatic", "active",
"actual", "adept", "admirable", "admired", "adolescent", "adorable", "adored",
"advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating",
"aggressive", "agile", "agitated", "agonizing", "agreeable", "ajar",
"alarmed", "alarming", "alert", "alienated", "alive", "all", "altruistic",
"amazing", "ambitious", "ample", "amused", "amusing", "anchored", "ancient",
"angelic", "angry", "anguished", "animated", "annual", "another", "antique",
"anxious", "any", "apprehensive", "appropriate", "apt", "arctic", "arid",
"aromatic", "artistic", "ashamed", "assured", "astonishing", "athletic",
"attached", "attentive", "attractive", "austere", "authentic", "authorized",
"automatic", "avaricious", "average", "aware", "awesome", "awful", "awkward",
"babyish", "back", "bad", "baggy", "bare", "barren", "basic", "beautiful",
"belated", "beloved", "beneficial", "best", "better", "bewitched", "big",
"big-hearted", "biodegradable", "bite-sized", "bitter", "black",
"black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful",
"blond", "blue", "blushing", "bogus", "boiling", "bold", "bony", "boring",
"bossy", "both", "bouncy", "bountiful", "bowed", "brave", "breakable",
"brief", "bright", "brilliant", "brisk", "broken", "bronze", "brown",
"bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly",
"bustling", "busy", "buttery", "buzzing", "calculating", "calm", "candid",
"canine", "capital", "carefree", "careful", "careless", "caring", "cautious",
"cavernous", "celebrated", "charming", "cheap", "cheerful", "cheery", "chief",
"chilly", "chubby", "circular", "classic", "clean", "clear", "clear-cut",
"clever", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered",
"coarse", "cold", "colorful", "colorless", "colossal", "comfortable",
"common", "compassionate", "competent", "complete", "complex", "complicated",
"composed", "concerned", "concrete", "confused", "conscious", "considerate",
"constant", "content", "conventional", "cooked", "cool", "cooperative",
"coordinated", "corny", "corrupt", "costly", "courageous", "courteous",
"crafty"
];
let animals = [
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
"wallaby", "wildebeest"
"aardvark", "african buffalo", "albatross", "alligator", "alpaca", "ant",
"anteater", "antelope", "ape", "armadillo", "baboon", "badger", "barracuda",
"bat", "bear", "beaver", "bee", "bison", "black panther", "blue jay", "boar",
"butterfly", "camel", "capybara", "carduelis", "caribou", "cassowary", "cat",
"caterpillar", "cattle", "chamois", "cheetah", "chicken", "chimpanzee",
"chinchilla", "chough", "clam", "cobra", "cockroach", "cod", "cormorant",
"coyote", "crab", "crane", "crocodile", "crow", "curlew", "deer", "dinosaur",
"dog", "dolphin", "domestic pig", "donkey", "dotterel", "dove", "dragonfly",
"duck", "dugong", "dunlin", "eagle", "echidna", "eel", "elephant seal",
"elephant", "elk", "emu", "falcon", "ferret", "finch", "fish", "flamingo",
"fly", "fox", "frog", "gaur", "gazelle", "gerbil", "giant panda", "giraffe",
"gnat", "goat", "goldfish", "goose", "gorilla", "goshawk", "grasshopper",
"grouse", "guanaco", "guinea fowl", "guinea pig", "gull", "hamster", "hare",
"hawk", "hedgehog", "heron", "herring", "hippopotamus", "hornet", "horse",
"human", "hummingbird", "hyena", "ibex", "ibis", "jackal", "jaguar", "jay",
"jellyfish", "kangaroo", "kingfisher", "koala", "komodo dragon", "kookabura",
"kouprey", "kudu", "lapwing", "lark", "lemur", "leopard", "lion", "llama",
"lobster", "locust", "loris", "louse", "lyrebird", "magpie", "mallard",
"manatee", "mandrill", "mantis", "marten", "meerkat", "mink", "mole",
"mongoose", "monkey", "moose", "mosquito", "mouse", "mule", "narwhal", "newt",
"nightingale", "octopus", "okapi", "opossum", "oryx", "ostrich", "otter",
"owl", "oyster", "parrot", "partridge", "peafowl", "pelican", "penguin",
"pheasant", "pigeon", "pinniped", "polar bear", "pony", "porcupine",
"porpoise", "prairie dog", "quail", "quelea", "quetzal", "rabbit", "raccoon",
"ram", "rat", "raven", "red deer", "red panda", "reindeer", "rhinoceros",
"rook", "salamander", "salmon", "sand dollar", "sandpiper", "sardine",
"scorpion", "sea lion", "sea urchin", "seahorse", "shark", "sheep", "shrew",
"skunk", "snail", "snake", "sparrow", "spider", "spoonbill", "squid",
"wallaby", "wildebeest"
];
let keys = [];
for animal in animals {
for adjective in adjectives {
for adverb in adverbs {
keys.push(`${adverb} ${adjective} ${animal}`)
for adjective in adjectives {
for adverb in adverbs {
keys.push(`${adverb} ${adjective} ${animal}`)
}
}
}
}
let map = #{};
@ -85,18 +85,18 @@ let map = #{};
let i = 0;
for key in keys {
map[key] = i;
i += 1;
map[key] = i;
i += 1;
}
let sum = 0;
for key in keys {
sum += map[key];
sum += map[key];
}
for key in keys {
map.remove(key);
map.remove(key);
}
print(`Sum = ${sum}`);

View File

@ -13,7 +13,6 @@ use std::{
collections::BTreeMap,
fmt,
hash::Hash,
iter::empty,
mem,
num::{NonZeroU8, NonZeroUsize},
ops::{Add, AddAssign, Deref, DerefMut},
@ -254,12 +253,11 @@ impl AST {
/// Set the source.
#[inline(always)]
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
self.source = Some(source.into());
if let Some(module) = Shared::get_mut(&mut self.functions) {
module.set_id(self.source.clone());
}
let source = Some(source.into());
Shared::get_mut(&mut self.functions)
.as_mut()
.map(|m| m.set_id(source.clone()));
self.source = source;
self
}
/// Clear the source.
@ -910,6 +908,7 @@ impl DerefMut for StmtBlock {
impl fmt::Debug for StmtBlock {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Block")?;
fmt::Debug::fmt(&self.0, f)?;
self.1.debug_print(f)
}
@ -1226,6 +1225,7 @@ impl Stmt {
path: &mut Vec<ASTNode<'a>>,
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
) -> bool {
// Push the current node onto the path
path.push(self.into());
if !on_node(path) {
@ -1341,7 +1341,8 @@ impl Stmt {
_ => (),
}
path.pop().unwrap();
path.pop()
.expect("never fails because `path` always contains the current node");
true
}
@ -1399,13 +1400,13 @@ impl OpAssignment {
pub fn new(op: Token) -> Self {
let op_raw = op
.map_op_assignment()
.expect("token must be an op-assignment operator")
.expect("never fails because token must be an op-assignment operator")
.keyword_syntax();
let op_assignment = op.keyword_syntax();
Self {
hash_op_assign: calc_fn_hash(empty(), op_assignment, 2),
hash_op: calc_fn_hash(empty(), op_raw, 2),
hash_op_assign: calc_fn_hash(op_assignment, 2),
hash_op: calc_fn_hash(op_raw, 2),
op: op_assignment,
}
}
@ -1493,13 +1494,9 @@ impl FnCallHashes {
self.script.is_none()
}
/// Get the script function hash from this [`FnCallHashes`].
///
/// # Panics
///
/// Panics if the [`FnCallHashes`] is native Rust only.
#[inline(always)]
pub fn script_hash(&self) -> u64 {
self.script.unwrap()
pub fn script_hash(&self) -> Option<u64> {
self.script
}
/// Get the naive Rust function hash from this [`FnCallHashes`].
#[inline(always)]
@ -1715,7 +1712,13 @@ pub enum Expr {
)>,
),
/// Property access - ((getter, hash), (setter, hash), prop)
Property(Box<((Identifier, u64), (Identifier, u64), Ident)>),
Property(
Box<(
(Identifier, u64),
(Identifier, u64),
(ImmutableString, Position),
)>,
),
/// { [statement][Stmt] ... }
Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)`
@ -1780,16 +1783,14 @@ impl fmt::Debug for Expr {
}
f.write_str(")")
}
Self::Property(x) => write!(f, "Property({})", x.2.name),
Self::Property(x) => write!(f, "Property({})", (x.2).0),
Self::Stmt(x) => {
f.write_str("Stmt")?;
f.write_str("ExprStmtBlock")?;
f.debug_list().entries(x.0.iter()).finish()
}
Self::FnCall(x, _) => {
let mut ff = f.debug_struct("FnCall");
if let Some(ref ns) = x.namespace {
ff.field("namespace", ns);
}
x.namespace.as_ref().map(|ns| ff.field("namespace", ns));
ff.field("name", &x.name)
.field("hash", &x.hashes)
.field("args", &x.args);
@ -1843,7 +1844,10 @@ impl Expr {
#[cfg(not(feature = "no_index"))]
Self::Array(x, _) if self.is_constant() => {
let mut arr = Array::with_capacity(x.len());
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
arr.extend(x.iter().map(|v| {
v.get_constant_value()
.expect("never fails because a constant array always has a constant value")
}));
Dynamic::from_array(arr)
}
@ -1851,7 +1855,10 @@ impl Expr {
Self::Map(x, _) if self.is_constant() => {
let mut map = x.1.clone();
x.0.iter().for_each(|(k, v)| {
*map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap()
*map.get_mut(k.name.as_str())
.expect("never fails because the template should contain all the keys") = v
.get_constant_value()
.expect("never fails because a constant map always has a constant value")
});
Dynamic::from_map(map)
}
@ -1894,9 +1901,14 @@ impl Expr {
| Self::FnCall(_, pos)
| Self::Custom(_, pos) => *pos,
Self::InterpolatedString(x) => x.first().unwrap().position(),
Self::InterpolatedString(x) => x
.first()
.expect(
"never fails because an interpolated string always contains at least one item",
)
.position(),
Self::Property(x) => (x.2).pos,
Self::Property(x) => (x.2).1,
Self::Stmt(x) => x.1,
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => {
@ -1928,10 +1940,12 @@ impl Expr {
| Self::Custom(_, pos) => *pos = new_pos,
Self::InterpolatedString(x) => {
x.first_mut().unwrap().set_position(new_pos);
x.first_mut()
.expect("never fails because an interpolated string always contains at least one item")
.set_position(new_pos);
}
Self::Property(x) => (x.2).pos = new_pos,
Self::Property(x) => (x.2).1 = new_pos,
Self::Stmt(x) => x.1 = new_pos,
}
@ -2045,6 +2059,7 @@ impl Expr {
path: &mut Vec<ASTNode<'a>>,
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
) -> bool {
// Push the current node onto the path
path.push(self.into());
if !on_node(path) {
@ -2098,7 +2113,8 @@ impl Expr {
_ => (),
}
path.pop().unwrap();
path.pop()
.expect("never fails because `path` always contains the current node");
true
}
@ -2128,7 +2144,7 @@ mod tests {
96
}
);
assert_eq!(size_of::<Scope>(), 288);
assert_eq!(size_of::<Scope>(), 160);
assert_eq!(size_of::<LexError>(), 56);
assert_eq!(
size_of::<ParseError>(),

View File

@ -141,6 +141,11 @@ pub enum AccessMode {
}
/// Arbitrary data attached to a [`Dynamic`] value.
#[cfg(target_pointer_width = "64")]
pub type Tag = i32;
/// Arbitrary data attached to a [`Dynamic`] value.
#[cfg(target_pointer_width = "32")]
pub type Tag = i16;
/// Default tag value for [`Dynamic`].
@ -235,9 +240,10 @@ impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> {
fn deref(&self) -> &Self::Target {
match &self.0 {
DynamicReadLockInner::Reference(reference) => *reference,
// Unwrapping is safe because all checking is already done in its constructor
#[cfg(not(feature = "no_closure"))]
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().expect(
"never fails because the read guard was created after checking the data type",
),
}
}
}
@ -277,9 +283,10 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
fn deref(&self) -> &Self::Target {
match &self.0 {
DynamicWriteLockInner::Reference(reference) => *reference,
// Unwrapping is safe because all checking is already done in its constructor
#[cfg(not(feature = "no_closure"))]
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().expect(
"never fails because the write guard was created after checking the data type",
),
}
}
}
@ -289,16 +296,17 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match &mut self.0 {
DynamicWriteLockInner::Reference(reference) => *reference,
// Unwrapping is safe because all checking is already done in its constructor
#[cfg(not(feature = "no_closure"))]
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(),
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().expect(
"never fails because the write guard was created after checking the data type",
),
}
}
}
impl Dynamic {
/// Get the arbitrary data attached to this [`Dynamic`].
pub fn tag(&self) -> Tag {
pub const fn tag(&self) -> Tag {
match self.0 {
Union::Unit(_, tag, _)
| Union::Bool(_, tag, _)
@ -350,7 +358,7 @@ impl Dynamic {
/// Does this [`Dynamic`] hold a variant data type
/// instead of one of the supported system primitive types?
#[inline(always)]
pub fn is_variant(&self) -> bool {
pub const fn is_variant(&self) -> bool {
match self.0 {
Union::Variant(_, _, _) => true,
_ => false,
@ -361,7 +369,7 @@ impl Dynamic {
/// Not available under `no_closure`.
#[cfg(not(feature = "no_closure"))]
#[inline(always)]
pub fn is_shared(&self) -> bool {
pub const fn is_shared(&self) -> bool {
#[cfg(not(feature = "no_closure"))]
match self.0 {
Union::Shared(_, _, _) => return true,
@ -409,7 +417,7 @@ impl Dynamic {
#[cfg(not(feature = "no_std"))]
Union::TimeStamp(_, _, _) => TypeId::of::<Instant>(),
Union::Variant(value, _, _) => (***value).type_id(),
Union::Variant(value, _, _) => value.as_ref().as_ref().type_id(),
#[cfg(not(feature = "no_closure"))]
Union::Shared(cell, _, _) => {
@ -447,7 +455,7 @@ impl Dynamic {
#[cfg(not(feature = "no_std"))]
Union::TimeStamp(_, _, _) => "timestamp",
Union::Variant(value, _, _) => (***value).type_name(),
Union::Variant(value, _, _) => value.as_ref().as_ref().type_name(),
#[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))]
@ -473,17 +481,12 @@ impl Hash for Dynamic {
#[cfg(not(feature = "no_float"))]
Union::Float(f, _, _) => f.hash(state),
#[cfg(not(feature = "no_index"))]
Union::Array(a, _, _) => (**a).hash(state),
Union::Array(a, _, _) => a.as_ref().hash(state),
#[cfg(not(feature = "no_object"))]
Union::Map(m, _, _) => {
let mut buf: crate::StaticVec<_> = m.iter().collect();
buf.sort_by(|(a, _), (b, _)| a.cmp(b));
buf.into_iter().for_each(|(key, value)| {
key.hash(state);
value.hash(state);
})
}
Union::Map(m, _, _) => m.iter().for_each(|(key, value)| {
key.hash(state);
value.hash(state);
}),
Union::FnPtr(f, _, _) if f.is_curried() => {
unimplemented!(
"{} with curried arguments cannot be hashed",
@ -566,43 +569,47 @@ impl fmt::Display for Dynamic {
Union::TimeStamp(_, _, _) => f.write_str("<timestamp>"),
Union::Variant(value, _, _) => {
let _type_id = (***value).type_id();
let value = value.as_ref().as_ref();
let _type_id = value.type_id();
let _value_any = value.as_any();
const CHECKED: &str = "never fails because the type was checked";
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
if _type_id == TypeId::of::<u8>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<u8>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<u8>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<u16>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<u16>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<u16>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<u32>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<u32>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<u32>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<u64>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<u64>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<u64>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i8>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<i8>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<i8>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i16>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<i16>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<i16>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i32>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<i32>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<i32>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i64>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<i64>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<i64>().expect(CHECKED), f);
}
#[cfg(not(feature = "no_float"))]
if _type_id == TypeId::of::<f32>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<f32>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<f64>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<f64>().expect(CHECKED), f);
}
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
if _type_id == TypeId::of::<u128>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<u128>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i128>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<i128>().unwrap());
return fmt::Display::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
}
f.write_str((***value).type_name())
f.write_str(value.type_name())
}
#[cfg(not(feature = "no_closure"))]
@ -645,51 +652,47 @@ impl fmt::Debug for Dynamic {
Union::TimeStamp(_, _, _) => write!(f, "<timestamp>"),
Union::Variant(value, _, _) => {
let _type_id = (***value).type_id();
let value = value.as_ref().as_ref();
let _type_id = value.type_id();
let _value_any = value.as_any();
const CHECKED: &str = "never fails because the type was checked";
#[cfg(not(feature = "only_i32"))]
#[cfg(not(feature = "only_i64"))]
if _type_id == TypeId::of::<u8>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u8>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<u8>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<u16>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u16>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<u16>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<u32>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u32>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<u32>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<u64>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u64>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<u64>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i8>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i8>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<i8>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i16>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i16>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<i16>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i32>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i32>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<i32>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i64>() {
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i64>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<i64>().expect(CHECKED), f);
}
#[cfg(not(feature = "no_float"))]
if _type_id == TypeId::of::<f32>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<f32>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<f64>() {
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
return fmt::Debug::fmt(_value_any.downcast_ref::<f64>().expect(CHECKED), f);
}
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
if _type_id == TypeId::of::<u128>() {
return write!(
f,
"{:?}",
(**value).as_any().downcast_ref::<u128>().unwrap()
);
return fmt::Debug::fmt(_value_any.downcast_ref::<u128>().expect(CHECKED), f);
} else if _type_id == TypeId::of::<i128>() {
return write!(
f,
"{:?}",
(**value).as_any().downcast_ref::<i128>().unwrap()
);
return fmt::Debug::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
}
write!(f, "{}", (*value).type_name())
write!(f, "{}", value.type_name())
}
#[cfg(not(feature = "no_closure"))]
@ -746,7 +749,7 @@ impl Clone for Dynamic {
}
Union::Variant(ref value, tag, _) => {
let mut x = (***value).clone_into_dynamic();
let mut x = value.as_ref().as_ref().clone_into_dynamic();
x.set_tag(tag);
x
}
@ -830,7 +833,7 @@ impl Dynamic {
));
/// Get the [`AccessMode`] for this [`Dynamic`].
pub(crate) fn access_mode(&self) -> AccessMode {
pub(crate) const fn access_mode(&self) -> AccessMode {
match self.0 {
Union::Unit(_, _, access)
| Union::Bool(_, _, access)
@ -980,53 +983,40 @@ impl Dynamic {
pub fn from<T: Variant + Clone>(mut value: T) -> Self {
// Coded this way in order to maximally leverage potentials for dead-code removal.
const CHECKED: &str = "never fails because the type was checked";
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return unsafe_try_cast::<_, Dynamic>(value).ok().unwrap();
return unsafe_try_cast::<_, Dynamic>(value).ok().expect(CHECKED);
}
let val = value.as_any();
if TypeId::of::<T>() == TypeId::of::<INT>() {
return <dyn Any>::downcast_ref::<INT>(&value)
.unwrap()
.clone()
.into();
return val.downcast_ref::<INT>().expect(CHECKED).clone().into();
}
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
return <dyn Any>::downcast_ref::<FLOAT>(&value)
.unwrap()
.clone()
.into();
return val.downcast_ref::<FLOAT>().expect(CHECKED).clone().into();
}
#[cfg(feature = "decimal")]
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
return <dyn Any>::downcast_ref::<Decimal>(&value)
.unwrap()
.clone()
.into();
return val.downcast_ref::<Decimal>().expect(CHECKED).clone().into();
}
if TypeId::of::<T>() == TypeId::of::<bool>() {
return <dyn Any>::downcast_ref::<bool>(&value)
.unwrap()
.clone()
.into();
return val.downcast_ref::<bool>().expect(CHECKED).clone().into();
}
if TypeId::of::<T>() == TypeId::of::<char>() {
return <dyn Any>::downcast_ref::<char>(&value)
.unwrap()
.clone()
.into();
return val.downcast_ref::<char>().expect(CHECKED).clone().into();
}
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return <dyn Any>::downcast_ref::<ImmutableString>(&value)
.unwrap()
return val
.downcast_ref::<ImmutableString>()
.expect(CHECKED)
.clone()
.into();
}
if TypeId::of::<T>() == TypeId::of::<&str>() {
return <dyn Any>::downcast_ref::<&str>(&value)
.unwrap()
.deref()
.into();
return val.downcast_ref::<&str>().expect(CHECKED).deref().into();
}
if TypeId::of::<T>() == TypeId::of::<()>() {
return ().into();
@ -1450,9 +1440,9 @@ impl Dynamic {
#[cfg(feature = "sync")]
let value = cell.read().unwrap();
let type_id = (*value).type_id();
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
if (*value).type_id() != TypeId::of::<T>()
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
{
return None;
} else {
return Some(DynamicReadLock(DynamicReadLockInner::Guard(value)));
@ -1483,9 +1473,9 @@ impl Dynamic {
#[cfg(feature = "sync")]
let value = cell.write().unwrap();
let type_id = (*value).type_id();
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
if (*value).type_id() != TypeId::of::<T>()
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
{
return None;
} else {
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)));
@ -1507,77 +1497,77 @@ impl Dynamic {
if TypeId::of::<T>() == TypeId::of::<INT>() {
return match &self.0 {
Union::Int(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
Union::Int(value, _, _) => value.as_any().downcast_ref::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
return match &self.0 {
Union::Float(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::Float(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
_ => None,
};
}
#[cfg(feature = "decimal")]
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
return match &self.0 {
Union::Decimal(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::Decimal(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<bool>() {
return match &self.0 {
Union::Bool(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
Union::Bool(value, _, _) => value.as_any().downcast_ref::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return match &self.0 {
Union::Str(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
Union::Str(value, _, _) => value.as_any().downcast_ref::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<char>() {
return match &self.0 {
Union::Char(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
Union::Char(value, _, _) => value.as_any().downcast_ref::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() {
return match &self.0 {
Union::Array(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::Array(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_object"))]
if TypeId::of::<T>() == TypeId::of::<Map>() {
return match &self.0 {
Union::Map(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::Map(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
return match &self.0 {
Union::FnPtr(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::FnPtr(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_std"))]
if TypeId::of::<T>() == TypeId::of::<Instant>() {
return match &self.0 {
Union::TimeStamp(value, _, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
Union::TimeStamp(value, _, _) => value.as_ref().as_any().downcast_ref::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<()>() {
return match &self.0 {
Union::Unit(value, _, _) => <dyn Any>::downcast_ref::<T>(value),
Union::Unit(value, _, _) => value.as_any().downcast_ref::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return <dyn Any>::downcast_ref::<T>(self);
return self.as_any().downcast_ref::<T>();
}
match &self.0 {
@ -1597,77 +1587,77 @@ impl Dynamic {
if TypeId::of::<T>() == TypeId::of::<INT>() {
return match &mut self.0 {
Union::Int(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
Union::Int(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
return match &mut self.0 {
Union::Float(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::Float(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
#[cfg(feature = "decimal")]
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
return match &mut self.0 {
Union::Decimal(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::Decimal(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<bool>() {
return match &mut self.0 {
Union::Bool(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
Union::Bool(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return match &mut self.0 {
Union::Str(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
Union::Str(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<char>() {
return match &mut self.0 {
Union::Char(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
Union::Char(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() {
return match &mut self.0 {
Union::Array(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::Array(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_object"))]
if TypeId::of::<T>() == TypeId::of::<Map>() {
return match &mut self.0 {
Union::Map(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::Map(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
return match &mut self.0 {
Union::FnPtr(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::FnPtr(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
#[cfg(not(feature = "no_std"))]
if TypeId::of::<T>() == TypeId::of::<Instant>() {
return match &mut self.0 {
Union::TimeStamp(value, _, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
Union::TimeStamp(value, _, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<()>() {
return match &mut self.0 {
Union::Unit(value, _, _) => <dyn Any>::downcast_mut::<T>(value),
Union::Unit(value, _, _) => value.as_mut_any().downcast_mut::<T>(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
return <dyn Any>::downcast_mut::<T>(self);
return self.as_mut_any().downcast_mut::<T>();
}
match &mut self.0 {
@ -1720,8 +1710,8 @@ impl Dynamic {
#[cfg(feature = "decimal")]
#[inline(always)]
pub fn as_decimal(&self) -> Result<Decimal, &'static str> {
match &self.0 {
Union::Decimal(n, _, _) => Ok(**n),
match self.0 {
Union::Decimal(ref n, _, _) => Ok(**n),
#[cfg(not(feature = "no_closure"))]
Union::Shared(_, _, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
_ => Err(self.type_name()),
@ -1757,8 +1747,8 @@ impl Dynamic {
/// Panics if the value is shared.
#[inline(always)]
pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> {
match &self.0 {
Union::Str(s, _, _) => Ok(s),
match self.0 {
Union::Str(ref s, _, _) => Ok(s),
#[cfg(not(feature = "no_closure"))]
Union::Shared(_, _, _) => panic!("as_str() cannot be called on shared values"),
_ => Err(self.type_name()),

View File

@ -29,7 +29,7 @@ use std::{
};
#[cfg(not(feature = "no_index"))]
use crate::{calc_fn_hash, Array};
use crate::Array;
#[cfg(not(feature = "no_object"))]
use crate::Map;
@ -224,9 +224,9 @@ pub const KEYWORD_GLOBAL: &str = "global";
pub const FN_GET: &str = "get$";
#[cfg(not(feature = "no_object"))]
pub const FN_SET: &str = "set$";
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
pub const FN_IDX_GET: &str = "index$get$";
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
pub const FN_IDX_SET: &str = "index$set$";
#[cfg(not(feature = "no_function"))]
pub const FN_ANONYMOUS: &str = "anon$";
@ -429,15 +429,15 @@ impl<'a> Target<'a> {
/// This has no effect except for string indexing.
#[cfg(not(feature = "no_object"))]
#[inline(always)]
pub fn propagate_changed_value(&mut self) {
pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> {
match self {
Self::Ref(_) | Self::Value(_) => (),
Self::Ref(_) | Self::Value(_) => Ok(()),
#[cfg(not(feature = "no_closure"))]
Self::LockGuard(_) => (),
Self::LockGuard(_) => Ok(()),
#[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ch) => {
let char_value = ch.clone();
self.set_value(char_value, Position::NONE).unwrap();
self.set_value(char_value, Position::NONE)
}
}
}
@ -455,7 +455,9 @@ impl<'a> Target<'a> {
Self::Value(_) => panic!("cannot update a value"),
#[cfg(not(feature = "no_index"))]
Self::StringChar(s, index, _) => {
let s = &mut *s.write_lock::<ImmutableString>().unwrap();
let s = &mut *s
.write_lock::<ImmutableString>()
.expect("never fails because `StringChar` always holds an `ImmutableString`");
// Replace the character at the specified index position
let new_ch = new_val.as_char().map_err(|err| {
@ -488,7 +490,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
if value.is_shared() {
// Cloning is cheap for a shared value
let container = value.clone();
return Self::LockGuard((value.write_lock::<Dynamic>().unwrap(), container));
return Self::LockGuard((
value
.write_lock::<Dynamic>()
.expect("never fails when casting to `Dynamic`"),
container,
));
}
Self::Ref(value)
@ -598,9 +605,12 @@ impl State {
#[inline(always)]
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
if self.fn_resolution_caches.0.is_empty() {
// Push a new function resolution cache if the stack is empty
self.fn_resolution_caches.0.push(BTreeMap::new());
}
self.fn_resolution_caches.0.last_mut().unwrap()
self.fn_resolution_caches.0.last_mut().expect(
"never fails because there is at least one function resolution cache by this point",
)
}
/// Push an empty function resolution cache onto the stack and make it current.
#[allow(dead_code)]
@ -617,7 +627,11 @@ impl State {
/// Panics if there are no more function resolution cache in the stack.
#[inline(always)]
pub fn pop_fn_resolution_cache(&mut self) {
let mut cache = self.fn_resolution_caches.0.pop().unwrap();
let mut cache = self
.fn_resolution_caches
.0
.pop()
.expect("there should be at least one function resolution cache");
cache.clear();
self.fn_resolution_caches.1.push(cache);
}
@ -899,11 +913,7 @@ impl Engine {
progress: None,
// optimization level
optimization_level: if cfg!(feature = "no_optimize") {
OptimizationLevel::None
} else {
OptimizationLevel::Simple
},
optimization_level: Default::default(),
#[cfg(not(feature = "unchecked"))]
limits: Limits {
@ -956,11 +966,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))]
progress: None,
optimization_level: if cfg!(feature = "no_optimize") {
OptimizationLevel::None
} else {
OptimizationLevel::Simple
},
optimization_level: Default::default(),
#[cfg(not(feature = "unchecked"))]
limits: Limits {
@ -1004,10 +1010,16 @@ impl Engine {
if let Some(index) = index {
let offset = mods.len() - index.get();
Some(mods.get(offset).expect("invalid index in Imports"))
Some(
mods.get(offset)
.expect("never fails because offset should be within range"),
)
} else {
mods.find(root)
.map(|n| mods.get(n).expect("invalid index in Imports"))
.map(|n| {
mods.get(n)
.expect("never fails because the index came from `find`")
})
.or_else(|| self.global_sub_modules.get(root).cloned())
}
}
@ -1059,6 +1071,10 @@ impl Engine {
}
/// Search for a variable within the scope
///
/// # Panics
///
/// Panics if `expr` is not [`Expr::Variable`].
pub(crate) fn search_scope_only<'s>(
&self,
scope: &'s mut Scope,
@ -1096,9 +1112,13 @@ impl Engine {
this_ptr,
level: 0,
};
if let Some(mut result) =
resolve_var(expr.get_variable_name(true).unwrap(), index, &context)
.map_err(|err| err.fill_position(var_pos))?
if let Some(mut result) = resolve_var(
expr.get_variable_name(true)
.expect("`expr` should be `Variable`"),
index,
&context,
)
.map_err(|err| err.fill_position(var_pos))?
{
result.set_access_mode(AccessMode::ReadOnly);
return Ok((result.into(), var_pos));
@ -1109,7 +1129,9 @@ impl Engine {
scope.len() - index
} else {
// Find the variable in the scope
let var_name = expr.get_variable_name(true).unwrap();
let var_name = expr
.get_variable_name(true)
.expect("`expr` should be `Variable`");
scope
.get_index(var_name)
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
@ -1138,18 +1160,22 @@ impl Engine {
level: usize,
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
fn match_chain_type(expr: &Expr) -> ChainType {
match expr {
#[cfg(not(feature = "no_index"))]
Expr::Index(_, _) => ChainType::Index,
#[cfg(not(feature = "no_object"))]
Expr::Dot(_, _) => ChainType::Dot,
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
}
}
let is_ref = target.is_ref();
let rhs_chain = match rhs {
#[cfg(not(feature = "no_index"))]
Expr::Index(_, _) => Some(ChainType::Index),
#[cfg(not(feature = "no_object"))]
Expr::Dot(_, _) => Some(ChainType::Dot),
_ => None,
};
// Pop the last index value
let idx_val = idx_values.pop().unwrap();
let idx_val = idx_values
.pop()
.expect("never fails because an index chain is never empty");
match chain_type {
#[cfg(not(feature = "no_index"))]
@ -1162,9 +1188,9 @@ impl Engine {
let idx_pos = x.lhs.position();
let idx_val = idx_val.as_index_value();
let obj_ptr = &mut self.get_indexed_mut(
mods, state, lib, target, idx_val, idx_pos, false, is_ref, true, level,
mods, state, lib, target, idx_val, idx_pos, false, true, level,
)?;
let rhs_chain = rhs_chain.unwrap();
let rhs_chain = match_chain_type(rhs);
self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
@ -1174,69 +1200,48 @@ impl Engine {
}
// xxx[rhs] op= new_val
_ if new_val.is_some() => {
let ((mut new_val, new_pos), (op_info, op_pos)) =
new_val.expect("never fails because `new_val` is `Some`");
let idx_val = idx_val.as_index_value();
#[cfg(not(feature = "no_index"))]
let mut idx_val2 = idx_val.clone();
let mut idx_val_for_setter = idx_val.clone();
// `call_setter` is introduced to bypass double mutable borrowing of target
let _call_setter = match self.get_indexed_mut(
mods, state, lib, target, idx_val, pos, true, is_ref, false, level,
match self.get_indexed_mut(
mods, state, lib, target, idx_val, pos, true, false, level,
) {
// Indexed value is a reference - update directly
Ok(obj_ptr) => {
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
self.eval_op_assignment(
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val,
new_pos,
)?;
None
return Ok((Dynamic::UNIT, true));
}
Err(err) => match *err {
// No index getter - try to call an index setter
#[cfg(not(feature = "no_index"))]
EvalAltResult::ErrorIndexingType(_, _) => Some(new_val.unwrap()),
// Any other error - return
err => return err.into(),
},
};
#[cfg(not(feature = "no_index"))]
if let Some(mut new_val) = _call_setter {
let val_type_name = target.type_name();
let ((_, val_pos), _) = new_val;
let hash_set = FnCallHashes::from_native(calc_fn_hash(
std::iter::empty(),
FN_IDX_SET,
3,
));
let args = &mut [target, &mut idx_val2, &mut (new_val.0).0];
self.exec_fn_call(
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
val_pos, None, level,
)
.map_err(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
if fn_sig.ends_with("]=") =>
{
EvalAltResult::ErrorIndexingType(
self.map_type_name(val_type_name).into(),
Position::NONE,
)
}
err => err,
})?;
// Can't index - try to call an index setter
#[cfg(not(feature = "no_index"))]
Err(err) if matches!(*err, EvalAltResult::ErrorIndexingType(_, _)) => {}
// Any other error
Err(err) => return Err(err),
}
// Try to call index setter
let hash_set =
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
self.exec_fn_call(
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true, new_pos,
None, level,
)?;
Ok((Dynamic::UNIT, true))
}
// xxx[rhs]
_ => {
let idx_val = idx_val.as_index_value();
self.get_indexed_mut(
mods, state, lib, target, idx_val, pos, false, is_ref, true, level,
mods, state, lib, target, idx_val, pos, false, true, level,
)
.map(|v| (v.take_or_clone(), false))
}
@ -1264,12 +1269,13 @@ impl Engine {
}
// {xxx:map}.id op= ???
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
let Ident { name, pos, .. } = &x.2;
let (name, pos) = &x.2;
let index = name.into();
let val = self.get_indexed_mut(
mods, state, lib, target, index, *pos, true, is_ref, false, level,
mods, state, lib, target, index, *pos, true, false, level,
)?;
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
let ((new_val, new_pos), (op_info, op_pos)) =
new_val.expect("never fails because `new_val` is `Some`");
self.eval_op_assignment(
mods, state, lib, op_info, op_pos, val, root, new_val, new_pos,
)?;
@ -1277,27 +1283,46 @@ impl Engine {
}
// {xxx:map}.id
Expr::Property(x) if target.is::<Map>() => {
let Ident { name, pos, .. } = &x.2;
let (name, pos) = &x.2;
let index = name.into();
let val = self.get_indexed_mut(
mods, state, lib, target, index, *pos, false, is_ref, false, level,
mods, state, lib, target, index, *pos, false, false, level,
)?;
Ok((val.take_or_clone(), false))
}
// xxx.id op= ???
Expr::Property(x) if new_val.is_some() => {
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
x.as_ref();
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
let ((mut new_val, new_pos), (op_info, op_pos)) =
new_val.expect("never fails because `new_val` is `Some`");
if op_info.is_some() {
let hash = FnCallHashes::from_native(*hash_get);
let mut args = [target.as_mut()];
let (mut orig_val, _) = self.exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos,
None, level,
)?;
let (mut orig_val, _) = self
.exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos,
None, level,
)
.or_else(|err| match *err {
// Try an indexer if property does not exist
EvalAltResult::ErrorDotExpr(_, _) => {
let prop = name.into();
self.get_indexed_mut(
mods, state, lib, target, prop, *pos, false, true,
level,
)
.map(|v| (v.take_or_clone(), false))
.map_err(
|idx_err| match *idx_err {
EvalAltResult::ErrorIndexingType(_, _) => err,
_ => idx_err,
},
)
}
_ => Err(err),
})?;
let obj_ptr = (&mut orig_val).into();
self.eval_op_assignment(
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, new_pos,
@ -1311,28 +1336,65 @@ impl Engine {
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
level,
)
.map(|(v, _)| (v, true))
.or_else(|err| match *err {
// Try an indexer if property does not exist
EvalAltResult::ErrorDotExpr(_, _) => {
let mut prop = name.into();
let args = &mut [target, &mut prop, &mut new_val];
let hash_set =
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
self.exec_fn_call(
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
*pos, None, level,
)
.map_err(
|idx_err| match *idx_err {
EvalAltResult::ErrorIndexingType(_, _) => err,
_ => idx_err,
},
)
}
_ => Err(err),
})
}
// xxx.id
Expr::Property(x) => {
let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref();
let ((getter, hash_get), _, (name, pos)) = x.as_ref();
let hash = FnCallHashes::from_native(*hash_get);
let mut args = [target.as_mut()];
self.exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
level,
)
.map(|(v, _)| (v, false))
.map_or_else(
|err| match *err {
// Try an indexer if property does not exist
EvalAltResult::ErrorDotExpr(_, _) => {
let prop = name.into();
self.get_indexed_mut(
mods, state, lib, target, prop, *pos, false, true, level,
)
.map(|v| (v.take_or_clone(), false))
.map_err(|idx_err| {
match *idx_err {
EvalAltResult::ErrorIndexingType(_, _) => err,
_ => idx_err,
}
})
}
_ => Err(err),
},
|(v, _)| Ok((v, false)),
)
}
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
let mut val = match &x.lhs {
Expr::Property(p) => {
let Ident { name, pos, .. } = &p.2;
let (name, pos) = &p.2;
let index = name.into();
self.get_indexed_mut(
mods, state, lib, target, index, *pos, false, is_ref, true,
level,
mods, state, lib, target, index, *pos, false, true, level,
)?
}
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
@ -1351,7 +1413,7 @@ impl Engine {
// Others - syntax error
expr => unreachable!("invalid dot expression: {:?}", expr),
};
let rhs_chain = rhs_chain.unwrap();
let rhs_chain = match_chain_type(rhs);
self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values,
@ -1364,17 +1426,36 @@ impl Engine {
match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => {
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
p.as_ref();
let rhs_chain = rhs_chain.unwrap();
let rhs_chain = match_chain_type(rhs);
let hash_get = FnCallHashes::from_native(*hash_get);
let hash_set = FnCallHashes::from_native(*hash_set);
let arg_values = &mut [target.as_mut(), &mut Default::default()];
let mut arg_values = [target.as_mut(), &mut Default::default()];
let args = &mut arg_values[..1];
let (mut val, updated) = self.exec_fn_call(
mods, state, lib, getter, hash_get, args, is_ref, true, *pos,
None, level,
)?;
let (mut val, updated) = self
.exec_fn_call(
mods, state, lib, getter, hash_get, args, is_ref, true,
*pos, None, level,
)
.or_else(|err| match *err {
// Try an indexer if property does not exist
EvalAltResult::ErrorDotExpr(_, _) => {
let prop = name.into();
self.get_indexed_mut(
mods, state, lib, target, prop, *pos, false, true,
level,
)
.map(|v| (v.take_or_clone(), false))
.map_err(
|idx_err| match *idx_err {
EvalAltResult::ErrorIndexingType(_, _) => err,
_ => idx_err,
},
)
}
_ => Err(err),
})?;
let val = &mut val;
@ -1397,17 +1478,33 @@ impl Engine {
// Feed the value back via a setter just in case it has been updated
if updated || may_be_changed {
// Re-use args because the first &mut parameter will not be consumed
arg_values[1] = val;
let mut arg_values = [target.as_mut(), val];
let args = &mut arg_values;
self.exec_fn_call(
mods, state, lib, setter, hash_set, arg_values, is_ref,
true, *pos, None, level,
mods, state, lib, setter, hash_set, args, is_ref, true,
*pos, None, level,
)
.or_else(
|err| match *err {
// If there is no setter, no need to feed it back because
// the property is read-only
// Try an indexer if property does not exist
EvalAltResult::ErrorDotExpr(_, _) => {
Ok((Dynamic::UNIT, false))
let mut prop = name.into();
let args = &mut [target.as_mut(), &mut prop, val];
let hash_set = FnCallHashes::from_native(
crate::calc_fn_hash(FN_IDX_SET, 3),
);
self.exec_fn_call(
mods, state, lib, FN_IDX_SET, hash_set, args,
is_ref, true, *pos, None, level,
)
.or_else(|idx_err| match *idx_err {
EvalAltResult::ErrorIndexingType(_, _) => {
// If there is no setter, no need to feed it back because
// the property is read-only
Ok((Dynamic::UNIT, false))
}
_ => Err(idx_err),
})
}
_ => Err(err),
},
@ -1419,7 +1516,7 @@ impl Engine {
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f, pos) if !f.is_qualified() => {
let FnCallExpr { name, hashes, .. } = f.as_ref();
let rhs_chain = rhs_chain.unwrap();
let rhs_chain = match_chain_type(rhs);
let mut args = idx_val.as_fn_call_args();
let (mut val, _) = self.make_method_call(
mods, state, lib, name, *hashes, target, &mut args, *pos, level,
@ -1558,7 +1655,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
idx_values.push(ChainArgument::Property(x.2.pos))
idx_values.push(ChainArgument::Property((x.2).1))
}
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
@ -1569,7 +1666,7 @@ impl Engine {
let lhs_val = match lhs {
#[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
ChainArgument::Property(x.2.pos)
ChainArgument::Property((x.2).1)
}
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
@ -1646,16 +1743,15 @@ impl Engine {
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn get_indexed_mut<'t>(
&self,
_mods: &mut Imports,
mods: &mut Imports,
state: &mut State,
_lib: &[&Module],
lib: &[&Module],
target: &'t mut Dynamic,
mut _idx: Dynamic,
mut idx: Dynamic,
idx_pos: Position,
_create: bool,
_is_ref: bool,
_indexers: bool,
_level: usize,
indexers: bool,
level: usize,
) -> Result<Target<'t>, Box<EvalAltResult>> {
#[cfg(not(feature = "unchecked"))]
self.inc_operations(state, Position::NONE)?;
@ -1664,7 +1760,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(arr, _, _)) => {
// val_array[idx]
let index = _idx
let index = idx
.as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
@ -1706,8 +1802,8 @@ impl Engine {
#[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map, _, _)) => {
// val_map[idx]
let index = &*_idx.read_lock::<ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<ImmutableString>(_idx.type_name(), idx_pos)
let index = &*idx.read_lock::<ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<ImmutableString>(idx.type_name(), idx_pos)
})?;
if _create && !map.contains_key(index.as_str()) {
@ -1723,7 +1819,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))]
Dynamic(Union::Str(s, _, _)) => {
// val_string[idx]
let index = _idx
let index = idx
.as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
@ -1754,27 +1850,14 @@ impl Engine {
Ok(Target::StringChar(target, offset, ch.into()))
}
#[cfg(not(feature = "no_index"))]
_ if _indexers => {
let type_name = target.type_name();
let args = &mut [target, &mut _idx];
let hash_get =
FnCallHashes::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2));
_ if indexers => {
let args = &mut [target, &mut idx];
let hash_get = FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_GET, 2));
self.exec_fn_call(
_mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None,
_level,
mods, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, level,
)
.map(|(v, _)| v.into())
.map_err(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
Box::new(EvalAltResult::ErrorIndexingType(
type_name.into(),
Position::NONE,
))
}
_ => err,
})
}
_ => EvalAltResult::ErrorIndexingType(
@ -1878,7 +1961,10 @@ impl Engine {
Expr::Map(x, _) => {
let mut map = x.1.clone();
for (Ident { name: key, .. }, expr) in &x.0 {
*map.get_mut(key.as_str()).unwrap() = self
let value_ref = map
.get_mut(key.as_str())
.expect("never fails because the template should contain all the keys");
*value_ref = self
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.flatten();
}
@ -1895,7 +1981,9 @@ impl Engine {
literal_args: c_args,
..
} = x.as_ref();
let namespace = namespace.as_ref();
let namespace = namespace
.as_ref()
.expect("never fails because function call is qualified");
let hash = hashes.native_hash();
self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
@ -1954,10 +2042,12 @@ impl Engine {
.iter()
.map(Into::into)
.collect::<StaticVec<_>>();
let key_token = custom.tokens.first().expect(
"never fails because a custom syntax stream must contain at least one token",
);
let custom_def = self
.custom_syntax
.get(custom.tokens.first().unwrap())
.unwrap();
.custom_syntax.get(key_token)
.expect("never fails because the custom syntax leading token should match with definition");
let mut context = EvalContext {
engine: self,
scope,
@ -2088,7 +2178,9 @@ impl Engine {
let target_is_shared = false;
if target_is_shared {
lock_guard = target.write_lock::<Dynamic>().unwrap();
lock_guard = target
.write_lock::<Dynamic>()
.expect("never fails when casting to `Dynamic`");
lhs_ptr_inner = &mut *lock_guard;
} else {
lhs_ptr_inner = &mut *target;
@ -2158,12 +2250,13 @@ impl Engine {
let (lhs_ptr, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
let var_name = lhs_expr
.get_variable_name(false)
.expect("never fails because `lhs_ptr` is a `Variable`s");
if !lhs_ptr.is_ref() {
return EvalAltResult::ErrorAssignmentToConstant(
lhs_expr.get_variable_name(false).unwrap().to_string(),
pos,
)
.into();
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
.into();
}
#[cfg(not(feature = "unchecked"))]
@ -2176,7 +2269,7 @@ impl Engine {
op_info.clone(),
*op_pos,
lhs_ptr,
(lhs_expr.get_variable_name(false).unwrap(), pos),
(var_name, pos),
rhs_val,
rhs_expr.position(),
)?;
@ -2401,7 +2494,10 @@ impl Engine {
let loop_var_is_shared = false;
if loop_var_is_shared {
*loop_var.write_lock().unwrap() = value;
let mut value_ref = loop_var
.write_lock()
.expect("never fails when casting to `Dynamic`");
*value_ref = value;
} else {
*loop_var = value;
}
@ -2451,7 +2547,9 @@ impl Engine {
literal_args: c_args,
..
} = x.as_ref();
let namespace = namespace.as_ref();
let namespace = namespace
.as_ref()
.expect("never fails because function call is qualified");
let hash = hashes.native_hash();
self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
@ -2505,26 +2603,22 @@ impl Engine {
err_map.insert("message".into(), err.to_string().into());
if let Some(ref source) = state.source {
err_map.insert("source".into(), source.into());
}
state
.source
.as_ref()
.map(|source| err_map.insert("source".into(), source.into()));
if err_pos.is_none() {
// No position info
} else {
err_map.insert(
"line".into(),
(err_pos.line().unwrap() as INT).into(),
);
err_map.insert(
"position".into(),
if err_pos.is_beginning_of_line() {
0
} else {
err_pos.position().unwrap() as INT
}
.into(),
);
let line = err_pos.line().expect("never fails because a non-NONE `Position` always has a line number") as INT;
let position = if err_pos.is_beginning_of_line() {
0
} else {
err_pos.position().expect("never fails because a non-NONE `Position` always has a character position")
} as INT;
err_map.insert("line".into(), line.into());
err_map.insert("position".into(), position.into());
}
err.dump_fields(&mut err_map);
@ -2535,9 +2629,9 @@ impl Engine {
let orig_scope_len = scope.len();
state.scope_level += 1;
if let Some(Ident { name, .. }) = err_var {
scope.push(unsafe_cast_var_name_to_lifetime(&name), err_value);
}
err_var.as_ref().map(|Ident { name, .. }| {
scope.push(unsafe_cast_var_name_to_lifetime(name), err_value)
});
let result = self.eval_stmt_block(
scope, mods, state, lib, this_ptr, catch_stmt, true, level,
@ -2604,7 +2698,10 @@ impl Engine {
#[cfg(not(feature = "no_function"))]
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) {
match mods.get_mut(index).unwrap() {
match mods
.get_mut(index)
.expect("never fails because the index came from `find`")
{
m if m.internal => Some(m),
_ => None,
}
@ -2613,12 +2710,16 @@ impl Engine {
let mut global = Module::new();
global.internal = true;
mods.push(KEYWORD_GLOBAL, global);
Some(mods.get_mut(mods.len() - 1).unwrap())
Some(
mods.get_mut(mods.len() - 1)
.expect("never fails because the global module was just added"),
)
};
if let Some(global) = global {
let global = Shared::get_mut(global).unwrap();
global.set_var(name.clone(), value.clone());
Shared::get_mut(global)
.expect("never fails because the global module is never shared")
.set_var(name.clone(), value.clone());
}
}
@ -2635,9 +2736,8 @@ impl Engine {
scope.push_dynamic_value(var_name, entry_type, value);
#[cfg(not(feature = "no_module"))]
if let Some(alias) = _alias {
scope.add_entry_alias(scope.len() - 1, alias);
}
_alias.map(|alias| scope.add_entry_alias(scope.len() - 1, alias));
Ok(Dynamic::UNIT)
}
@ -2673,7 +2773,7 @@ impl Engine {
self.module_resolver.resolve(self, source, &path, expr_pos)
})?;
if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
export.as_ref().map(|x| x.name.clone()).map(|name| {
if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module);
@ -2682,7 +2782,7 @@ impl Engine {
} else {
mods.push(name, module);
}
}
});
state.modules += 1;
@ -2710,14 +2810,14 @@ impl Engine {
// Share statement
#[cfg(not(feature = "no_closure"))]
Stmt::Share(name) => {
if let Some((index, _)) = scope.get_index(name) {
scope.get_index(name).map(|(index, _)| {
let val = scope.get_mut_by_index(index);
if !val.is_shared() {
// Replace the variable with a shared value.
*val = std::mem::take(val).into_shared();
}
}
});
Ok(Dynamic::UNIT)
}
};
@ -2732,9 +2832,10 @@ impl Engine {
/// Check a result to ensure that the data size is within allowable limit.
#[cfg(not(feature = "unchecked"))]
fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> {
if result.is_err() {
return Ok(());
}
let result = match result {
Err(_) => return Ok(()),
Ok(r) => r,
};
// If no data size limits, just return
let mut _has_limit = self.limits.max_string_size.is_some();
@ -2803,7 +2904,7 @@ impl Engine {
}
}
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
let (_arr, _map, s) = calc_size(result);
if s > self
.limits
@ -2860,7 +2961,7 @@ impl Engine {
}
// Report progress - only in steps
if let Some(progress) = &self.progress {
if let Some(ref progress) = self.progress {
if let Some(token) = progress(state.operations) {
// Terminate script if progress returns a termination token
return EvalAltResult::ErrorTerminated(token, pos).into();

View File

@ -477,9 +477,7 @@ impl Engine {
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self {
use crate::engine::make_setter;
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: V| {
set_fn(obj, value)
})
self.register_result_fn(&make_setter(name), set_fn)
}
/// Short-hand for registering both getter and setter functions
/// of a registered type with the [`Engine`].
@ -536,7 +534,7 @@ impl Engine {
///
/// The function signature must start with `&mut self` and not `&self`.
///
/// Not available under `no_index`.
/// Not available under both `no_index` and `no_object`.
///
/// # Panics
///
@ -572,16 +570,18 @@ impl Engine {
/// // Register an indexer.
/// .register_indexer_get(TestStruct::get_field);
///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
&mut self,
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
@ -602,7 +602,7 @@ impl Engine {
///
/// The function signature must start with `&mut self` and not `&self`.
///
/// Not available under `no_index`.
/// Not available under both `no_index` and `no_object`.
///
/// # Panics
///
@ -640,11 +640,12 @@ impl Engine {
/// // Register an indexer.
/// .register_indexer_get_result(TestStruct::get_field);
///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn register_indexer_get_result<
T: Variant + Clone,
@ -654,6 +655,7 @@ impl Engine {
&mut self,
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
@ -672,7 +674,7 @@ impl Engine {
}
/// Register an index setter for a custom type with the [`Engine`].
///
/// Not available under `no_index`.
/// Not available under both `no_index` and `no_object`.
///
/// # Panics
///
@ -707,6 +709,7 @@ impl Engine {
/// // Register an indexer.
/// .register_indexer_set(TestStruct::set_field);
///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!(
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
/// 42
@ -714,12 +717,13 @@ impl Engine {
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
&mut self,
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
@ -738,7 +742,7 @@ impl Engine {
}
/// Register an index setter for a custom type with the [`Engine`].
///
/// Not available under `no_index`.
/// Not available under both `no_index` and `no_object`.
///
/// # Panics
///
@ -776,6 +780,7 @@ impl Engine {
/// // Register an indexer.
/// .register_indexer_set_result(TestStruct::set_field);
///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!(
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
/// 42
@ -783,7 +788,7 @@ impl Engine {
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn register_indexer_set_result<
T: Variant + Clone,
@ -793,6 +798,7 @@ impl Engine {
&mut self,
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
@ -807,14 +813,11 @@ impl Engine {
panic!("Cannot register indexer for strings.");
}
self.register_result_fn(
crate::engine::FN_IDX_SET,
move |obj: &mut T, index: X, value: V| set_fn(obj, index, value),
)
self.register_result_fn(crate::engine::FN_IDX_SET, set_fn)
}
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
/// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`].
///
/// Not available under `no_index`.
/// Not available under both `no_index` and `no_object`.
///
/// # Panics
///
@ -852,11 +855,12 @@ impl Engine {
/// // Register an indexer.
/// .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field);
///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?, 42);
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
&mut self,
@ -947,8 +951,14 @@ impl Engine {
}
} else {
let mut iter = name.as_ref().splitn(2, separator.as_ref());
let sub_module = iter.next().unwrap().trim();
let remainder = iter.next().unwrap().trim();
let sub_module = iter
.next()
.expect("never fails because the name contains a separator")
.trim();
let remainder = iter
.next()
.expect("never fails because the name contains a separator")
.trim();
if !root.contains_key(sub_module) {
let mut m: Module = Default::default();
@ -956,7 +966,9 @@ impl Engine {
m.build_index();
root.insert(sub_module.into(), m.into());
} else {
let m = root.remove(sub_module).unwrap();
let m = root
.remove(sub_module)
.expect("never fails because the root contains the sub-module");
let mut m = crate::fn_native::shared_take_or_clone(m);
register_static_module_raw(m.sub_modules_mut(), remainder, module);
m.build_index();
@ -1074,7 +1086,10 @@ impl Engine {
resolver: &StaticModuleResolver,
imports: &mut BTreeSet<Identifier>,
) {
ast.walk(&mut |path| match path.last().unwrap() {
ast.walk(&mut |path| match path
.last()
.expect("never fails because `path` always contains the current node")
{
// Collect all `import` statements with a string constant path
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>

View File

@ -13,6 +13,8 @@ use crate::FLOAT;
#[cfg(feature = "decimal")]
use rust_decimal::Decimal;
const BUILTIN: &str = "never fails because this is built-in code and the type is already checked";
/// Is the type a numeric type?
#[inline(always)]
fn is_numeric(type_id: TypeId) -> bool {
@ -75,22 +77,22 @@ pub fn get_builtin_binary_op_fn(
macro_rules! impl_op {
($xx:ident $op:tt $yy:ident) => {
return Some(|_, args| {
let x = &*args[0].read_lock::<$xx>().unwrap();
let y = &*args[1].read_lock::<$yy>().unwrap();
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
Ok((x $op y).into())
})
};
($xx:ident . $func:ident ( $yy:ty )) => {
return Some(|_, args| {
let x = &*args[0].read_lock::<$xx>().unwrap();
let y = &*args[1].read_lock::<$yy>().unwrap();
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
Ok(x.$func(y).into())
})
};
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => {
return Some(|_, args| {
let x = &*args[0].read_lock::<$xx>().unwrap();
let y = &*args[1].read_lock::<$yy>().unwrap();
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
Ok(x.$func(y.$yyy()).into())
})
};
@ -102,43 +104,43 @@ pub fn get_builtin_binary_op_fn(
};
($base:ty => $xx:ident $op:tt $yy:ident) => {
return Some(|_, args| {
let x = args[0].$xx().unwrap() as $base;
let y = args[1].$yy().unwrap() as $base;
let x = args[0].$xx().expect(BUILTIN) as $base;
let y = args[1].$yy().expect(BUILTIN) as $base;
Ok((x $op y).into())
})
};
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => {
return Some(|_, args| {
let x = args[0].$xx().unwrap() as $base;
let y = args[1].$yy().unwrap() as $base;
let x = args[0].$xx().expect(BUILTIN) as $base;
let y = args[1].$yy().expect(BUILTIN) as $base;
Ok(x.$func(y as $yyy).into())
})
};
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| {
let x = args[0].$xx().unwrap() as $base;
let y = args[1].$yy().unwrap() as $base;
let x = args[0].$xx().expect(BUILTIN) as $base;
let y = args[1].$yy().expect(BUILTIN) as $base;
$func(x, y).map(Into::<Dynamic>::into)
})
};
(from $base:ty => $xx:ident $op:tt $yy:ident) => {
return Some(|_, args| {
let x = <$base>::from(args[0].$xx().unwrap());
let y = <$base>::from(args[1].$yy().unwrap());
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
Ok((x $op y).into())
})
};
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => {
return Some(|_, args| {
let x = <$base>::from(args[0].$xx().unwrap());
let y = <$base>::from(args[1].$yy().unwrap());
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
Ok(x.$func(y).into())
})
};
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| {
let x = <$base>::from(args[0].$xx().unwrap());
let y = <$base>::from(args[1].$yy().unwrap());
let x = <$base>::from(args[0].$xx().expect(BUILTIN));
let y = <$base>::from(args[1].$yy().expect(BUILTIN));
$func(x, y).map(Into::<Dynamic>::into)
})
};
@ -175,7 +177,7 @@ pub fn get_builtin_binary_op_fn(
($x:ty, $xx:ident, $y:ty, $yy:ident) => {
#[cfg(feature = "decimal")]
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "unBUILTIN")) {
use crate::packages::arithmetic::decimal_functions::*;
match op {
@ -222,8 +224,8 @@ pub fn get_builtin_binary_op_fn(
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
#[inline(always)]
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
let x = args[0].as_char().unwrap();
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
let x = args[0].as_char().expect(BUILTIN);
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
let s1 = [x, '\0'];
let mut y = y.chars();
let s2 = [y.next().unwrap_or('\0'), y.next().unwrap_or('\0')];
@ -233,8 +235,8 @@ pub fn get_builtin_binary_op_fn(
match op {
"+" => {
return Some(|_, args| {
let x = args[0].as_char().unwrap();
let y = &*args[1].read_lock::<ImmutableString>().unwrap();
let x = args[0].as_char().expect(BUILTIN);
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
Ok(format!("{}{}", x, y).into())
})
}
@ -251,8 +253,8 @@ pub fn get_builtin_binary_op_fn(
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
#[inline(always)]
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
let y = args[1].as_char().unwrap();
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN);
let mut x = x.chars();
let s1 = [x.next().unwrap_or('\0'), x.next().unwrap_or('\0')];
let s2 = [y, '\0'];
@ -262,15 +264,15 @@ pub fn get_builtin_binary_op_fn(
match op {
"+" => {
return Some(|_, args| {
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
let y = args[1].as_char().unwrap();
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN);
Ok((x + y).into())
})
}
"-" => {
return Some(|_, args| {
let x = &*args[0].read_lock::<ImmutableString>().unwrap();
let y = args[1].as_char().unwrap();
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN);
Ok((x - y).into())
})
}
@ -282,8 +284,8 @@ pub fn get_builtin_binary_op_fn(
"<=" => impl_op!(get_s1s2(<=)),
OP_CONTAINS => {
return Some(|_, args| {
let s = &*args[0].read_lock::<ImmutableString>().unwrap();
let c = args[1].as_char().unwrap();
let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let c = args[1].as_char().expect(BUILTIN);
Ok((s.contains(c)).into())
})
}
@ -314,7 +316,7 @@ pub fn get_builtin_binary_op_fn(
// Beyond here, type1 == type2
if type1 == TypeId::of::<INT>() {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "unBUILTIN")) {
use crate::packages::arithmetic::arith_basic::INT::functions::*;
match op {
@ -383,8 +385,8 @@ pub fn get_builtin_binary_op_fn(
"<=" => impl_op!(ImmutableString <= ImmutableString),
OP_CONTAINS => {
return Some(|_, args| {
let s1 = &*args[0].read_lock::<ImmutableString>().unwrap();
let s2 = &*args[1].read_lock::<ImmutableString>().unwrap();
let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
Ok((s1.contains(s2.as_str())).into())
})
}
@ -396,8 +398,8 @@ pub fn get_builtin_binary_op_fn(
match op {
"+" => {
return Some(|_, args| {
let x = args[0].as_char().unwrap();
let y = args[1].as_char().unwrap();
let x = args[0].as_char().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN);
Ok(format!("{}{}", x, y).into())
})
}
@ -440,55 +442,55 @@ pub fn get_builtin_op_assignment_fn(
macro_rules! impl_op {
($x:ty = x $op:tt $yy:ident) => {
return Some(|_, args| {
let x = args[0].$yy().unwrap();
let y = args[1].$yy().unwrap() as $x;
Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into())
let x = args[0].$yy().expect(BUILTIN);
let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into())
})
};
($x:ident $op:tt $yy:ident) => {
return Some(|_, args| {
let y = args[1].$yy().unwrap() as $x;
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
})
};
($x:ident $op:tt $yy:ident as $yyy:ty) => {
return Some(|_, args| {
let y = args[1].$yy().unwrap() as $yyy;
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
let y = args[1].$yy().expect(BUILTIN) as $yyy;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
})
};
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => {
return Some(|_, args| {
let x = args[0].$xx().unwrap();
let y = args[1].$yy().unwrap() as $x;
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into())
let x = args[0].$xx().expect(BUILTIN);
let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into())
})
};
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| {
let x = args[0].$xx().unwrap();
let y = args[1].$yy().unwrap() as $x;
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
let x = args[0].$xx().expect(BUILTIN);
let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
})
};
(from $x:ident $op:tt $yy:ident) => {
return Some(|_, args| {
let y = <$x>::from(args[1].$yy().unwrap());
Ok((*args[0].write_lock::<$x>().unwrap() $op y).into())
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
})
};
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => {
return Some(|_, args| {
let x = args[0].$xx().unwrap();
let y = <$x>::from(args[1].$yy().unwrap());
Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into())
let x = args[0].$xx().expect(BUILTIN);
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into())
})
};
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| {
let x = args[0].$xx().unwrap();
let y = <$x>::from(args[1].$yy().unwrap());
Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into())
let x = args[0].$xx().expect(BUILTIN);
let y = <$x>::from(args[1].$yy().expect(BUILTIN));
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
})
};
}
@ -517,7 +519,7 @@ pub fn get_builtin_op_assignment_fn(
($x:ident, $xx:ident, $y:ty, $yy:ident) => {
#[cfg(feature = "decimal")]
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "unBUILTIN")) {
use crate::packages::arithmetic::decimal_functions::*;
match op {
@ -562,10 +564,15 @@ pub fn get_builtin_op_assignment_fn(
match op {
"+=" => {
return Some(|_, args| {
let mut ch = args[0].as_char().unwrap().to_string();
ch.push_str(args[1].read_lock::<ImmutableString>().unwrap().as_str());
let mut ch = args[0].as_char().expect(BUILTIN).to_string();
ch.push_str(
args[1]
.read_lock::<ImmutableString>()
.expect(BUILTIN)
.as_str(),
);
let mut x = args[0].write_lock::<Dynamic>().unwrap();
let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = ch.into()).into())
})
}
@ -580,7 +587,7 @@ pub fn get_builtin_op_assignment_fn(
// Beyond here, type1 == type2
if type1 == TypeId::of::<INT>() {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "unBUILTIN")) {
use crate::packages::arithmetic::arith_basic::INT::functions::*;
match op {
@ -628,8 +635,8 @@ pub fn get_builtin_op_assignment_fn(
match op {
"+=" => {
return Some(|_, args| {
let y = args[1].as_char().unwrap();
let mut x = args[0].write_lock::<Dynamic>().unwrap();
let y = args[1].as_char().expect(BUILTIN);
let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = format!("{}{}", *x, y).into()).into())
})
}
@ -641,17 +648,17 @@ pub fn get_builtin_op_assignment_fn(
match op {
"+=" => {
return Some(|_, args| {
let (first, second) = args.split_first_mut().unwrap();
let mut x = first.write_lock::<ImmutableString>().unwrap();
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
let (first, second) = args.split_first_mut().expect(BUILTIN);
let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN);
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
Ok((*x += y).into())
})
}
"-=" => {
return Some(|_, args| {
let (first, second) = args.split_first_mut().unwrap();
let mut x = first.write_lock::<ImmutableString>().unwrap();
let y = &*second[0].read_lock::<ImmutableString>().unwrap();
let (first, second) = args.split_first_mut().expect(BUILTIN);
let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN);
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
Ok((*x -= y).into())
})
}

View File

@ -24,7 +24,7 @@ use std::prelude::v1::*;
use std::{
any::{type_name, TypeId},
convert::TryFrom,
iter::{empty, once},
iter::once,
mem,
};
@ -49,7 +49,12 @@ impl<'a> ArgBackup<'a> {
///
/// This method blindly casts a reference to another lifetime, which saves allocation and string cloning.
///
/// If `restore_first_arg` is called before the end of the scope, the shorter lifetime will not leak.
/// As long as `restore_first_arg` is called before the end of the scope, the shorter lifetime
/// will not leak.
///
/// # Panics
///
/// Panics when `args` is empty.
#[inline(always)]
fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) {
// Clone the original value.
@ -65,7 +70,7 @@ impl<'a> ArgBackup<'a> {
//
// We can do this here because, before the end of this scope, we'd restore the original reference
// via `restore_first_arg`. Therefore this shorter lifetime does not leak.
self.orig_mut = Some(mem::replace(args.get_mut(0).unwrap(), unsafe {
self.orig_mut = Some(mem::replace(&mut args[0], unsafe {
mem::transmute(&mut self.value_copy)
}));
}
@ -77,9 +82,7 @@ impl<'a> ArgBackup<'a> {
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
#[inline(always)]
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
if let Some(this_pointer) = self.orig_mut.take() {
args[0] = this_pointer;
}
self.orig_mut.take().map(|p| args[0] = p);
}
}
@ -162,12 +165,10 @@ impl Engine {
allow_dynamic: bool,
is_op_assignment: bool,
) -> &'s Option<Box<FnResolutionCacheEntry>> {
let mut hash = if let Some(ref args) = args {
let mut hash = args.as_ref().map_or(hash_script, |args| {
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
combine_hashes(hash_script, hash_params)
} else {
hash_script
};
});
&*state
.fn_resolution_cache_mut()
@ -239,7 +240,8 @@ impl Engine {
FnResolutionCacheEntry { func, source: None }
})
} else {
let (first, second) = args.split_first().unwrap();
let (first, second) = args.split_first()
.expect("never fails because an op-assignment must have two arguments");
get_builtin_op_assignment_fn(fn_name, *first, second[0]).map(
|f| {
@ -257,7 +259,9 @@ impl Engine {
// Try all permutations with `Dynamic` wildcards
None => {
let hash_params = calc_fn_params_hash(
args.as_ref().unwrap().iter().enumerate().map(|(i, a)| {
args.as_ref().expect("never fails because there are no permutations if there are no arguments")
.iter().enumerate().map(|(i, a)|
{
let mask = 1usize << (num_args - i - 1);
if bitmask & mask != 0 {
// Replace with `Dynamic`
@ -320,7 +324,7 @@ impl Engine {
let mut backup: Option<ArgBackup> = None;
if is_ref && func.is_pure() && !args.is_empty() {
backup = Some(Default::default());
backup.as_mut().unwrap().change_first_arg_to_copy(args);
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
}
// Run external function
@ -336,9 +340,7 @@ impl Engine {
};
// Restore the original reference
if let Some(backup) = backup {
backup.restore_first_arg(args);
}
backup.map(|bk| bk.restore_first_arg(args));
let result = result.map_err(|err| err.fill_position(pos))?;
@ -371,32 +373,24 @@ impl Engine {
match fn_name {
// index getter function not found?
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
crate::engine::FN_IDX_GET => {
assert!(args.len() == 2);
EvalAltResult::ErrorFunctionNotFound(
format!(
"{} [{}]",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
),
EvalAltResult::ErrorIndexingType(
self.map_type_name(args[0].type_name()).to_string(),
pos,
)
.into()
}
// index setter function not found?
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
crate::engine::FN_IDX_SET => {
assert!(args.len() == 3);
EvalAltResult::ErrorFunctionNotFound(
format!(
"{} [{}]=",
self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()),
),
EvalAltResult::ErrorIndexingType(
self.map_type_name(args[0].type_name()).to_string(),
pos,
)
.into()
@ -654,14 +648,18 @@ impl Engine {
crate::engine::KEYWORD_IS_DEF_FN
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
{
let fn_name = &*args[0].read_lock::<ImmutableString>().unwrap();
let num_params = args[1].as_int().unwrap();
let fn_name = &*args[0]
.read_lock::<ImmutableString>()
.expect("never fails because `args[0]` is `FnPtr`");
let num_params = args[1]
.as_int()
.expect("never fails because `args[1]` is `INT`");
return Ok((
if num_params < 0 {
Dynamic::FALSE
} else {
let hash_script = calc_fn_hash(empty(), fn_name, num_params as usize);
let hash_script = calc_fn_hash(fn_name, num_params as usize);
self.has_script_fn(Some(mods), state, lib, hash_script)
.into()
},
@ -712,11 +710,7 @@ impl Engine {
// Scripted function call?
#[cfg(not(feature = "no_function"))]
let hash_script = if hash.is_native_only() {
None
} else {
Some(hash.script_hash())
};
let hash_script = hash.script_hash();
#[cfg(not(feature = "no_function"))]
if let Some(f) = hash_script.and_then(|hash| {
@ -737,21 +731,23 @@ impl Engine {
// Move captured variables into scope
#[cfg(not(feature = "no_closure"))]
if let Some(captured) = _capture_scope {
if !func.externals.is_empty() {
if !func.externals.is_empty() {
_capture_scope.map(|captured| {
captured
.into_iter()
.filter(|(name, _, _)| func.externals.contains(name.as_ref()))
.for_each(|(name, value, _)| {
// Consume the scope values.
scope.push_dynamic(name, value);
});
}
})
});
}
let result = if _is_method {
// Method call of script function - map first argument to `this`
let (first, rest) = args.split_first_mut().unwrap();
let (first, rest) = args
.split_first_mut()
.expect("never fails because a method call always has a first parameter");
let orig_source = state.source.take();
state.source = source;
@ -780,7 +776,7 @@ impl Engine {
let mut backup: Option<ArgBackup> = None;
if is_ref && !args.is_empty() {
backup = Some(Default::default());
backup.as_mut().unwrap().change_first_arg_to_copy(args);
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
}
let orig_source = state.source.take();
@ -795,9 +791,7 @@ impl Engine {
state.source = orig_source;
// Restore the original reference
if let Some(backup) = backup {
backup.restore_first_arg(args);
}
backup.map(|bk| bk.restore_first_arg(args));
result?
};
@ -914,12 +908,14 @@ impl Engine {
let (result, updated) = match fn_name {
KEYWORD_FN_PTR_CALL if obj.is::<FnPtr>() => {
// FnPtr call
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
let fn_ptr = obj
.read_lock::<FnPtr>()
.expect("never fails because `obj` is `FnPtr`");
// Redirect function name
let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hashes
let new_hash = FnCallHashes::from_script(calc_fn_hash(empty(), fn_name, args_len));
let new_hash = FnCallHashes::from_script(calc_fn_hash(fn_name, args_len));
// Arguments are passed as-is, adding the curried arguments
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
let mut args = curry
@ -955,8 +951,8 @@ impl Engine {
let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash
let new_hash = FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), fn_name, args_len),
calc_fn_hash(empty(), fn_name, args_len + 1),
calc_fn_hash(fn_name, args_len),
calc_fn_hash(fn_name, args_len + 1),
);
// Replace the first argument with the object pointer, adding the curried arguments
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
@ -978,7 +974,9 @@ impl Engine {
));
}
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
let fn_ptr = obj
.read_lock::<FnPtr>()
.expect("never fails because `obj` is `FnPtr`");
// Curry call
Ok((
@ -1029,8 +1027,8 @@ impl Engine {
});
// Recalculate the hash based on the new function name and new arguments
hash = FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), fn_name, call_args.len()),
calc_fn_hash(empty(), fn_name, call_args.len() + 1),
calc_fn_hash(fn_name, call_args.len()),
calc_fn_hash(fn_name, call_args.len() + 1),
);
}
}
@ -1049,7 +1047,9 @@ impl Engine {
// Propagate the changed value back to the source if necessary
if updated {
target.propagate_changed_value();
target
.propagate_changed_value()
.map_err(|err| err.fill_position(pos))?;
}
Ok((result, updated))
@ -1115,9 +1115,9 @@ impl Engine {
// Recalculate hash
let args_len = total_args + curry.len();
hashes = if !hashes.is_native_only() {
FnCallHashes::from_script(calc_fn_hash(empty(), name, args_len))
FnCallHashes::from_script(calc_fn_hash(name, args_len))
} else {
FnCallHashes::from_native(calc_fn_hash(empty(), name, args_len))
FnCallHashes::from_native(calc_fn_hash(name, args_len))
};
}
// Handle Fn()
@ -1214,7 +1214,7 @@ impl Engine {
return Ok(if num_params < 0 {
Dynamic::FALSE
} else {
let hash_script = calc_fn_hash(empty(), &fn_name, num_params as usize);
let hash_script = calc_fn_hash(&fn_name, num_params as usize);
self.has_script_fn(Some(mods), state, lib, hash_script)
.into()
});
@ -1332,9 +1332,8 @@ impl Engine {
} else {
// Turn it into a method call only if the object is not shared and not a simple value
is_ref = true;
once(target.take_ref().unwrap())
.chain(arg_values.iter_mut())
.collect()
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
once(obj_ref).chain(arg_values.iter_mut()).collect()
};
} else {
// func(..., ...)
@ -1365,7 +1364,7 @@ impl Engine {
state: &mut State,
lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>,
namespace: Option<&NamespaceRef>,
namespace: &NamespaceRef,
fn_name: &str,
args_expr: &[Expr],
literal_args: &[(Dynamic, Position)],
@ -1373,7 +1372,6 @@ impl Engine {
pos: Position,
level: usize,
) -> RhaiResult {
let namespace = namespace.unwrap();
let mut arg_values: StaticVec<_>;
let mut first_arg_value = None;
let mut args: StaticVec<_>;
@ -1418,11 +1416,12 @@ impl Engine {
arg_values[0] = target.take_or_clone().flatten();
args = arg_values.iter_mut().collect();
} else {
let (first, rest) = arg_values.split_first_mut().unwrap();
let (first, rest) = arg_values
.split_first_mut()
.expect("never fails because the arguments list is not empty");
first_arg_value = Some(first);
args = once(target.take_ref().unwrap())
.chain(rest.iter_mut())
.collect();
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
args = once(obj_ref).chain(rest.iter_mut()).collect();
}
} else {
// func(..., ...) or func(mod::x, ...)
@ -1459,12 +1458,11 @@ impl Engine {
};
// Clone first argument if the function is not a method after-all
if let Some(first) = first_arg_value {
if !func.map(|f| f.is_method()).unwrap_or(true) {
let first_val = args[0].clone();
if !func.map(|f| f.is_method()).unwrap_or(true) {
first_arg_value.map(|first| {
*first = args[0].clone();
args[0] = first;
*args[0] = first_val;
}
});
}
match func {

View File

@ -13,7 +13,7 @@ use std::prelude::v1::*;
use std::{
convert::{TryFrom, TryInto},
fmt,
iter::{empty, once},
iter::once,
mem,
};
@ -202,11 +202,11 @@ impl<'a> NativeCallContext<'a> {
let hash = if is_method {
FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), fn_name, args.len() - 1),
calc_fn_hash(empty(), fn_name, args.len()),
calc_fn_hash(fn_name, args.len() - 1),
calc_fn_hash(fn_name, args.len()),
)
} else {
FnCallHashes::from_script(calc_fn_hash(empty(), fn_name, args.len()))
FnCallHashes::from_script(calc_fn_hash(fn_name, args.len()))
};
self.engine()
@ -253,7 +253,9 @@ pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
/// Panics if the resource is shared (i.e. has other outstanding references).
#[inline(always)]
pub fn shared_take<T>(value: Shared<T>) -> T {
shared_try_take(value).map_err(|_| ()).unwrap()
shared_try_take(value)
.ok()
.expect("resource should have no outstanding references")
}
/// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic].

View File

@ -32,7 +32,8 @@ pub struct Mut<T>(T);
#[inline(always)]
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data.
data.write_lock::<T>().unwrap()
data.write_lock::<T>()
.expect("never fails because the type was checked")
}
/// Dereference into value.
@ -43,12 +44,15 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
data.flatten_in_place();
let ref_str = data
.as_str_ref()
.expect("argument passed by value should not be shared");
.expect("never fails because argument passed by value should not be shared");
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
ref_t.clone()
} else if TypeId::of::<T>() == TypeId::of::<String>() {
// If T is `String`, data must be `ImmutableString`, so map directly to it
unsafe_try_cast(mem::take(data).take_string().unwrap()).unwrap()
let value = mem::take(data)
.take_string()
.expect("never fails because the type was checked");
unsafe_try_cast(value).expect("never fails because the type was checked")
} else {
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
@ -118,7 +122,7 @@ macro_rules! def_register {
// The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut();
$($let $par = ($clone)(_drain.next().unwrap()); )*
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
// Call the function with each argument value
let r = self($($arg),*);
@ -146,7 +150,7 @@ macro_rules! def_register {
// The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut();
$($let $par = ($clone)(_drain.next().unwrap()); )*
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
// Call the function with each argument value
let r = self(ctx, $($arg),*);
@ -174,7 +178,7 @@ macro_rules! def_register {
// The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut();
$($let $par = ($clone)(_drain.next().unwrap()); )*
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
// Call the function with each argument value
self($($arg),*).map(Dynamic::from)
@ -199,7 +203,7 @@ macro_rules! def_register {
// The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut();
$($let $par = ($clone)(_drain.next().unwrap()); )*
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
// Call the function with each argument value
self(ctx, $($arg),*).map(Dynamic::from)

View File

@ -169,7 +169,7 @@ pub use fn_native::Shared;
#[cfg(not(feature = "no_closure"))]
use fn_native::Locked;
pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, combine_hashes};
pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes};
pub use rhai_codegen::*;

View File

@ -7,8 +7,8 @@ use crate::fn_register::RegisterNativeFunction;
use crate::token::Token;
use crate::utils::IdentifierBuilder;
use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, Identifier,
ImmutableString, NativeCallContext, Position, Shared, StaticVec,
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult,
Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -24,7 +24,6 @@ use std::{
#[cfg(not(feature = "no_index"))]
use crate::Array;
#[cfg(not(feature = "no_index"))]
#[cfg(not(feature = "no_object"))]
use crate::Map;
@ -117,7 +116,7 @@ fn calc_native_fn_hash<'a>(
fn_name: impl AsRef<str>,
params: &[TypeId],
) -> u64 {
let hash_script = calc_fn_hash(modules, fn_name, params.len());
let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len());
let hash_params = calc_fn_params_hash(params.iter().cloned());
combine_hashes(hash_script, hash_params)
}
@ -156,20 +155,7 @@ pub struct Module {
impl Default for Module {
#[inline(always)]
fn default() -> Self {
Self {
id: None,
internal: false,
modules: Default::default(),
variables: Default::default(),
all_variables: Default::default(),
functions: Default::default(),
all_functions: Default::default(),
type_iterators: Default::default(),
all_type_iterators: Default::default(),
indexed: true,
contains_indexed_global_functions: false,
identifiers: Default::default(),
}
Self::new()
}
}
@ -177,9 +163,7 @@ impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Module");
if let Some(ref id) = self.id {
d.field("id", id);
}
self.id.as_ref().map(|id| d.field("id", id));
if !self.modules.is_empty() {
d.field(
@ -257,7 +241,20 @@ impl Module {
/// ```
#[inline(always)]
pub fn new() -> Self {
Default::default()
Self {
id: None,
internal: false,
modules: Default::default(),
variables: Default::default(),
all_variables: Default::default(),
functions: Default::default(),
all_functions: Default::default(),
type_iterators: Default::default(),
all_type_iterators: Default::default(),
indexed: true,
contains_indexed_global_functions: false,
identifiers: Default::default(),
}
}
/// Get the ID of the [`Module`], if any.
@ -441,7 +438,7 @@ impl Module {
let value = Dynamic::from(value);
if self.indexed {
let hash_var = crate::calc_fn_hash(once(""), &ident, 0);
let hash_var = crate::calc_qualified_fn_hash(once(""), &ident, 0);
self.all_variables.insert(hash_var, value.clone());
}
self.variables.insert(ident, value);
@ -467,7 +464,7 @@ impl Module {
// None + function name + number of arguments.
let num_params = fn_def.params.len();
let hash_script = crate::calc_fn_hash(empty(), &fn_def.name, num_params);
let hash_script = crate::calc_fn_hash(&fn_def.name, num_params);
let mut param_names = fn_def.params.clone();
param_names.push("Dynamic".into());
self.functions.insert(
@ -622,9 +619,10 @@ impl Module {
.map(|&name| self.identifiers.get(name))
.collect();
if let Some(f) = self.functions.get_mut(&hash_fn) {
f.param_names = param_names;
}
self.functions
.get_mut(&hash_fn)
.map(|f| f.param_names = param_names);
self
}
@ -956,7 +954,7 @@ impl Module {
/// });
/// assert!(module.contains_fn(hash));
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
where
@ -966,6 +964,7 @@ impl Module {
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
{
#[cfg(not(feature = "no_index"))]
if TypeId::of::<A>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
@ -1016,7 +1015,7 @@ impl Module {
/// });
/// assert!(module.contains_fn(hash));
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
where
@ -1026,6 +1025,7 @@ impl Module {
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
{
#[cfg(not(feature = "no_index"))]
if TypeId::of::<A>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays.");
}
@ -1082,7 +1082,7 @@ impl Module {
/// assert!(module.contains_fn(hash_get));
/// assert!(module.contains_fn(hash_set));
/// ```
#[cfg(not(feature = "no_index"))]
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)]
pub fn set_indexer_get_set_fn<A, B, T>(
&mut self,
@ -1414,7 +1414,10 @@ impl Module {
match aliases.len() {
0 => (),
1 => {
module.set_var(aliases.pop().unwrap(), value);
let alias = aliases
.pop()
.expect("never fails because the list has one item");
module.set_var(alias, value);
}
_ => aliases.into_iter().for_each(|alias| {
module.set_var(alias, value.clone());
@ -1490,7 +1493,7 @@ impl Module {
// Index all variables
module.variables.iter().for_each(|(var_name, value)| {
let hash_var = crate::calc_fn_hash(path.iter().map(|&v| v), var_name, 0);
let hash_var = crate::calc_qualified_fn_hash(path.iter().map(|&v| v), var_name, 0);
variables.insert(hash_var, value.clone());
});
@ -1520,8 +1523,11 @@ impl Module {
calc_native_fn_hash(path.iter().cloned(), f.name.as_str(), &f.param_types);
functions.insert(hash_qualified_fn, f.func.clone());
} else if cfg!(not(feature = "no_function")) {
let hash_qualified_script =
crate::calc_fn_hash(path.iter().cloned(), f.name.as_str(), f.params);
let hash_qualified_script = crate::calc_qualified_fn_hash(
path.iter().cloned(),
f.name.as_str(),
f.params,
);
functions.insert(hash_qualified_script, f.func.clone());
}
});

View File

@ -31,8 +31,8 @@ impl DummyModuleResolver {
/// engine.set_module_resolver(resolver);
/// ```
#[inline(always)]
pub fn new() -> Self {
Default::default()
pub const fn new() -> Self {
Self
}
}

View File

@ -4,7 +4,6 @@ use crate::ast::{Expr, OpAssignment, Stmt};
use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::fn_builtin::get_builtin_binary_op_fn;
use crate::parser::map_dynamic_to_expr;
use crate::token::Token;
use crate::utils::get_hasher;
use crate::{
@ -16,7 +15,6 @@ use std::prelude::v1::*;
use std::{
any::TypeId,
hash::{Hash, Hasher},
iter::empty,
mem,
};
@ -32,24 +30,14 @@ pub enum OptimizationLevel {
Full,
}
impl OptimizationLevel {
/// Is the `OptimizationLevel` [`None`][OptimizationLevel::None]?
#[allow(dead_code)]
impl Default for OptimizationLevel {
#[inline(always)]
pub fn is_none(self) -> bool {
self == Self::None
}
/// Is the `OptimizationLevel` [`Simple`][OptimizationLevel::Simple]?
#[allow(dead_code)]
#[inline(always)]
pub fn is_simple(self) -> bool {
self == Self::Simple
}
/// Is the `OptimizationLevel` [`Full`][OptimizationLevel::Full]?
#[allow(dead_code)]
#[inline(always)]
pub fn is_full(self) -> bool {
self == Self::Full
fn default() -> Self {
if cfg!(feature = "no_optimize") {
Self::None
} else {
Self::Simple
}
}
}
@ -59,7 +47,7 @@ struct State<'a> {
/// Has the [`AST`] been changed during this pass?
changed: bool,
/// Collection of constants to use for eager function evaluations.
variables: Vec<(String, AccessMode, Expr)>,
variables: Vec<(String, AccessMode, Option<Dynamic>)>,
/// Activate constants propagation?
propagate_constants: bool,
/// An [`Engine`] instance for eager function evaluation.
@ -109,21 +97,21 @@ impl<'a> State<'a> {
}
/// Add a new constant to the list.
#[inline(always)]
pub fn push_var(&mut self, name: &str, access: AccessMode, value: Expr) {
pub fn push_var(&mut self, name: &str, access: AccessMode, value: Option<Dynamic>) {
self.variables.push((name.into(), access, value))
}
/// Look up a constant from the list.
#[inline]
pub fn find_constant(&self, name: &str) -> Option<&Expr> {
pub fn find_constant(&self, name: &str) -> Option<&Dynamic> {
if !self.propagate_constants {
return None;
}
self.variables.iter().rev().find_map(|(n, access, expr)| {
self.variables.iter().rev().find_map(|(n, access, value)| {
if n == name {
match access {
AccessMode::ReadWrite => None,
AccessMode::ReadOnly => Some(expr),
AccessMode::ReadOnly => value.as_ref(),
}
} else {
None
@ -158,7 +146,7 @@ fn call_fn_with_constant_arguments(
&mut Default::default(),
state.lib,
fn_name,
calc_fn_hash(empty(), fn_name, arg_values.len()),
calc_fn_hash(fn_name, arg_values.len()),
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
false,
false,
@ -217,13 +205,17 @@ fn optimize_stmt_block(
optimize_expr(value_expr, state);
if value_expr.is_constant() {
state.push_var(&x.name, AccessMode::ReadOnly, value_expr.clone());
state.push_var(
&x.name,
AccessMode::ReadOnly,
value_expr.get_constant_value(),
);
}
}
// Add variables into the state
Stmt::Let(value_expr, x, _, _) => {
optimize_expr(value_expr, state);
state.push_var(&x.name, AccessMode::ReadWrite, Expr::Unit(x.pos));
state.push_var(&x.name, AccessMode::ReadWrite, None);
}
// Optimize the statement
_ => optimize_stmt(stmt, state, preserve_result),
@ -512,12 +504,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
Stmt::Switch(match_expr, x, _) => {
optimize_expr(match_expr, state);
x.0.values_mut().for_each(|block| {
let condition = if let Some(mut condition) = mem::take(&mut block.0) {
optimize_expr(&mut condition, state);
condition
} else {
Expr::Unit(Position::NONE)
};
let condition = mem::take(&mut block.0).map_or_else(
|| Expr::Unit(Position::NONE),
|mut condition| {
optimize_expr(&mut condition, state);
condition
},
);
match condition {
Expr::Unit(_) | Expr::BoolConstant(true, _) => (),
@ -703,7 +696,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
// map.string
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
let prop = &p.2.name;
let prop = p.2.0.as_str();
// Map literal where everything is pure - promote the indexed item.
// All other items can be thrown away.
state.set_dirty();
@ -925,15 +918,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// Search for overloaded operators (can override built-in).
if !has_native_fn(state, x.hashes.native_hash(), arg_types.as_ref()) {
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| {
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
let (first, second) = arg_values.split_first_mut().unwrap();
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
})
.and_then(|result| map_dynamic_to_expr(result, *pos))
if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| {
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
let (first, second) = arg_values.split_first_mut().unwrap();
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
})
.map(Expr::from)
{
state.set_dirty();
result.set_position(*pos);
*expr = result;
return;
}
@ -977,18 +971,19 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
""
};
if let Some(result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into())
} else {
None
}
})
.and_then(|result| map_dynamic_to_expr(result, *pos))
if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values)
.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into())
} else {
None
}
})
.map(Expr::from)
{
state.set_dirty();
result.set_position(*pos);
*expr = result;
return;
}
@ -1014,12 +1009,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// constant-name
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
state.set_dirty();
// Replace constant with value
let mut result = state.find_constant(&x.2).unwrap().clone();
result.set_position(*pos);
*expr = result;
let pos = *pos;
*expr = Expr::from(state.find_constant(&x.2).unwrap().clone());
expr.set_position(pos);
state.set_dirty();
}
// Custom syntax
@ -1055,13 +1049,9 @@ fn optimize_top_level(
// Add constants and variables from the scope
scope.iter().for_each(|(name, constant, value)| {
if !constant {
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(Position::NONE));
state.push_var(name, AccessMode::ReadWrite, None);
} else {
state.push_var(
name,
AccessMode::ReadOnly,
Expr::DynamicConstant(Box::new(value), Position::NONE),
);
state.push_var(name, AccessMode::ReadOnly, Some(value));
}
});
@ -1078,7 +1068,7 @@ pub fn optimize_into_ast(
optimization_level: OptimizationLevel,
) -> AST {
let level = if cfg!(feature = "no_optimize") {
OptimizationLevel::None
Default::default()
} else {
optimization_level
};
@ -1087,7 +1077,7 @@ pub fn optimize_into_ast(
let lib = {
let mut module = Module::new();
if !level.is_none() {
if level != OptimizationLevel::None {
// We only need the script library's signatures for optimization purposes
let mut lib2 = Module::new();

View File

@ -604,27 +604,13 @@ mod array_functions {
.call_dynamic(&ctx, None, [x.clone(), y.clone()])
.ok()
.and_then(|v| v.as_int().ok())
.map(|v| {
if v > 0 {
Ordering::Greater
} else if v < 0 {
Ordering::Less
} else {
Ordering::Equal
}
})
.unwrap_or_else(|| {
let x_type_id = x.type_id();
let y_type_id = y.type_id();
if x_type_id > y_type_id {
Ordering::Greater
} else if x_type_id < y_type_id {
Ordering::Less
} else {
Ordering::Equal
}
.map(|v| match v {
v if v > 0 => Ordering::Greater,
v if v < 0 => Ordering::Less,
0 => Ordering::Equal,
_ => unreachable!(),
})
.unwrap_or_else(|| x.type_id().cmp(&y.type_id()))
});
Ok(())

View File

@ -45,29 +45,31 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
namespace: Option<Identifier>,
f: &ScriptFnDef,
) -> Map {
const DICT: &str = "never fails because the dictionary is pre-filled with all the keys";
let mut map = Map::new();
if let Some(ns) = namespace {
map.insert(dict.get("namespace").unwrap().clone().into(), ns.into());
map.insert(dict.get("namespace").expect(DICT).clone().into(), ns.into());
}
map.insert(
dict.get("name").unwrap().clone().into(),
dict.get("name").expect(DICT).clone().into(),
f.name.clone().into(),
);
map.insert(
dict.get("access").unwrap().clone().into(),
dict.get("access").expect(DICT).clone().into(),
match f.access {
FnAccess::Public => dict.get("public").unwrap().clone(),
FnAccess::Private => dict.get("private").unwrap().clone(),
FnAccess::Public => dict.get("public").expect(DICT).clone(),
FnAccess::Private => dict.get("private").expect(DICT).clone(),
}
.into(),
);
map.insert(
dict.get("is_anonymous").unwrap().clone().into(),
dict.get("is_anonymous").expect(DICT).clone().into(),
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
);
map.insert(
dict.get("params").unwrap().clone().into(),
dict.get("params").expect(DICT).clone().into(),
f.params
.iter()
.cloned()

View File

@ -311,21 +311,14 @@ mod decimal_functions {
#[rhai_fn(return_raw)]
pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
x.sqrt()
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
} else {
Ok(x.sqrt().unwrap())
}
x.sqrt()
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
}
#[rhai_fn(return_raw)]
pub fn exp(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
if x > Decimal::from_parts(117578, 0, 0, false, 4) {
Err(make_err(format!("Exponential overflow: e ** {}", x,)))
} else {
Ok(x.exp())
}
x.checked_exp()
.ok_or_else(|| make_err(format!("Exponential overflow: e ** {}", x,)))
} else {
Ok(x.exp())
}
@ -348,15 +341,15 @@ mod decimal_functions {
#[rhai_fn(name = "round", return_raw)]
pub fn round_dp(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 {
return Err(make_err(format!(
"Decimal value {} round to a negative index: {}",
x, dp
"Invalid number of digits for rounding: {}",
dp
)));
}
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
}
Ok(x.round_dp(dp as u32))
@ -364,15 +357,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)]
pub fn round_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 {
return Err(make_err(format!(
"Decimal value {} round to a negative index: {}",
x, dp
"Invalid number of digits for rounding: {}",
dp
)));
}
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
}
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero))
@ -380,15 +373,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)]
pub fn round_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 {
return Err(make_err(format!(
"Decimal value {} round to a negative index: {}",
x, dp
"Invalid number of digits for rounding: {}",
dp
)));
}
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
}
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero))
@ -396,15 +389,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)]
pub fn round_half_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 {
return Err(make_err(format!(
"Decimal value {} round to a negative index: {}",
x, dp
"Invalid number of digits for rounding: {}",
dp
)));
}
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
}
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero))
@ -412,15 +405,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)]
pub fn round_half_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 {
return Err(make_err(format!(
"Decimal value {} round to a negative index: {}",
x, dp
"Invalid number of digits for rounding: {}",
dp
)));
}
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
}
Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointTowardZero))

View File

@ -27,9 +27,9 @@ pub fn print_with_func(
value: &mut Dynamic,
) -> crate::ImmutableString {
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
Ok(result) if result.is::<crate::ImmutableString>() => {
result.take_immutable_string().unwrap()
}
Ok(result) if result.is::<crate::ImmutableString>() => result
.take_immutable_string()
.expect("never fails as the result is `ImmutableString`"),
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
}

File diff suppressed because it is too large Load Diff

View File

@ -181,7 +181,9 @@ impl fmt::Display for EvalAltResult {
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
let s = &*d.read_lock::<ImmutableString>().unwrap();
let s = &*d
.read_lock::<ImmutableString>()
.expect("never fails because the type was checked");
write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
}
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
@ -336,7 +338,11 @@ impl EvalAltResult {
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
map.insert(
"error".into(),
format!("{:?}", self).split('(').next().unwrap().into(),
format!("{:?}", self)
.split('(')
.next()
.expect("never fails because the debug format of an error is `ErrorXXX(...)`")
.into(),
);
match self {

View File

@ -7,7 +7,7 @@ use std::prelude::v1::*;
use std::{borrow::Cow, iter::Extend};
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
const SCOPE_SIZE: usize = 16;
const SCOPE_SIZE: usize = 8;
/// Type containing information about the current scope.
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
@ -62,10 +62,7 @@ pub struct Scope<'a> {
impl Default for Scope<'_> {
#[inline(always)]
fn default() -> Self {
Self {
values: Default::default(),
names: Vec::with_capacity(SCOPE_SIZE),
}
Self::new()
}
}
@ -99,7 +96,10 @@ impl<'a> Scope<'a> {
/// ```
#[inline(always)]
pub fn new() -> Self {
Default::default()
Self {
values: Default::default(),
names: Default::default(),
}
}
/// Empty the [`Scope`].
///
@ -373,7 +373,11 @@ impl<'a> Scope<'a> {
}
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name),
Some((index, AccessMode::ReadWrite)) => {
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
let value_ref = self
.values
.get_mut(index)
.expect("never fails because the index is returned by `get_index`");
*value_ref = Dynamic::from(value);
}
}
self
@ -406,24 +410,38 @@ impl<'a> Scope<'a> {
})
}
/// Get a mutable reference to an entry in the [`Scope`] based on the index.
///
/// # Panics
///
/// Panics if the index is out of bounds.
#[inline(always)]
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
self.values.get_mut(index).expect("invalid index in Scope")
self.values
.get_mut(index)
.expect("never fails unless the index is out of bounds")
}
/// Update the access type of an entry in the [`Scope`].
///
/// # Panics
///
/// Panics if the index is out of bounds.
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub(crate) fn add_entry_alias(
&mut self,
index: usize,
alias: impl Into<Identifier> + PartialEq<Identifier>,
) -> &mut Self {
let entry = self.names.get_mut(index).expect("invalid index in Scope");
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
let entry = self
.names
.get_mut(index)
.expect("never fails unless the index is out of bounds");
if entry.1.is_none() {
// Initialize the alias list if it is empty.
entry.1 = Some(Default::default());
}
if !entry.1.as_ref().unwrap().iter().any(|a| &alias == a) {
entry.1.as_mut().unwrap().push(alias.into());
let list = entry
.1
.as_mut()
.expect("never fails because the list is initialized");
if !list.iter().any(|a| &alias == a) {
list.push(alias);
}
self
}

View File

@ -91,21 +91,19 @@ struct FnMetadata {
impl PartialOrd for FnMetadata {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(match self.name.partial_cmp(&other.name).unwrap() {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => match self.num_params.partial_cmp(&other.num_params).unwrap() {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => self.params.partial_cmp(&other.params).unwrap(),
},
})
Some(self.cmp(other))
}
}
impl Ord for FnMetadata {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
match self.name.cmp(&other.name) {
Ordering::Equal => match self.num_params.cmp(&other.num_params) {
Ordering::Equal => self.params.cmp(&other.params),
cmp => cmp,
},
cmp => cmp,
}
}
}

View File

@ -1181,7 +1181,10 @@ pub fn parse_string_literal(
#[cfg(not(feature = "no_position"))]
{
skip_whitespace_until = start.position().unwrap() + 1;
let start_position = start
.position()
.expect("never fails because the string must have a starting position");
skip_whitespace_until = start_position + 1;
}
}
@ -1201,7 +1204,11 @@ pub fn parse_string_literal(
// Whitespace to skip
#[cfg(not(feature = "no_position"))]
_ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {}
_ if next_char.is_whitespace()
&& pos
.position()
.expect("never fails because a character must have a position")
< skip_whitespace_until => {}
// All other characters
_ => {
@ -1237,37 +1244,29 @@ fn scan_block_comment(
stream: &mut impl InputStream,
mut level: usize,
pos: &mut Position,
comment: &mut Option<String>,
mut comment: Option<&mut String>,
) -> usize {
let comment = &mut comment;
while let Some(c) = stream.get_next() {
pos.advance();
if let Some(ref mut comment) = comment {
comment.push(c);
}
comment.as_mut().map(|comment| comment.push(c));
match c {
'/' => {
if let Some(c2) = stream.peek_next() {
if c2 == '*' {
eat_next(stream, pos);
if let Some(ref mut comment) = comment {
comment.push(c2);
}
level += 1;
}
}
stream.peek_next().filter(|&c2| c2 == '*').map(|c2| {
eat_next(stream, pos);
comment.as_mut().map(|comment| comment.push(c2));
level += 1;
});
}
'*' => {
if let Some(c2) = stream.peek_next() {
if c2 == '/' {
eat_next(stream, pos);
if let Some(ref mut comment) = comment {
comment.push(c2);
}
level -= 1;
}
}
stream.peek_next().filter(|&c2| c2 == '/').map(|c2| {
eat_next(stream, pos);
comment.as_mut().map(|comment| comment.push(c2));
level -= 1;
});
}
'\n' => pos.new_line(),
_ => (),
@ -1347,21 +1346,27 @@ fn get_next_token_inner(
None
};
state.comment_level = scan_block_comment(stream, state.comment_level, pos, &mut comment);
state.comment_level =
scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
let include_comments = state.include_comments;
let return_comment = state.include_comments;
#[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")]
let include_comments = if is_doc_comment(comment.as_ref().unwrap()) {
true
} else {
include_comments
};
let return_comment = return_comment
|| is_doc_comment(
comment
.as_ref()
.expect("never fails because `include_comments` is true"),
);
if include_comments {
return Some((Token::Comment(comment.unwrap()), start_pos));
} else if state.comment_level > 0 {
if return_comment {
return Some((
Token::Comment(comment.expect("never fails because `return_comment` is true")),
start_pos,
));
}
if state.comment_level > 0 {
// Reached EOF without ending comment block
return None;
}
@ -1409,7 +1414,7 @@ fn get_next_token_inner(
}
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
'.' => {
stream.get_next().unwrap();
stream.get_next().expect("never fails because it is `.`");
// Check if followed by digits or something that cannot start a property name
match stream.peek_next().unwrap_or('\0') {
@ -1443,7 +1448,7 @@ fn get_next_token_inner(
}
#[cfg(not(feature = "no_float"))]
'e' => {
stream.get_next().unwrap();
stream.get_next().expect("never fails it is `e`");
// Check if followed by digits or +/-
match stream.peek_next().unwrap_or('\0') {
@ -1456,7 +1461,11 @@ fn get_next_token_inner(
'+' | '-' => {
result.push(next_char);
pos.advance();
result.push(stream.get_next().unwrap());
result.push(
stream
.get_next()
.expect("never fails because it is `+` or `-`"),
);
pos.advance();
}
// Not a floating-point number
@ -1492,12 +1501,10 @@ fn get_next_token_inner(
}
}
let num_pos = if let Some(negated_pos) = negated {
let num_pos = negated.map_or(start_pos, |negated_pos| {
result.insert(0, '-');
negated_pos
} else {
start_pos
};
});
// Parse number
return Some((
@ -1718,9 +1725,7 @@ fn get_next_token_inner(
pos.new_line();
break;
}
if let Some(ref mut comment) = comment {
comment.push(c);
}
comment.as_mut().map(|comment| comment.push(c));
pos.advance();
}
@ -1749,7 +1754,7 @@ fn get_next_token_inner(
};
state.comment_level =
scan_block_comment(stream, state.comment_level, pos, &mut comment);
scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
if let Some(comment) = comment {
return Some((Token::Comment(comment), start_pos));
@ -2179,8 +2184,8 @@ impl<'a> Iterator for TokenIterator<'a> {
};
// Run the mapper, if any
let token = if let Some(map) = self.map {
map(token)
let token = if let Some(map_func) = self.map {
map_func(token)
} else {
token
};

View File

@ -10,7 +10,7 @@ use std::{
cmp::Ordering,
fmt,
hash::{BuildHasher, Hash, Hasher},
iter::FromIterator,
iter::{empty, FromIterator},
ops::{Add, AddAssign, Deref, Sub, SubAssign},
str::FromStr,
};
@ -58,9 +58,8 @@ pub fn get_hasher() -> ahash::AHasher {
Default::default()
}
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a namespace-qualified function name
/// Calculate a [`u64`] hash key from a namespace-qualified function name
/// and the number of parameters, but no parameter types.
/// Exported under the `internals` feature only.
///
/// Module names are passed in via `&str` references from an iterator.
/// Parameter types are passed in via [`TypeId`] values from an iterator.
@ -68,8 +67,8 @@ pub fn get_hasher() -> ahash::AHasher {
/// # Note
///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline(always)]
pub fn calc_fn_hash<'a>(
#[inline]
pub fn calc_qualified_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>,
num: usize,
@ -88,11 +87,19 @@ pub fn calc_fn_hash<'a>(
s.finish()
}
/// _(INTERNALS)_ Calculate a [`u64`] hash key from a list of parameter types.
/// Exported under the `internals` feature only.
/// Calculate a [`u64`] hash key from a non-namespace-qualified function name
/// and the number of parameters, but no parameter types.
///
/// Parameter types are passed in via [`TypeId`] values from an iterator.
#[inline(always)]
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
calc_qualified_fn_hash(empty(), fn_name, num)
}
/// Calculate a [`u64`] hash key from a list of parameter types.
///
/// Parameter types are passed in via [`TypeId`] values from an iterator.
#[inline]
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
let s = &mut get_hasher();
let mut len = 0;

View File

@ -1,6 +1,6 @@
#![cfg(not(feature = "no_object"))]
use rhai::{Engine, EvalAltResult, ImmutableString, INT};
use rhai::{Engine, EvalAltResult, INT};
#[test]
fn test_get_set() -> Result<(), Box<EvalAltResult>> {
@ -46,21 +46,26 @@ fn test_get_set() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.x.add(); a.x")?, 42);
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.y.add(); a.y")?, 0);
#[cfg(not(feature = "no_index"))]
{
engine.register_indexer_get_set(
|value: &mut TestStruct, index: ImmutableString| value.array[index.len()],
|value: &mut TestStruct, index: ImmutableString, new_val: INT| {
value.array[index.len()] = new_val
},
);
engine.register_indexer_get_set(
|value: &mut TestStruct, index: &str| value.array[index.len()],
|value: &mut TestStruct, index: &str, new_val: INT| value.array[index.len()] = new_val,
);
#[cfg(not(feature = "no_index"))]
assert_eq!(engine.eval::<INT>(r#"let a = new_ts(); a["abc"]"#)?, 4);
#[cfg(not(feature = "no_index"))]
assert_eq!(
engine.eval::<INT>(r#"let a = new_ts(); a["abc"] = 42; a["abc"]"#)?,
42
);
assert_eq!(engine.eval::<INT>(r"let a = new_ts(); a.abc")?, 4);
assert_eq!(
engine.eval::<INT>(r"let a = new_ts(); a.abc = 42; a.abc")?,
42
);
assert_eq!(engine.eval::<INT>(r#"let a = new_ts(); a["abc"]"#)?, 4);
assert_eq!(
engine.eval::<INT>(r#"let a = new_ts(); a["abc"] = 42; a["abc"]"#)?,
42
);
}
Ok(())
}

View File

@ -59,21 +59,21 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
assert_eq!(
format!("{:?}", ast),
"AST { source: None, body: [Expr(123 @ 1:53)], functions: Module, resolver: None }"
"AST { source: None, body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }"
);
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
assert_eq!(
format!("{:?}", ast),
r#"AST { source: None, body: [Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
r#"AST { source: None, body: Block[Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
);
let ast = engine.compile("if 1 == 2 { 42 }")?;
assert_eq!(
format!("{:?}", ast),
"AST { source: None, body: [], functions: Module, resolver: None }"
"AST { source: None, body: Block[], functions: Module, resolver: None }"
);
engine.set_optimization_level(OptimizationLevel::Full);
@ -82,7 +82,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
assert_eq!(
format!("{:?}", ast),
"AST { source: None, body: [Expr(42 @ 1:1)], functions: Module, resolver: None }"
"AST { source: None, body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }"
);
Ok(())

View File

@ -61,7 +61,7 @@ fn test_scope_eval() -> Result<(), Box<EvalAltResult>> {
// First invocation
engine
.eval_with_scope::<()>(&mut scope, " let x = 4 + 5 - y + z; y = 1;")
.expect("y and z not found?");
.expect("variables y and z should exist");
// Second invocation using the same state
let result = engine.eval_with_scope::<INT>(&mut scope, "x")?;