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 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 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 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. * `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. * 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). * 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 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. * 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`. * `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`. * `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 Enhancements
------------ ------------

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,7 +29,7 @@ use std::{
}; };
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
use crate::{calc_fn_hash, Array}; use crate::Array;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::Map; use crate::Map;
@ -224,9 +224,9 @@ pub const KEYWORD_GLOBAL: &str = "global";
pub const FN_GET: &str = "get$"; pub const FN_GET: &str = "get$";
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub const FN_SET: &str = "set$"; 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$"; 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$"; pub const FN_IDX_SET: &str = "index$set$";
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub const FN_ANONYMOUS: &str = "anon$"; pub const FN_ANONYMOUS: &str = "anon$";
@ -429,15 +429,15 @@ impl<'a> Target<'a> {
/// This has no effect except for string indexing. /// This has no effect except for string indexing.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline(always)]
pub fn propagate_changed_value(&mut self) { pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> {
match self { match self {
Self::Ref(_) | Self::Value(_) => (), Self::Ref(_) | Self::Value(_) => Ok(()),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::LockGuard(_) => (), Self::LockGuard(_) => Ok(()),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::StringChar(_, _, ch) => { Self::StringChar(_, _, ch) => {
let char_value = ch.clone(); 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"), Self::Value(_) => panic!("cannot update a value"),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::StringChar(s, 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 // Replace the character at the specified index position
let new_ch = new_val.as_char().map_err(|err| { 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() { if value.is_shared() {
// Cloning is cheap for a shared value // Cloning is cheap for a shared value
let container = value.clone(); 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) Self::Ref(value)
@ -598,9 +605,12 @@ impl State {
#[inline(always)] #[inline(always)]
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
if self.fn_resolution_caches.0.is_empty() { 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.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. /// Push an empty function resolution cache onto the stack and make it current.
#[allow(dead_code)] #[allow(dead_code)]
@ -617,7 +627,11 @@ impl State {
/// Panics if there are no more function resolution cache in the stack. /// Panics if there are no more function resolution cache in the stack.
#[inline(always)] #[inline(always)]
pub fn pop_fn_resolution_cache(&mut self) { 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(); cache.clear();
self.fn_resolution_caches.1.push(cache); self.fn_resolution_caches.1.push(cache);
} }
@ -899,11 +913,7 @@ impl Engine {
progress: None, progress: None,
// optimization level // optimization level
optimization_level: if cfg!(feature = "no_optimize") { optimization_level: Default::default(),
OptimizationLevel::None
} else {
OptimizationLevel::Simple
},
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
limits: Limits { limits: Limits {
@ -956,11 +966,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
progress: None, progress: None,
optimization_level: if cfg!(feature = "no_optimize") { optimization_level: Default::default(),
OptimizationLevel::None
} else {
OptimizationLevel::Simple
},
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
limits: Limits { limits: Limits {
@ -1004,10 +1010,16 @@ impl Engine {
if let Some(index) = index { if let Some(index) = index {
let offset = mods.len() - index.get(); 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 { } else {
mods.find(root) 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()) .or_else(|| self.global_sub_modules.get(root).cloned())
} }
} }
@ -1059,6 +1071,10 @@ impl Engine {
} }
/// Search for a variable within the scope /// Search for a variable within the scope
///
/// # Panics
///
/// Panics if `expr` is not [`Expr::Variable`].
pub(crate) fn search_scope_only<'s>( pub(crate) fn search_scope_only<'s>(
&self, &self,
scope: &'s mut Scope, scope: &'s mut Scope,
@ -1096,9 +1112,13 @@ impl Engine {
this_ptr, this_ptr,
level: 0, level: 0,
}; };
if let Some(mut result) = if let Some(mut result) = resolve_var(
resolve_var(expr.get_variable_name(true).unwrap(), index, &context) expr.get_variable_name(true)
.map_err(|err| err.fill_position(var_pos))? .expect("`expr` should be `Variable`"),
index,
&context,
)
.map_err(|err| err.fill_position(var_pos))?
{ {
result.set_access_mode(AccessMode::ReadOnly); result.set_access_mode(AccessMode::ReadOnly);
return Ok((result.into(), var_pos)); return Ok((result.into(), var_pos));
@ -1109,7 +1129,9 @@ impl Engine {
scope.len() - index scope.len() - index
} else { } else {
// Find the variable in the scope // 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 scope
.get_index(var_name) .get_index(var_name)
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))? .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
@ -1138,18 +1160,22 @@ impl Engine {
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> 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 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 // 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 { match chain_type {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -1162,9 +1188,9 @@ impl Engine {
let idx_pos = x.lhs.position(); let idx_pos = x.lhs.position();
let idx_val = idx_val.as_index_value(); let idx_val = idx_val.as_index_value();
let obj_ptr = &mut self.get_indexed_mut( 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( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values, mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
@ -1174,69 +1200,48 @@ impl Engine {
} }
// xxx[rhs] op= new_val // xxx[rhs] op= new_val
_ if new_val.is_some() => { _ 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(); let idx_val = idx_val.as_index_value();
#[cfg(not(feature = "no_index"))] #[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 match self.get_indexed_mut(
let _call_setter = match self.get_indexed_mut( mods, state, lib, target, idx_val, pos, true, false, level,
mods, state, lib, target, idx_val, pos, true, is_ref, false, level,
) { ) {
// Indexed value is a reference - update directly // Indexed value is a reference - update directly
Ok(obj_ptr) => { Ok(obj_ptr) => {
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
self.eval_op_assignment( self.eval_op_assignment(
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, mods, state, lib, op_info, op_pos, obj_ptr, root, new_val,
new_pos, new_pos,
)?; )?;
None return Ok((Dynamic::UNIT, true));
} }
Err(err) => match *err { // Can't index - try to call an index setter
// No index getter - try to call an index setter #[cfg(not(feature = "no_index"))]
#[cfg(not(feature = "no_index"))] Err(err) if matches!(*err, EvalAltResult::ErrorIndexingType(_, _)) => {}
EvalAltResult::ErrorIndexingType(_, _) => Some(new_val.unwrap()), // Any other error
// Any other error - return Err(err) => return Err(err),
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,
})?;
} }
// 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)) Ok((Dynamic::UNIT, true))
} }
// xxx[rhs] // xxx[rhs]
_ => { _ => {
let idx_val = idx_val.as_index_value(); let idx_val = idx_val.as_index_value();
self.get_indexed_mut( 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)) .map(|v| (v.take_or_clone(), false))
} }
@ -1264,12 +1269,13 @@ impl Engine {
} }
// {xxx:map}.id op= ??? // {xxx:map}.id op= ???
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => { 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 index = name.into();
let val = self.get_indexed_mut( 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( self.eval_op_assignment(
mods, state, lib, op_info, op_pos, val, root, new_val, new_pos, mods, state, lib, op_info, op_pos, val, root, new_val, new_pos,
)?; )?;
@ -1277,27 +1283,46 @@ impl Engine {
} }
// {xxx:map}.id // {xxx:map}.id
Expr::Property(x) if target.is::<Map>() => { Expr::Property(x) if target.is::<Map>() => {
let Ident { name, pos, .. } = &x.2; let (name, pos) = &x.2;
let index = name.into(); let index = name.into();
let val = self.get_indexed_mut( 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)) Ok((val.take_or_clone(), false))
} }
// xxx.id op= ??? // xxx.id op= ???
Expr::Property(x) if new_val.is_some() => { Expr::Property(x) if new_val.is_some() => {
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
x.as_ref(); let ((mut new_val, new_pos), (op_info, op_pos)) =
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap(); new_val.expect("never fails because `new_val` is `Some`");
if op_info.is_some() { if op_info.is_some() {
let hash = FnCallHashes::from_native(*hash_get); let hash = FnCallHashes::from_native(*hash_get);
let mut args = [target.as_mut()]; let mut args = [target.as_mut()];
let (mut orig_val, _) = self.exec_fn_call( let (mut orig_val, _) = self
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, .exec_fn_call(
None, level, 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(); let obj_ptr = (&mut orig_val).into();
self.eval_op_assignment( self.eval_op_assignment(
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, new_pos, 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, mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
level, 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 // xxx.id
Expr::Property(x) => { 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 hash = FnCallHashes::from_native(*hash_get);
let mut args = [target.as_mut()]; let mut args = [target.as_mut()];
self.exec_fn_call( self.exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None, mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
level, 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 // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
let mut val = match &x.lhs { let mut val = match &x.lhs {
Expr::Property(p) => { Expr::Property(p) => {
let Ident { name, pos, .. } = &p.2; let (name, pos) = &p.2;
let index = name.into(); let index = name.into();
self.get_indexed_mut( self.get_indexed_mut(
mods, state, lib, target, index, *pos, false, is_ref, true, mods, state, lib, target, index, *pos, false, true, level,
level,
)? )?
} }
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr // {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 // Others - syntax error
expr => unreachable!("invalid dot expression: {:?}", expr), 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( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values, mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values,
@ -1364,17 +1426,36 @@ impl Engine {
match &x.lhs { match &x.lhs {
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => { Expr::Property(p) => {
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = let ((getter, hash_get), (setter, hash_set), (name, pos)) =
p.as_ref(); 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_get = FnCallHashes::from_native(*hash_get);
let hash_set = FnCallHashes::from_native(*hash_set); 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 args = &mut arg_values[..1];
let (mut val, updated) = self.exec_fn_call( let (mut val, updated) = self
mods, state, lib, getter, hash_get, args, is_ref, true, *pos, .exec_fn_call(
None, level, 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; let val = &mut val;
@ -1397,17 +1478,33 @@ impl Engine {
// Feed the value back via a setter just in case it has been updated // Feed the value back via a setter just in case it has been updated
if updated || may_be_changed { if updated || may_be_changed {
// Re-use args because the first &mut parameter will not be consumed // 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( self.exec_fn_call(
mods, state, lib, setter, hash_set, arg_values, is_ref, mods, state, lib, setter, hash_set, args, is_ref, true,
true, *pos, None, level, *pos, None, level,
) )
.or_else( .or_else(
|err| match *err { |err| match *err {
// If there is no setter, no need to feed it back because // Try an indexer if property does not exist
// the property is read-only
EvalAltResult::ErrorDotExpr(_, _) => { 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), _ => Err(err),
}, },
@ -1419,7 +1516,7 @@ impl Engine {
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f, pos) if !f.is_qualified() => { Expr::FnCall(f, pos) if !f.is_qualified() => {
let FnCallExpr { name, hashes, .. } = f.as_ref(); 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 args = idx_val.as_fn_call_args();
let (mut val, _) = self.make_method_call( let (mut val, _) = self.make_method_call(
mods, state, lib, name, *hashes, target, &mut args, *pos, level, mods, state, lib, name, *hashes, target, &mut args, *pos, level,
@ -1558,7 +1655,7 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => { 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"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
@ -1569,7 +1666,7 @@ impl Engine {
let lhs_val = match lhs { let lhs_val = match lhs {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => { 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"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
@ -1646,16 +1743,15 @@ impl Engine {
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn get_indexed_mut<'t>( fn get_indexed_mut<'t>(
&self, &self,
_mods: &mut Imports, mods: &mut Imports,
state: &mut State, state: &mut State,
_lib: &[&Module], lib: &[&Module],
target: &'t mut Dynamic, target: &'t mut Dynamic,
mut _idx: Dynamic, mut idx: Dynamic,
idx_pos: Position, idx_pos: Position,
_create: bool, _create: bool,
_is_ref: bool, indexers: bool,
_indexers: bool, level: usize,
_level: usize,
) -> Result<Target<'t>, Box<EvalAltResult>> { ) -> Result<Target<'t>, Box<EvalAltResult>> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(state, Position::NONE)?; self.inc_operations(state, Position::NONE)?;
@ -1664,7 +1760,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(arr, _, _)) => { Dynamic(Union::Array(arr, _, _)) => {
// val_array[idx] // val_array[idx]
let index = _idx let index = idx
.as_int() .as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?; .map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
@ -1706,8 +1802,8 @@ impl Engine {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map, _, _)) => { Dynamic(Union::Map(map, _, _)) => {
// val_map[idx] // val_map[idx]
let index = &*_idx.read_lock::<ImmutableString>().ok_or_else(|| { let index = &*idx.read_lock::<ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<ImmutableString>(_idx.type_name(), idx_pos) self.make_type_mismatch_err::<ImmutableString>(idx.type_name(), idx_pos)
})?; })?;
if _create && !map.contains_key(index.as_str()) { if _create && !map.contains_key(index.as_str()) {
@ -1723,7 +1819,7 @@ impl Engine {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Str(s, _, _)) => { Dynamic(Union::Str(s, _, _)) => {
// val_string[idx] // val_string[idx]
let index = _idx let index = idx
.as_int() .as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?; .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())) Ok(Target::StringChar(target, offset, ch.into()))
} }
#[cfg(not(feature = "no_index"))] _ if indexers => {
_ if _indexers => { let args = &mut [target, &mut idx];
let type_name = target.type_name(); let hash_get = FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_GET, 2));
let args = &mut [target, &mut _idx];
let hash_get =
FnCallHashes::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2));
self.exec_fn_call( self.exec_fn_call(
_mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None, mods, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, level,
_level,
) )
.map(|(v, _)| v.into()) .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( _ => EvalAltResult::ErrorIndexingType(
@ -1878,7 +1961,10 @@ impl Engine {
Expr::Map(x, _) => { Expr::Map(x, _) => {
let mut map = x.1.clone(); let mut map = x.1.clone();
for (Ident { name: key, .. }, expr) in &x.0 { 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)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.flatten(); .flatten();
} }
@ -1895,7 +1981,9 @@ impl Engine {
literal_args: c_args, literal_args: c_args,
.. ..
} = x.as_ref(); } = 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(); let hash = hashes.native_hash();
self.make_qualified_function_call( self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
@ -1954,10 +2042,12 @@ impl Engine {
.iter() .iter()
.map(Into::into) .map(Into::into)
.collect::<StaticVec<_>>(); .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 let custom_def = self
.custom_syntax .custom_syntax.get(key_token)
.get(custom.tokens.first().unwrap()) .expect("never fails because the custom syntax leading token should match with definition");
.unwrap();
let mut context = EvalContext { let mut context = EvalContext {
engine: self, engine: self,
scope, scope,
@ -2088,7 +2178,9 @@ impl Engine {
let target_is_shared = false; let target_is_shared = false;
if target_is_shared { 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; lhs_ptr_inner = &mut *lock_guard;
} else { } else {
lhs_ptr_inner = &mut *target; lhs_ptr_inner = &mut *target;
@ -2158,12 +2250,13 @@ impl Engine {
let (lhs_ptr, pos) = let (lhs_ptr, pos) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?; 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() { if !lhs_ptr.is_ref() {
return EvalAltResult::ErrorAssignmentToConstant( return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
lhs_expr.get_variable_name(false).unwrap().to_string(), .into();
pos,
)
.into();
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -2176,7 +2269,7 @@ impl Engine {
op_info.clone(), op_info.clone(),
*op_pos, *op_pos,
lhs_ptr, lhs_ptr,
(lhs_expr.get_variable_name(false).unwrap(), pos), (var_name, pos),
rhs_val, rhs_val,
rhs_expr.position(), rhs_expr.position(),
)?; )?;
@ -2401,7 +2494,10 @@ impl Engine {
let loop_var_is_shared = false; let loop_var_is_shared = false;
if loop_var_is_shared { 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 { } else {
*loop_var = value; *loop_var = value;
} }
@ -2451,7 +2547,9 @@ impl Engine {
literal_args: c_args, literal_args: c_args,
.. ..
} = x.as_ref(); } = 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(); let hash = hashes.native_hash();
self.make_qualified_function_call( self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, 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()); err_map.insert("message".into(), err.to_string().into());
if let Some(ref source) = state.source { state
err_map.insert("source".into(), source.into()); .source
} .as_ref()
.map(|source| err_map.insert("source".into(), source.into()));
if err_pos.is_none() { if err_pos.is_none() {
// No position info // No position info
} else { } else {
err_map.insert( let line = err_pos.line().expect("never fails because a non-NONE `Position` always has a line number") as INT;
"line".into(), let position = if err_pos.is_beginning_of_line() {
(err_pos.line().unwrap() as INT).into(), 0
); } else {
err_map.insert( err_pos.position().expect("never fails because a non-NONE `Position` always has a character position")
"position".into(), } as INT;
if err_pos.is_beginning_of_line() { err_map.insert("line".into(), line.into());
0 err_map.insert("position".into(), position.into());
} else {
err_pos.position().unwrap() as INT
}
.into(),
);
} }
err.dump_fields(&mut err_map); err.dump_fields(&mut err_map);
@ -2535,9 +2629,9 @@ impl Engine {
let orig_scope_len = scope.len(); let orig_scope_len = scope.len();
state.scope_level += 1; state.scope_level += 1;
if let Some(Ident { name, .. }) = err_var { err_var.as_ref().map(|Ident { name, .. }| {
scope.push(unsafe_cast_var_name_to_lifetime(&name), err_value); scope.push(unsafe_cast_var_name_to_lifetime(name), err_value)
} });
let result = self.eval_stmt_block( let result = self.eval_stmt_block(
scope, mods, state, lib, this_ptr, catch_stmt, true, level, scope, mods, state, lib, this_ptr, catch_stmt, true, level,
@ -2604,7 +2698,10 @@ impl Engine {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) { if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) { 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), m if m.internal => Some(m),
_ => None, _ => None,
} }
@ -2613,12 +2710,16 @@ impl Engine {
let mut global = Module::new(); let mut global = Module::new();
global.internal = true; global.internal = true;
mods.push(KEYWORD_GLOBAL, global); 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 { if let Some(global) = global {
let global = Shared::get_mut(global).unwrap(); Shared::get_mut(global)
global.set_var(name.clone(), value.clone()); .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); scope.push_dynamic_value(var_name, entry_type, value);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some(alias) = _alias { _alias.map(|alias| scope.add_entry_alias(scope.len() - 1, alias));
scope.add_entry_alias(scope.len() - 1, alias);
}
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
@ -2673,7 +2773,7 @@ impl Engine {
self.module_resolver.resolve(self, source, &path, expr_pos) 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() { if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not 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); let mut module = crate::fn_native::shared_take_or_clone(module);
@ -2682,7 +2782,7 @@ impl Engine {
} else { } else {
mods.push(name, module); mods.push(name, module);
} }
} });
state.modules += 1; state.modules += 1;
@ -2710,14 +2810,14 @@ impl Engine {
// Share statement // Share statement
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Stmt::Share(name) => { 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); let val = scope.get_mut_by_index(index);
if !val.is_shared() { if !val.is_shared() {
// Replace the variable with a shared value. // Replace the variable with a shared value.
*val = std::mem::take(val).into_shared(); *val = std::mem::take(val).into_shared();
} }
} });
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
}; };
@ -2732,9 +2832,10 @@ impl Engine {
/// Check a result to ensure that the data size is within allowable limit. /// Check a result to ensure that the data size is within allowable limit.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> { fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> {
if result.is_err() { let result = match result {
return Ok(()); Err(_) => return Ok(()),
} Ok(r) => r,
};
// If no data size limits, just return // If no data size limits, just return
let mut _has_limit = self.limits.max_string_size.is_some(); 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 if s > self
.limits .limits
@ -2860,7 +2961,7 @@ impl Engine {
} }
// Report progress - only in steps // 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) { if let Some(token) = progress(state.operations) {
// Terminate script if progress returns a termination token // Terminate script if progress returns a termination token
return EvalAltResult::ErrorTerminated(token, pos).into(); 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, set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
use crate::engine::make_setter; use crate::engine::make_setter;
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: V| { self.register_result_fn(&make_setter(name), set_fn)
set_fn(obj, value)
})
} }
/// Short-hand for registering both getter and setter functions /// Short-hand for registering both getter and setter functions
/// of a registered type with the [`Engine`]. /// of a registered type with the [`Engine`].
@ -536,7 +534,7 @@ impl Engine {
/// ///
/// The function signature must start with `&mut self` and not `&self`. /// 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 /// # Panics
/// ///
@ -572,16 +570,18 @@ impl Engine {
/// // Register an indexer. /// // Register an indexer.
/// .register_indexer_get(TestStruct::get_field); /// .register_indexer_get(TestStruct::get_field);
/// ///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3); /// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_index"))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)] #[inline(always)]
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>( pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
&mut self, &mut self,
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
} }
@ -602,7 +602,7 @@ impl Engine {
/// ///
/// The function signature must start with `&mut self` and not `&self`. /// 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 /// # Panics
/// ///
@ -640,11 +640,12 @@ impl Engine {
/// // Register an indexer. /// // Register an indexer.
/// .register_indexer_get_result(TestStruct::get_field); /// .register_indexer_get_result(TestStruct::get_field);
/// ///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3); /// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_index"))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)] #[inline(always)]
pub fn register_indexer_get_result< pub fn register_indexer_get_result<
T: Variant + Clone, T: Variant + Clone,
@ -654,6 +655,7 @@ impl Engine {
&mut self, &mut self,
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static, get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
} }
@ -672,7 +674,7 @@ impl Engine {
} }
/// Register an index setter for a custom type with the [`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 /// # Panics
/// ///
@ -707,6 +709,7 @@ impl Engine {
/// // Register an indexer. /// // Register an indexer.
/// .register_indexer_set(TestStruct::set_field); /// .register_indexer_set(TestStruct::set_field);
/// ///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!( /// assert_eq!(
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2], /// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
/// 42 /// 42
@ -714,12 +717,13 @@ impl Engine {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_index"))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)] #[inline(always)]
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>( pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
&mut self, &mut self,
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static, set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
} }
@ -738,7 +742,7 @@ impl Engine {
} }
/// Register an index setter for a custom type with the [`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 /// # Panics
/// ///
@ -776,6 +780,7 @@ impl Engine {
/// // Register an indexer. /// // Register an indexer.
/// .register_indexer_set_result(TestStruct::set_field); /// .register_indexer_set_result(TestStruct::set_field);
/// ///
/// # #[cfg(not(feature = "no_index"))]
/// assert_eq!( /// assert_eq!(
/// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2], /// engine.eval::<TestStruct>("let a = new_ts(); a[2] = 42; a")?.fields[2],
/// 42 /// 42
@ -783,7 +788,7 @@ impl Engine {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_index"))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)] #[inline(always)]
pub fn register_indexer_set_result< pub fn register_indexer_set_result<
T: Variant + Clone, T: Variant + Clone,
@ -793,6 +798,7 @@ impl Engine {
&mut self, &mut self,
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
#[cfg(not(feature = "no_index"))]
if TypeId::of::<T>() == TypeId::of::<Array>() { if TypeId::of::<T>() == TypeId::of::<Array>() {
panic!("Cannot register indexer for arrays."); panic!("Cannot register indexer for arrays.");
} }
@ -807,14 +813,11 @@ impl Engine {
panic!("Cannot register indexer for strings."); panic!("Cannot register indexer for strings.");
} }
self.register_result_fn( self.register_result_fn(crate::engine::FN_IDX_SET, set_fn)
crate::engine::FN_IDX_SET,
move |obj: &mut T, index: X, value: V| set_fn(obj, index, value),
)
} }
/// 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 /// # Panics
/// ///
@ -852,11 +855,12 @@ impl Engine {
/// // Register an indexer. /// // Register an indexer.
/// .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field); /// .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); /// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?, 42);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_index"))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[inline(always)] #[inline(always)]
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>( pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
&mut self, &mut self,
@ -947,8 +951,14 @@ impl Engine {
} }
} else { } else {
let mut iter = name.as_ref().splitn(2, separator.as_ref()); let mut iter = name.as_ref().splitn(2, separator.as_ref());
let sub_module = iter.next().unwrap().trim(); let sub_module = iter
let remainder = iter.next().unwrap().trim(); .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) { if !root.contains_key(sub_module) {
let mut m: Module = Default::default(); let mut m: Module = Default::default();
@ -956,7 +966,9 @@ impl Engine {
m.build_index(); m.build_index();
root.insert(sub_module.into(), m.into()); root.insert(sub_module.into(), m.into());
} else { } 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); let mut m = crate::fn_native::shared_take_or_clone(m);
register_static_module_raw(m.sub_modules_mut(), remainder, module); register_static_module_raw(m.sub_modules_mut(), remainder, module);
m.build_index(); m.build_index();
@ -1074,7 +1086,10 @@ impl Engine {
resolver: &StaticModuleResolver, resolver: &StaticModuleResolver,
imports: &mut BTreeSet<Identifier>, 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 // Collect all `import` statements with a string constant path
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _)) ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
if !resolver.contains_path(s) && !imports.contains(s.as_str()) => if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>

