Add NativeCallContext::position.

This commit is contained in:
Stephen Chung 2021-11-05 19:35:33 +08:00
parent ff9ac41da2
commit affbb81d8a
6 changed files with 57 additions and 18 deletions

View File

@ -19,6 +19,7 @@ Enhancements
* Array adds a `sort` method with no parameters which sorts homogeneous arrays of built-in comparable types (e.g. `INT`). * Array adds a `sort` method with no parameters which sorts homogeneous arrays of built-in comparable types (e.g. `INT`).
* Inlining is disabled for error-path functions because errors are exceptional and scripts usually fail completely when an error is encountered. * Inlining is disabled for error-path functions because errors are exceptional and scripts usually fail completely when an error is encountered.
* The `optimize` module is completely eliminated under `no_optimize`, which should yield smaller code size. * The `optimize` module is completely eliminated under `no_optimize`, which should yield smaller code size.
* Add `NativeCallContext::position` to return the position of the function call.
Deprecated API's Deprecated API's
---------------- ----------------

View File

@ -339,12 +339,14 @@ impl Engine {
.map(|s| s.as_str()); .map(|s| s.as_str());
let result = if func.is_plugin_fn() { let result = if func.is_plugin_fn() {
let context = (self, name, source, mods, lib, pos).into();
func.get_plugin_fn() func.get_plugin_fn()
.expect("plugin function") .expect("plugin function")
.call((self, name, source, mods, lib).into(), args) .call(context, args)
} else { } else {
let func = func.get_native_fn().expect("native function"); let func = func.get_native_fn().expect("native function");
func((self, name, source, mods, lib).into(), args) let context = (self, name, source, mods, lib, pos).into();
func(context, args)
}; };
// Restore the original reference // Restore the original reference
@ -1448,17 +1450,19 @@ impl Engine {
} }
} }
Some(f) if f.is_plugin_fn() => f Some(f) if f.is_plugin_fn() => {
.get_plugin_fn() let context = (self, fn_name, module.id(), &*mods, lib, pos).into();
.expect("plugin function") f.get_plugin_fn()
.clone() .expect("plugin function")
.call((self, fn_name, module.id(), &*mods, lib).into(), &mut args) .clone()
.map_err(|err| err.fill_position(pos)), .call(context, &mut args)
.map_err(|err| err.fill_position(pos))
}
Some(f) if f.is_native() => { Some(f) if f.is_native() => {
let func = f.get_native_fn().expect("native function"); let func = f.get_native_fn().expect("native function");
func((self, fn_name, module.id(), &*mods, lib).into(), &mut args) let context = (self, fn_name, module.id(), &*mods, lib, pos).into();
.map_err(|err| err.fill_position(pos)) func(context, &mut args).map_err(|err| err.fill_position(pos))
} }
Some(f) => unreachable!("unknown function type: {:?}", f), Some(f) => unreachable!("unknown function type: {:?}", f),

View File

