Browse Source

Add `Array.prototype.splice` (#1365)

* Add `Array.prototype.splice` (#1365)
pull/1563/head
neeldug 3 years ago committed by GitHub
parent
commit
e9d7ac9333
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 178
      boa/src/builtins/array/mod.rs

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

@ -103,6 +103,7 @@ impl BuiltIn for Array {
.method(Self::slice, "slice", 2) .method(Self::slice, "slice", 2)
.method(Self::some, "some", 2) .method(Self::some, "some", 2)
.method(Self::sort, "sort", 1) .method(Self::sort, "sort", 1)
.method(Self::splice, "splice", 3)
.method(Self::reduce, "reduce", 2) .method(Self::reduce, "reduce", 2)
.method(Self::reduce_right, "reduceRight", 2) .method(Self::reduce_right, "reduceRight", 2)
.method(Self::keys, "keys", 0) .method(Self::keys, "keys", 0)
@ -1782,6 +1783,183 @@ impl Array {
Ok(a.into()) Ok(a.into())
} }
/// `Array.prototype.splice ( start, [deleteCount[, ...items]] )`
///
/// Splices an array by following
/// The deleteCount elements of the array starting at integer index start are replaced by the elements of items.
/// An Array object containing the deleted elements (if any) is returned.
pub(crate) fn splice(
this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let O be ? ToObject(this value).
let o = this.to_object(context)?;
// 2. Let len be ? LengthOfArrayLike(O).
let len = o.length_of_array_like(context)?;
let start = args.get(0);
let delete_count = args.get(1);
let items = args.get(2..).unwrap_or(&[]);
// 3. Let relativeStart be ? ToIntegerOrInfinity(start).
// 4. If relativeStart is -∞, let actualStart be 0.
// 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart, 0).
// 6. Else, let actualStart be min(relativeStart, len).
let actual_start = Self::get_relative_start(context, start, len)?;
// 7. If start is not present, then
let insert_count = if start.is_none() || delete_count.is_none() {
// 7a. Let insertCount be 0.
// 8. Else if deleteCount is not present, then
// a. Let insertCount be 0.
0
// 9. Else,
} else {
// 9a. Let insertCount be the number of elements in items.
items.len()
};
let actual_delete_count = if start.is_none() {
// 7b. Let actualDeleteCount be 0.
0
// 8. Else if deleteCount is not present, then
} else if delete_count.is_none() {
// 8b. Let actualDeleteCount be len - actualStart.
len - actual_start
// 9. Else,
} else {
// b. Let dc be ? ToIntegerOrInfinity(deleteCount).
let dc = delete_count
.cloned()
.unwrap_or_default()
.to_integer_or_infinity(context)?;
// c. Let actualDeleteCount be the result of clamping dc between 0 and len - actualStart.
let max = len - actual_start;
match dc {
IntegerOrInfinity::Integer(i) => (i as usize).clamp(0, max),
IntegerOrInfinity::PositiveInfinity => max,
IntegerOrInfinity::NegativeInfinity => 0,
}
};
// 10. If len + insertCount - actualDeleteCount > 2^53 - 1, throw a TypeError exception.
if len + insert_count - actual_delete_count > Number::MAX_SAFE_INTEGER as usize {
return context.throw_type_error("Target splice exceeded max safe integer value");
}
// 11. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
let arr = Self::array_species_create(&o, actual_delete_count, context)?;
// 12. Let k be 0.
// 13. Repeat, while k < actualDeleteCount,
for k in 0..actual_delete_count {
// a. Let from be ! ToString(𝔽(actualStart + k)).
// b. Let fromPresent be ? HasProperty(O, from).
let from_present = o.has_property(actual_start + k, context)?;
// c. If fromPresent is true, then
if from_present {
// i. Let fromValue be ? Get(O, from).
let from_value = o.get(actual_start + k, context)?;
// ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue).
arr.create_data_property_or_throw(k, from_value, context)?;
}
// d. Set k to k + 1.
}
// 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true).
arr.set("length", actual_delete_count, true, context)?;
// 15. Let itemCount be the number of elements in items.
let item_count = items.len();
match item_count.cmp(&actual_delete_count) {
// 16. If itemCount < actualDeleteCount, then
Ordering::Less => {
// a. Set k to actualStart.
// b. Repeat, while k < (len - actualDeleteCount),
for k in actual_start..(len - actual_delete_count) {
// i. Let from be ! ToString(𝔽(k + actualDeleteCount)).
let from = k + actual_delete_count;
// ii. Let to be ! ToString(𝔽(k + itemCount)).
let to = k + item_count;
// iii. Let fromPresent be ? HasProperty(O, from).
let from_present = o.has_property(from, context)?;
// iv. If fromPresent is true, then
if from_present {
// 1. Let fromValue be ? Get(O, from).
let from_value = o.get(from, context)?;
// 2. Perform ? Set(O, to, fromValue, true).
o.set(to, from_value, true, context)?;
// v. Else,
} else {
// 1. Assert: fromPresent is false.
debug_assert!(!from_present);
// 2. Perform ? DeletePropertyOrThrow(O, to).
o.delete_property_or_throw(to, context)?;
}
// vi. Set k to k + 1.
}
// c. Set k to len.
// d. Repeat, while k > (len - actualDeleteCount + itemCount),
for k in ((len - actual_delete_count + item_count)..len).rev() {
// i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))).
o.delete_property_or_throw(k, context)?;
// ii. Set k to k - 1.
}
}
// 17. Else if itemCount > actualDeleteCount, then
Ordering::Greater => {
// a. Set k to (len - actualDeleteCount).
// b. Repeat, while k > actualStart,
for k in (actual_start..len - actual_delete_count).rev() {
// i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)).
let from = k + actual_delete_count;
// ii. Let to be ! ToString(𝔽(k + itemCount - 1)).
let to = k + item_count;
// iii. Let fromPresent be ? HasProperty(O, from).
let from_present = o.has_property(from, context)?;
// iv. If fromPresent is true, then
if from_present {
// 1. Let fromValue be ? Get(O, from).
let from_value = o.get(from, context)?;
// 2. Perform ? Set(O, to, fromValue, true).
o.set(to, from_value, true, context)?;
// v. Else,
} else {
// 1. Assert: fromPresent is false.
debug_assert!(!from_present);
// 2. Perform ? DeletePropertyOrThrow(O, to).
o.delete_property_or_throw(to, context)?;
}
// vi. Set k to k - 1.
}
}
Ordering::Equal => {}
};
// 18. Set k to actualStart.
// 19. For each element E of items, do
if item_count > 0 {
for (k, item) in items
.iter()
.enumerate()
.map(|(i, val)| (i + actual_start, val))
{
// a. Perform ? Set(O, ! ToString(𝔽(k)), E, true).
o.set(k, item, true, context)?;
// b. Set k to k + 1.
}
}
// 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true).
o.set(
"length",
len - actual_delete_count + item_count,
true,
context,
)?;
// 21. Return A.
Ok(JsValue::from(arr))
}
/// `Array.prototype.filter( callback, [ thisArg ] )` /// `Array.prototype.filter( callback, [ thisArg ] )`
/// ///
/// For each element in the array the callback function is called, and a new /// For each element in the array the callback function is called, and a new

Loading…
Cancel
Save