Allow NativeCallContext in function arguments.

This commit is contained in:
Stephen Chung
2020-10-18 21:47:34 +08:00
parent dc4c47e008
commit 46b92c9d1f
17 changed files with 710 additions and 534 deletions

View File

@@ -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
----------------------------