Add storage API for NativeCallContext.

This commit is contained in:
Stephen Chung 2022-11-07 16:19:10 +08:00
parent 0c79471fd3
commit b4529b6a64
3 changed files with 83 additions and 47 deletions

View File

@ -41,6 +41,11 @@ New features
* `Scope` is now serializable and deserializable via `serde`. * `Scope` is now serializable and deserializable via `serde`.
### Store and recreate `NativeCallContext`
* A convenient API is added to store a `NativeCallContext` into a new `NativeCallContextStore` type.
* This allows a `NativeCallContext` to be stored and recreated later on.
### Call native Rust functions in `NativeCallContext` ### Call native Rust functions in `NativeCallContext`
* `NativeCallContext::call_native_fn` is added to call registered native Rust functions only. * `NativeCallContext::call_native_fn` is added to call registered native Rust functions only.

View File

@ -72,22 +72,51 @@ pub struct NativeCallContext<'a> {
/// Function source, if any. /// Function source, if any.
source: Option<&'a str>, source: Option<&'a str>,
/// The current [`GlobalRuntimeState`], if any. /// The current [`GlobalRuntimeState`], if any.
global: Option<&'a GlobalRuntimeState<'a>>, global: Option<&'a GlobalRuntimeState>,
/// The current stack of loaded [modules][Module]. /// The current stack of loaded [modules][Module].
lib: &'a [&'a Module], lib: &'a [Shared<Module>],
/// [Position] of the function call. /// [Position] of the function call.
pos: Position, pos: Position,
/// The current nesting level of function calls. /// The current nesting level of function calls.
level: usize, level: usize,
} }
/// _(internals)_ Context of a native Rust function call.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[derive(Debug, Clone)]
pub struct NativeCallContextStore {
/// Name of function called.
pub fn_name: String,
/// Function source, if any.
pub source: Option<String>,
/// The current [`GlobalRuntimeState`], if any.
pub global: GlobalRuntimeState,
/// The current stack of loaded [modules][Module].
pub lib: StaticVec<Shared<Module>>,
/// [Position] of the function call.
pub pos: Position,
/// The current nesting level of function calls.
pub level: usize,
}
#[cfg(feature = "internals")]
impl NativeCallContextStore {
/// Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
#[inline(always)]
#[must_use]
pub fn create_context<'a>(&'a self, engine: &'a Engine) -> NativeCallContext<'a> {
NativeCallContext::from_stored_data(engine, self)
}
}
impl<'a> impl<'a>
From<( From<(
&'a Engine, &'a Engine,
&'a str, &'a str,
Option<&'a str>, Option<&'a str>,
&'a GlobalRuntimeState<'a>, &'a GlobalRuntimeState,
&'a [&Module], &'a [Shared<Module>],
Position, Position,
usize, usize,
)> for NativeCallContext<'a> )> for NativeCallContext<'a>
@ -99,7 +128,7 @@ impl<'a>
&'a str, &'a str,
Option<&'a str>, Option<&'a str>,
&'a GlobalRuntimeState, &'a GlobalRuntimeState,
&'a [&Module], &'a [Shared<Module>],
Position, Position,
usize, usize,
), ),
@ -116,9 +145,9 @@ impl<'a>
} }
} }
impl<'a> From<(&'a Engine, &'a str, &'a [&'a Module])> for NativeCallContext<'a> { impl<'a> From<(&'a Engine, &'a str, &'a [Shared<Module>])> for NativeCallContext<'a> {
#[inline(always)] #[inline(always)]
fn from(value: (&'a Engine, &'a str, &'a [&Module])) -> Self { fn from(value: (&'a Engine, &'a str, &'a [Shared<Module>])) -> Self {
Self { Self {
engine: value.0, engine: value.0,
fn_name: value.1, fn_name: value.1,
@ -140,7 +169,7 @@ impl<'a> NativeCallContext<'a> {
)] )]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [Shared<Module>]) -> Self {
Self { Self {
engine, engine,
fn_name, fn_name,
@ -164,7 +193,7 @@ impl<'a> NativeCallContext<'a> {
fn_name: &'a str, fn_name: &'a str,
source: Option<&'a str>, source: Option<&'a str>,
global: &'a GlobalRuntimeState, global: &'a GlobalRuntimeState,
lib: &'a [&Module], lib: &'a [Shared<Module>],
pos: Position, pos: Position,
level: usize, level: usize,
) -> Self { ) -> Self {
@ -178,6 +207,39 @@ impl<'a> NativeCallContext<'a> {
level, level,
} }
} }
/// _(internals)_ Create a [`NativeCallContext`] from a [`NativeCallContextClone`].
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline]
#[must_use]
pub fn from_stored_data(engine: &'a Engine, context: &'a NativeCallContextStore) -> Self {
Self {
engine,
fn_name: &context.fn_name,
source: context.source.as_ref().map(String::as_str),
global: Some(&context.global),
lib: &context.lib,
pos: context.pos,
level: context.level,
}
}
/// _(internals)_ Store this [`NativeCallContext`] into a [`NativeCallContextClone`].
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline]
#[must_use]
pub fn store_data(&self) -> NativeCallContextStore {
NativeCallContextStore {
fn_name: self.fn_name.to_string(),
source: self.source.map(|s| s.to_string()),
global: self.global.unwrap().clone(),
lib: self.lib.iter().cloned().collect(),
pos: self.pos,
level: self.level,
}
}
/// The current [`Engine`]. /// The current [`Engine`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
@ -246,14 +308,14 @@ impl<'a> NativeCallContext<'a> {
/// in reverse order (i.e. parent namespaces are iterated after child namespaces). /// in reverse order (i.e. parent namespaces are iterated after child namespaces).
#[inline] #[inline]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
self.lib.iter().copied() self.lib.iter().map(|m| m.as_ref())
} }
/// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions. /// _(internals)_ The current stack of namespaces containing definitions of all script-defined functions.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn namespaces(&self) -> &[&Module] { pub const fn namespaces(&self) -> &[Shared<Module>] {
self.lib self.lib
} }
/// Call a function inside the call context with the provided arguments. /// Call a function inside the call context with the provided arguments.

