Skip to content

Commit

Permalink
Implement Object.getOwnPropertyDescriptor() and Object.getOwnProperty…
Browse files Browse the repository at this point in the history
…Descriptors() (#798)
  • Loading branch information
JohnDoneth authored Oct 12, 2020
1 parent 36b7c31 commit 3047ed6
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 2 deletions.
121 changes: 120 additions & 1 deletion boa/src/builtins/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, Object as BuiltinObject, ObjectData},
object::{ConstructorBuilder, Object as BuiltinObject, ObjectData, ObjectInitializer},
property::Attribute,
property::DataDescriptor,
property::PropertyDescriptor,
value::{same_value, Value},
BoaProfiler, Context, Result,
};
Expand Down Expand Up @@ -55,6 +57,16 @@ impl BuiltIn for Object {
.static_method(Self::define_property, "defineProperty", 3)
.static_method(Self::define_properties, "defineProperties", 2)
.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 @@ -109,6 +121,113 @@ impl Object {
Ok(obj)
}

/// `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://tc39.es/ecma262/#sec-object.getownpropertydescriptor
/// [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_or(&Value::undefined()).to_object(ctx)?;
if let Some(key) = args.get(1) {
let key = key.to_property_key(ctx)?;

if let Some(desc) = object.borrow().get_own_property(&key) {
return Ok(Self::from_property_descriptor(desc, ctx)?);
}
}

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://tc39.es/ecma262/#sec-object.getownpropertydescriptors
/// [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().keys() {
let descriptor = {
let desc = object
.borrow()
.get_own_property(&key)
.expect("Expected property to be on object.");
Self::from_property_descriptor(desc, ctx)?
};

if !descriptor.is_undefined() {
descriptors.borrow_mut().insert(
key,
PropertyDescriptor::from(DataDescriptor::new(descriptor, Attribute::all())),
);
}
}

Ok(Value::Object(descriptors))
}

/// The abstract operation `FromPropertyDescriptor`.
///
/// [ECMAScript reference][spec]
/// [spec]: https://tc39.es/ecma262/#sec-frompropertydescriptor
fn from_property_descriptor(desc: PropertyDescriptor, ctx: &mut Context) -> Result<Value> {
let mut descriptor = ObjectInitializer::new(ctx);

if let PropertyDescriptor::Data(data_desc) = &desc {
descriptor.property("value", data_desc.value(), Attribute::all());
}

if let PropertyDescriptor::Accessor(accessor_desc) = &desc {
if let Some(setter) = accessor_desc.setter() {
descriptor.property("set", Value::Object(setter.to_owned()), Attribute::all());
}
if let Some(getter) = accessor_desc.getter() {
descriptor.property("get", Value::Object(getter.to_owned()), Attribute::all());
}
}

let writable = if let PropertyDescriptor::Data(data_desc) = &desc {
data_desc.writable()
} else {
false
};

descriptor
.property("writable", Value::from(writable), Attribute::all())
.property(
"enumerable",
Value::from(desc.enumerable()),
Attribute::all(),
)
.property(
"configurable",
Value::from(desc.configurable()),
Attribute::all(),
);

Ok(descriptor.build().into())
}

/// 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 @@ -194,6 +194,55 @@ 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");
}

#[test]
fn object_define_properties() {
let mut ctx = Context::new();
Expand Down

0 comments on commit 3047ed6

Please sign in to comment.