use crate::{
builtins::{
object::Object,
property::Property,
value::{to_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
syntax::ast::expr::Expr,
};
use gc::{custom_trace, Gc};
use gc_derive::{Finalize, Trace};
use std::fmt::{self, Debug};
pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue;
#[derive(Trace, Finalize, Debug, Clone)]
pub enum Function {
NativeFunc(NativeFunction),
RegularFunc(RegularFunction),
}
#[derive(Trace, Finalize, Debug, Clone)]
pub struct RegularFunction {
pub object: Object,
pub expr: Expr,
pub args: Vec<Expr>,
}
impl RegularFunction {
#[allow(clippy::cast_possible_wrap)]
pub fn new(expr: Expr, args: Vec<Expr>) -> Self {
let mut object = Object::default();
object.properties.insert(
"arguments".to_string(),
Property::default().value(Gc::new(ValueData::Integer(args.len() as i32))),
);
Self { object, expr, args }
}
}
#[derive(Finalize, Clone)]
pub struct NativeFunction {
pub object: Object,
pub data: NativeFunctionData,
}
impl NativeFunction {
pub fn new(data: NativeFunctionData) -> Self {
let object = Object::default();
Self { object, data }
}
}
impl Debug for NativeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{")?;
for (key, val) in self.object.properties.iter() {
write!(
f,
"{}: {}",
key,
val.value
.as_ref()
.unwrap_or(&Gc::new(ValueData::Undefined))
.clone()
)?;
}
write!(f, "}}")
}
}
unsafe impl gc::Trace for NativeFunction {
custom_trace!(this, mark(&this.object));
}
pub fn _create() -> Value {
let function: Object = Object::default();
to_value(function)
}
pub fn init(global: &Value) {
let global_ptr = global;
global_ptr.set_field_slice("Function", _create());
}
pub fn create_unmapped_arguments_object(arguments_list: Vec<Value>) -> Value {
let len = arguments_list.len();
let mut obj = Object::default();
obj.set_internal_slot("ParameterMap", Gc::new(ValueData::Undefined));
let mut length = Property::default();
length = length.writable(true).value(to_value(len));
obj.define_own_property("length".to_string(), length);
let mut index: usize = 0;
while index < len {
let val = arguments_list.get(index).expect("Could not get argument");
let mut prop = Property::default();
prop = prop
.value(val.clone())
.enumerable(true)
.writable(true)
.configurable(true);
obj.properties.insert(index.to_string(), prop);
index += 1;
}
to_value(obj)
}
#[cfg(test)]
mod tests {
use crate::exec::Executor;
use crate::realm::Realm;
use crate::{builtins::value::from_value, forward, forward_val};
#[allow(clippy::float_cmp)]
#[test]
fn check_arguments_object() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#"
function jason(a, b) {
return arguments[0];
}
var val = jason(100, 6);
"#;
forward(&mut engine, init);
let expected_return_val: f64 = 100.0;
let return_val = forward_val(&mut engine, "val").expect("value expected");
assert_eq!(return_val.is_double(), true);
assert_eq!(
from_value::<f64>(return_val).expect("Could not convert value to f64"),
expected_return_val
);
}
}