Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Object.getOwnPropertyDescriptor() and Object.getOwnPropertyDescriptors() #798

Merged
merged 16 commits into from
Oct 12, 2020
106 changes: 105 additions & 1 deletion boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, Object as BuiltinObject, ObjectData},
property::{Attribute, Property},
property::{Attribute, Property, PropertyKey},
value::{same_value, Value},
BoaProfiler, Context, Result,
};
Expand Down Expand Up @@ -54,6 +54,16 @@ impl BuiltIn for Object {
.static_method(Self::get_prototype_of, "getPrototypeOf", 1)
.static_method(Self::define_property, "defineProperty", 3)
.static_method(Self::is, "is", 2)
.static_method(
Self::get_own_property_descriptor,
"getOwnPropertyDescriptor",
2,
)
.static_method(
Self::get_own_property_descriptors,
"getOwnPropertyDescriptors",
1,
)
.build();

(Self::NAME, object.into(), Self::attribute())
Expand Down Expand Up @@ -104,6 +114,100 @@ impl Object {
}
}

/// `Object.getOwnPropertyDescriptor( object, property )`
///
/// Returns an object describing the configuration of a specific property on a given object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://www.ecma-international.org/ecma-262/10.0/index.html#sec-object.getownpropertydescriptor
JohnDoneth marked this conversation as resolved.
Show resolved Hide resolved
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
pub fn get_own_property_descriptor(
_: &Value,
args: &[Value],
ctx: &mut Context,
) -> Result<Value> {
let object = args.get(0).unwrap().to_object(ctx)?;
if let Some(key) = args.get(1) {
let key = key.to_property_key(ctx)?;
let desc = object.borrow().get_own_property(&key);
Self::from_property_descriptor(desc, ctx)
} else {
Ok(Value::undefined())
}
}

/// `Object.getOwnPropertyDescriptors( object )`
///
/// Returns all own property descriptors of a given object.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://www.ecma-international.org/ecma-262/10.0/index.html#sec-object.getownpropertydescriptor
JohnDoneth marked this conversation as resolved.
Show resolved Hide resolved
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors
pub fn get_own_property_descriptors(
_: &Value,
args: &[Value],
ctx: &mut Context,
) -> Result<Value> {
let object = args.get(0).unwrap_or(&Value::undefined()).to_object(ctx)?;
let descriptors = ctx.construct_object();

for (key, _) in object.borrow().string_properties() {
JohnDoneth marked this conversation as resolved.
Show resolved Hide resolved
let key = PropertyKey::from(key.clone());
let desc = object.borrow().get_own_property(&key);
let descriptor = Self::from_property_descriptor(desc, ctx)?;

if descriptor != Value::undefined() {
JohnDoneth marked this conversation as resolved.
Show resolved Hide resolved
descriptors
.borrow_mut()
.insert(key, Property::data_descriptor(descriptor, Attribute::all()));
}
}

Ok(Value::Object(descriptors))
}

/// https://www.ecma-international.org/ecma-262/10.0/index.html#sec-frompropertydescriptor
JohnDoneth marked this conversation as resolved.
Show resolved Hide resolved
fn from_property_descriptor(desc: Property, ctx: &mut Context) -> Result<Value> {
let descriptor = ctx.construct_object();
if let Some(value) = &desc.value {
descriptor.borrow_mut().insert(
"value",
Property::data_descriptor(value.clone(), Attribute::all()),
);
}
if let Some(set) = &desc.set {
descriptor.borrow_mut().insert(
"set",
Property::data_descriptor(set.clone(), Attribute::all()),
);
}
if let Some(get) = &desc.get {
descriptor.borrow_mut().insert(
"get",
Property::data_descriptor(get.clone(), Attribute::all()),
);
}
descriptor.borrow_mut().insert(
"writable",
Property::data_descriptor(desc.writable().into(), Attribute::all()),
);
descriptor.borrow_mut().insert(
"enumerable",
Property::data_descriptor(desc.enumerable().into(), Attribute::all()),
);
descriptor.borrow_mut().insert(
"configurable",
Property::data_descriptor(desc.configurable().into(), Attribute::all()),
);
Ok(Value::Object(descriptor))
JohnDoneth marked this conversation as resolved.
Show resolved Hide resolved
}

/// Uses the SameValue algorithm to check equality of objects
pub fn is(_: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let x = args.get(0).cloned().unwrap_or_else(Value::undefined);
Expand Down
51 changes: 50 additions & 1 deletion boa/src/builtins/object/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{forward, Context};
use crate::{forward, Context, Value};

#[test]
fn object_create_with_regular_object() {
Expand Down Expand Up @@ -191,3 +191,52 @@ fn define_symbol_property() {

assert_eq!(forward(&mut ctx, "obj[sym]"), "\"val\"");
}

#[test]
fn get_own_property_descriptor_1_arg_returns_undefined() {
let mut ctx = Context::new();
let code = r#"
let obj = {a: 2};
Object.getOwnPropertyDescriptor(obj)
"#;
assert_eq!(ctx.eval(code).unwrap(), Value::undefined());
}

#[test]
fn get_own_property_descriptor() {
let mut ctx = Context::new();
forward(
&mut ctx,
r#"
let obj = {a: 2};
let result = Object.getOwnPropertyDescriptor(obj, "a");
"#,
);

assert_eq!(forward(&mut ctx, "result.enumerable"), "true");
assert_eq!(forward(&mut ctx, "result.writable"), "true");
assert_eq!(forward(&mut ctx, "result.configurable"), "true");
assert_eq!(forward(&mut ctx, "result.value"), "2");
}

#[test]
fn get_own_property_descriptors() {
let mut ctx = Context::new();
forward(
&mut ctx,
r#"
let obj = {a: 1, b: 2};
let result = Object.getOwnPropertyDescriptors(obj);
"#,
);

assert_eq!(forward(&mut ctx, "result.a.enumerable"), "true");
assert_eq!(forward(&mut ctx, "result.a.writable"), "true");
assert_eq!(forward(&mut ctx, "result.a.configurable"), "true");
assert_eq!(forward(&mut ctx, "result.a.value"), "1");

assert_eq!(forward(&mut ctx, "result.b.enumerable"), "true");
assert_eq!(forward(&mut ctx, "result.b.writable"), "true");
assert_eq!(forward(&mut ctx, "result.b.configurable"), "true");
assert_eq!(forward(&mut ctx, "result.b.value"), "2");
}