use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn},
object::{Object as BuiltinObject, ObjectData},
property::Property,
value::{same_value, Value},
BoaProfiler, Context, Result,
};
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Copy)]
pub struct Object;
impl Object {
pub fn make_object(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() {
return Ok(arg.to_object(ctx)?.into());
}
}
let global = ctx.global_object();
Ok(Value::new_object(Some(global)))
}
pub fn create(_: &Value, args: &[Value], interpreter: &mut Context) -> Result<Value> {
let prototype = args.get(0).cloned().unwrap_or_else(Value::undefined);
let properties = args.get(1).cloned().unwrap_or_else(Value::undefined);
if properties != Value::Undefined {
unimplemented!("propertiesObject argument of Object.create")
}
match prototype {
Value::Object(_) | Value::Null => Ok(Value::object(BuiltinObject::with_prototype(
prototype,
ObjectData::Ordinary,
))),
_ => interpreter.throw_type_error(format!(
"Object prototype may only be an Object or null: {}",
prototype.display()
)),
}
}
pub fn is(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let x = args.get(0).cloned().unwrap_or_else(Value::undefined);
let y = args.get(1).cloned().unwrap_or_else(Value::undefined);
Ok(same_value(&x, &y).into())
}
pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object");
Ok(obj.as_object().map_or_else(Value::undefined, |object| {
object.prototype_instance().clone()
}))
}
pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).expect("Cannot get object").clone();
obj.as_object_mut().unwrap().set_prototype_instance(proto);
Ok(obj)
}
pub fn define_property(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let obj = args.get(0).expect("Cannot get object");
let prop = args.get(1).expect("Cannot get object").to_string(ctx)?;
let desc = Property::from(args.get(2).expect("Cannot get object"));
obj.set_property(prop, desc);
Ok(Value::undefined())
}
#[allow(clippy::wrong_self_convention)]
pub fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
if this.is_undefined() {
Ok("[object Undefined]".into())
} else if this.is_null() {
Ok("[object Null]".into())
} else {
let gc_o = this.to_object(ctx)?;
let o = gc_o.borrow();
let builtin_tag = match &o.data {
ObjectData::Array => "Array",
ObjectData::Function(_) => "Function",
ObjectData::Error => "Error",
ObjectData::Boolean(_) => "Boolean",
ObjectData::Number(_) => "Number",
ObjectData::String(_) => "String",
ObjectData::Date(_) => "Date",
ObjectData::RegExp(_) => "RegExp",
_ => "Object",
};
let tag = o.get(&ctx.well_known_symbols().to_string_tag_symbol().into());
let tag_str = tag.as_string().map(|s| s.as_str()).unwrap_or(builtin_tag);
Ok(format!("[object {}]", tag_str).into())
}
}
pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let prop = if args.is_empty() {
None
} else {
Some(args.get(0).expect("Cannot get object").to_string(ctx)?)
};
let own_property = this
.as_object()
.as_deref()
.expect("Cannot get THIS object")
.get_own_property(&prop.expect("cannot get prop").into());
if own_property.is_none() {
Ok(Value::from(false))
} else {
Ok(Value::from(true))
}
}
pub fn property_is_enumerable(
this: &Value,
args: &[Value],
ctx: &mut Context,
) -> Result<Value> {
let key = match args.get(0) {
None => return Ok(Value::from(false)),
Some(key) => key,
};
let key = key.to_property_key(ctx)?;
let own_property = this
.to_object(ctx)
.map(|obj| obj.borrow().get_own_property(&key));
Ok(own_property.map_or(Value::from(false), |own_prop| {
Value::from(own_prop.enumerable_or(false))
}))
}
#[inline]
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("object", "init");
let prototype = Value::new_object(None);
make_builtin_fn(
Self::has_own_property,
"hasOwnProperty",
&prototype,
0,
interpreter,
);
make_builtin_fn(
Self::property_is_enumerable,
"propertyIsEnumerable",
&prototype,
0,
interpreter,
);
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
let object = make_constructor_fn(
"Object",
1,
Self::make_object,
global,
prototype,
true,
true,
);
make_builtin_fn(Self::create, "create", &object, 2, interpreter);
make_builtin_fn(
Self::set_prototype_of,
"setPrototypeOf",
&object,
2,
interpreter,
);
make_builtin_fn(
Self::get_prototype_of,
"getPrototypeOf",
&object,
1,
interpreter,
);
make_builtin_fn(
Self::define_property,
"defineProperty",
&object,
3,
interpreter,
);
make_builtin_fn(Self::is, "is", &object, 2, interpreter);
("Object", object)
}
}