Add call_native_fn for context.
This commit is contained in:
parent
71e475be13
commit
ea63c66cf0
35
CHANGELOG.md
35
CHANGELOG.md
@ -11,23 +11,44 @@ Bug fixes
|
||||
* `import` statements inside `eval` no longer cause errors in subsequent code.
|
||||
* Functions marked `global` in `import`ed modules with no alias names now work properly.
|
||||
|
||||
Speed Improvements
|
||||
------------------
|
||||
|
||||
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
||||
|
||||
New features
|
||||
------------
|
||||
|
||||
### Stable hashing
|
||||
|
||||
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures. This is necessary when using Rhai across shared-library boundaries.
|
||||
* A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation.
|
||||
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures.
|
||||
* This is necessary when using Rhai across shared-library boundaries.
|
||||
* A build script is used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation.
|
||||
|
||||
### Serializable `Scope`
|
||||
|
||||
* `Scope` is now serializable and deserializable via `serde`.
|
||||
|
||||
### Call native Rust functions in `NativeCallContext`
|
||||
|
||||
* `NativeCallContext::call_native_fn` is added to call registered native Rust functions only.
|
||||
* `NativeCallContext::call_native_fn_raw` is added as the advanced version.
|
||||
* This is often desirable as Rust functions typically do not want a similar-named scripted function to hijack the process -- which will cause brittleness.
|
||||
|
||||
### Custom syntax improvements
|
||||
|
||||
* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`).
|
||||
* This facilitates more accurate parsing by separating strings and identifiers.
|
||||
|
||||
### Limits API
|
||||
|
||||
* Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked`.
|
||||
* This helps avoid the proliferation of unnecessary feature flags in third-party library code.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers.
|
||||
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
|
||||
* `Scope` is now serializable and deserializable via `serde`.
|
||||
* `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline.
|
||||
* `parse_json` function is added to parse a JSON string into an object map.
|
||||
* Methods returning maximum limits (e.g. `Engine::max_string_len`) are now available even under `unchecked` in order to avoid unnecessary feature flags in third-party library code.
|
||||
|
||||
|
||||
Version 1.10.1
|
||||
|
@ -233,6 +233,7 @@ impl Engine {
|
||||
arg_values,
|
||||
)
|
||||
}
|
||||
|
||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||
fn _call_fn(
|
||||
&self,
|
||||
|
@ -135,7 +135,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
|
||||
|
||||
impl<'a> NativeCallContext<'a> {
|
||||
/// _(internals)_ Create a new [`NativeCallContext`].
|
||||
/// Exported under the `metadata` feature only.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[deprecated(
|
||||
since = "1.3.0",
|
||||
note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly."
|
||||
@ -274,7 +274,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
|
||||
let result = self.call_fn_raw(fn_name, false, false, &mut args)?;
|
||||
let result = self._call_fn_raw(fn_name, false, false, false, &mut args)?;
|
||||
|
||||
let typ = self.engine().map_type_name(result.type_name());
|
||||
|
||||
@ -283,7 +283,32 @@ impl<'a> NativeCallContext<'a> {
|
||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||
})
|
||||
}
|
||||
/// Call a function inside the call context.
|
||||
/// Call a registered native Rust function inside the call context with the provided arguments.
|
||||
///
|
||||
/// This is often useful because Rust functions typically only want to cross-call other
|
||||
/// registered Rust functions and not have to worry about scripted functions hijacking the
|
||||
/// process unknowingly (or deliberately).
|
||||
#[inline]
|
||||
pub fn call_native_fn<T: Variant + Clone>(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
args: impl FuncArgs,
|
||||
) -> RhaiResultOf<T> {
|
||||
let mut arg_values = StaticVec::new_const();
|
||||
args.parse(&mut arg_values);
|
||||
|
||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
||||
|
||||
let result = self._call_fn_raw(fn_name, true, false, false, &mut args)?;
|
||||
|
||||
let typ = self.engine().map_type_name(result.type_name());
|
||||
|
||||
result.try_cast().ok_or_else(|| {
|
||||
let t = self.engine().map_type_name(type_name::<T>()).into();
|
||||
ERR::ErrorMismatchOutputType(t, typ.into(), Position::NONE).into()
|
||||
})
|
||||
}
|
||||
/// Call a function (native Rust or scripted) inside the call context.
|
||||
///
|
||||
/// If `is_method_call` is [`true`], the first argument is assumed to be the `this` pointer for
|
||||
/// a script-defined function (or the object of a method call).
|
||||
@ -302,6 +327,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
///
|
||||
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
||||
/// not consumed.
|
||||
#[inline(always)]
|
||||
pub fn call_fn_raw(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
@ -309,15 +335,76 @@ impl<'a> NativeCallContext<'a> {
|
||||
is_method_call: bool,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
let mut global = self
|
||||
self._call_fn_raw(fn_name, false, is_ref_mut, is_method_call, args)
|
||||
}
|
||||
/// Call a registered native Rust function inside the call context.
|
||||
///
|
||||
/// This is often useful because Rust functions typically only want to cross-call other
|
||||
/// registered Rust functions and not have to worry about scripted functions hijacking the
|
||||
/// process unknowingly (or deliberately).
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
///
|
||||
/// This function is very low level.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// All arguments may be _consumed_, meaning that they may be replaced by `()`. This is to avoid
|
||||
/// unnecessarily cloning the arguments.
|
||||
///
|
||||
/// **DO NOT** reuse the arguments after this call. If they are needed afterwards, clone them
|
||||
/// _before_ calling this function.
|
||||
///
|
||||
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
||||
/// not consumed.
|
||||
#[inline(always)]
|
||||
pub fn call_native_fn_raw(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
is_ref_mut: bool,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
self._call_fn_raw(fn_name, true, is_ref_mut, false, args)
|
||||
}
|
||||
|
||||
/// Call a function (native Rust or scripted) inside the call context.
|
||||
fn _call_fn_raw(
|
||||
&self,
|
||||
fn_name: impl AsRef<str>,
|
||||
native_only: bool,
|
||||
is_ref_mut: bool,
|
||||
is_method_call: bool,
|
||||
args: &mut [&mut Dynamic],
|
||||
) -> RhaiResult {
|
||||
let global = &mut self
|
||||
.global
|
||||
.cloned()
|
||||
.unwrap_or_else(|| GlobalRuntimeState::new(self.engine()));
|
||||
let mut caches = Caches::new();
|
||||
let caches = &mut Caches::new();
|
||||
|
||||
let fn_name = fn_name.as_ref();
|
||||
let args_len = args.len();
|
||||
|
||||
if native_only {
|
||||
return self
|
||||
.engine()
|
||||
.call_native_fn(
|
||||
global,
|
||||
caches,
|
||||
self.lib,
|
||||
fn_name,
|
||||
calc_fn_hash(None, fn_name, args_len),
|
||||
args,
|
||||
is_ref_mut,
|
||||
false,
|
||||
Position::NONE,
|
||||
self.level + 1,
|
||||
)
|
||||
.map(|(r, ..)| r);
|
||||
}
|
||||
|
||||
// Native or script
|
||||
|
||||
let hash = if is_method_call {
|
||||
FnCallHashes::from_all(
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
@ -331,8 +418,8 @@ impl<'a> NativeCallContext<'a> {
|
||||
self.engine()
|
||||
.exec_fn_call(
|
||||
None,
|
||||
&mut global,
|
||||
&mut caches,
|
||||
global,
|
||||
caches,
|
||||
self.lib,
|
||||
fn_name,
|
||||
hash,
|
||||
|
@ -835,7 +835,7 @@ pub mod array_functions {
|
||||
|
||||
for item in array {
|
||||
if ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||
.or_else(|err| match *err {
|
||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||
if item.type_id() == value.type_id() {
|
||||
@ -927,7 +927,7 @@ pub mod array_functions {
|
||||
|
||||
for (i, item) in array.iter_mut().enumerate().skip(start) {
|
||||
if ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [item, &mut value.clone()])
|
||||
.or_else(|err| match *err {
|
||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||
if item.type_id() == value.type_id() {
|
||||
@ -2313,7 +2313,7 @@ pub mod array_functions {
|
||||
|
||||
for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
|
||||
if !ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2])
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [a1, a2])
|
||||
.or_else(|err| match *err {
|
||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||
if a1.type_id() == a2.type_id() {
|
||||
|
@ -213,7 +213,7 @@ mod map_functions {
|
||||
for (m1, v1) in map1 {
|
||||
if let Some(v2) = map2.get_mut(m1) {
|
||||
let equals = ctx
|
||||
.call_fn_raw(OP_EQUALS, true, false, &mut [v1, v2])?
|
||||
.call_native_fn_raw(OP_EQUALS, true, &mut [v1, v2])?
|
||||
.as_bool()
|
||||
.unwrap_or(false);
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub fn print_with_func(
|
||||
ctx: &NativeCallContext,
|
||||
value: &mut Dynamic,
|
||||
) -> crate::ImmutableString {
|
||||
match ctx.call_fn_raw(fn_name, true, false, &mut [value]) {
|
||||
match ctx.call_native_fn_raw(fn_name, true, &mut [value]) {
|
||||
Ok(result) if result.is::<crate::ImmutableString>() => {
|
||||
result.into_immutable_string().expect("`ImmutableString`")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user