@ -54,34 +54,53 @@ pub struct NativeCallContext<'a> {
source: Option<&'a str>, source: Option<&'a str>,
mods: Option<&'a Imports>, mods: Option<&'a Imports>,
lib: &'a [&'a Module], lib: &'a [&'a Module],
pos: Position,
} }
impl<'a, M: AsRef<[&'a Module]> + ?Sized> impl<'a, M: AsRef<[&'a Module]> + ?Sized>
From<(&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)> for NativeCallContext<'a> From<(
&'a Engine,
&'a str,
Option<&'a str>,
&'a Imports,
&'a M,
Position,
)> for NativeCallContext<'a>
{ {
#[inline(always)] #[inline(always)]
fn from(value: (&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)) -> Self { fn from(
value: (
&'a Engine,
&'a str,
Option<&'a str>,
&'a Imports,
&'a M,
Position,
),
) -> Self {
Self { Self {
engine: value.0, engine: value.0,
fn_name: value.1, fn_name: value.1,
source: value.2, source: value.2,
mods: Some(value.3), mods: Some(value.3),
lib: value.4.as_ref(), lib: value.4.as_ref(),
pos: value.5,
} }
} }
} }
impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, &'a M)> impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, &'a M, Position)>
for NativeCallContext<'a> for NativeCallContext<'a>
{ {
#[inline(always)] #[inline(always)]
fn from(value: (&'a Engine, &'a str, &'a M)) -> Self { fn from(value: (&'a Engine, &'a str, &'a M, Position)) -> Self {
Self { Self {
engine: value.0, engine: value.0,
fn_name: value.1, fn_name: value.1,
source: None, source: None,
mods: None, mods: None,
lib: value.2.as_ref(), lib: value.2.as_ref(),
pos: value.3,
} }
} }
} }
@ -90,13 +109,19 @@ impl<'a> NativeCallContext<'a> {
/// Create a new [`NativeCallContext`]. /// Create a new [`NativeCallContext`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { pub const fn new(
engine: &'a Engine,
fn_name: &'a str,
lib: &'a [&Module],
pos: Position,
) -> Self {
Self { Self {
engine, engine,
fn_name, fn_name,
source: None, source: None,
mods: None, mods: None,
lib, lib,
pos,
} }
} }
/// _(internals)_ Create a new [`NativeCallContext`]. /// _(internals)_ Create a new [`NativeCallContext`].
@ -113,6 +138,7 @@ impl<'a> NativeCallContext<'a> {
source: Option<&'a str>, source: Option<&'a str>,
imports: &'a Imports, imports: &'a Imports,
lib: &'a [&Module], lib: &'a [&Module],
pos: Position,
) -> Self { ) -> Self {
Self { Self {
engine, engine,
@ -120,6 +146,7 @@ impl<'a> NativeCallContext<'a> {
source, source,
mods: Some(imports), mods: Some(imports),
lib, lib,
pos,
} }
} }
/// The current [`Engine`]. /// The current [`Engine`].
@ -134,6 +161,12 @@ impl<'a> NativeCallContext<'a> {
pub const fn fn_name(&self) -> &str { pub const fn fn_name(&self) -> &str {
self.fn_name self.fn_name
} }
/// [Position][`Position`] of the function call.
#[inline(always)]
#[must_use]
pub const fn position(&self) -> Position {
self.pos
}
/// The current source. /// The current source.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]

View File

@ -1000,9 +1000,9 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
_ if x.args.len() == 2 && !state.has_native_fn(x.hashes.native, arg_types.as_ref()) => { _ if x.args.len() == 2 && !state.has_native_fn(x.hashes.native, arg_types.as_ref()) => {
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
let ctx = (state.engine, x.name.as_ref(), state.lib).into(); let context = (state.engine, x.name.as_ref(), state.lib, Position::NONE).into();
let (first, second) = arg_values.split_first_mut().expect("`arg_values` is not empty"); let (first, second) = arg_values.split_first_mut().expect("`arg_values` is not empty");
(f)(ctx, &mut [ first, &mut second[0] ]).ok() (f)(context, &mut [ first, &mut second[0] ]).ok()
}) { }) {
state.set_dirty(); state.set_dirty();
*expr = Expr::from_dynamic(result, *pos); *expr = Expr::from_dynamic(result, *pos);

View File

@ -36,4 +36,5 @@ fn check_struct_sizes() {
if cfg!(feature = "no_position") { 8 } else { 16 } if cfg!(feature = "no_position") { 8 } else { 16 }
); );
assert_eq!(size_of::<EvalAltResult>(), 72); assert_eq!(size_of::<EvalAltResult>(), 72);
assert_eq!(size_of::<NativeCallContext>(), 72);
} }

View File

@ -338,7 +338,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
// Create native call context // Create native call context
let fn_name = fn_ptr.fn_name().to_string(); let fn_name = fn_ptr.fn_name().to_string();
let context = NativeCallContext::new(&engine, &fn_name, &lib); let context = NativeCallContext::new(&engine, &fn_name, &lib, rhai::Position::NONE);
// Closure 'f' captures: the engine, the AST, and the curried function pointer // Closure 'f' captures: the engine, the AST, and the curried function pointer
let f = move |x: INT| fn_ptr.call_dynamic(&context, None, [x.into()]); let f = move |x: INT| fn_ptr.call_dynamic(&context, None, [x.into()]);