From 3f6db1d9d4dcb11155b6fef9b7ddd6026fd9ddae Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 4 Nov 2020 20:20:55 +0800 Subject: [PATCH 1/5] Remove extra export of LexError. --- src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 71fbf3cf..e8019d24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,11 +161,6 @@ pub mod serde { pub use optimize::OptimizationLevel; // Expose internal data structures. - -#[cfg(feature = "internals")] -#[deprecated(note = "this type is volatile and may change")] -pub use parse_error::LexError; - #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] pub use token::{get_next_token, parse_string_literal, InputStream, Token, TokenizeState}; From c6ef2d1e958ec77741184162a15b9ef7e05c26fb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 6 Nov 2020 13:41:04 +0800 Subject: [PATCH 2/5] Add test to make sure call_fn does not pollute scope. --- tests/call_fn.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 7e9170b0..7b959472 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -38,6 +38,10 @@ fn test_call_fn() -> Result<(), Box> { fn hello() { 41 + foo } + fn define_var() { + let bar = 21; + bar * 2 + } ", )?; @@ -50,6 +54,11 @@ fn test_call_fn() -> Result<(), Box> { let r: INT = engine.call_fn(&mut scope, &ast, "hello", ())?; assert_eq!(r, 42); + let r: INT = engine.call_fn(&mut scope, &ast, "define_var", ())?; + assert_eq!(r, 42); + + assert!(!scope.contains("bar")); + assert_eq!( scope .get_value::("foo") From 07a8a43298647b4e498d0fa29b37eef2faa1afbf Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 6 Nov 2020 16:27:40 +0800 Subject: [PATCH 3/5] Optimize data structures. --- src/ast.rs | 63 ++++++++++++++++++++++++++++++------------------- src/dynamic.rs | 17 ------------- src/engine.rs | 4 ++-- src/fn_call.rs | 25 +++++++++++--------- src/optimize.rs | 21 ++++++++++------- src/parser.rs | 21 ++++++++++------- src/scope.rs | 27 ++++----------------- src/syntax.rs | 5 ++++ 8 files changed, 88 insertions(+), 95 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 4a31d1f9..25bdace5 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -4,7 +4,7 @@ use crate::dynamic::{Dynamic, Union}; use crate::fn_native::{FnPtr, Shared}; use crate::module::{Module, ModuleRef}; use crate::syntax::FnCustomSyntaxEval; -use crate::token::{Position, Token}; +use crate::token::{Position, Token, NO_POS}; use crate::utils::ImmutableString; use crate::StaticVec; use crate::INT; @@ -93,9 +93,9 @@ pub struct ScriptFnDef { pub access: FnAccess, /// Names of function parameters. pub params: StaticVec, - /// Access to external variables. + /// Access to external variables. Boxed because it occurs rarely. #[cfg(not(feature = "no_closure"))] - pub externals: HashSet, + pub externals: Option>>, } impl fmt::Display for ScriptFnDef { @@ -590,11 +590,12 @@ pub enum ReturnType { Exception, } -/// _[INTERNALS]_ A Rhai statement. +/// _[INTERNALS]_ A statement. /// Exported under the `internals` feature only. /// -/// Each variant is at most one pointer in size (for speed), -/// with everything being allocated together in one single tuple. +/// ## WARNING +/// +/// This type is volatile and may change. #[derive(Debug, Clone, Hash)] pub enum Stmt { /// No-op. @@ -616,7 +617,7 @@ pub enum Stmt { /// { stmt; ... } Block(Vec, Position), /// try { stmt; ... } catch ( var ) { stmt; ... } - TryCatch(Box<(Stmt, Option, Stmt, (Position, Position))>), + TryCatch(Box<(Stmt, Option, Stmt)>, Position, Position), /// expr Expr(Expr), /// continue @@ -639,7 +640,7 @@ pub enum Stmt { impl Default for Stmt { #[inline(always)] fn default() -> Self { - Self::Noop(Default::default()) + Self::Noop(NO_POS) } } @@ -667,7 +668,7 @@ impl Stmt { | Self::ReturnWithVal((_, pos), _, _) => *pos, Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos, - Self::TryCatch(x) => (x.3).0, + Self::TryCatch(_, pos, _) => *pos, Self::Expr(x) => x.position(), @@ -696,7 +697,7 @@ impl Stmt { | Self::ReturnWithVal((_, pos), _, _) => *pos = new_pos, Self::Let(x, _, _) | Self::Const(x, _, _) => x.pos = new_pos, - Self::TryCatch(x) => (x.3).0 = new_pos, + Self::TryCatch(_, pos, _) => *pos = new_pos, Self::Expr(x) => { x.set_position(new_pos); @@ -722,7 +723,7 @@ impl Stmt { | Self::Loop(_, _) | Self::For(_, _, _) | Self::Block(_, _) - | Self::TryCatch(_) => true, + | Self::TryCatch(_, _, _) => true, // A No-op requires a semicolon in order to know it is an empty statement! Self::Noop(_) => false, @@ -758,7 +759,7 @@ impl Stmt { Self::Let(_, _, _) | Self::Const(_, _, _) | Self::Assignment(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Continue(_) | Self::Break(_) | Self::ReturnWithVal(_, _, _) => false, - Self::TryCatch(x) => x.0.is_pure() && x.2.is_pure(), + Self::TryCatch(x, _, _) => x.0.is_pure() && x.2.is_pure(), #[cfg(not(feature = "no_module"))] Self::Import(_, _, _) => false, @@ -779,7 +780,9 @@ impl Stmt { /// This type is volatile and may change. #[derive(Clone)] pub struct CustomExpr { + /// List of keywords. pub(crate) keywords: StaticVec, + /// Implementation function. pub(crate) func: Shared, } @@ -848,6 +851,11 @@ impl From for FloatWrapper { } /// A binary expression structure. +/// Exported under the `internals` feature only. +/// +/// ## WARNING +/// +/// This type is volatile and may change. #[derive(Debug, Clone, Hash)] pub struct BinaryExpr { /// LHS expression. @@ -856,7 +864,12 @@ pub struct BinaryExpr { pub rhs: Expr, } -/// A function call. +/// _[INTERNALS]_ A function call. +/// Exported under the `internals` feature only. +/// +/// ## WARNING +/// +/// This type is volatile and may change. #[derive(Debug, Clone, Hash, Default)] pub struct FnCallInfo { /// Pre-calculated hash for a script-defined function of the same name and number of parameters. @@ -869,7 +882,7 @@ pub struct FnCallInfo { /// Default value when the function is not found, mostly used to provide a default for comparison functions. /// Type is `bool` in order for `FnCallInfo` to be `Hash` pub def_value: Option, - /// Namespace of the function, if any. + /// Namespace of the function, if any. Boxed because it occurs rarely. pub namespace: Option>, /// Function name. /// Use `Cow<'static, str>` because a lot of operators (e.g. `==`, `>=`) are implemented as function calls @@ -882,9 +895,6 @@ pub struct FnCallInfo { /// _[INTERNALS]_ An expression sub-tree. /// Exported under the `internals` feature only. /// -/// Each variant is at most one pointer in size (for speed), -/// with everything being allocated together in one single tuple. -/// /// ## WARNING /// /// This type is volatile and may change. @@ -938,7 +948,7 @@ pub enum Expr { impl Default for Expr { #[inline(always)] fn default() -> Self { - Self::Unit(Default::default()) + Self::Unit(NO_POS) } } @@ -1241,16 +1251,21 @@ impl Expr { #[cfg(test)] mod tests { - use super::*; - /// This test is to make sure no code changes increase the sizes of critical data structures. #[test] fn check_struct_sizes() { use std::mem::size_of; - assert_eq!(size_of::(), 16); - assert_eq!(size_of::>(), 16); - assert_eq!(size_of::(), 16); - assert_eq!(size_of::(), 32); + assert_eq!(size_of::(), 16); + assert_eq!(size_of::>(), 16); + assert_eq!(size_of::(), 4); + assert_eq!(size_of::(), 16); + assert_eq!(size_of::>(), 16); + assert_eq!(size_of::(), 32); + assert_eq!(size_of::>(), 32); + assert_eq!(size_of::(), 72); + assert_eq!(size_of::(), 32); + assert_eq!(size_of::(), 16); + assert_eq!(size_of::(), 64); } } diff --git a/src/dynamic.rs b/src/dynamic.rs index b405fd8a..6b3cc8b1 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -619,7 +619,6 @@ impl Dynamic { Self(Union::Variant(Box::new(boxed))) } - /// Turn the `Dynamic` value into a shared `Dynamic` value backed by an `Rc>` /// or `Arc>` depending on the `sync` feature. /// @@ -644,7 +643,6 @@ impl Dynamic { #[cfg(feature = "no_closure")] panic!("converting into a shared value is not supported under 'no_closure'"); } - /// Convert the `Dynamic` value into specific type. /// /// Casting to a `Dynamic` just returns as is, but if it contains a shared value, @@ -775,7 +773,6 @@ impl Dynamic { _ => None, } } - /// Convert the `Dynamic` value into a specific type. /// /// Casting to a `Dynamic` just returns as is, but if it contains a shared value, @@ -819,7 +816,6 @@ impl Dynamic { ) }) } - /// Flatten the `Dynamic` and clone it. /// /// If the `Dynamic` is not a shared value, it returns a cloned copy. @@ -839,7 +835,6 @@ impl Dynamic { _ => self.clone(), } } - /// Flatten the `Dynamic`. /// /// If the `Dynamic` is not a shared value, it returns itself. @@ -867,7 +862,6 @@ impl Dynamic { _ => self, } } - /// Is the `Dynamic` a shared value that is locked? /// /// ## Note @@ -889,7 +883,6 @@ impl Dynamic { _ => false, } } - /// Get a reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a reference to it. /// @@ -922,7 +915,6 @@ impl Dynamic { .map(|r| DynamicReadLock(DynamicReadLockInner::Reference(r))), } } - /// Get a mutable reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a mutable reference to it. /// @@ -955,7 +947,6 @@ impl Dynamic { .map(|r| DynamicWriteLock(DynamicWriteLockInner::Reference(r))), } } - /// Get a reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a reference to it. /// @@ -1045,7 +1036,6 @@ impl Dynamic { _ => None, } } - /// Get a mutable reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a mutable reference to it. /// @@ -1129,7 +1119,6 @@ impl Dynamic { _ => None, } } - /// Cast the `Dynamic` as the system integer type `INT` and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] @@ -1141,7 +1130,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as the system floating-point type `FLOAT` and return it. /// Returns the name of the actual type if the cast fails. #[cfg(not(feature = "no_float"))] @@ -1154,7 +1142,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a `bool` and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] @@ -1166,7 +1153,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a `char` and return it. /// Returns the name of the actual type if the cast fails. #[inline(always)] @@ -1178,7 +1164,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the `Dynamic` as a string and return the string slice. /// Returns the name of the actual type if the cast fails. /// @@ -1191,7 +1176,6 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Convert the `Dynamic` into `String` and return it. /// If there are other references to the same string, a cloned copy is returned. /// Returns the name of the actual type if the cast fails. @@ -1200,7 +1184,6 @@ impl Dynamic { self.take_immutable_string() .map(ImmutableString::into_owned) } - /// Convert the `Dynamic` into `ImmutableString` and return it. /// Returns the name of the actual type if the cast fails. #[inline] diff --git a/src/engine.rs b/src/engine.rs index bb1f0725..e88cee23 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2034,8 +2034,8 @@ impl Engine { Stmt::Break(pos) => EvalAltResult::LoopBreak(true, *pos).into(), // Try/Catch statement - Stmt::TryCatch(x) => { - let (try_body, var_def, catch_body, _) = x.as_ref(); + Stmt::TryCatch(x, _, _) => { + let (try_body, var_def, catch_body) = x.as_ref(); let result = self .eval_stmt(scope, mods, state, lib, this_ptr, try_body, level) diff --git a/src/fn_call.rs b/src/fn_call.rs index 2979c401..18932715 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -118,7 +118,6 @@ impl<'a> ArgBackup<'a> { mem::transmute(&mut self.value_copy) })); } - /// This function restores the first argument that was replaced by `change_first_arg_to_copy`. /// /// # Safety @@ -541,16 +540,20 @@ impl Engine { // Move captured variables into scope #[cfg(not(feature = "no_closure"))] if let Some(captured) = _capture_scope { - captured - .into_iter() - .filter(|(name, _, _, _)| func.externals.contains(name.as_ref())) - .for_each(|(name, typ, value, _)| { - // Consume the scope values. - match typ { - ScopeEntryType::Normal => scope.push(name, value), - ScopeEntryType::Constant => scope.push_constant(name, value), - }; - }); + if let Some(ref externals) = func.externals { + captured + .into_iter() + .filter(|(name, _, _, _)| externals.contains(name.as_ref())) + .for_each(|(name, typ, value, _)| { + // Consume the scope values. + match typ { + ScopeEntryType::Normal => scope.push(name, value), + ScopeEntryType::Constant => { + scope.push_constant(name, value) + } + }; + }); + } } let result = if _is_method { diff --git a/src/optimize.rs b/src/optimize.rs index 862aa855..4b023627 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -387,7 +387,7 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { } } // try { block } catch ( var ) { block } - Stmt::TryCatch(x) if x.0.is_pure() => { + Stmt::TryCatch(x, _, _) if x.0.is_pure() => { // If try block is pure, there will never be any exceptions state.set_dirty(); let pos = x.0.position(); @@ -399,14 +399,17 @@ fn optimize_stmt(stmt: Stmt, state: &mut State, preserve_result: bool) -> Stmt { Stmt::Block(statements, pos) } // try { block } catch ( var ) { block } - Stmt::TryCatch(x) => { - let (try_block, var_name, catch_block, pos) = *x; - Stmt::TryCatch(Box::new(( - optimize_stmt(try_block, state, false), - var_name, - optimize_stmt(catch_block, state, false), - pos, - ))) + Stmt::TryCatch(x, try_pos, catch_pos) => { + let (try_block, var_name, catch_block) = *x; + Stmt::TryCatch( + Box::new(( + optimize_stmt(try_block, state, false), + var_name, + optimize_stmt(catch_block, state, false), + )), + try_pos, + catch_pos, + ) } // {} Stmt::Expr(Expr::Stmt(x, pos)) if x.is_empty() => { diff --git a/src/parser.rs b/src/parser.rs index 448df46f..f4f08b81 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -32,7 +32,7 @@ use crate::{ use crate::stdlib::{ borrow::Cow, boxed::Box, - collections::HashMap, + collections::{HashMap, HashSet}, format, hash::Hash, iter::empty, @@ -2376,12 +2376,11 @@ fn parse_try_catch( // try { body } catch ( var ) { catch_block } let catch_body = parse_block(input, state, lib, settings.level_up())?; - Ok(Stmt::TryCatch(Box::new(( - body, - var_def, - catch_body, - (token_pos, catch_pos), - )))) + Ok(Stmt::TryCatch( + Box::new((body, var_def, catch_body)), + token_pos, + catch_pos, + )) } /// Parse a function definition. @@ -2470,7 +2469,7 @@ fn parse_fn( let params: StaticVec<_> = params.into_iter().map(|(p, _)| p).collect(); #[cfg(not(feature = "no_closure"))] - let externals = state + let externals: HashSet<_> = state .externals .iter() .map(|(name, _)| name) @@ -2483,7 +2482,11 @@ fn parse_fn( access, params, #[cfg(not(feature = "no_closure"))] - externals, + externals: if externals.is_empty() { + None + } else { + Some(Box::new(externals)) + }, body, lib: None, }) diff --git a/src/scope.rs b/src/scope.rs index 1f2befb3..4282cfae 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -70,8 +70,8 @@ pub struct Scope<'a> { values: Vec, /// Type of the entry. types: Vec, - /// (Name, alias) of the entry. - names: Vec<(Cow<'a, str>, Option)>, + /// (Name, alias) of the entry. The alias is Boxed because it occurs rarely. + names: Vec<(Cow<'a, str>, Option>)>, } impl<'a> Scope<'a> { @@ -91,7 +91,6 @@ impl<'a> Scope<'a> { pub fn new() -> Self { Default::default() } - /// Empty the Scope. /// /// # Example @@ -118,7 +117,6 @@ impl<'a> Scope<'a> { self.values.clear(); self } - /// Get the number of entries inside the Scope. /// /// # Example @@ -136,7 +134,6 @@ impl<'a> Scope<'a> { pub fn len(&self) -> usize { self.values.len() } - /// Is the Scope empty? /// /// # Example @@ -154,7 +151,6 @@ impl<'a> Scope<'a> { pub fn is_empty(&self) -> bool { self.values.len() == 0 } - /// Add (push) a new entry to the Scope. /// /// # Example @@ -175,7 +171,6 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value)) } - /// Add (push) a new `Dynamic` entry to the Scope. /// /// # Example @@ -192,7 +187,6 @@ impl<'a> Scope<'a> { pub fn push_dynamic(&mut self, name: impl Into>, value: Dynamic) -> &mut Self { self.push_dynamic_value(name, EntryType::Normal, value) } - /// Add (push) a new constant to the Scope. /// /// Constants are immutable and cannot be assigned to. Their values never change. @@ -219,7 +213,6 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value)) } - /// Add (push) a new constant with a `Dynamic` value to the Scope. /// /// Constants are immutable and cannot be assigned to. Their values never change. @@ -247,7 +240,6 @@ impl<'a> Scope<'a> { ) -> &mut Self { self.push_dynamic_value(name, EntryType::Constant, value) } - /// Add (push) a new entry with a `Dynamic` value to the Scope. #[inline] pub(crate) fn push_dynamic_value( @@ -261,7 +253,6 @@ impl<'a> Scope<'a> { self.values.push(value.into()); self } - /// Truncate (rewind) the Scope to a previous size. /// /// # Example @@ -295,7 +286,6 @@ impl<'a> Scope<'a> { self.values.truncate(size); self } - /// Does the scope contain the entry? /// /// # Example @@ -316,7 +306,6 @@ impl<'a> Scope<'a> { .rev() // Always search a Scope in reverse order .any(|(key, _)| name == key.as_ref()) } - /// Find an entry in the Scope, starting from the last. #[inline(always)] pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> { @@ -332,7 +321,6 @@ impl<'a> Scope<'a> { } }) } - /// Get the value of an entry in the Scope, starting from the last. /// /// # Example @@ -354,7 +342,6 @@ impl<'a> Scope<'a> { .find(|(_, (key, _))| name == key.as_ref()) .and_then(|(index, _)| self.values[index].flatten_clone().try_cast()) } - /// Update the value of the named entry. /// Search starts backwards from the last, and only the first entry matching the specified name is updated. /// If no entry matching the specified name is found, a new one is added. @@ -389,7 +376,6 @@ impl<'a> Scope<'a> { } self } - /// Get a mutable reference to an entry in the Scope. #[inline(always)] pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) { @@ -398,16 +384,14 @@ impl<'a> Scope<'a> { self.types[index], ) } - /// Update the access type of an entry in the Scope. #[cfg(not(feature = "no_module"))] #[inline(always)] pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) -> &mut Self { let entry = self.names.get_mut(index).expect("invalid index in Scope"); - entry.1 = Some(alias); + entry.1 = Some(Box::new(alias)); self } - /// Clone the Scope, keeping only the last instances of each variable name. /// Shadowed variables are omitted in the copy. #[inline] @@ -428,7 +412,6 @@ impl<'a> Scope<'a> { entries } - /// Get an iterator to entries in the Scope. #[inline(always)] pub(crate) fn into_iter( @@ -437,9 +420,8 @@ impl<'a> Scope<'a> { self.names .into_iter() .zip(self.types.into_iter().zip(self.values.into_iter())) - .map(|((name, alias), (typ, value))| (name, typ, value, alias)) + .map(|((name, alias), (typ, value))| (name, typ, value, alias.map(|v| *v))) } - /// Get an iterator to entries in the Scope. /// Shared values are flatten-cloned. /// @@ -470,7 +452,6 @@ impl<'a> Scope<'a> { self.iter_raw() .map(|(name, constant, value)| (name, constant, value.flatten_clone())) } - /// Get an iterator to entries in the Scope. /// Shared values are not expanded. #[inline(always)] diff --git a/src/syntax.rs b/src/syntax.rs index e03c6a21..4baa5a7a 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -85,9 +85,14 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> { } } +/// Definition of a custom syntax definition. pub struct CustomSyntax { + /// A parsing function to return the next keyword in a custom syntax based on the + /// keywords parsed so far. pub parse: Box, + /// Custom syntax implementation function. pub func: Shared, + /// Delta number of variables in the scope. pub scope_delta: isize, } From 6bc5ba666831886083008f13e05cb310b19eb444 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 6 Nov 2020 19:17:07 +0800 Subject: [PATCH 4/5] Enable functions to use global imports. --- Cargo.toml | 2 +- RELEASES.md | 8 ++ doc/src/context.json | 2 +- doc/src/language/fn-namespaces.md | 3 +- doc/src/language/fn-ptr.md | 1 - src/engine.rs | 125 ++++++++++++------------------ src/fn_call.rs | 17 ++-- src/fn_native.rs | 1 + src/module/mod.rs | 9 +-- 9 files changed, 76 insertions(+), 92 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b4b99a8b..4fc4877a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "rhai" -version = "0.19.4" +version = "0.19.5" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" diff --git a/RELEASES.md b/RELEASES.md index 9c654cdd..0b9659df 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,14 @@ Rhai Release Notes ================== +Version 0.19.5 +============== + +Breaking changes +---------------- + +* Modules imported at global level can now be accessed in functions. + Version 0.19.4 ============== diff --git a/doc/src/context.json b/doc/src/context.json index 7bf82955..597eb0df 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -1,5 +1,5 @@ { - "version": "0.19.4", + "version": "0.19.5", "repoHome": "https://github.com/jonathandturner/rhai/blob/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master", "rootUrl": "", diff --git a/doc/src/language/fn-namespaces.md b/doc/src/language/fn-namespaces.md index 0e3819b2..f4071421 100644 --- a/doc/src/language/fn-namespaces.md +++ b/doc/src/language/fn-namespaces.md @@ -82,8 +82,9 @@ fn get_message() { "Hello!" } | script.rhai | --------------- +import "message" as msg; + fn say_hello() { - import "message" as msg; print(msg::get_message()); } say_hello(); diff --git a/doc/src/language/fn-ptr.md b/doc/src/language/fn-ptr.md index 71d384c4..d10ff4d4 100644 --- a/doc/src/language/fn-ptr.md +++ b/doc/src/language/fn-ptr.md @@ -77,7 +77,6 @@ f::do_work(); // works! let p = Fn("f::do_work"); // error: invalid function name fn do_work_now() { // call it from a local function - import "foo" as f; f::do_work(); } diff --git a/src/engine.rs b/src/engine.rs index e88cee23..8f6429e1 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -88,10 +88,6 @@ impl Imports { pub fn get(&self, index: usize) -> Option<&Module> { self.0.get(index) } - /// Get a mutable reference to the imported module at a particular index. - pub fn get_mut(&mut self, index: usize) -> Option<&mut Module> { - self.0.get_mut(index) - } /// Get the index of an imported module by name. pub fn find(&self, name: &str) -> Option { self.1 @@ -658,35 +654,6 @@ pub fn search_imports<'s>( }) } -/// Search for a module within an imports stack. -/// Position in `EvalAltResult` is `None` and must be set afterwards. -pub fn search_imports_mut<'s>( - mods: &'s mut Imports, - state: &mut State, - modules: &ModuleRef, -) -> Result<&'s mut Module, Box> { - let Ident { name: root, pos } = &modules[0]; - - // Qualified - check if the root module is directly indexed - let index = if state.always_search { - 0 - } else { - modules.index().map_or(0, NonZeroUsize::get) - }; - - Ok(if index > 0 { - let offset = mods.len() - index; - mods.get_mut(offset).expect("invalid index in Imports") - } else { - if let Some(n) = mods.find(root) { - mods.get_mut(n) - } else { - None - } - .ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))? - }) -} - impl Engine { /// Create a new `Engine` #[inline(always)] @@ -803,7 +770,7 @@ impl Engine { pub(crate) fn search_namespace<'s, 'a>( &self, scope: &'s mut Scope, - mods: &'s mut Imports, + mods: &mut Imports, state: &mut State, lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, @@ -813,8 +780,8 @@ impl Engine { Expr::Variable(v) => match v.as_ref() { // Qualified variable (_, Some(modules), hash_var, Ident { name, pos }) => { - let module = search_imports_mut(mods, state, modules)?; - let target = module.get_qualified_var_mut(*hash_var).map_err(|mut err| { + let module = search_imports(mods, state, modules)?; + let target = module.get_qualified_var(*hash_var).map_err(|mut err| { match *err { EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { *err_name = format!("{}{}", modules, name); @@ -825,7 +792,7 @@ impl Engine { })?; // Module variables are constant - Ok((target.into(), name, ScopeEntryType::Constant, *pos)) + Ok((target.clone().into(), name, ScopeEntryType::Constant, *pos)) } // Normal variable access _ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), @@ -909,6 +876,7 @@ impl Engine { #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn eval_dot_index_chain_helper( &self, + mods: &mut Imports, state: &mut State, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, @@ -945,12 +913,12 @@ impl Engine { let idx_pos = x.lhs.position(); let idx_val = idx_val.as_value(); let obj_ptr = &mut self.get_indexed_mut( - state, lib, target, idx_val, idx_pos, false, true, level, + mods, state, lib, target, idx_val, idx_pos, false, true, level, )?; self.eval_dot_index_chain_helper( - state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain, level, - new_val, + mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain, + level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) } @@ -960,9 +928,9 @@ impl Engine { let mut idx_val2 = idx_val.clone(); // `call_setter` is introduced to bypass double mutable borrowing of target - let _call_setter = match self - .get_indexed_mut(state, lib, target, idx_val, pos, true, false, level) - { + let _call_setter = match self.get_indexed_mut( + mods, state, lib, target, idx_val, pos, true, false, level, + ) { // Indexed value is a reference - update directly Ok(ref mut obj_ptr) => { obj_ptr.set_value(new_val.unwrap())?; @@ -984,8 +952,8 @@ impl Engine { let args = &mut [val, &mut idx_val2, &mut new_val.0]; self.exec_fn_call( - state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None, None, - level, + mods, state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None, + None, level, ) .map_err(|err| match *err { EvalAltResult::ErrorFunctionNotFound(fn_sig, _) @@ -1005,8 +973,10 @@ impl Engine { // xxx[rhs] _ => { let idx_val = idx_val.as_value(); - self.get_indexed_mut(state, lib, target, idx_val, pos, false, true, level) - .map(|v| (v.take_or_clone(), false)) + self.get_indexed_mut( + mods, state, lib, target, idx_val, pos, false, true, level, + ) + .map(|v| (v.take_or_clone(), false)) } } } @@ -1026,7 +996,8 @@ impl Engine { let def_value = def_value.map(Into::::into); let args = idx_val.as_fn_call_args(); self.make_method_call( - state, lib, name, *hash, target, args, def_value, *native, false, level, + mods, state, lib, name, *hash, target, args, def_value, *native, false, + level, ) .map_err(|err| err.fill_position(*pos)) } @@ -1036,8 +1007,9 @@ impl Engine { Expr::Property(x) if target.is::() && new_val.is_some() => { let IdentX { name, pos } = &x.1; let index = name.clone().into(); - let mut val = self - .get_indexed_mut(state, lib, target, index, *pos, true, false, level)?; + let mut val = self.get_indexed_mut( + mods, state, lib, target, index, *pos, true, false, level, + )?; val.set_value(new_val.unwrap())?; Ok((Default::default(), true)) @@ -1047,7 +1019,7 @@ impl Engine { let IdentX { name, pos } = &x.1; let index = name.clone().into(); let val = self.get_indexed_mut( - state, lib, target, index, *pos, false, false, level, + mods, state, lib, target, index, *pos, false, false, level, )?; Ok((val.take_or_clone(), false)) @@ -1058,8 +1030,8 @@ impl Engine { let mut new_val = new_val; let mut args = [target.as_mut(), &mut new_val.as_mut().unwrap().0]; self.exec_fn_call( - state, lib, setter, 0, &mut args, is_ref, true, false, None, None, - level, + mods, state, lib, setter, 0, &mut args, is_ref, true, false, None, + None, level, ) .map(|(v, _)| (v, true)) .map_err(|err| err.fill_position(*pos)) @@ -1069,8 +1041,8 @@ impl Engine { let ((getter, _), IdentX { pos, .. }) = x.as_ref(); let mut args = [target.as_mut()]; self.exec_fn_call( - state, lib, getter, 0, &mut args, is_ref, true, false, None, None, - level, + mods, state, lib, getter, 0, &mut args, is_ref, true, false, None, + None, level, ) .map(|(v, _)| (v, false)) .map_err(|err| err.fill_position(*pos)) @@ -1082,7 +1054,7 @@ impl Engine { let IdentX { name, pos } = &p.1; let index = name.clone().into(); self.get_indexed_mut( - state, lib, target, index, *pos, false, true, level, + mods, state, lib, target, index, *pos, false, true, level, )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr @@ -1098,8 +1070,8 @@ impl Engine { let args = idx_val.as_fn_call_args(); let (val, _) = self .make_method_call( - state, lib, name, *hash, target, args, def_value, *native, - false, level, + mods, state, lib, name, *hash, target, args, def_value, + *native, false, level, ) .map_err(|err| err.fill_position(*pos))?; val.into() @@ -1111,8 +1083,8 @@ impl Engine { }; self.eval_dot_index_chain_helper( - state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, level, - new_val, + mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain, + level, new_val, ) .map_err(|err| err.fill_position(*x_pos)) } @@ -1126,8 +1098,8 @@ impl Engine { let args = &mut arg_values[..1]; let (mut val, updated) = self .exec_fn_call( - state, lib, getter, 0, args, is_ref, true, false, None, - None, level, + mods, state, lib, getter, 0, args, is_ref, true, false, + None, None, level, ) .map_err(|err| err.fill_position(*pos))?; @@ -1135,6 +1107,7 @@ impl Engine { let (result, may_be_changed) = self .eval_dot_index_chain_helper( + mods, state, lib, this_ptr, @@ -1152,8 +1125,8 @@ impl Engine { // Re-use args because the first &mut parameter will not be consumed arg_values[1] = val; self.exec_fn_call( - state, lib, setter, 0, arg_values, is_ref, true, false, - None, None, level, + mods, state, lib, setter, 0, arg_values, is_ref, true, + false, None, None, level, ) .or_else( |err| match *err { @@ -1181,16 +1154,16 @@ impl Engine { let args = idx_val.as_fn_call_args(); let (mut val, _) = self .make_method_call( - state, lib, name, *hash, target, args, def_value, *native, - false, level, + mods, state, lib, name, *hash, target, args, def_value, + *native, false, level, ) .map_err(|err| err.fill_position(*pos))?; let val = &mut val; let target = &mut val.into(); self.eval_dot_index_chain_helper( - state, lib, this_ptr, target, &x.rhs, idx_values, next_chain, - level, new_val, + mods, state, lib, this_ptr, target, &x.rhs, idx_values, + next_chain, level, new_val, ) .map_err(|err| err.fill_position(*pos)) } @@ -1275,7 +1248,8 @@ impl Engine { let obj_ptr = &mut target.into(); self.eval_dot_index_chain_helper( - state, lib, &mut None, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val, + mods, state, lib, &mut None, obj_ptr, dot_rhs, idx_values, chain_type, level, + new_val, ) .map(|(v, _)| v) .map_err(|err| err.fill_position(op_pos)) @@ -1287,7 +1261,8 @@ impl Engine { let val = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?; let obj_ptr = &mut val.into(); self.eval_dot_index_chain_helper( - state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, new_val, + mods, state, lib, this_ptr, obj_ptr, dot_rhs, idx_values, chain_type, level, + new_val, ) .map(|(v, _)| v) .map_err(|err| err.fill_position(op_pos)) @@ -1375,6 +1350,7 @@ impl Engine { #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn get_indexed_mut<'a>( &self, + _mods: &mut Imports, state: &mut State, _lib: &[&Module], target: &'a mut Target, @@ -1457,7 +1433,8 @@ impl Engine { let mut idx = idx; let args = &mut [val, &mut idx]; self.exec_fn_call( - state, _lib, FN_IDX_GET, 0, args, is_ref, true, false, None, None, _level, + _mods, state, _lib, FN_IDX_GET, 0, args, is_ref, true, false, None, None, + _level, ) .map(|(v, _)| v.into()) .map_err(|err| match *err { @@ -1846,8 +1823,8 @@ impl Engine { // Run function let (value, _) = self .exec_fn_call( - state, lib, op, 0, args, false, false, false, None, None, - level, + mods, state, lib, op, 0, args, false, false, false, None, + None, level, ) .map_err(|err| err.fill_position(*op_pos))?; @@ -1884,7 +1861,7 @@ impl Engine { let result = self .exec_fn_call( - state, lib, op, 0, args, false, false, false, None, None, level, + mods, state, lib, op, 0, args, false, false, false, None, None, level, ) .map(|(v, _)| v) .map_err(|err| err.fill_position(*op_pos))?; diff --git a/src/fn_call.rs b/src/fn_call.rs index 18932715..d1fb23d3 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -469,6 +469,7 @@ impl Engine { /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! pub(crate) fn exec_fn_call( &self, + mods: &mut Imports, state: &mut State, lib: &[&Module], fn_name: &str, @@ -535,7 +536,6 @@ impl Engine { let func = func.get_fn_def(); let scope: &mut Scope = &mut Default::default(); - let mods = &mut Default::default(); // Move captured variables into scope #[cfg(not(feature = "no_closure"))] @@ -684,6 +684,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] pub(crate) fn make_method_call( &self, + mods: &mut Imports, state: &mut State, lib: &[&Module], name: &str, @@ -723,7 +724,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - state, lib, fn_name, hash, args, false, false, pub_only, None, def_val, level, + mods, state, lib, fn_name, hash, args, false, false, pub_only, None, def_val, level, ) } else if _fn_name == KEYWORD_FN_PTR_CALL && call_args.len() > 0 @@ -750,7 +751,7 @@ impl Engine { // Map it to name(args) in function-call style self.exec_fn_call( - state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level, + mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level, ) } else if _fn_name == KEYWORD_FN_PTR_CURRY && obj.is::() { // Curry call @@ -818,7 +819,8 @@ impl Engine { let args = arg_values.as_mut(); self.exec_fn_call( - state, lib, _fn_name, hash, args, is_ref, true, pub_only, None, def_val, level, + mods, state, lib, _fn_name, hash, args, is_ref, true, pub_only, None, def_val, + level, ) }?; @@ -1064,7 +1066,7 @@ impl Engine { let args = args.as_mut(); self.exec_fn_call( - state, lib, name, hash, args, is_ref, false, pub_only, capture, def_val, level, + mods, state, lib, name, hash, args, is_ref, false, pub_only, capture, def_val, level, ) .map(|(v, _)| v) } @@ -1176,12 +1178,11 @@ impl Engine { } let args = args.as_mut(); - let fn_def = f.get_fn_def(); + let fn_def = f.get_shared_fn_def().clone(); let new_scope = &mut Default::default(); - let mods = &mut Default::default(); - self.call_script_fn(new_scope, mods, state, lib, &mut None, fn_def, args, level) + self.call_script_fn(new_scope, mods, state, lib, &mut None, &fn_def, args, level) } Some(f) if f.is_plugin_fn() => { f.get_plugin_fn().call((self, lib).into(), args.as_mut()) diff --git a/src/fn_native.rs b/src/fn_native.rs index ef30149c..9b80d59a 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -183,6 +183,7 @@ impl FnPtr { ctx.engine() .exec_fn_call( + &mut Default::default(), &mut Default::default(), ctx.lib, fn_name, diff --git a/src/module/mod.rs b/src/module/mod.rs index c4853cf3..b0226d37 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -268,20 +268,17 @@ impl Module { self } - /// Get a mutable reference to a modules-qualified variable. + /// Get a reference to a modules-qualified variable. /// Name and Position in `EvalAltResult` are None and must be set afterwards. /// /// The `u64` hash is calculated by the function `crate::calc_native_fn_hash`. #[inline(always)] - pub(crate) fn get_qualified_var_mut( - &mut self, - hash_var: u64, - ) -> Result<&mut Dynamic, Box> { + pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box> { if hash_var == 0 { Err(EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into()) } else { self.all_variables - .get_mut(&hash_var) + .get(&hash_var) .ok_or_else(|| EvalAltResult::ErrorVariableNotFound(String::new(), NO_POS).into()) } } From d5d70367fa0afa7ec1c45871bbd3617b704b91f4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 6 Nov 2020 19:22:00 +0800 Subject: [PATCH 5/5] Fix no-std buld. --- src/scope.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scope.rs b/src/scope.rs index 4282cfae..2dfff5c9 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -2,7 +2,7 @@ use crate::dynamic::{Dynamic, Variant}; -use crate::stdlib::{borrow::Cow, iter, string::String, vec::Vec}; +use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec}; /// Type of an entry in the Scope. #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]