Propagate source info.
This commit is contained in:
parent
386e34aacd
commit
ba7f8c6391
14
RELEASES.md
14
RELEASES.md
@ -1,6 +1,20 @@
|
||||
Rhai Release Notes
|
||||
==================
|
||||
|
||||
Version 0.19.10
|
||||
===============
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* The error variant `EvalAltResult::ErrorInFunctionCall` has a new parameter holding the _source_ of the function.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* Source information is provided when there is an error within a call to a function defined in another module.
|
||||
|
||||
|
||||
Version 0.19.9
|
||||
==============
|
||||
|
||||
|
@ -89,7 +89,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
let module = match engine
|
||||
let mut module = match engine
|
||||
.compile(&contents)
|
||||
.map_err(|err| err.into())
|
||||
.and_then(|ast| Module::eval_ast_as_new(Default::default(), &ast, &engine))
|
||||
@ -106,6 +106,8 @@ fn main() {
|
||||
Ok(m) => m,
|
||||
};
|
||||
|
||||
module.set_id(Some(&filename));
|
||||
|
||||
engine.register_global_module(module.into());
|
||||
|
||||
has_init_scripts = true;
|
||||
|
@ -508,7 +508,11 @@ pub struct State {
|
||||
/// Number of modules loaded.
|
||||
pub modules: usize,
|
||||
/// Cached lookup values for function hashes.
|
||||
pub functions_cache: HashMap<NonZeroU64, Option<CallableFunction>, StraightHasherBuilder>,
|
||||
pub functions_cache: HashMap<
|
||||
NonZeroU64,
|
||||
Option<(CallableFunction, Option<ImmutableString>)>,
|
||||
StraightHasherBuilder,
|
||||
>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
105
src/fn_call.rs
105
src/fn_call.rs
@ -175,11 +175,8 @@ impl Engine {
|
||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||
self.inc_operations(state, pos)?;
|
||||
|
||||
let func = state.functions_cache.get(&hash_fn).cloned();
|
||||
|
||||
let func = if let Some(ref f) = func {
|
||||
f.as_ref()
|
||||
} else {
|
||||
// Check if function access already in the cache
|
||||
if !state.functions_cache.contains_key(&hash_fn) {
|
||||
// Search for the native function
|
||||
// First search registered functions (can override packages)
|
||||
// Then search packages
|
||||
@ -189,18 +186,24 @@ impl Engine {
|
||||
let f = self
|
||||
.global_namespace
|
||||
.get_fn(hash_fn, pub_only)
|
||||
.cloned()
|
||||
.map(|f| (f, None))
|
||||
.or_else(|| {
|
||||
self.global_modules
|
||||
.iter()
|
||||
.find_map(|m| m.get_fn(hash_fn, false))
|
||||
self.global_modules.iter().find_map(|m| {
|
||||
m.get_fn(hash_fn, false)
|
||||
.cloned()
|
||||
.map(|f| (f, m.id_raw().clone()))
|
||||
})
|
||||
.or_else(|| mods.get_fn(hash_fn));
|
||||
})
|
||||
.or_else(|| mods.get_fn(hash_fn).cloned().map(|f| (f, None)));
|
||||
|
||||
state.functions_cache.insert(hash_fn, f.cloned());
|
||||
f
|
||||
};
|
||||
// Store into cache
|
||||
state.functions_cache.insert(hash_fn, f);
|
||||
}
|
||||
|
||||
if let Some(func) = func {
|
||||
let func = state.functions_cache.get(&hash_fn).unwrap();
|
||||
|
||||
if let Some((func, source)) = func {
|
||||
assert!(func.is_native());
|
||||
|
||||
// Calling pure function but the first argument is a reference?
|
||||
@ -208,11 +211,16 @@ impl Engine {
|
||||
backup.change_first_arg_to_copy(is_ref && func.is_pure(), args);
|
||||
|
||||
// Run external function
|
||||
let source = if source.is_none() {
|
||||
&state.source
|
||||
} else {
|
||||
source
|
||||
};
|
||||
let result = if func.is_plugin_fn() {
|
||||
func.get_plugin_fn()
|
||||
.call((self, &state.source, mods, lib).into(), args)
|
||||
.call((self, source, mods, lib).into(), args)
|
||||
} else {
|
||||
func.get_native_fn()((self, &state.source, mods, lib).into(), args)
|
||||
func.get_native_fn()((self, source, mods, lib).into(), args)
|
||||
};
|
||||
|
||||
// Restore the original reference
|
||||
@ -413,9 +421,26 @@ impl Engine {
|
||||
.or_else(|err| match *err {
|
||||
// Convert return statement to return value
|
||||
EvalAltResult::Return(x, _) => Ok(x),
|
||||
EvalAltResult::ErrorInFunctionCall(name, err, _) => {
|
||||
EvalAltResult::ErrorInFunctionCall(name, src, err, _) => {
|
||||
EvalAltResult::ErrorInFunctionCall(
|
||||
format!("{} > {}", fn_def.name, name),
|
||||
format!(
|
||||
"{}{} < {}",
|
||||
name,
|
||||
if src.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(" @ '{}'", src)
|
||||
},
|
||||
fn_def.name
|
||||
),
|
||||
fn_def
|
||||
.lib
|
||||
.as_ref()
|
||||
.map(|m| m.id())
|
||||
.flatten()
|
||||
.or_else(|| state.source.as_ref().map(|s| s.as_str()))
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
err,
|
||||
pos,
|
||||
)
|
||||
@ -424,7 +449,20 @@ impl Engine {
|
||||
// System errors are passed straight-through
|
||||
err if err.is_system_exception() => Err(Box::new(err)),
|
||||
// Other errors are wrapped in `ErrorInFunctionCall`
|
||||
_ => EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, pos).into(),
|
||||
_ => EvalAltResult::ErrorInFunctionCall(
|
||||
fn_def.name.to_string(),
|
||||
fn_def
|
||||
.lib
|
||||
.as_ref()
|
||||
.map(|m| m.id())
|
||||
.flatten()
|
||||
.or_else(|| state.source.as_ref().map(|s| s.as_str()))
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
err,
|
||||
pos,
|
||||
)
|
||||
.into(),
|
||||
});
|
||||
|
||||
// Remove all local variables
|
||||
@ -553,15 +591,16 @@ impl Engine {
|
||||
})
|
||||
//.or_else(|| self.global_namespace.get_fn(hash_script, pub_only))
|
||||
.or_else(|| {
|
||||
self.global_modules
|
||||
.iter()
|
||||
.find_map(|m| m.get_fn(hash_script, false))
|
||||
.map(|f| (f, None))
|
||||
self.global_modules.iter().find_map(|m| {
|
||||
m.get_fn(hash_script, false)
|
||||
.map(|f| (f, m.id_raw().clone()))
|
||||
})
|
||||
})
|
||||
//.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.id_raw().clone()))))
|
||||
.unwrap();
|
||||
|
||||
if func.is_script() {
|
||||
assert!(func.is_script());
|
||||
|
||||
let func = func.get_fn_def();
|
||||
|
||||
let scope: &mut Scope = &mut Default::default();
|
||||
@ -614,9 +653,8 @@ impl Engine {
|
||||
|
||||
let level = _level + 1;
|
||||
|
||||
let result = self.call_script_fn(
|
||||
scope, mods, state, lib, &mut None, func, args, pos, level,
|
||||
);
|
||||
let result = self
|
||||
.call_script_fn(scope, mods, state, lib, &mut None, func, args, pos, level);
|
||||
|
||||
// Restore the original source
|
||||
state.source = source;
|
||||
@ -628,21 +666,6 @@ impl Engine {
|
||||
};
|
||||
|
||||
Ok((result, false))
|
||||
} else {
|
||||
// If it is a native function, redirect it
|
||||
self.call_native_fn(
|
||||
mods,
|
||||
state,
|
||||
lib,
|
||||
fn_name,
|
||||
hash_script,
|
||||
args,
|
||||
is_ref,
|
||||
pub_only,
|
||||
pos,
|
||||
def_val,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Normal native function call
|
||||
|
@ -214,6 +214,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"map".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -245,6 +246,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"filter".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -278,6 +280,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"index_of".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -311,6 +314,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"some".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -344,6 +348,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"all".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -379,6 +384,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -397,6 +403,7 @@ mod array_functions {
|
||||
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -416,6 +423,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -446,6 +454,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce_rev".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -464,6 +473,7 @@ mod array_functions {
|
||||
let mut result = initial.call_dynamic(ctx, None, []).map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce_rev".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -483,6 +493,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"reduce_rev".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -553,6 +564,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"drain".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
@ -612,6 +624,7 @@ mod array_functions {
|
||||
.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
"retain".to_string(),
|
||||
ctx.source().unwrap_or("").to_string(),
|
||||
err,
|
||||
Position::NONE,
|
||||
))
|
||||
|
@ -34,8 +34,8 @@ pub enum EvalAltResult {
|
||||
/// Call to an unknown function. Wrapped value is the function signature.
|
||||
ErrorFunctionNotFound(String, Position),
|
||||
/// An error has occurred inside a called function.
|
||||
/// Wrapped values are the function name and the interior error.
|
||||
ErrorInFunctionCall(String, Box<EvalAltResult>, Position),
|
||||
/// Wrapped values are the function name, function source, and the interior error.
|
||||
ErrorInFunctionCall(String, String, Box<EvalAltResult>, Position),
|
||||
/// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name.
|
||||
ErrorModuleNotFound(String, Position),
|
||||
/// An error has occurred while loading a [module][crate::Module].
|
||||
@ -98,7 +98,7 @@ impl EvalAltResult {
|
||||
#[allow(deprecated)]
|
||||
Self::ErrorSystem(_, s) => s.description(),
|
||||
Self::ErrorParsing(p, _) => p.desc(),
|
||||
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
||||
Self::ErrorInFunctionCall(_,_, _, _) => "Error in called function",
|
||||
Self::ErrorInModule(_, _, _) => "Error in module",
|
||||
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
||||
Self::ErrorUnboundThis(_) => "'this' is not bound",
|
||||
@ -152,12 +152,19 @@ impl fmt::Display for EvalAltResult {
|
||||
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::ErrorInFunctionCall(s, err, _) if crate::engine::is_anonymous_fn(s) => {
|
||||
write!(f, "Error in call to closure: {}", err)?
|
||||
Self::ErrorInFunctionCall(s, src, err, _) if crate::engine::is_anonymous_fn(s) => {
|
||||
write!(f, "{}, in call to closure", err)?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
}
|
||||
Self::ErrorInFunctionCall(s, err, _) => {
|
||||
write!(f, "Error in call to function '{}': {}", s, err)?
|
||||
}
|
||||
Self::ErrorInFunctionCall(s, src, err, _) => {
|
||||
write!(f, "{}, in call to function {}", err, s)?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
}
|
||||
}
|
||||
|
||||
Self::ErrorInModule(s, err, _) if s.is_empty() => {
|
||||
write!(f, "Error in module: {}", err)?
|
||||
}
|
||||
@ -165,8 +172,9 @@ impl fmt::Display for EvalAltResult {
|
||||
|
||||
Self::ErrorFunctionNotFound(s, _)
|
||||
| Self::ErrorVariableNotFound(s, _)
|
||||
| Self::ErrorDataRace(s, _)
|
||||
| Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
||||
| Self::ErrorDataRace(s, _) => write!(f, "{}: {}", desc, s)?,
|
||||
|
||||
Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
||||
|
||||
Self::ErrorDotExpr(s, _) if !s.is_empty() => write!(f, "{}", s)?,
|
||||
|
||||
@ -187,9 +195,7 @@ impl fmt::Display for EvalAltResult {
|
||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
||||
Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?,
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, _) => {
|
||||
write!(f, "Cannot assign to constant '{}'", s)?
|
||||
}
|
||||
Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot assign to constant {}", s)?,
|
||||
Self::ErrorMismatchOutputType(s, r, _) => {
|
||||
write!(f, "Output type is incorrect: {} (expecting {})", r, s)?
|
||||
}
|
||||
@ -276,7 +282,7 @@ impl EvalAltResult {
|
||||
Self::ErrorParsing(_, _) => false,
|
||||
|
||||
Self::ErrorFunctionNotFound(_, _)
|
||||
| Self::ErrorInFunctionCall(_, _, _)
|
||||
| Self::ErrorInFunctionCall(_, _, _, _)
|
||||
| Self::ErrorInModule(_, _, _)
|
||||
| Self::ErrorUnboundThis(_)
|
||||
| Self::ErrorMismatchDataType(_, _, _)
|
||||
@ -334,7 +340,7 @@ impl EvalAltResult {
|
||||
|
||||
Self::ErrorParsing(_, pos)
|
||||
| Self::ErrorFunctionNotFound(_, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, _, pos)
|
||||
| Self::ErrorInModule(_, _, pos)
|
||||
| Self::ErrorUnboundThis(pos)
|
||||
| Self::ErrorMismatchDataType(_, _, pos)
|
||||
@ -367,7 +373,7 @@ impl EvalAltResult {
|
||||
|
||||
Self::ErrorParsing(_, pos)
|
||||
| Self::ErrorFunctionNotFound(_, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, _, pos)
|
||||
| Self::ErrorInModule(_, _, pos)
|
||||
| Self::ErrorUnboundThis(pos)
|
||||
| Self::ErrorMismatchDataType(_, _, pos)
|
||||
|
@ -212,6 +212,12 @@ impl From<&str> for ImmutableString {
|
||||
Self(value.to_string().into())
|
||||
}
|
||||
}
|
||||
impl From<&String> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: &String) -> Self {
|
||||
Self(value.to_string().into())
|
||||
}
|
||||
}
|
||||
impl From<String> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: String) -> Self {
|
||||
|
@ -73,7 +73,7 @@ fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
||||
"#
|
||||
)
|
||||
.expect_err("should error"),
|
||||
EvalAltResult::ErrorInFunctionCall(fn_name, err, _)
|
||||
EvalAltResult::ErrorInFunctionCall(fn_name, _, err, _)
|
||||
if fn_name == "foo" && matches!(*err, EvalAltResult::ErrorUnboundThis(_))
|
||||
));
|
||||
|
||||
|
@ -227,7 +227,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
||||
"#
|
||||
)
|
||||
.expect_err("should error"),
|
||||
EvalAltResult::ErrorInFunctionCall(fn_name, _, _) if fn_name == "foo"
|
||||
EvalAltResult::ErrorInFunctionCall(fn_name, _, _, _) if fn_name == "foo"
|
||||
));
|
||||
|
||||
engine.set_max_modules(1000);
|
||||
|
Loading…
Reference in New Issue
Block a user