View File

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

View File

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

View File

@ -13,7 +13,7 @@ use std::prelude::v1::*;
use std::{ use std::{
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
fmt, fmt,
iter::{empty, once}, iter::once,
mem, mem,
}; };
@ -202,11 +202,11 @@ impl<'a> NativeCallContext<'a> {
let hash = if is_method { let hash = if is_method {
FnCallHashes::from_script_and_native( FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), fn_name, args.len() - 1), calc_fn_hash(fn_name, args.len() - 1),
calc_fn_hash(empty(), fn_name, args.len()), calc_fn_hash(fn_name, args.len()),
) )
} else { } else {
FnCallHashes::from_script(calc_fn_hash(empty(), fn_name, args.len())) FnCallHashes::from_script(calc_fn_hash(fn_name, args.len()))
}; };
self.engine() 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). /// Panics if the resource is shared (i.e. has other outstanding references).
#[inline(always)] #[inline(always)]
pub fn shared_take<T>(value: Shared<T>) -> T { 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]. /// 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)] #[inline(always)]
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> { pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data. // 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. /// Dereference into value.
@ -43,12 +44,15 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
data.flatten_in_place(); data.flatten_in_place();
let ref_str = data let ref_str = data
.as_str_ref() .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) }; let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
ref_t.clone() ref_t.clone()
} else if TypeId::of::<T>() == TypeId::of::<String>() { } else if TypeId::of::<T>() == TypeId::of::<String>() {
// If T is `String`, data must be `ImmutableString`, so map directly to it // 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 { } else {
// We consume the argument and then replace it with () - the argument is not supposed to be used again. // 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. // 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! // The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut(); 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 // Call the function with each argument value
let r = self($($arg),*); let r = self($($arg),*);
@ -146,7 +150,7 @@ macro_rules! def_register {
// The arguments are assumed to be of the correct number and types! // The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut(); 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 // Call the function with each argument value
let r = self(ctx, $($arg),*); 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! // The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut(); 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 // Call the function with each argument value
self($($arg),*).map(Dynamic::from) 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! // The arguments are assumed to be of the correct number and types!
let mut _drain = args.iter_mut(); 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 // Call the function with each argument value
self(ctx, $($arg),*).map(Dynamic::from) self(ctx, $($arg),*).map(Dynamic::from)

View File

@ -169,7 +169,7 @@ pub use fn_native::Shared;
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
use fn_native::Locked; 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::*; pub use rhai_codegen::*;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -311,21 +311,14 @@ mod decimal_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { x.sqrt()
x.sqrt() .ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
} else {
Ok(x.sqrt().unwrap())
}
} }
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn exp(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> { pub fn exp(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if x > Decimal::from_parts(117578, 0, 0, false, 4) { x.checked_exp()
Err(make_err(format!("Exponential overflow: e ** {}", x,))) .ok_or_else(|| make_err(format!("Exponential overflow: e ** {}", x,)))
} else {
Ok(x.exp())
}
} else { } else {
Ok(x.exp()) Ok(x.exp())
} }
@ -348,15 +341,15 @@ mod decimal_functions {
#[rhai_fn(name = "round", return_raw)] #[rhai_fn(name = "round", return_raw)]
pub fn round_dp(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_dp(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
"Decimal value {} round to a negative index: {}", "Invalid number of digits for rounding: {}",
x, dp dp
))); )));
} }
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
} }
Ok(x.round_dp(dp as u32)) Ok(x.round_dp(dp as u32))
@ -364,15 +357,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
"Decimal value {} round to a negative index: {}", "Invalid number of digits for rounding: {}",
x, dp 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)) Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::AwayFromZero))
@ -380,15 +373,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
"Decimal value {} round to a negative index: {}", "Invalid number of digits for rounding: {}",
x, dp 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)) Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::ToZero))
@ -396,15 +389,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_half_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_half_up(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
"Decimal value {} round to a negative index: {}", "Invalid number of digits for rounding: {}",
x, dp 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)) Ok(x.round_dp_with_strategy(dp as u32, RoundingStrategy::MidpointAwayFromZero))
@ -412,15 +405,15 @@ mod decimal_functions {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn round_half_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> { pub fn round_half_down(x: Decimal, dp: INT) -> Result<Decimal, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
if cfg!(not(feature = "only_i32")) && dp > (u32::MAX as INT) {
return Ok(x);
}
if dp < 0 { if dp < 0 {
return Err(make_err(format!( return Err(make_err(format!(
"Decimal value {} round to a negative index: {}", "Invalid number of digits for rounding: {}",
x, dp 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)) 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, value: &mut Dynamic,
) -> crate::ImmutableString { ) -> crate::ImmutableString {
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) { match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
Ok(result) if result.is::<crate::ImmutableString>() => { Ok(result) if result.is::<crate::ImmutableString>() => result
result.take_immutable_string().unwrap() .take_immutable_string()
} .expect("never fails as the result is `ImmutableString`"),
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(), Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
Err(_) => ctx.engine().map_type_name(value.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::ErrorTerminated(_, _) => f.write_str(desc)?,
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => { 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 })? write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
} }
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?, 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) { pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
map.insert( map.insert(
"error".into(), "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 { match self {

View File

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

View File

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

View File

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

View File

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

View File

@ -59,21 +59,21 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
format!("{:?}", ast), 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 }")?; let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
assert_eq!( assert_eq!(
format!("{:?}", ast), 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 }")?; let ast = engine.compile("if 1 == 2 { 42 }")?;
assert_eq!( assert_eq!(
format!("{:?}", ast), 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); engine.set_optimization_level(OptimizationLevel::Full);
@ -82,7 +82,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
format!("{:?}", ast), 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(()) Ok(())

View File

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