Browse Source

Make `Array.prototype` methods spec compliant (#1449)

pull/1455/head
Halid Odat 3 years ago committed by GitHub
parent
commit
b55b2af801
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1015
      boa/src/builtins/array/mod.rs
  2. 18
      boa/src/builtins/array/tests.rs
  3. 20
      boa/src/builtins/iterable/mod.rs
  4. 10
      boa/src/property/mod.rs
  5. 11
      boa/src/value/conversions.rs
  6. 35
      boa/src/value/mod.rs

1015
boa/src/builtins/array/mod.rs

File diff suppressed because it is too large Load Diff

18
boa/src/builtins/array/tests.rs

@ -1077,7 +1077,7 @@ fn reduce() {
); );
assert_eq!( assert_eq!(
result, result,
"\"Reduce was called on an empty array and with no initial value\"" "\"Array.prototype.reduce: called on an empty array and with no initial value\""
); );
// Array with no defined elements // Array with no defined elements
@ -1096,7 +1096,7 @@ fn reduce() {
); );
assert_eq!( assert_eq!(
result, result,
"\"Reduce was called on an empty array and with no initial value\"" "\"Array.prototype.reduce: called on an empty array and with no initial value\""
); );
// No callback // No callback
@ -1110,7 +1110,10 @@ fn reduce() {
} }
"#, "#,
); );
assert_eq!(result, "\"Reduce was called without a callback\""); assert_eq!(
result,
"\"Array.prototype.reduce: callback function is not callable\""
);
} }
#[test] #[test]
@ -1199,7 +1202,7 @@ fn reduce_right() {
); );
assert_eq!( assert_eq!(
result, result,
"\"reduceRight was called on an empty array and with no initial value\"" "\"Array.prototype.reduceRight: called on an empty array and with no initial value\""
); );
// Array with no defined elements // Array with no defined elements
@ -1218,7 +1221,7 @@ fn reduce_right() {
); );
assert_eq!( assert_eq!(
result, result,
"\"reduceRight was called on an empty array and with no initial value\"" "\"Array.prototype.reduceRight: called on an empty array and with no initial value\""
); );
// No callback // No callback
@ -1232,7 +1235,10 @@ fn reduce_right() {
} }
"#, "#,
); );
assert_eq!(result, "\"reduceRight was called without a callback\""); assert_eq!(
result,
"\"Array.prototype.reduceRight: callback function is not callable\""
);
} }
#[test] #[test]

20
boa/src/builtins/iterable/mod.rs

