Browse Source

Fix Array.prototype.filter (#1272)

Co-authored-by: tofpie <tofpie@users.noreply.github.com>
pull/1286/head
Iban Eguia 4 years ago committed by GitHub
parent
commit
68543e8fb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 105
      boa/src/builtins/array/mod.rs
  2. 3
      boa/src/builtins/json/mod.rs
  3. 14
      boa/src/builtins/math/mod.rs

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

@ -262,6 +262,51 @@ impl Array {
Ok(array_obj_ptr) Ok(array_obj_ptr)
} }
/// Utility function used to specify the creation of a new Array object using a constructor
/// function that is derived from original_array.
///
/// see: <https://tc39.es/ecma262/#sec-arrayspeciescreate>
pub(crate) fn array_species_create(
original_array: &GcObject,
length: u32,
context: &mut Context,
) -> Result<Value> {
if !original_array.is_array() {
return Ok(Self::array_create(length, None, context));
}
let c = original_array.get(
&"constructor".into(),
original_array.clone().into(),
context,
)?;
// Step 4 is ignored, as there are no different realms for now
let c = if let Some(c) = c.as_object() {
let c = c.get(
&WellKnownSymbols::species().into(),
c.clone().into(),
context,
)?;
if c.is_null_or_undefined() {
Value::undefined()
} else {
c
}
} else {
c
};
if c.is_undefined() {
return Ok(Self::array_create(length, None, context));
}
if let Some(c) = c.as_object() {
if !c.is_constructable() {
return context.throw_type_error("Symbol.species must be a constructor");
}
c.construct(&[Value::from(length)], c.clone().into(), context)
} else {
context.throw_type_error("Symbol.species must be a constructor")
}
}
/// Utility function which takes an existing array object and puts additional /// Utility function which takes an existing array object and puts additional
/// values on the end, correctly rewriting the length /// values on the end, correctly rewriting the length
pub(crate) fn add_to_array_object( pub(crate) fn add_to_array_object(
@ -1211,37 +1256,57 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
pub(crate) fn filter(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> { pub(crate) fn filter(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if args.is_empty() { let o = this.to_object(context)?;
return Err(Value::from( let length = o
"missing argument 0 when calling function Array.prototype.filter", .get(&"length".into(), Value::from(o.clone()), context)?
)); .to_length(context)?;
}
let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); let callback = args
.get(0)
.map(|a| a.to_object(context))
.transpose()?
.ok_or_else(|| {
context.construct_type_error(
"missing argument 0 when calling function Array.prototype.filter",
)
})?;
let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length = this.get_field("length", context)?.to_length(context)?; if !callback.is_callable() {
return context.throw_type_error("the callback must be callable");
}
let new = Self::new_array(context); let mut a = Self::array_species_create(&o, 0, context)?
.as_object()
.expect("array_species_create must create an object");
let values = (0..length) let mut to = 0u32;
.map(|idx| { for idx in 0..length {
let element = this.get_field(idx, context)?; if o.has_property(&idx.into()) {
let element = o.get(&idx.into(), Value::from(o.clone()), context)?;
let args = [element.clone(), Value::from(idx), new.clone()]; let args = [element.clone(), Value::from(idx), Value::from(o.clone())];
let callback_result = context.call(&callback, &this_val, &args)?; let callback_result = callback.call(&this_val, &args, context)?;
if callback_result.to_boolean() { if callback_result.to_boolean() {
Ok(Some(element)) if !a.define_own_property(
} else { to,
Ok(None) DataDescriptor::new(
element,
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
)
.into(),
context,
)? {
return context.throw_type_error("cannot set property in array");
}
to += 1;
}
}
} }
})
.collect::<Result<Vec<Option<Value>>>>()?;
let values = values.into_iter().flatten().collect::<Vec<_>>();
Self::construct_array(&new, &values, context) Ok(a.into())
} }
/// Array.prototype.some ( callbackfn [ , thisArg ] ) /// Array.prototype.some ( callbackfn [ , thisArg ] )

3
boa/src/builtins/json/mod.rs

@ -42,13 +42,12 @@ impl BuiltIn for Json {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let to_string_tag = WellKnownSymbols::to_string_tag(); let to_string_tag = WellKnownSymbols::to_string_tag();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
let json_object = ObjectInitializer::new(context) let json_object = ObjectInitializer::new(context)
.property(to_string_tag, Self::NAME, attribute)
.function(Self::parse, "parse", 2) .function(Self::parse, "parse", 2)
.function(Self::stringify, "stringify", 3) .function(Self::stringify, "stringify", 3)
.property(to_string_tag, Self::NAME, attribute)
.build(); .build();
(Self::NAME, json_object.into(), Self::attribute()) (Self::NAME, json_object.into(), Self::attribute())

14
boa/src/builtins/math/mod.rs

@ -31,12 +31,12 @@ impl BuiltIn for Math {
} }
fn init(context: &mut Context) -> (&'static str, Value, Attribute) { fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
use std::f64; use std::f64;
let to_string_tag = WellKnownSymbols::to_string_tag(); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let string_tag = WellKnownSymbols::to_string_tag();
let object = ObjectInitializer::new(context) let object = ObjectInitializer::new(context)
.property("E", f64::consts::E, attribute) .property("E", f64::consts::E, attribute)
.property("LN2", f64::consts::LN_2, attribute) .property("LN2", f64::consts::LN_2, attribute)
@ -46,11 +46,6 @@ impl BuiltIn for Math {
.property("SQRT1_2", 0.5_f64.sqrt(), attribute) .property("SQRT1_2", 0.5_f64.sqrt(), attribute)
.property("SQRT2", f64::consts::SQRT_2, attribute) .property("SQRT2", f64::consts::SQRT_2, attribute)
.property("PI", f64::consts::PI, attribute) .property("PI", f64::consts::PI, attribute)
.property(
to_string_tag,
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.function(Self::abs, "abs", 1) .function(Self::abs, "abs", 1)
.function(Self::acos, "acos", 1) .function(Self::acos, "acos", 1)
.function(Self::acosh, "acosh", 1) .function(Self::acosh, "acosh", 1)
@ -86,6 +81,11 @@ impl BuiltIn for Math {
.function(Self::tan, "tan", 1) .function(Self::tan, "tan", 1)
.function(Self::tanh, "tanh", 1) .function(Self::tanh, "tanh", 1)
.function(Self::trunc, "trunc", 1) .function(Self::trunc, "trunc", 1)
.property(
string_tag,
Math::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build(); .build();
(Self::NAME, object.into(), Self::attribute()) (Self::NAME, object.into(), Self::attribute())

Loading…
Cancel
Save