use datavalue::OwnedDataValue;
use crate::arena::{ContextStack, DataValue};
use crate::{CompiledNode, Engine, Error, Result};
use bumpalo::Bump;
#[inline]
pub(crate) fn evaluate_throw<'a>(
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
if ctx.in_catch_scope() && !ctx.is_tracing() {
let av: &'a DataValue<'a> = if args.is_empty() {
crate::arena::singletons::singleton_null()
} else if let CompiledNode::Value { value, .. } = &args[0] {
arena.alloc(value.to_arena(arena))
} else {
engine.dispatch_node(&args[0], ctx, arena)?
};
ctx.set_thrown_slot(normalize_thrown_arena(av, arena));
return Err(Error::deferred_thrown());
}
let owned: OwnedDataValue = if args.is_empty() {
OwnedDataValue::Null
} else if let CompiledNode::Value { value, .. } = &args[0] {
value.clone()
} else {
let av = engine.dispatch_node(&args[0], ctx, arena)?;
av.to_owned()
};
let owned = match owned {
OwnedDataValue::Object(_) => owned,
OwnedDataValue::String(s) => OwnedDataValue::object([("type", s)]),
ref other => OwnedDataValue::object([("type", value_type_name(other))]),
};
Err(Error::thrown(owned))
}
#[inline]
fn normalize_thrown_arena<'a>(av: &'a DataValue<'a>, arena: &'a Bump) -> &'a DataValue<'a> {
let type_val: DataValue<'a> = match av {
DataValue::Object(_) => return av,
DataValue::String(s) => DataValue::String(s),
other => DataValue::String(arena_type_name(other)),
};
let entry = arena.alloc([("type", type_val)]);
arena.alloc(DataValue::Object(&entry[..]))
}
#[inline]
fn arena_type_name(v: &DataValue<'_>) -> &'static str {
match v {
DataValue::Null => "null",
DataValue::Bool(_) => "boolean",
DataValue::Number(_) => "number",
DataValue::String(_) => "string",
DataValue::Array(_) => "array",
DataValue::Object(_) => "object",
#[cfg(feature = "datetime")]
DataValue::DateTime(_) => "datetime",
#[cfg(feature = "datetime")]
DataValue::Duration(_) => "duration",
}
}
#[inline]
fn value_type_name(v: &OwnedDataValue) -> &'static str {
match v {
OwnedDataValue::Null => "null",
OwnedDataValue::Bool(_) => "boolean",
OwnedDataValue::Number(_) => "number",
OwnedDataValue::String(_) => "string",
OwnedDataValue::Array(_) => "array",
OwnedDataValue::Object(_) => "object",
#[cfg(feature = "datetime")]
OwnedDataValue::DateTime(_) => "datetime",
#[cfg(feature = "datetime")]
OwnedDataValue::Duration(_) => "duration",
}
}
#[inline]
pub(crate) fn evaluate_try<'a>(
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
if args.is_empty() {
return Ok(crate::arena::singletons::singleton_null());
}
if args.len() == 1 {
return engine.dispatch_node(&args[0], ctx, arena);
}
let last_idx = args.len() - 1;
let mut last_err: Option<Error> = None;
for (i, arg) in args.iter().enumerate() {
if i == last_idx {
return try_last_with_error_context(arg, &mut last_err, ctx, engine, arena);
}
let saved_len = ctx.error_path_len();
ctx.clear_thrown_slot();
ctx.enter_catch_scope();
let result = engine.dispatch_node(arg, ctx, arena);
ctx.exit_catch_scope();
match result {
Ok(v) => return Ok(v),
Err(e) => {
ctx.truncate_error_path(saved_len);
last_err = Some(e);
}
}
}
Err(last_err.unwrap_or_else(Error::invalid_args))
}
#[inline]
fn try_last_with_error_context<'a>(
arg: &'a CompiledNode,
last_error: &mut Option<Error>,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let slot = ctx.take_thrown_slot();
if let Some(Error {
kind: crate::ErrorKind::Thrown(error_obj),
..
}) = last_error.take()
{
if matches!(arg, CompiledNode::Value { .. }) {
return engine.dispatch_node(arg, ctx, arena);
}
let av: &'a DataValue<'a> = match slot {
Some(av) => av,
None => arena.alloc(error_obj.to_arena(arena)),
};
ctx.push(av);
let result = engine.dispatch_node(arg, ctx, arena);
ctx.pop();
result
} else {
engine.dispatch_node(arg, ctx, arena)
}
}