diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 27c445ae06..00cef05873 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -995,7 +995,7 @@ impl Array { /// Create a new `Array` object. pub(crate) fn create(global: &Value) -> Value { // Create prototype - let prototype = Value::new_object(None); + let prototype = Value::new_object(Some(global)); let length = Property::default().value(Value::from(0)); prototype.set_property("length", length); diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index 80cb1c4619..b35d0b15ba 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -282,3 +282,27 @@ fn json_parse_sets_prototypes() { true ); } + +#[test] +fn json_fields_should_be_enumerable() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let actual_object = forward( + &mut engine, + r#" + var a = JSON.parse('{"x":0}'); + a.propertyIsEnumerable('x'); + "#, + ); + let actual_array_index = forward( + &mut engine, + r#" + var b = JSON.parse('[0, 1]'); + b.propertyIsEnumerable('0'); + "#, + ); + let expected = forward(&mut engine, r#"true"#); + + assert_eq!(actual_object, expected); + assert_eq!(actual_array_index, expected); +} diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 0a7468f4e7..d87509b825 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -472,11 +472,40 @@ pub fn has_own_property(this: &mut Value, args: &[Value], ctx: &mut Interpreter) } } +pub fn property_is_enumerable( + this: &mut Value, + args: &[Value], + ctx: &mut Interpreter, +) -> ResultValue { + let key = if args.is_empty() { + return Ok(Value::from(false)); + } else { + args.get(0).expect("Cannot get key") + }; + + let property_key = ctx.to_property_key(&mut key.clone())?; + let own_property = ctx.to_object(this).map(|obj| { + obj.as_object() + .as_deref() + .expect("Unable to deref object") + .get_own_property(&property_key) + }); + own_property.map_or(Ok(Value::from(false)), |own_prop| { + Ok(Value::from(own_prop.enumerable.unwrap_or(false))) + }) +} + /// Create a new `Object` object. pub fn create(global: &Value) -> Value { let prototype = Value::new_object(None); make_builtin_fn(has_own_property, "hasOwnProperty", &prototype, 0); + make_builtin_fn( + property_is_enumerable, + "propertyIsEnumerable", + &prototype, + 0, + ); make_builtin_fn(to_string, "toString", &prototype, 0); let object = make_constructor_fn("Object", 1, make_object, global, prototype, true); diff --git a/boa/src/builtins/object/tests.rs b/boa/src/builtins/object/tests.rs index a699e7b290..82659ae43e 100644 --- a/boa/src/builtins/object/tests.rs +++ b/boa/src/builtins/object/tests.rs @@ -20,3 +20,26 @@ fn object_has_own_property() { "false" ); } + +#[test] +fn object_property_is_enumerable() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + let x = { enumerableProp: 'yes' }; + "#; + eprintln!("{}", forward(&mut engine, init)); + assert_eq!( + forward(&mut engine, r#"x.propertyIsEnumerable('enumerableProp')"#), + "true" + ); + assert_eq!( + forward(&mut engine, r#"x.propertyIsEnumerable('hasOwnProperty')"#), + "false" + ); + assert_eq!( + forward(&mut engine, r#"x.propertyIsEnumerable('not_here')"#), + "false", + ); + assert_eq!(forward(&mut engine, r#"x.propertyIsEnumerable()"#), "false",) +} diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 50f518b2e0..c91e0c7cfa 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -194,7 +194,8 @@ impl Value { Property::default() .value(Self::from_json(json, interpreter)) .writable(true) - .configurable(true), + .configurable(true) + .enumerable(true), ); } new_obj.set_property( @@ -212,7 +213,8 @@ impl Value { Property::default() .value(value) .writable(true) - .configurable(true), + .configurable(true) + .enumerable(true), ); } new_obj