Allow NativeCallContext in function arguments.
This commit is contained in:
parent
dc4c47e008
commit
46b92c9d1f
@ -24,7 +24,7 @@ categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smallvec = { version = "1.4.2", default-features = false }
|
smallvec = { version = "1.4.2", default-features = false }
|
||||||
rhai_codegen = { version = "0.1", path = "codegen" }
|
rhai_codegen = { version = "0.2", path = "codegen" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -12,7 +12,13 @@ Breaking changes
|
|||||||
* `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`.
|
* `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`.
|
||||||
* `Engine::register_raw_fn` function signature has changed.
|
* `Engine::register_raw_fn` function signature has changed.
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
* The plugins system is enhanced to support functions taking a `NativeCallContext` as the first parameter.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
* Calling `eval` or `Fn` in method-call style, which is an error, is now caught during parsing.
|
* Calling `eval` or `Fn` in method-call style, which is an error, is now caught during parsing.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rhai_codegen"
|
name = "rhai_codegen"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["jhwgh1968"]
|
authors = ["jhwgh1968"]
|
||||||
description = "Procedural macro support package for Rhai, a scripting language for Rust"
|
description = "Procedural macro support package for Rhai, a scripting language for Rust"
|
||||||
|
@ -222,6 +222,7 @@ pub(crate) struct ExportedFn {
|
|||||||
entire_span: proc_macro2::Span,
|
entire_span: proc_macro2::Span,
|
||||||
signature: syn::Signature,
|
signature: syn::Signature,
|
||||||
is_public: bool,
|
is_public: bool,
|
||||||
|
pass_context: bool,
|
||||||
return_dynamic: bool,
|
return_dynamic: bool,
|
||||||
mut_receiver: bool,
|
mut_receiver: bool,
|
||||||
params: ExportedFnParams,
|
params: ExportedFnParams,
|
||||||
@ -237,15 +238,36 @@ impl Parse for ExportedFn {
|
|||||||
let dynamic_type_path2 = syn::parse2::<syn::Path>(quote! { rhai::Dynamic }).unwrap();
|
let dynamic_type_path2 = syn::parse2::<syn::Path>(quote! { rhai::Dynamic }).unwrap();
|
||||||
let mut return_dynamic = false;
|
let mut return_dynamic = false;
|
||||||
|
|
||||||
|
let context_type_path1 = syn::parse2::<syn::Path>(quote! { NativeCallContext }).unwrap();
|
||||||
|
let context_type_path2 =
|
||||||
|
syn::parse2::<syn::Path>(quote! { rhai::NativeCallContext }).unwrap();
|
||||||
|
let mut pass_context = false;
|
||||||
|
|
||||||
// #[cfg] attributes are not allowed on functions due to what is generated for them
|
// #[cfg] attributes are not allowed on functions due to what is generated for them
|
||||||
crate::attrs::deny_cfg_attr(&fn_all.attrs)?;
|
crate::attrs::deny_cfg_attr(&fn_all.attrs)?;
|
||||||
|
|
||||||
// Determine if the function is public.
|
// Determine if the function is public.
|
||||||
let is_public = matches!(fn_all.vis, syn::Visibility::Public(_));
|
let is_public = matches!(fn_all.vis, syn::Visibility::Public(_));
|
||||||
// Determine whether function generates a special calling convention for a mutable
|
|
||||||
// reciever.
|
// Determine if the function requires a call context
|
||||||
let mut_receiver = {
|
|
||||||
if let Some(first_arg) = fn_all.sig.inputs.first() {
|
if let Some(first_arg) = fn_all.sig.inputs.first() {
|
||||||
|
if let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = first_arg {
|
||||||
|
match flatten_type_groups(ty.as_ref()) {
|
||||||
|
syn::Type::Path(p)
|
||||||
|
if p.path == context_type_path1 || p.path == context_type_path2 =>
|
||||||
|
{
|
||||||
|
pass_context = true;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let skip_slots = if pass_context { 1 } else { 0 };
|
||||||
|
|
||||||
|
// Determine whether function generates a special calling convention for a mutable receiver.
|
||||||
|
let mut_receiver = {
|
||||||
|
if let Some(first_arg) = fn_all.sig.inputs.iter().skip(skip_slots).next() {
|
||||||
match first_arg {
|
match first_arg {
|
||||||
syn::FnArg::Receiver(syn::Receiver {
|
syn::FnArg::Receiver(syn::Receiver {
|
||||||
reference: Some(_), ..
|
reference: Some(_), ..
|
||||||
@ -265,8 +287,7 @@ impl Parse for ExportedFn {
|
|||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
ty.span(),
|
ty.span(),
|
||||||
"references from Rhai in this position \
|
"references from Rhai in this position must be mutable",
|
||||||
must be mutable",
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -281,7 +302,7 @@ impl Parse for ExportedFn {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// All arguments after the first must be moved except for &str.
|
// All arguments after the first must be moved except for &str.
|
||||||
for arg in fn_all.sig.inputs.iter().skip(1) {
|
for arg in fn_all.sig.inputs.iter().skip(skip_slots + 1) {
|
||||||
let ty = match arg {
|
let ty = match arg {
|
||||||
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => ty,
|
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => ty,
|
||||||
_ => panic!("internal error: receiver argument outside of first position!?"),
|
_ => panic!("internal error: receiver argument outside of first position!?"),
|
||||||
@ -304,8 +325,7 @@ impl Parse for ExportedFn {
|
|||||||
if !is_ok {
|
if !is_ok {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
ty.span(),
|
ty.span(),
|
||||||
"this type in this position passes from \
|
"this type in this position passes from Rhai by value",
|
||||||
Rhai by value",
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,6 +357,7 @@ impl Parse for ExportedFn {
|
|||||||
entire_span,
|
entire_span,
|
||||||
signature: fn_all.sig,
|
signature: fn_all.sig,
|
||||||
is_public,
|
is_public,
|
||||||
|
pass_context,
|
||||||
return_dynamic,
|
return_dynamic,
|
||||||
mut_receiver,
|
mut_receiver,
|
||||||
params: ExportedFnParams::default(),
|
params: ExportedFnParams::default(),
|
||||||
@ -363,6 +384,10 @@ impl ExportedFn {
|
|||||||
self.params.skip
|
self.params.skip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pass_context(&self) -> bool {
|
||||||
|
self.pass_context
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn signature(&self) -> &syn::Signature {
|
pub(crate) fn signature(&self) -> &syn::Signature {
|
||||||
&self.signature
|
&self.signature
|
||||||
}
|
}
|
||||||
@ -418,11 +443,13 @@ impl ExportedFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
|
pub(crate) fn arg_list(&self) -> impl Iterator<Item = &syn::FnArg> {
|
||||||
self.signature.inputs.iter()
|
let skip = if self.pass_context { 1 } else { 0 };
|
||||||
|
self.signature.inputs.iter().skip(skip)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn arg_count(&self) -> usize {
|
pub(crate) fn arg_count(&self) -> usize {
|
||||||
self.signature.inputs.len()
|
let skip = if self.pass_context { 1 } else { 0 };
|
||||||
|
self.signature.inputs.len() - skip
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn return_type(&self) -> Option<&syn::Type> {
|
pub(crate) fn return_type(&self) -> Option<&syn::Type> {
|
||||||
@ -625,6 +652,10 @@ impl ExportedFn {
|
|||||||
let mut input_type_exprs: Vec<syn::Expr> = Vec::new();
|
let mut input_type_exprs: Vec<syn::Expr> = Vec::new();
|
||||||
let skip_first_arg;
|
let skip_first_arg;
|
||||||
|
|
||||||
|
if self.pass_context {
|
||||||
|
unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { context }).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the first argument separately if the function has a "method like" receiver
|
// Handle the first argument separately if the function has a "method like" receiver
|
||||||
if is_method_call {
|
if is_method_call {
|
||||||
skip_first_arg = true;
|
skip_first_arg = true;
|
||||||
@ -764,9 +795,7 @@ impl ExportedFn {
|
|||||||
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
|
let type_name = syn::Ident::new(on_type_name, proc_macro2::Span::call_site());
|
||||||
quote! {
|
quote! {
|
||||||
impl PluginFunction for #type_name {
|
impl PluginFunction for #type_name {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), #arg_count,
|
debug_assert_eq!(args.len(), #arg_count,
|
||||||
"wrong arg count: {} != {}",
|
"wrong arg count: {} != {}",
|
||||||
args.len(), #arg_count);
|
args.len(), #arg_count);
|
||||||
|
@ -68,7 +68,7 @@ pub(crate) fn generate_body(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: these are token streams, because reparsing messes up "> >" vs ">>"
|
// NB: these are token streams, because re-parsing messes up "> >" vs ">>"
|
||||||
let mut gen_fn_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
|
let mut gen_fn_tokens: Vec<proc_macro2::TokenStream> = Vec::new();
|
||||||
for function in fns {
|
for function in fns {
|
||||||
function.update_scope(&parent_scope);
|
function.update_scope(&parent_scope);
|
||||||
|
@ -277,9 +277,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(Dynamic::from(do_nothing()))
|
Ok(Dynamic::from(do_nothing()))
|
||||||
@ -320,9 +318,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
@ -352,6 +348,49 @@ mod generate_tests {
|
|||||||
assert_streams_eq(item_fn.generate(), expected_tokens);
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn one_arg_fn_with_context() {
|
||||||
|
let input_tokens: TokenStream = quote! {
|
||||||
|
pub fn do_something(context: NativeCallContext, x: usize) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_tokens = quote! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub mod rhai_fn_do_something {
|
||||||
|
use super::*;
|
||||||
|
struct Token();
|
||||||
|
impl PluginFunction for Token {
|
||||||
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
|
Ok(Dynamic::from(do_something(context, arg0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_method_call(&self) -> bool { false }
|
||||||
|
fn is_variadic(&self) -> bool { false }
|
||||||
|
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
|
||||||
|
fn input_types(&self) -> Box<[TypeId]> {
|
||||||
|
new_vec![TypeId::of::<usize>()].into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn token_callable() -> CallableFunction {
|
||||||
|
Token().into()
|
||||||
|
}
|
||||||
|
pub fn token_input_types() -> Box<[TypeId]> {
|
||||||
|
Token().input_types()
|
||||||
|
}
|
||||||
|
pub fn dynamic_result_fn(context: NativeCallContext, x: usize) -> Result<Dynamic, Box<EvalAltResult> > {
|
||||||
|
Ok(Dynamic::from(super::do_something(context, x)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let item_fn = syn::parse2::<ExportedFn>(input_tokens).unwrap();
|
||||||
|
assert!(item_fn.pass_context());
|
||||||
|
assert_streams_eq(item_fn.generate(), expected_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_dynamic() {
|
fn return_dynamic() {
|
||||||
let input_tokens: TokenStream = quote! {
|
let input_tokens: TokenStream = quote! {
|
||||||
@ -366,9 +405,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(return_dynamic())
|
Ok(return_dynamic())
|
||||||
@ -405,9 +442,7 @@ mod generate_tests {
|
|||||||
|
|
||||||
let expected_tokens = quote! {
|
let expected_tokens = quote! {
|
||||||
impl PluginFunction for MyType {
|
impl PluginFunction for MyType {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
@ -439,9 +474,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
let arg0 = mem::take(args[0usize]).cast::<usize>();
|
||||||
@ -485,9 +518,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
@ -532,9 +563,7 @@ mod generate_tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
struct Token();
|
struct Token();
|
||||||
impl PluginFunction for Token {
|
impl PluginFunction for Token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap();
|
let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap();
|
||||||
|
@ -302,9 +302,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_mystic_number_token();
|
struct get_mystic_number_token();
|
||||||
impl PluginFunction for get_mystic_number_token {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(Dynamic::from(get_mystic_number()))
|
Ok(Dynamic::from(get_mystic_number()))
|
||||||
@ -364,9 +362,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_one_to_token();
|
struct add_one_to_token();
|
||||||
impl PluginFunction for add_one_to_token {
|
impl PluginFunction for add_one_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -441,9 +437,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_one_to_token();
|
struct add_one_to_token();
|
||||||
impl PluginFunction for add_one_to_token {
|
impl PluginFunction for add_one_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -469,9 +463,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_n_to_token();
|
struct add_n_to_token();
|
||||||
impl PluginFunction for add_n_to_token {
|
impl PluginFunction for add_n_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -535,9 +527,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_together_token();
|
struct add_together_token();
|
||||||
impl PluginFunction for add_together_token {
|
impl PluginFunction for add_together_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -608,9 +598,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct add_together_token();
|
struct add_together_token();
|
||||||
impl PluginFunction for add_together_token {
|
impl PluginFunction for add_together_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
let arg0 = mem::take(args[0usize]).cast::<INT>();
|
||||||
@ -850,9 +838,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_mystic_number_token();
|
struct get_mystic_number_token();
|
||||||
impl PluginFunction for get_mystic_number_token {
|
impl PluginFunction for get_mystic_number_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 0usize,
|
debug_assert_eq!(args.len(), 0usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 0usize);
|
"wrong arg count: {} != {}", args.len(), 0usize);
|
||||||
Ok(Dynamic::from(get_mystic_number()))
|
Ok(Dynamic::from(get_mystic_number()))
|
||||||
@ -943,9 +929,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct print_out_to_token();
|
struct print_out_to_token();
|
||||||
impl PluginFunction for print_out_to_token {
|
impl PluginFunction for print_out_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap();
|
let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap();
|
||||||
@ -1007,9 +991,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct print_out_to_token();
|
struct print_out_to_token();
|
||||||
impl PluginFunction for print_out_to_token {
|
impl PluginFunction for print_out_to_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = mem::take(args[0usize]).take_string().unwrap();
|
let arg0 = mem::take(args[0usize]).take_string().unwrap();
|
||||||
@ -1071,9 +1053,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct increment_token();
|
struct increment_token();
|
||||||
impl PluginFunction for increment_token {
|
impl PluginFunction for increment_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
@ -1138,9 +1118,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct increment_token();
|
struct increment_token();
|
||||||
impl PluginFunction for increment_token {
|
impl PluginFunction for increment_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
@ -1225,9 +1203,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct increment_token();
|
struct increment_token();
|
||||||
impl PluginFunction for increment_token {
|
impl PluginFunction for increment_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
@ -1310,9 +1286,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
@ -1376,9 +1350,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 1usize,
|
debug_assert_eq!(args.len(), 1usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 1usize);
|
"wrong arg count: {} != {}", args.len(), 1usize);
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
@ -1442,9 +1414,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1513,9 +1483,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct int_foo_token();
|
struct int_foo_token();
|
||||||
impl PluginFunction for int_foo_token {
|
impl PluginFunction for int_foo_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1580,9 +1548,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_by_index_token();
|
struct get_by_index_token();
|
||||||
impl PluginFunction for get_by_index_token {
|
impl PluginFunction for get_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1652,9 +1618,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct get_by_index_token();
|
struct get_by_index_token();
|
||||||
impl PluginFunction for get_by_index_token {
|
impl PluginFunction for get_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 2usize,
|
debug_assert_eq!(args.len(), 2usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 2usize);
|
"wrong arg count: {} != {}", args.len(), 2usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1721,9 +1685,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct set_by_index_token();
|
struct set_by_index_token();
|
||||||
impl PluginFunction for set_by_index_token {
|
impl PluginFunction for set_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 3usize,
|
debug_assert_eq!(args.len(), 3usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
@ -1797,9 +1759,7 @@ mod generate_tests {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct set_by_index_token();
|
struct set_by_index_token();
|
||||||
impl PluginFunction for set_by_index_token {
|
impl PluginFunction for set_by_index_token {
|
||||||
fn call(&self,
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
args: &mut [&mut Dynamic]
|
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
||||||
debug_assert_eq!(args.len(), 3usize,
|
debug_assert_eq!(args.len(), 3usize,
|
||||||
"wrong arg count: {} != {}", args.len(), 3usize);
|
"wrong arg count: {} != {}", args.len(), 3usize);
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
|
@ -18,9 +18,9 @@ The following standard methods (mostly defined in the [`BasicFnPackage`][package
|
|||||||
using a [raw `Engine`]) operate on function pointers:
|
using a [raw `Engine`]) operate on function pointers:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ---------------------------------- | ------------ | ---------------------------------------------------------------------------- |
|
| ---------------------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
|
||||||
| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer |
|
| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer |
|
||||||
| `is_anonymous` method and property | _none_ | does the function pointer refer to an [anonymous function]? |
|
| `is_anonymous` method and property | _none_ | does the function pointer refer to an [anonymous function]? Not available under [`no_function`]. |
|
||||||
| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ |
|
| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ |
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,3 +75,76 @@ fn main() {
|
|||||||
register_exported_fn!(engine, "+", double_and_divide);
|
register_exported_fn!(engine, "+", double_and_divide);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`NativeCallContext` Parameter
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
If the _first_ parameter of a function is of type `rhai::NativeCallContext`, then it is treated
|
||||||
|
specially by the plugins system.
|
||||||
|
|
||||||
|
`NativeCallContext` is a type that encapsulates the current _native call context_ and exposes the following:
|
||||||
|
|
||||||
|
* `NativeCallContext::engine(): &Engine` - the current [`Engine`], with all configurations and settings.
|
||||||
|
This is sometimes useful for calling a script-defined function within the same evaluation context
|
||||||
|
using [`Engine::call_fn`][`call_fn`].
|
||||||
|
|
||||||
|
* `NativeCallContext::namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`].
|
||||||
|
|
||||||
|
This first parameter, if exists, will be stripped before all other processing. It is _virtual_.
|
||||||
|
Most importantly, it does _not_ count as a parameter to the function and there is no need to provide
|
||||||
|
this argument when calling the function in Rhai.
|
||||||
|
|
||||||
|
The native call context can be used to call a [function pointer] or [closure] that has been passed
|
||||||
|
as a parameter to the function, thereby implementing a _callback_:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, FnPtr, NativeCallContext, EvalAltResult};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
#[export_fn(return_raw)]
|
||||||
|
pub fn greet(context: NativeCallContext, callback: FnPtr)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Call the callback closure with the current context
|
||||||
|
// to obtain the name to greet!
|
||||||
|
let name = callback.call_dynamic(context, None, [])?;
|
||||||
|
Ok(format!("hello, {}!", name).into())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, INT, Array, NativeCallContext, EvalAltResult, Position};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
// This function builds an array of arbitrary size, but is protected
|
||||||
|
// against attacks by first checking with the allowed limit set
|
||||||
|
// into the 'Engine'.
|
||||||
|
#[export_fn(return_raw)]
|
||||||
|
pub fn grow(context: NativeCallContext, size: INT)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Make sure the function does not generate a
|
||||||
|
// data structure larger than the allowed limit
|
||||||
|
// for the Engine!
|
||||||
|
if size as usize > context.engine().max_array_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Size to grow".to_string(),
|
||||||
|
context.engine().max_array_size(),
|
||||||
|
size as usize,
|
||||||
|
Position::none(),
|
||||||
|
).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let array = Array::new();
|
||||||
|
|
||||||
|
for x in 0..size {
|
||||||
|
array.push(x.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(array.into())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -334,6 +334,85 @@ mod my_module {
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`NativeCallContext` Parameter
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
If the _first_ parameter of a function is of type `rhai::NativeCallContext`, then it is treated
|
||||||
|
specially by the plugins system.
|
||||||
|
|
||||||
|
`NativeCallContext` is a type that encapsulates the current _native call context_ and exposes the following:
|
||||||
|
|
||||||
|
* `NativeCallContext::engine(): &Engine` - the current [`Engine`], with all configurations and settings.
|
||||||
|
This is sometimes useful for calling a script-defined function within the same evaluation context
|
||||||
|
using [`Engine::call_fn`][`call_fn`].
|
||||||
|
|
||||||
|
* `NativeCallContext::namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`].
|
||||||
|
|
||||||
|
This first parameter, if exists, will be stripped before all other processing. It is _virtual_.
|
||||||
|
Most importantly, it does _not_ count as a parameter to the function and there is no need to provide
|
||||||
|
this argument when calling the function in Rhai.
|
||||||
|
|
||||||
|
The native call context can be used to call a [function pointer] or [closure] that has been passed
|
||||||
|
as a parameter to the function, thereby implementing a _callback_:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, FnPtr, NativeCallContext, EvalAltResult};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod my_module {
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn greet(context: NativeCallContext, callback: FnPtr)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Call the callback closure with the current context
|
||||||
|
// to obtain the name to greet!
|
||||||
|
let name = callback.call_dynamic(context, None, [])?;
|
||||||
|
Ok(format!("hello, {}!", name).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The native call context is also useful in another scenario: protecting a function from malicious scripts.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Dynamic, INT, Array, NativeCallContext, EvalAltResult, Position};
|
||||||
|
use rhai::plugin::*; // a "prelude" import for macros
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod my_module {
|
||||||
|
// This function builds an array of arbitrary size, but is protected
|
||||||
|
// against attacks by first checking with the allowed limit set
|
||||||
|
// into the 'Engine'.
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn grow(context: NativeCallContext, size: INT)
|
||||||
|
-> Result<Dynamic, Box<EvalAltResult>>
|
||||||
|
{
|
||||||
|
// Make sure the function does not generate a
|
||||||
|
// data structure larger than the allowed limit
|
||||||
|
// for the Engine!
|
||||||
|
if size as usize > context.engine().max_array_size()
|
||||||
|
{
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Size to grow".to_string(),
|
||||||
|
context.engine().max_array_size(),
|
||||||
|
size as usize,
|
||||||
|
Position::none(),
|
||||||
|
).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let array = Array::new();
|
||||||
|
|
||||||
|
for x in 0..size {
|
||||||
|
array.push(x.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(array.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
`#[export_module]` Parameters
|
`#[export_module]` Parameters
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ where:
|
|||||||
|
|
||||||
* `context.engine(): &Engine` - the current [`Engine`], with all configurations and settings.
|
* `context.engine(): &Engine` - the current [`Engine`], with all configurations and settings.
|
||||||
This is sometimes useful for calling a script-defined function within the same evaluation context
|
This is sometimes useful for calling a script-defined function within the same evaluation context
|
||||||
using [`Engine::call_fn`][`call_fn`].
|
using [`Engine::call_fn`][`call_fn`], or calling a [function pointer].
|
||||||
|
|
||||||
* `context.namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`].
|
* `context.namespace(): &Module` - the global namespace of script-defined functions, as a [`Module`].
|
||||||
|
|
||||||
|
@ -1561,7 +1561,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Overriding exact implementation
|
// Overriding exact implementation
|
||||||
if func.is_plugin_fn() {
|
if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn().call(args)?;
|
func.get_plugin_fn().call((self, lib).into(), args)?;
|
||||||
} else {
|
} else {
|
||||||
func.get_native_fn()((self, lib).into(), args)?;
|
func.get_native_fn()((self, lib).into(), args)?;
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Run external function
|
// Run external function
|
||||||
let result = if func.is_plugin_fn() {
|
let result = if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn().call(args)
|
func.get_plugin_fn().call((self, lib).into(), args)
|
||||||
} else {
|
} else {
|
||||||
func.get_native_fn()((self, lib).into(), args)
|
func.get_native_fn()((self, lib).into(), args)
|
||||||
};
|
};
|
||||||
@ -1185,7 +1185,9 @@ impl Engine {
|
|||||||
|
|
||||||
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(args.as_mut()),
|
Some(f) if f.is_plugin_fn() => {
|
||||||
|
f.get_plugin_fn().call((self, lib).into(), args.as_mut())
|
||||||
|
}
|
||||||
Some(f) if f.is_native() => {
|
Some(f) if f.is_native() => {
|
||||||
if !f.is_method() {
|
if !f.is_method() {
|
||||||
// Clone first argument
|
// Clone first argument
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! Module defining external-loaded modules for Rhai.
|
//! Module defining external-loaded modules for Rhai.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::engine::Engine;
|
|
||||||
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
|
use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn, NativeCallContext, SendSync};
|
||||||
use crate::fn_register::by_value as cast_arg;
|
use crate::fn_register::by_value as cast_arg;
|
||||||
use crate::parser::FnAccess;
|
use crate::parser::FnAccess;
|
||||||
@ -15,7 +14,7 @@ use crate::{fn_native::Shared, parser::ScriptFnDef};
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::Imports,
|
engine::{Engine, Imports},
|
||||||
parser::AST,
|
parser::AST,
|
||||||
scope::{Entry as ScopeEntry, Scope},
|
scope::{Entry as ScopeEntry, Scope},
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![cfg(not(feature = "no_index"))]
|
#![cfg(not(feature = "no_index"))]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::Dynamic;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::engine::Array;
|
use crate::engine::Array;
|
||||||
use crate::fn_native::{FnPtr, NativeCallContext};
|
use crate::fn_native::{FnPtr, NativeCallContext};
|
||||||
@ -38,6 +38,23 @@ macro_rules! gen_array_functions {
|
|||||||
list.insert(position as usize, Dynamic::from(item));
|
list.insert(position as usize, Dynamic::from(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn pad(context: NativeCallContext, list: &mut Array, len: INT, item: $arg_type) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
// Check if array will be over max size limit
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if context.engine().max_array_size() > 0 && len > 0 && (len as usize) > context.engine().max_array_size() {
|
||||||
|
return EvalAltResult::ErrorDataTooLarge(
|
||||||
|
"Size of array".to_string(), context.engine().max_array_size(), len as usize, Position::none(),
|
||||||
|
).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
if len > 0 && len as usize > list.len() {
|
||||||
|
list.resize(len as usize, Dynamic::from(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})* }
|
})* }
|
||||||
}
|
}
|
||||||
@ -46,10 +63,6 @@ macro_rules! gen_array_functions {
|
|||||||
macro_rules! reg_functions {
|
macro_rules! reg_functions {
|
||||||
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $(
|
||||||
combine_with_exported_module!($mod_name, "array_functions", $root::$arg_type::functions);
|
combine_with_exported_module!($mod_name, "array_functions", $root::$arg_type::functions);
|
||||||
|
|
||||||
$mod_name.set_raw_fn("pad",
|
|
||||||
&[TypeId::of::<Array>(), TypeId::of::<INT>(), TypeId::of::<$arg_type>()],
|
|
||||||
pad::<$arg_type>);
|
|
||||||
)* }
|
)* }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,18 +84,6 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
reg_functions!(lib += map; Map);
|
reg_functions!(lib += map; Map);
|
||||||
|
|
||||||
lib.set_raw_fn("map", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], map);
|
|
||||||
lib.set_raw_fn("filter", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], filter);
|
|
||||||
lib.set_raw_fn("drain", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], drain);
|
|
||||||
lib.set_raw_fn("retain", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], retain);
|
|
||||||
lib.set_raw_fn("reduce", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], reduce);
|
|
||||||
lib.set_raw_fn("reduce", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>(), TypeId::of::<FnPtr>()], reduce_with_initial);
|
|
||||||
lib.set_raw_fn("reduce_rev", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], reduce_rev);
|
|
||||||
lib.set_raw_fn("reduce_rev", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>(), TypeId::of::<FnPtr>()], reduce_rev_with_initial);
|
|
||||||
lib.set_raw_fn("some", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], some);
|
|
||||||
lib.set_raw_fn("all", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], all);
|
|
||||||
lib.set_raw_fn("sort", &[TypeId::of::<Array>(), TypeId::of::<FnPtr>()], sort);
|
|
||||||
|
|
||||||
// Merge in the module at the end to override `+=` for arrays
|
// Merge in the module at the end to override `+=` for arrays
|
||||||
combine_with_exported_module!(lib, "array", array_functions);
|
combine_with_exported_module!(lib, "array", array_functions);
|
||||||
|
|
||||||
@ -193,87 +194,12 @@ mod array_functions {
|
|||||||
|
|
||||||
list[start..].iter().cloned().collect()
|
list[start..].iter().cloned().collect()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "drain")]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn drain_range(list: &mut Array, start: INT, len: INT) -> Array {
|
pub fn map(
|
||||||
let start = if start < 0 {
|
context: NativeCallContext,
|
||||||
0
|
list: &mut Array,
|
||||||
} else if start as usize >= list.len() {
|
mapper: FnPtr,
|
||||||
list.len() - 1
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
} else {
|
|
||||||
start as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
let len = if len < 0 {
|
|
||||||
0
|
|
||||||
} else if len as usize > list.len() - start {
|
|
||||||
list.len() - start
|
|
||||||
} else {
|
|
||||||
len as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
list.drain(start..start + len - 1).collect()
|
|
||||||
}
|
|
||||||
#[rhai_fn(name = "retain")]
|
|
||||||
pub fn retain_range(list: &mut Array, start: INT, len: INT) -> Array {
|
|
||||||
let start = if start < 0 {
|
|
||||||
0
|
|
||||||
} else if start as usize >= list.len() {
|
|
||||||
list.len() - 1
|
|
||||||
} else {
|
|
||||||
start as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
let len = if len < 0 {
|
|
||||||
0
|
|
||||||
} else if len as usize > list.len() - start {
|
|
||||||
list.len() - start
|
|
||||||
} else {
|
|
||||||
len as usize
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut drained = list.drain(start + len..).collect::<Array>();
|
|
||||||
drained.extend(list.drain(..start));
|
|
||||||
|
|
||||||
drained
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pad<T: Variant + Clone>(
|
|
||||||
_context: NativeCallContext,
|
|
||||||
args: &mut [&mut Dynamic],
|
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let len = *args[1].read_lock::<INT>().unwrap();
|
|
||||||
|
|
||||||
// Check if array will be over max size limit
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
if _context.engine().max_array_size() > 0
|
|
||||||
&& len > 0
|
|
||||||
&& (len as usize) > _context.engine().max_array_size()
|
|
||||||
{
|
|
||||||
return EvalAltResult::ErrorDataTooLarge(
|
|
||||||
"Size of array".to_string(),
|
|
||||||
_context.engine().max_array_size(),
|
|
||||||
len as usize,
|
|
||||||
Position::none(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
if len > 0 {
|
|
||||||
let item = args[2].clone();
|
|
||||||
let mut list = args[0].write_lock::<Array>().unwrap();
|
|
||||||
|
|
||||||
if len as usize > list.len() {
|
|
||||||
list.resize(len as usize, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn map(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Array, Box<EvalAltResult>> {
|
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
|
||||||
let mapper = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut array = Array::with_capacity(list.len());
|
let mut array = Array::with_capacity(list.len());
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
@ -281,7 +207,9 @@ fn map(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Array, B
|
|||||||
mapper
|
mapper
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(mapper.fn_name()) =>
|
||||||
|
{
|
||||||
mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
mapper.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
@ -296,23 +224,23 @@ fn map(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Array, B
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(array)
|
Ok(array.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn filter(
|
pub fn filter(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Array, Box<EvalAltResult>> {
|
filter: FnPtr,
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut array = Array::with_capacity(list.len());
|
let mut array = Array::with_capacity(list.len());
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if filter
|
if filter
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
@ -331,18 +259,21 @@ fn filter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(array)
|
Ok(array.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn some(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<bool, Box<EvalAltResult>> {
|
pub fn some(
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
context: NativeCallContext,
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap();
|
list: &mut Array,
|
||||||
|
filter: FnPtr,
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if filter
|
if filter
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
@ -362,17 +293,20 @@ fn some(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<bool, B
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn all(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<bool, Box<EvalAltResult>> {
|
pub fn all(
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
context: NativeCallContext,
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap();
|
list: &mut Array,
|
||||||
|
filter: FnPtr,
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
if !filter
|
if !filter
|
||||||
.call_dynamic(context, None, [item.clone()])
|
.call_dynamic(context, None, [item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
filter.call_dynamic(context, None, [item.clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
@ -392,23 +326,27 @@ fn all(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<bool, Bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(true.into())
|
Ok(true.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn reduce(
|
pub fn reduce(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
reducer: FnPtr,
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result: Dynamic = ().into();
|
let mut result: Dynamic = ().into();
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate() {
|
for (i, item) in list.iter().enumerate() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
|
None,
|
||||||
|
[result, item.clone(), (i as INT).into()],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -422,16 +360,14 @@ fn reduce(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "reduce", return_raw)]
|
||||||
fn reduce_with_initial(
|
pub fn reduce_with_initial(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
reducer: FnPtr,
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
initial: FnPtr,
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let initial = args[2].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
@ -444,8 +380,14 @@ fn reduce_with_initial(
|
|||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
|
None,
|
||||||
|
[result, item.clone(), (i as INT).into()],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -459,23 +401,27 @@ fn reduce_with_initial(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn reduce_rev(
|
pub fn reduce_rev(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
reducer: FnPtr,
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result: Dynamic = ().into();
|
let mut result: Dynamic = ().into();
|
||||||
|
|
||||||
for (i, item) in list.iter().enumerate().rev() {
|
for (i, item) in list.iter().enumerate().rev() {
|
||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
|
None,
|
||||||
|
[result, item.clone(), (i as INT).into()],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -489,16 +435,14 @@ fn reduce_rev(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "reduce_rev", return_raw)]
|
||||||
fn reduce_rev_with_initial(
|
pub fn reduce_rev_with_initial(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
reducer: FnPtr,
|
||||||
let list = args[0].read_lock::<Array>().unwrap();
|
initial: FnPtr,
|
||||||
let reducer = args[1].read_lock::<FnPtr>().unwrap();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let initial = args[2].read_lock::<FnPtr>().unwrap();
|
|
||||||
|
|
||||||
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
let mut result = initial.call_dynamic(context, None, []).map_err(|err| {
|
||||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||||
"reduce".to_string(),
|
"reduce".to_string(),
|
||||||
@ -511,8 +455,14 @@ fn reduce_rev_with_initial(
|
|||||||
result = reducer
|
result = reducer
|
||||||
.call_dynamic(context, None, [result.clone(), item.clone()])
|
.call_dynamic(context, None, [result.clone(), item.clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
reducer.call_dynamic(context, None, [result, item.clone(), (i as INT).into()])
|
if fn_sig.starts_with(reducer.fn_name()) =>
|
||||||
|
{
|
||||||
|
reducer.call_dynamic(
|
||||||
|
context,
|
||||||
|
None,
|
||||||
|
[result, item.clone(), (i as INT).into()],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
@ -526,15 +476,13 @@ fn reduce_rev_with_initial(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn sort(
|
pub fn sort(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
comparer: FnPtr,
|
||||||
let comparer = args[1].read_lock::<FnPtr>().unwrap().clone();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut list = args[0].write_lock::<Array>().unwrap();
|
|
||||||
|
|
||||||
list.sort_by(|x, y| {
|
list.sort_by(|x, y| {
|
||||||
comparer
|
comparer
|
||||||
.call_dynamic(context, None, [x.clone(), y.clone()])
|
.call_dynamic(context, None, [x.clone(), y.clone()])
|
||||||
@ -564,15 +512,13 @@ fn sort(
|
|||||||
});
|
});
|
||||||
|
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
fn drain(
|
pub fn drain(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Array, Box<EvalAltResult>> {
|
filter: FnPtr,
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap().clone();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut list = args[0].write_lock::<Array>().unwrap();
|
|
||||||
|
|
||||||
let mut drained = Array::with_capacity(list.len());
|
let mut drained = Array::with_capacity(list.len());
|
||||||
|
|
||||||
let mut i = list.len();
|
let mut i = list.len();
|
||||||
@ -583,7 +529,9 @@ fn drain(
|
|||||||
if filter
|
if filter
|
||||||
.call_dynamic(context, None, [list[i].clone()])
|
.call_dynamic(context, None, [list[i].clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
@ -602,16 +550,34 @@ fn drain(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(drained)
|
Ok(drained.into())
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "drain")]
|
||||||
|
pub fn drain_range(list: &mut Array, start: INT, len: INT) -> Array {
|
||||||
|
let start = if start < 0 {
|
||||||
|
0
|
||||||
|
} else if start as usize >= list.len() {
|
||||||
|
list.len() - 1
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
fn retain(
|
let len = if len < 0 {
|
||||||
|
0
|
||||||
|
} else if len as usize > list.len() - start {
|
||||||
|
list.len() - start
|
||||||
|
} else {
|
||||||
|
len as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
list.drain(start..start + len - 1).collect()
|
||||||
|
}
|
||||||
|
#[rhai_fn(return_raw)]
|
||||||
|
pub fn retain(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
list: &mut Array,
|
||||||
) -> Result<Array, Box<EvalAltResult>> {
|
filter: FnPtr,
|
||||||
let filter = args[1].read_lock::<FnPtr>().unwrap().clone();
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut list = args[0].write_lock::<Array>().unwrap();
|
|
||||||
|
|
||||||
let mut drained = Array::with_capacity(list.len());
|
let mut drained = Array::with_capacity(list.len());
|
||||||
|
|
||||||
let mut i = list.len();
|
let mut i = list.len();
|
||||||
@ -622,7 +588,9 @@ fn retain(
|
|||||||
if !filter
|
if !filter
|
||||||
.call_dynamic(context, None, [list[i].clone()])
|
.call_dynamic(context, None, [list[i].clone()])
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(_, _) => {
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
|
if fn_sig.starts_with(filter.fn_name()) =>
|
||||||
|
{
|
||||||
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
filter.call_dynamic(context, None, [list[i].clone(), (i as INT).into()])
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
@ -641,7 +609,31 @@ fn retain(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(drained)
|
Ok(drained.into())
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "retain")]
|
||||||
|
pub fn retain_range(list: &mut Array, start: INT, len: INT) -> Array {
|
||||||
|
let start = if start < 0 {
|
||||||
|
0
|
||||||
|
} else if start as usize >= list.len() {
|
||||||
|
list.len() - 1
|
||||||
|
} else {
|
||||||
|
start as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = if len < 0 {
|
||||||
|
0
|
||||||
|
} else if len as usize > list.len() - start {
|
||||||
|
list.len() - start
|
||||||
|
} else {
|
||||||
|
len as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut drained = list.drain(start + len..).collect::<Array>();
|
||||||
|
drained.extend(list.drain(..start));
|
||||||
|
|
||||||
|
drained
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gen_array_functions!(basic => INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
gen_array_functions!(basic => INT, bool, char, ImmutableString, FnPtr, Array, Unit);
|
||||||
|
@ -3,7 +3,10 @@ use crate::def_package;
|
|||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
|
|
||||||
use crate::stdlib::ops::{Add, Range};
|
use crate::stdlib::{
|
||||||
|
boxed::Box,
|
||||||
|
ops::{Add, Range},
|
||||||
|
};
|
||||||
|
|
||||||
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
|
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
|
||||||
Ok(from..to)
|
Ok(from..to)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
pub use crate::any::Dynamic;
|
pub use crate::any::Dynamic;
|
||||||
pub use crate::engine::Engine;
|
pub use crate::engine::Engine;
|
||||||
pub use crate::fn_native::CallableFunction;
|
pub use crate::fn_native::{CallableFunction, NativeCallContext};
|
||||||
pub use crate::fn_register::{RegisterFn, RegisterResultFn};
|
pub use crate::fn_register::{RegisterFn, RegisterResultFn};
|
||||||
pub use crate::module::Module;
|
pub use crate::module::Module;
|
||||||
pub use crate::parser::FnAccess;
|
pub use crate::parser::FnAccess;
|
||||||
@ -22,7 +22,11 @@ pub use rhai_codegen::{export_fn, register_exported_fn};
|
|||||||
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
|
||||||
pub trait PluginFunction {
|
pub trait PluginFunction {
|
||||||
/// Call the plugin function with the arguments provided.
|
/// Call the plugin function with the arguments provided.
|
||||||
fn call(&self, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>;
|
fn call(
|
||||||
|
&self,
|
||||||
|
context: NativeCallContext,
|
||||||
|
args: &mut [&mut Dynamic],
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||||
|
|
||||||
/// Is this plugin function a method?
|
/// Is this plugin function a method?
|
||||||
fn is_method_call(&self) -> bool;
|
fn is_method_call(&self) -> bool;
|
||||||
|
Loading…
Reference in New Issue
Block a user