Implement private modifier for functions.

This commit is contained in:
Stephen Chung
2020-05-09 11:29:30 +08:00
parent d1de84fdd2
commit 0d20137d6c
7 changed files with 183 additions and 49 deletions

View File

@@ -999,7 +999,7 @@ impl Engine {
let pos = Position::none();
let fn_def = fn_lib
.get_function_by_signature(name, args.len())
.get_function_by_signature(name, args.len(), true)
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
let state = State::new(fn_lib);

View File

@@ -7,7 +7,7 @@ use crate::optimize::OptimizationLevel;
use crate::packages::{
CorePackage, Package, PackageLibrary, PackageStore, PackagesCollection, StandardPackage,
};
use crate::parser::{Expr, FnDef, ReturnType, Stmt, AST};
use crate::parser::{Expr, FnAccess, FnDef, ReturnType, Stmt, AST};
use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::Position;
@@ -234,10 +234,22 @@ impl FunctionsLib {
self.get(&hash).map(|fn_def| fn_def.as_ref())
}
/// Get a function definition from the `FunctionsLib`.
pub fn get_function_by_signature(&self, name: &str, params: usize) -> Option<&FnDef> {
pub fn get_function_by_signature(
&self,
name: &str,
params: usize,
public_only: bool,
) -> Option<&FnDef> {
// Qualifiers (none) + function name + placeholders (one for each parameter).
let hash = calc_fn_hash(empty(), name, repeat(EMPTY_TYPE_ID()).take(params));
self.get_function(hash)
let fn_def = self.get_function(hash);
match fn_def.as_ref().map(|f| f.access) {
None => None,
Some(FnAccess::Private) if public_only => None,
Some(FnAccess::Private) => fn_def,
Some(FnAccess::Public) => fn_def,
}
}
/// Merge another `FunctionsLib` into this `FunctionsLib`.
pub fn merge(&self, other: &Self) -> Self {

View File

@@ -4,7 +4,7 @@
use crate::any::{Dynamic, Variant};
use crate::calc_fn_hash;
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib, NativeFunction, ScriptedFunction};
use crate::parser::{FnDef, AST};
use crate::parser::{FnAccess, FnDef, AST};
use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::token::{Position, Token};
@@ -57,7 +57,7 @@ pub struct Module {
all_variables: HashMap<u64, Dynamic>,
/// External Rust functions.
functions: HashMap<u64, (String, Vec<TypeId>, NativeFunction)>,
functions: HashMap<u64, (String, FnAccess, Vec<TypeId>, NativeFunction)>,
/// Flattened collection of all external Rust functions, including those in sub-modules.
all_functions: HashMap<u64, NativeFunction>,
@@ -260,15 +260,21 @@ impl Module {
/// Set a Rust function into the module, returning a hash key.
///
/// If there is an existing Rust function of the same hash, it is replaced.
pub fn set_fn(&mut self, fn_name: String, params: Vec<TypeId>, func: Box<FnAny>) -> u64 {
pub fn set_fn(
&mut self,
fn_name: String,
access: FnAccess,
params: Vec<TypeId>,
func: Box<FnAny>,
) -> u64 {
let hash = calc_fn_hash(empty(), &fn_name, params.iter().cloned());
#[cfg(not(feature = "sync"))]
self.functions
.insert(hash, (fn_name, params, Rc::new(func)));
.insert(hash, (fn_name, access, params, Rc::new(func)));
#[cfg(feature = "sync")]
self.functions
.insert(hash, (fn_name, params, Arc::new(func)));
.insert(hash, (fn_name, access, params, Arc::new(func)));
hash
}
@@ -283,7 +289,7 @@ impl Module {
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
/// let hash = module.set_fn_0("calc", || Ok(42_i64), false);
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_0<K: Into<String>, T: Into<Dynamic>>(
@@ -291,6 +297,7 @@ impl Module {
fn_name: K,
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
is_private: bool,
) -> u64 {
let f = move |_: &mut FnCallArgs, pos| {
func()
@@ -298,7 +305,12 @@ impl Module {
.map_err(|err| EvalAltResult::set_position(err, pos))
};
let arg_types = vec![];
self.set_fn(fn_name.into(), arg_types, Box::new(f))
let access = if is_private {
FnAccess::Private
} else {
FnAccess::Public
};
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
}
/// Set a Rust function taking one parameter into the module, returning a hash key.
@@ -311,7 +323,7 @@ impl Module {
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1), false);
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
@@ -319,6 +331,7 @@ impl Module {
fn_name: K,
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
is_private: bool,
) -> u64 {
let f = move |args: &mut FnCallArgs, pos| {
func(mem::take(args[0]).cast::<A>())
@@ -326,7 +339,12 @@ impl Module {
.map_err(|err| EvalAltResult::set_position(err, pos))
};
let arg_types = vec![TypeId::of::<A>()];
self.set_fn(fn_name.into(), arg_types, Box::new(f))
let access = if is_private {
FnAccess::Private
} else {
FnAccess::Public
};
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
}
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
@@ -339,7 +357,7 @@ impl Module {
/// use rhai::Module;
///
/// let mut module = Module::new();
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) }, false);
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
@@ -347,6 +365,7 @@ impl Module {
fn_name: K,
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
is_private: bool,
) -> u64 {
let f = move |args: &mut FnCallArgs, pos| {
func(args[0].downcast_mut::<A>().unwrap())
@@ -354,7 +373,12 @@ impl Module {
.map_err(|err| EvalAltResult::set_position(err, pos))
};
let arg_types = vec![TypeId::of::<A>()];
self.set_fn(fn_name.into(), arg_types, Box::new(f))
let access = if is_private {
FnAccess::Private
} else {
FnAccess::Public
};
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
}
/// Set a Rust function taking two parameters into the module, returning a hash key.
@@ -369,7 +393,7 @@ impl Module {
/// let mut module = Module::new();
/// let hash = module.set_fn_2("calc", |x: i64, y: String| {
/// Ok(x + y.len() as i64)
/// });
/// }, false);
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
@@ -377,6 +401,7 @@ impl Module {
fn_name: K,
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
is_private: bool,
) -> u64 {
let f = move |args: &mut FnCallArgs, pos| {
let a = mem::take(args[0]).cast::<A>();
@@ -387,7 +412,12 @@ impl Module {
.map_err(|err| EvalAltResult::set_position(err, pos))
};
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
self.set_fn(fn_name.into(), arg_types, Box::new(f))
let access = if is_private {
FnAccess::Private
} else {
FnAccess::Public
};
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
}
/// Set a Rust function taking two parameters (the first one mutable) into the module,
@@ -401,7 +431,7 @@ impl Module {
/// let mut module = Module::new();
/// let hash = module.set_fn_2_mut("calc", |x: &mut i64, y: String| {
/// *x += y.len() as i64; Ok(*x)
/// });
/// }, false);
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_2_mut<
@@ -414,6 +444,7 @@ impl Module {
fn_name: K,
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
is_private: bool,
) -> u64 {
let f = move |args: &mut FnCallArgs, pos| {
let b = mem::take(args[1]).cast::<B>();
@@ -424,7 +455,12 @@ impl Module {
.map_err(|err| EvalAltResult::set_position(err, pos))
};
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
self.set_fn(fn_name.into(), arg_types, Box::new(f))
let access = if is_private {
FnAccess::Private
} else {
FnAccess::Public
};
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
}
/// Set a Rust function taking three parameters into the module, returning a hash key.
@@ -439,7 +475,7 @@ impl Module {
/// let mut module = Module::new();
/// let hash = module.set_fn_3("calc", |x: i64, y: String, z: i64| {
/// Ok(x + y.len() as i64 + z)
/// });
/// }, false);
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_3<
@@ -453,6 +489,7 @@ impl Module {
fn_name: K,
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
is_private: bool,
) -> u64 {
let f = move |args: &mut FnCallArgs, pos| {
let a = mem::take(args[0]).cast::<A>();
@@ -464,7 +501,12 @@ impl Module {
.map_err(|err| EvalAltResult::set_position(err, pos))
};
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(fn_name.into(), arg_types, Box::new(f))
let access = if is_private {
FnAccess::Private
} else {
FnAccess::Public
};
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
}
/// Set a Rust function taking three parameters (the first one mutable) into the module,
@@ -480,7 +522,7 @@ impl Module {
/// let mut module = Module::new();
/// let hash = module.set_fn_3_mut("calc", |x: &mut i64, y: String, z: i64| {
/// *x += y.len() as i64 + z; Ok(*x)
/// });
/// }, false);
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn set_fn_3_mut<
@@ -494,6 +536,7 @@ impl Module {
fn_name: K,
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
is_private: bool,
) -> u64 {
let f = move |args: &mut FnCallArgs, pos| {
let b = mem::take(args[1]).cast::<B>();
@@ -505,7 +548,12 @@ impl Module {
.map_err(|err| EvalAltResult::set_position(err, pos))
};
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
self.set_fn(fn_name.into(), arg_types, Box::new(f))
let access = if is_private {
FnAccess::Private
} else {
FnAccess::Public
};
self.set_fn(fn_name.into(), access, arg_types, Box::new(f))
}
/// Get a Rust function.
@@ -523,7 +571,7 @@ impl Module {
/// assert!(module.get_fn(hash).is_some());
/// ```
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
self.functions.get(&hash).map(|(_, _, v)| v.as_ref())
self.functions.get(&hash).map(|(_, _, _, v)| v.as_ref())
}
/// Get a modules-qualified function.
@@ -588,11 +636,7 @@ impl Module {
scope.into_iter().for_each(
|ScopeEntry {
name,
typ,
value,
alias,
..
typ, value, alias, ..
}| {
match typ {
// Variables with an alias left in the scope become module variables
@@ -641,7 +685,12 @@ impl Module {
variables.push((hash, value.clone()));
}
// Index all Rust functions
for (fn_name, params, func) in module.functions.values() {
for (fn_name, access, params, func) in module.functions.values() {
match access {
// Private functions are not exported
FnAccess::Private => continue,
FnAccess::Public => (),
}
// Rust functions are indexed in two steps:
// 1) Calculate a hash in a similar manner to script-defined functions,
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
@@ -660,6 +709,11 @@ impl Module {
}
// Index all script-defined functions
for fn_def in module.fn_lib.values() {
match fn_def.access {
// Private functions are not exported
FnAccess::Private => continue,
FnAccess::Public => (),
}
// Qualifiers + function name + placeholders (one for each parameter)
let hash = calc_fn_hash(
qualifiers.iter().map(|v| *v),

View File

@@ -178,11 +178,22 @@ impl Add<Self> for &AST {
}
}
/// A script-function definition.
/// A type representing the access mode of a scripted function.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FnAccess {
/// Private function.
Private,
/// Public function.
Public,
}
/// A scripted function definition.
#[derive(Debug, Clone)]
pub struct FnDef {
/// Function name.
pub name: String,
/// Function access mode.
pub access: FnAccess,
/// Names of function parameters.
pub params: Vec<String>,
/// Function body.
@@ -2208,6 +2219,7 @@ fn parse_stmt<'a>(
fn parse_fn<'a>(
input: &mut Peekable<TokenIterator<'a>>,
stack: &mut Stack,
access: FnAccess,
allow_stmt_expr: bool,
) -> Result<FnDef, Box<ParseError>> {
let pos = eat_token(input, Token::Fn);
@@ -2283,6 +2295,7 @@ fn parse_fn<'a>(
Ok(FnDef {
name,
access,
params,
body,
pos,
@@ -2330,19 +2343,37 @@ fn parse_global_level<'a>(
// Collect all the function definitions
#[cfg(not(feature = "no_function"))]
{
if let (Token::Fn, _) = input.peek().unwrap() {
let mut stack = Stack::new();
let func = parse_fn(input, &mut stack, true)?;
let mut access = FnAccess::Public;
let mut must_be_fn = false;
// Qualifiers (none) + function name + argument `TypeId`'s
let hash = calc_fn_hash(
empty(),
&func.name,
repeat(EMPTY_TYPE_ID()).take(func.params.len()),
);
if match_token(input, Token::Private)? {
access = FnAccess::Private;
must_be_fn = true;
}
functions.insert(hash, func);
continue;
match input.peek().unwrap() {
(Token::Fn, _) => {
let mut stack = Stack::new();
let func = parse_fn(input, &mut stack, access, true)?;
// Qualifiers (none) + function name + argument `TypeId`'s
let hash = calc_fn_hash(
empty(),
&func.name,
repeat(EMPTY_TYPE_ID()).take(func.params.len()),
);
functions.insert(hash, func);
continue;
}
(_, pos) if must_be_fn => {
return Err(PERR::MissingToken(
Token::Fn.into(),
format!("following '{}'", Token::Private.syntax()),
)
.into_err(*pos))
}
_ => (),
}
}
// Actual statement

View File

@@ -196,6 +196,7 @@ pub enum Token {
XOrAssign,
ModuloAssign,
PowerOfAssign,
Private,
Import,
Export,
As,
@@ -279,6 +280,7 @@ impl Token {
ModuloAssign => "%=",
PowerOf => "~",
PowerOfAssign => "~=",
Private => "private",
Import => "import",
Export => "export",
As => "as",
@@ -750,6 +752,7 @@ impl<'a> TokenIterator<'a> {
"throw" => Token::Throw,
"for" => Token::For,
"in" => Token::In,
"private" => Token::Private,
#[cfg(not(feature = "no_module"))]
"import" => Token::Import,