Browse Source

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

pull/855/head
John Doneth 4 years ago committed by GitHub
parent
commit
3047ed698f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 121
      boa/src/builtins/object/mod.rs
  2. 51
      boa/src/builtins/object/tests.rs

121
boa/src/builtins/object/mod.rs

@ -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,
};
@ -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())
@ -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);

51
boa/src/builtins/object/tests.rs

@ -1,4 +1,4 @@
use crate::{forward, Context};
use crate::{forward, Context, Value};
#[test]
fn object_create_with_regular_object() {
@ -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();

Loading…
Cancel
Save