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.
|
* `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.
|
* 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
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
### Stable hashing
|
### 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.
|
* 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.
|
||||||
* A build script is now used to extract the environment variable (`RHAI_AHASH_SEED`) and splice it into the source code before compilation.
|
* 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
|
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.
|
* `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
|
Version 1.10.1
|
||||||
|
@ -233,6 +233,7 @@ impl Engine {
|
|||||||
arg_values,
|
arg_values,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
fn _call_fn(
|
fn _call_fn(
|
||||||
&self,
|
&self,
|
||||||
|
@ -135,7 +135,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
|
|||||||
|
|
||||||
impl<'a> NativeCallContext<'a> {
|
impl<'a> NativeCallContext<'a> {
|
||||||
/// _(internals)_ Create a new [`NativeCallContext`].
|
/// _(internals)_ Create a new [`NativeCallContext`].
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[deprecated(
|
#[deprecated(
|
||||||
since = "1.3.0",
|
since = "1.3.0",
|
||||||
note = "`NativeCallContext::new` will be moved under `internals`. Use `FnPtr::call` to call a function pointer directly."
|
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 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());
|
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()
|
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
|
/// 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).
|
/// 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
|
/// If `is_ref_mut` is [`true`], the first argument is assumed to be passed by reference and is
|
||||||
/// not consumed.
|
/// not consumed.
|
||||||
|
#[inline(always)]
|
||||||
pub fn call_fn_raw(
|
pub fn call_fn_raw(
|
||||||
&self,
|
&self,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: impl AsRef<str>,
|
||||||
@ -309,15 +335,76 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
is_method_call: bool,
|
is_method_call: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> RhaiResult {
|
) -> 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
|
.global
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| GlobalRuntimeState::new(self.engine()));
|
.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 fn_name = fn_name.as_ref();
|
||||||
let args_len = args.len();
|
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 {
|
let hash = if is_method_call {
|
||||||
FnCallHashes::from_all(
|
FnCallHashes::from_all(
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -331,8 +418,8 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
self.engine()
|
self.engine()
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
None,
|
None,
|
||||||
&mut global,
|
global,
|
||||||
&mut caches,
|
caches,
|
||||||
self.lib,
|
self.lib,
|
||||||
fn_name,
|
fn_name,
|
||||||
hash,
|
hash,
|
||||||
|
@ -835,7 +835,7 @@ pub mod array_functions {
|
|||||||
|
|
||||||
for item in array {
|
for item in array {
|
||||||
if ctx
|
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 {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||||
if item.type_id() == value.type_id() {
|
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) {
|
for (i, item) in array.iter_mut().enumerate().skip(start) {
|
||||||
if ctx
|
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 {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||||
if item.type_id() == value.type_id() {
|
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()) {
|
for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
|
||||||
if !ctx
|
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 {
|
.or_else(|err| match *err {
|
||||||
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
|
||||||
if a1.type_id() == a2.type_id() {
|
if a1.type_id() == a2.type_id() {
|
||||||
|
@ -213,7 +213,7 @@ mod map_functions {
|
|||||||
for (m1, v1) in map1 {
|
for (m1, v1) in map1 {
|
||||||
if let Some(v2) = map2.get_mut(m1) {
|
if let Some(v2) = map2.get_mut(m1) {
|
||||||
let equals = ctx
|
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()
|
.as_bool()
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ pub fn print_with_func(
|
|||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
value: &mut Dynamic,
|
value: &mut Dynamic,
|
||||||
) -> crate::ImmutableString {
|
) -> 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>() => {
|
Ok(result) if result.is::<crate::ImmutableString>() => {
|
||||||
result.into_immutable_string().expect("`ImmutableString`")
|
result.into_immutable_string().expect("`ImmutableString`")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user