View File

@ -2,7 +2,7 @@
use rhai::{ use rhai::{
module_resolvers::{DummyModuleResolver, StaticModuleResolver}, module_resolvers::{DummyModuleResolver, StaticModuleResolver},
Dynamic, Engine, EvalAltResult, FnNamespace, FnPtr, ImmutableString, Module, NativeCallContext, Dynamic, Engine, EvalAltResult, FnNamespace, FnPtr, ImmutableString, Module, NativeCallContext,
ParseError, ParseErrorType, Scope, Shared, INT, ParseError, ParseErrorType, Scope, INT,
}; };
#[test] #[test]
@ -546,44 +546,13 @@ fn test_module_context() -> Result<(), Box<EvalAltResult>> {
engine.register_fn( engine.register_fn(
"calc", "calc",
|context: NativeCallContext, fp: FnPtr| -> Result<INT, Box<EvalAltResult>> { |context: NativeCallContext, fp: FnPtr| -> Result<INT, Box<EvalAltResult>> {
// Store fields for later use
let engine = context.engine(); let engine = context.engine();
let fn_name = context.fn_name().to_string();
let source = context.source().map(|s| s.to_string());
let global = context.global_runtime_state().unwrap().clone();
let pos = context.position();
let call_level = context.call_level();
// Store the paths of the stack of call modules up to this point // Store context for later use - requires the 'internals' feature
let modules_list: Vec<String> = context let context_data = context.store_data();
.iter_namespaces()
.map(|m| m.id().unwrap_or("testing"))
.filter(|id| !id.is_empty())
.map(|id| id.to_string())
.collect();
// Recreate the 'NativeCallContext' - requires the 'internals' feature // Recreate the 'NativeCallContext'
let mut libraries = Vec::<Shared<Module>>::new(); let new_context = context_data.create_context(engine);
for path in modules_list {
// Recreate the stack of call modules by resolving each path with
// the module resolver.
let module = engine.module_resolver().resolve(engine, None, &path, pos)?;
libraries.push(module);
}
let lib: Vec<&Module> = libraries.iter().map(|m| m.as_ref()).collect();
let new_context = NativeCallContext::new_with_all_fields(
engine,
&fn_name,
source.as_ref().map(String::as_str),
&global,
&lib,
pos,
call_level,
);
fp.call_within_context(&new_context, (41 as INT,)) fp.call_within_context(&new_context, (41 as INT,))
}, },