@ -5,7 +5,6 @@ use crate::{
SetIterator, SetIterator,
}, },
object::{GcObject, ObjectInitializer}, object::{GcObject, ObjectInitializer},
property::{Attribute, DataDescriptor},
symbol::WellKnownSymbols, symbol::WellKnownSymbols,
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
@ -87,13 +86,18 @@ impl IteratorPrototypes {
/// ///
/// Generates an object supporting the IteratorResult interface. /// Generates an object supporting the IteratorResult interface.
pub fn create_iter_result_object(context: &mut Context, value: Value, done: bool) -> Value { pub fn create_iter_result_object(context: &mut Context, value: Value, done: bool) -> Value {
let object = Value::new_object(context); // 1. Assert: Type(done) is Boolean.
// TODO: Fix attributes of value and done // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
let value_property = DataDescriptor::new(value, Attribute::all()); let obj = context.construct_object();
let done_property = DataDescriptor::new(done, Attribute::all());
object.set_property("value", value_property); // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value).
object.set_property("done", done_property); obj.create_data_property_or_throw("value", value, context)
object .unwrap();
// 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done).
obj.create_data_property_or_throw("done", done, context)
.unwrap();
// 5. Return obj.
obj.into()
} }
/// Get an iterator record /// Get an iterator record

10
boa/src/property/mod.rs

@ -452,6 +452,16 @@ impl From<usize> for PropertyKey {
} }
} }
impl From<i64> for PropertyKey {
fn from(value: i64) -> Self {
if let Ok(index) = u32::try_from(value) {
PropertyKey::Index(index)
} else {
PropertyKey::String(JsString::from(value.to_string()))
}
}
}
impl From<u64> for PropertyKey { impl From<u64> for PropertyKey {
fn from(value: u64) -> Self { fn from(value: u64) -> Self {
if let Ok(index) = u32::try_from(value) { if let Ok(index) = u32::try_from(value) {

11
boa/src/value/conversions.rs

@ -121,6 +121,17 @@ impl From<u64> for Value {
} }
} }
impl From<i64> for Value {
#[inline]
fn from(value: i64) -> Value {
if let Ok(value) = i32::try_from(value) {
Value::integer(value)
} else {
Value::rational(value as f64)
}
}
}
impl From<bool> for Value { impl From<bool> for Value {
#[inline] #[inline]
fn from(value: bool) -> Self { fn from(value: bool) -> Self {

35
boa/src/value/mod.rs

@ -265,7 +265,7 @@ impl Value {
/// ///
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal> would turn `extensible` to `false` /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal> would turn `extensible` to `false`
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze> would also turn `extensible` to `false` /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze> would also turn `extensible` to `false`
pub fn is_extensible(&self) -> bool { pub(crate) fn is_extensible(&self) -> bool {
true true
} }
@ -422,7 +422,7 @@ impl Value {
/// Removes a property from a Value object. /// Removes a property from a Value object.
/// ///
/// It will return a boolean based on if the value was removed, if there was no value to remove false is returned. /// It will return a boolean based on if the value was removed, if there was no value to remove false is returned.
pub fn remove_property<Key>(&self, key: Key) -> bool pub(crate) fn remove_property<Key>(&self, key: Key) -> bool
where where
Key: Into<PropertyKey>, Key: Into<PropertyKey>,
{ {
@ -432,7 +432,7 @@ impl Value {
/// Resolve the property in the object. /// Resolve the property in the object.
/// ///
/// A copy of the Property is returned. /// A copy of the Property is returned.
pub fn get_property<Key>(&self, key: Key) -> Option<PropertyDescriptor> pub(crate) fn get_property<Key>(&self, key: Key) -> Option<PropertyDescriptor>
where where
Key: Into<PropertyKey>, Key: Into<PropertyKey>,
{ {
@ -453,7 +453,7 @@ impl Value {
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
/// get_field receives a Property from get_prop(). It should then return the `[[Get]]` result value if that's set, otherwise fall back to `[[Value]]` /// get_field receives a Property from get_prop(). It should then return the `[[Get]]` result value if that's set, otherwise fall back to `[[Value]]`
pub fn get_field<K>(&self, key: K, context: &mut Context) -> Result<Self> pub(crate) fn get_field<K>(&self, key: K, context: &mut Context) -> Result<Self>
where where
K: Into<PropertyKey>, K: Into<PropertyKey>,
{ {
@ -468,7 +468,7 @@ impl Value {
/// Check to see if the Value has the field, mainly used by environment records. /// Check to see if the Value has the field, mainly used by environment records.
#[inline] #[inline]
pub fn has_field<K>(&self, key: K) -> bool pub(crate) fn has_field<K>(&self, key: K) -> bool
where where
K: Into<PropertyKey>, K: Into<PropertyKey>,
{ {
@ -487,7 +487,7 @@ impl Value {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-set-o-p-v-throw /// [spec]: https://tc39.es/ecma262/#sec-set-o-p-v-throw
#[inline] #[inline]
pub fn set_field<K, V>( pub(crate) fn set_field<K, V>(
&self, &self,
key: K, key: K,
value: V, value: V,
@ -534,7 +534,7 @@ impl Value {
/// Set the property in the value. /// Set the property in the value.
#[inline] #[inline]
pub fn set_property<K, P>(&self, key: K, property: P) pub(crate) fn set_property<K, P>(&self, key: K, property: P)
where where
K: Into<PropertyKey>, K: Into<PropertyKey>,
P: Into<PropertyDescriptor>, P: Into<PropertyDescriptor>,
@ -962,6 +962,27 @@ impl Value {
} }
} }
} }
/// Check if it is an array.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isarray
pub(crate) fn is_array(&self, _context: &mut Context) -> Result<bool> {
// 1. If Type(argument) is not Object, return false.
if let Some(object) = self.as_object() {
// 2. If argument is an Array exotic object, return true.
// a. If argument.[[ProxyHandler]] is null, throw a TypeError exception.
// 3. If argument is a Proxy exotic object, then
// b. Let target be argument.[[ProxyTarget]].
// c. Return ? IsArray(target).
// 4. Return false.
Ok(object.is_array())
} else {
Ok(false)
}
}
} }
impl Default for Value { impl Default for Value {

Loading…
Cancel
Save