Skip to content

Commit

Permalink
Implement Array.prototype.sort spec
Browse files Browse the repository at this point in the history
  • Loading branch information
jedel1043 committed Jun 17, 2021
1 parent 1dbc7fc commit cbfabaf
Showing 1 changed file with 87 additions and 0 deletions.
87 changes: 87 additions & 0 deletions boa/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl BuiltIn for Array {
.method(Self::flat_map, "flatMap", 1)
.method(Self::slice, "slice", 2)
.method(Self::some, "some", 2)
.method(Self::sort, "sort", 1)
.method(Self::reduce, "reduce", 2)
.method(Self::reduce_right, "reduceRight", 2)
.method(Self::keys, "keys", 0)
Expand Down Expand Up @@ -1387,6 +1388,92 @@ impl Array {
Ok(Value::from(false))
}

/// Array.prototype.sort ( comparefn )
///
/// The sort method sorts the elements of an array in place and returns the sorted array.
/// The default sort order is ascending, built upon converting the elements into strings,
/// then comparing their sequences of UTF-16 code units values.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.sort
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
pub(crate) fn sort(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let comparefn = match args.get(0).cloned() {
Some(fun) if fun.is_function() => fun,
None => Value::undefined(),
_ => {
return context.throw_type_error(
"The comparison function must be either a function or undefined",
)
}
};

let length = this.get_field("length", context)?.to_length(context)?;

let mut items = Vec::with_capacity(length);

for k in 0..length {
// the spec checks for the presence of the field before obtaining the field
if this.has_field(k) {
let element = this.get_field(k, context)?;
items.push(element);
}
}

// todo: sort algorithm with early returns

for (j, item) in items.iter().cloned().enumerate() {
this.set_field(j, item, context)?;
}

for j in items.len()..length {
this.remove_property(j);
}

Ok(this.clone())
}

/// Abstract method `SortCompare`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.sort

fn sort_compare(context: &mut Context, x: &Value, y: &Value, comparefn: &Value) -> Result<f64> {
match (x.is_undefined(), y.is_undefined()) {
(true, true) => return Ok(0.0),
(true, false) => return Ok(1.0),
(false, true) => return Ok(-1.0),
_ => {}
}

if !comparefn.is_undefined() {
let args = [x.clone(), y.clone()];
let v = context
.call(&comparefn, &Value::Undefined, &args)?
.to_number(context)?;
return Ok(match v.is_nan() {
true => 0.0,
false => v,
});
}

let x_str = x.to_string(context)?;
let y_str = y.to_string(context)?;

if x_str < y_str {
return Ok(-1.0);
}
if y_str < x_str {
return Ok(1.0);
}
Ok(0.0)
}

/// `Array.prototype.reduce( callbackFn [ , initialValue ] )`
///
/// The reduce method traverses left to right starting from the first defined value in the array,
Expand Down

0 comments on commit cbfabaf

Please sign in to comment.