#[cfg(feature = "expressions")]
use boa_engine::{
context::ContextBuilder,
js_string,
native_function::NativeFunction,
object::builtins::JsArray,
property::Attribute,
Context, JsArgs, JsResult, JsValue, Source,
};
#[cfg(feature = "expressions")]
pub struct ExpressionEvaluator {
context: Context,
}
#[cfg(feature = "expressions")]
impl ExpressionEvaluator {
pub fn new() -> Self {
let mut context = ContextBuilder::default().build().unwrap();
context
.register_global_callable(
js_string!("add"),
1,
NativeFunction::from_fn_ptr(|_this, args, context| {
let a = args.get_or_undefined(0);
let b = args.get_or_undefined(1);
helper_add(a, b, context)
}),
)
.unwrap();
context
.register_global_callable(
js_string!("mul"),
1,
NativeFunction::from_fn_ptr(|_this, args, context| {
let a = args.get_or_undefined(0);
let b = args.get_or_undefined(1);
helper_mul(a, b, context)
}),
)
.unwrap();
context
.register_global_callable(
js_string!("sub"),
1,
NativeFunction::from_fn_ptr(|_this, args, context| {
let a = args.get_or_undefined(0);
let b = args.get_or_undefined(1);
helper_sub(a, b, context)
}),
)
.unwrap();
context
.register_global_callable(
js_string!("div"),
1,
NativeFunction::from_fn_ptr(|_this, args, context| {
let a = args.get_or_undefined(0);
let b = args.get_or_undefined(1);
helper_div(a, b, context)
}),
)
.unwrap();
Self { context }
}
pub fn evaluate(
&mut self,
script: &str,
current_value: &JsValue,
loop_value: &JsValue,
time: f32,
_frame_rate: f32,
) -> Result<JsValue, String> {
self.context
.register_global_property(js_string!("value"), current_value.clone(), Attribute::all())
.map_err(|e| format!("Failed to register value: {}", e))?;
self.context
.register_global_property(js_string!("__loop_value"), loop_value.clone(), Attribute::all())
.map_err(|e| format!("Failed to register loop value: {}", e))?;
self.context
.register_global_property(js_string!("time"), JsValue::new(time), Attribute::all())
.map_err(|e| format!("Failed to register time: {}", e))?;
self.context
.register_global_callable(
js_string!("loopOut"),
0,
NativeFunction::from_fn_ptr(|_this, _args, context| {
let val = context.global_object().get(js_string!("__loop_value"), context).unwrap_or_default();
Ok(val)
})
)
.map_err(|e| format!("Failed to register loopOut: {}", e))?;
match self.context.eval(Source::from_bytes(script)) {
Ok(res) => Ok(res),
Err(e) => Err(format!("Eval error: {}", e)),
}
}
pub fn context(&mut self) -> &mut Context {
&mut self.context
}
}
#[cfg(feature = "expressions")]
fn helper_add(a: &JsValue, b: &JsValue, context: &mut Context) -> JsResult<JsValue> {
if let (Some(obj_a), Some(obj_b)) = (a.as_object(), b.as_object()) {
if obj_a.is_array() && obj_b.is_array() {
let len_a = obj_a.get(js_string!("length"), context)?.to_number(context)? as u64;
let len_b = obj_b.get(js_string!("length"), context)?.to_number(context)? as u64;
let len = std::cmp::min(len_a, len_b);
let mut result = Vec::new();
for i in 0..len {
let val_a = obj_a.get(i, context)?.to_number(context)?;
let val_b = obj_b.get(i, context)?.to_number(context)?;
result.push(JsValue::new(val_a + val_b));
}
return Ok(JsArray::from_iter(result, context).into());
}
}
let num_a = a.to_number(context)?;
let num_b = b.to_number(context)?;
Ok(JsValue::new(num_a + num_b))
}
#[cfg(feature = "expressions")]
fn helper_sub(a: &JsValue, b: &JsValue, context: &mut Context) -> JsResult<JsValue> {
if let (Some(obj_a), Some(obj_b)) = (a.as_object(), b.as_object()) {
if obj_a.is_array() && obj_b.is_array() {
let len_a = obj_a.get(js_string!("length"), context)?.to_number(context)? as u64;
let len_b = obj_b.get(js_string!("length"), context)?.to_number(context)? as u64;
let len = std::cmp::min(len_a, len_b);
let mut result = Vec::new();
for i in 0..len {
let val_a = obj_a.get(i, context)?.to_number(context)?;
let val_b = obj_b.get(i, context)?.to_number(context)?;
result.push(JsValue::new(val_a - val_b));
}
return Ok(JsArray::from_iter(result, context).into());
}
}
let num_a = a.to_number(context)?;
let num_b = b.to_number(context)?;
Ok(JsValue::new(num_a - num_b))
}
#[cfg(feature = "expressions")]
fn helper_mul(a: &JsValue, b: &JsValue, context: &mut Context) -> JsResult<JsValue> {
if let Some(obj_a) = a.as_object() {
if obj_a.is_array() {
let scalar_b = b.to_number(context)?;
let len = obj_a.get(js_string!("length"), context)?.to_number(context)? as u64;
let mut result = Vec::new();
for i in 0..len {
let val_a = obj_a.get(i, context)?.to_number(context)?;
result.push(JsValue::new(val_a * scalar_b));
}
return Ok(JsArray::from_iter(result, context).into());
}
}
if let Some(obj_b) = b.as_object() {
if obj_b.is_array() {
let scalar_a = a.to_number(context)?;
let len = obj_b.get(js_string!("length"), context)?.to_number(context)? as u64;
let mut result = Vec::new();
for i in 0..len {
let val_b = obj_b.get(i, context)?.to_number(context)?;
result.push(JsValue::new(scalar_a * val_b));
}
return Ok(JsArray::from_iter(result, context).into());
}
}
let num_a = a.to_number(context)?;
let num_b = b.to_number(context)?;
Ok(JsValue::new(num_a * num_b))
}
#[cfg(feature = "expressions")]
fn helper_div(a: &JsValue, b: &JsValue, context: &mut Context) -> JsResult<JsValue> {
if let Some(obj_a) = a.as_object() {
if obj_a.is_array() {
let scalar_b = b.to_number(context)?;
if scalar_b == 0.0 { return Ok(JsValue::nan()); }
let len = obj_a.get(js_string!("length"), context)?.to_number(context)? as u64;
let mut result = Vec::new();
for i in 0..len {
let val_a = obj_a.get(i, context)?.to_number(context)?;
result.push(JsValue::new(val_a / scalar_b));
}
return Ok(JsArray::from_iter(result, context).into());
}
}
let num_a = a.to_number(context)?;
let num_b = b.to_number(context)?;
Ok(JsValue::new(num_a / num_b))
}