From f44a83e38c33b04a89700615c15084c7ad8868f6 Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sun, 27 Jun 2021 19:47:41 +0100 Subject: [PATCH 01/16] feat(boa): adds splice method - adds array.prototype.splice - todo: fix stalls at certain testcases Closes #36 --- boa/src/builtins/array/mod.rs | 128 ++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index daaa5d10865..e2f55af8b11 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -108,6 +108,7 @@ impl BuiltIn for Array { .method(Self::flat_map, "flatMap", 1) .method(Self::slice, "slice", 2) .method(Self::some, "some", 2) + .method(Self::splice, "splice", 3) .method(Self::reduce, "reduce", 2) .method(Self::reduce_right, "reduceRight", 2) .method(Self::keys, "keys", 0) @@ -1512,6 +1513,133 @@ impl Array { Ok(new_array) } + pub(crate) fn splice(this: &Value, args: &[Value], context: &mut Context) -> Result { + let o = this.to_object(context)?; + let len = this.get_field("length", context)?.to_length(context)?; + let actual_start = Self::get_relative_start(context, args.get(0), len)?; + let insert_count = if args.get(0).is_none() || args.get(1).is_none() { + 0 + } else { + args.len() + }; + let actual_delete_count = if args.get(0).is_none() { + 0 + } else if args.get(1).is_none() { + len - actual_start + } else { + let dc = args.get(1).ok_or(0)?.to_integer_or_infinity(context)?; + let max = len - actual_start; + match dc { + IntegerOrInfinity::Integer(i) => { + if i < 0 { + 0 + } else if i as usize > max { + max + } else { + i as usize + } + } + IntegerOrInfinity::PositiveInfinity => max, + IntegerOrInfinity::NegativeInfinity => 0, + } + }; + + 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"); + } + + let arr = Self::array_species_create(&o, actual_delete_count as u32, context)?; + + for k in 0..actual_delete_count { + let from_present = this.has_field(actual_start + k); + if from_present { + let from_value = this.get_field(actual_start + k, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + arr.set_property(actual_start + k, fv); + } + } + + let acd = DataDescriptor::new( + Value::from(len), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + + arr.set_property("length", acd); + + let items = match args.len() { + 0 | 1 | 2 => args.split_at(0).0, // empty arr + _ => args.split_at(2).1 + }; + + let item_count = items.len(); + + match item_count { + ic if ic < actual_delete_count => { + for k in actual_start..len-actual_delete_count { + let from = k + actual_delete_count; + let to = k + item_count; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + for k in ((len-actual_delete_count+item_count)+1..=len).rev() { + this.remove_property(k-1); + } + }, + ic if ic > actual_delete_count => { + for k in (actual_start+1..=len-actual_delete_count).rev() { + let from = k + actual_delete_count - 1; + let to = k + item_count - 1; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + }, + _ => {} + }; + + let mut k = actual_start; + + for item in items { + let prop = DataDescriptor::new( + item, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(k, prop); + k += 1; + } + + let length = DataDescriptor::new( + Value::from(len-actual_delete_count+item_count), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + + this.set_property("length", length); + + Ok(arr) + } + /// `Array.prototype.filter( callback, [ thisArg ] )` /// /// For each element in the array the callback function is called, and a new From 02b431ebee8f51344e89e5a72f014698ba05e1f4 Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sun, 27 Jun 2021 19:55:38 +0100 Subject: [PATCH 02/16] fix rust_fmt --- boa/src/builtins/array/mod.rs | 84 +++++++++++++++++------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index e2f55af8b11..747569716d5 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1571,52 +1571,52 @@ impl Array { let items = match args.len() { 0 | 1 | 2 => args.split_at(0).0, // empty arr - _ => args.split_at(2).1 + _ => args.split_at(2).1, }; let item_count = items.len(); match item_count { - ic if ic < actual_delete_count => { - for k in actual_start..len-actual_delete_count { - let from = k + actual_delete_count; - let to = k + item_count; - let from_present = this.has_field(from); - if from_present { - let from_value = this.get_field(from, context)?; - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); - } else { - debug_assert!(!from_present); - this.remove_property(to); - } - } - for k in ((len-actual_delete_count+item_count)+1..=len).rev() { - this.remove_property(k-1); - } - }, - ic if ic > actual_delete_count => { - for k in (actual_start+1..=len-actual_delete_count).rev() { - let from = k + actual_delete_count - 1; - let to = k + item_count - 1; - let from_present = this.has_field(from); - if from_present { - let from_value = this.get_field(from, context)?; - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); - } else { - debug_assert!(!from_present); - this.remove_property(to); - } - } - }, - _ => {} + ic if ic < actual_delete_count => { + for k in actual_start..len - actual_delete_count { + let from = k + actual_delete_count; + let to = k + item_count; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + for k in ((len - actual_delete_count + item_count) + 1..=len).rev() { + this.remove_property(k - 1); + } + } + ic if ic > actual_delete_count => { + for k in (actual_start + 1..=len - actual_delete_count).rev() { + let from = k + actual_delete_count - 1; + let to = k + item_count - 1; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + } + _ => {} }; let mut k = actual_start; @@ -1631,7 +1631,7 @@ impl Array { } let length = DataDescriptor::new( - Value::from(len-actual_delete_count+item_count), + Value::from(len - actual_delete_count + item_count), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); From 272a756a6cdf351f7951477ce5e46f205e005e59 Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Thu, 22 Jul 2021 00:06:51 +0100 Subject: [PATCH 03/16] add docs --- boa/src/builtins/array/mod.rs | 106 ++++++++++++++++++++++++++++------ boa/src/value/mod.rs | 13 +++++ 2 files changed, 101 insertions(+), 18 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 747569716d5..4021aa71d29 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1512,22 +1512,41 @@ impl Array { new_array.set_field("length", Value::from(new_array_len), true, context)?; Ok(new_array) } + /// `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: &Value, args: &[Value], context: &mut Context) -> Result { + // 1. Let O be ? ToObject(this value). let o = this.to_object(context)?; + // 2. Let len be ? LengthOfArrayLike(O). let len = this.get_field("length", context)?.to_length(context)?; + // 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, args.get(0), len)?; + // 7. If start is not present, then let insert_count = if args.get(0).is_none() || args.get(1).is_none() { + // 7a. Let insertCount be 0. + // 8a. Let insertCount be 0. 0 } else { + // 9a. Let insertCount be the number of elements in items. args.len() }; let actual_delete_count = if args.get(0).is_none() { + // 7b. Let actualDeleteCount be 0. 0 } else if args.get(1).is_none() { + // 8b. Let actualDeleteCount be len - actualStart. len - actual_start } else { + // 9b. Let dc be ? ToIntegerOrInfinity(deleteCount). let dc = args.get(1).ok_or(0)?.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) => { @@ -1543,93 +1562,143 @@ impl Array { IntegerOrInfinity::NegativeInfinity => 0, } }; - + // 10. If len + insertCount - actualDeleteCount > 253 - 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"); } + //temp until arrayspecies merges + if actual_delete_count >= u32::MAX 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 as u32, 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 = this.has_field(actual_start + k); + // c. If fromPresent is true, then if from_present { + // i. Let fromValue be ? Get(O, from). let from_value = this.get_field(actual_start + k, context)?; + // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue). let fv = DataDescriptor::new( from_value, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); arr.set_property(actual_start + k, fv); } + // d. Set k to k + 1. } + // 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true). let acd = DataDescriptor::new( - Value::from(len), + Value::from(actual_delete_count), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); arr.set_property("length", acd); - let items = match args.len() { - 0 | 1 | 2 => args.split_at(0).0, // empty arr - _ => args.split_at(2).1, - }; - - let item_count = items.len(); + // 15. Let itemCount be the number of elements in items. + let item_count = args.len().saturating_sub(2); match item_count { + // 16. If itemCount < actualDeleteCount, then ic if ic < actual_delete_count => { + // 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 = this.has_field(from); + // iv. If fromPresent is true, then if from_present { + // 1. Let fromValue be ? Get(O, from). let from_value = this.get_field(from, context)?; + // 2. Perform ? Set(O, to, fromValue, true). let fv = DataDescriptor::new( from_value, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); this.set_property(to, fv); + // v. Else, } else { + // 1. Assert: fromPresent is false. debug_assert!(!from_present); - this.remove_property(to); + // 2. Perform ? DeletePropertyOrThrow(O, to). + this.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) + 1..=len).rev() { - this.remove_property(k - 1); + // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))). + this.delete_property_or_throw(k - 1, context)?; + // ii. Set k to k - 1. } } + // 17. Else if itemCount > actualDeleteCount, then ic if ic > actual_delete_count => { + // a. Set k to (len - actualDeleteCount). + // b. Repeat, while k > actualStart, for k in (actual_start + 1..=len - actual_delete_count).rev() { + // i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)). let from = k + actual_delete_count - 1; + // ii. Let to be ! ToString(𝔽(k + itemCount - 1)). let to = k + item_count - 1; + // iii. Let fromPresent be ? HasProperty(O, from). let from_present = this.has_field(from); + // iv. If fromPresent is true, then if from_present { + // 1. Let fromValue be ? Get(O, from). let from_value = this.get_field(from, context)?; + // 2. Perform ? Set(O, to, fromValue, true). let fv = DataDescriptor::new( from_value, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); this.set_property(to, fv); + // v. Else, } else { + // 1. Assert: fromPresent is false. debug_assert!(!from_present); - this.remove_property(to); + // 2. Perform ? DeletePropertyOrThrow(O, to). + this.delete_property_or_throw(to, context)?; } + // vi. Set k to k - 1. } } _ => {} }; + // 18. Set k to actualStart. let mut k = actual_start; - for item in items { - let prop = DataDescriptor::new( - item, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(k, prop); - k += 1; + // 19. For each element E of items, do + if item_count > 0 { + let items = args.split_at(2).1; + for item in items { + // a. Perform ? Set(O, ! ToString(𝔽(k)), E, true). + let prop = DataDescriptor::new( + item, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(k, prop); + // b. Set k to k + 1. + k += 1; + } } + // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true). let length = DataDescriptor::new( Value::from(len - actual_delete_count + item_count), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, @@ -1637,6 +1706,7 @@ impl Array { this.set_property("length", length); + // 21. Return A. Ok(arr) } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 9c9a26a6dea..a6fa89c65f4 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -429,6 +429,19 @@ impl Value { self.as_object().map(|x| x.remove(&key.into())).is_some() } + pub fn delete_property_or_throw(&self, key: Key, context: &mut Context) -> Result + where + Key: Into, + { + debug_assert!(self.is_object()); + // debug_assert!(self.get_property(key).is_some()); + if self.remove_property(key) { + Ok(Value::from(true)) + } else { + context.throw_type_error("Unable to delete property") + } + } + /// Resolve the property in the object. /// /// A copy of the Property is returned. From e8e43176e68a6ba3dd0fe64966b99725069fa861 Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sun, 27 Jun 2021 19:47:41 +0100 Subject: [PATCH 04/16] feat(boa): adds splice method - adds array.prototype.splice - todo: fix stalls at certain testcases Closes #36 --- boa/src/builtins/array/mod.rs | 127 ++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 4021aa71d29..24391926784 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1710,6 +1710,133 @@ impl Array { Ok(arr) } + pub(crate) fn splice(this: &Value, args: &[Value], context: &mut Context) -> Result { + let o = this.to_object(context)?; + let len = this.get_field("length", context)?.to_length(context)?; + let actual_start = Self::get_relative_start(context, args.get(0), len)?; + let insert_count = if args.get(0).is_none() || args.get(1).is_none() { + 0 + } else { + args.len() + }; + let actual_delete_count = if args.get(0).is_none() { + 0 + } else if args.get(1).is_none() { + len - actual_start + } else { + let dc = args.get(1).ok_or(0)?.to_integer_or_infinity(context)?; + let max = len - actual_start; + match dc { + IntegerOrInfinity::Integer(i) => { + if i < 0 { + 0 + } else if i as usize > max { + max + } else { + i as usize + } + } + IntegerOrInfinity::PositiveInfinity => max, + IntegerOrInfinity::NegativeInfinity => 0, + } + }; + + 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"); + } + + let arr = Self::array_species_create(&o, actual_delete_count as u32, context)?; + + for k in 0..actual_delete_count { + let from_present = this.has_field(actual_start + k); + if from_present { + let from_value = this.get_field(actual_start + k, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + arr.set_property(actual_start + k, fv); + } + } + + let acd = DataDescriptor::new( + Value::from(len), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + + arr.set_property("length", acd); + + let items = match args.len() { + 0 | 1 | 2 => args.split_at(0).0, // empty arr + _ => args.split_at(2).1 + }; + + let item_count = items.len(); + + match item_count { + ic if ic < actual_delete_count => { + for k in actual_start..len-actual_delete_count { + let from = k + actual_delete_count; + let to = k + item_count; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + for k in ((len-actual_delete_count+item_count)+1..=len).rev() { + this.remove_property(k-1); + } + }, + ic if ic > actual_delete_count => { + for k in (actual_start+1..=len-actual_delete_count).rev() { + let from = k + actual_delete_count - 1; + let to = k + item_count - 1; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + }, + _ => {} + }; + + let mut k = actual_start; + + for item in items { + let prop = DataDescriptor::new( + item, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(k, prop); + k += 1; + } + + let length = DataDescriptor::new( + Value::from(len-actual_delete_count+item_count), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + + this.set_property("length", length); + + Ok(arr) + } + /// `Array.prototype.filter( callback, [ thisArg ] )` /// /// For each element in the array the callback function is called, and a new From 1b1173c4ae3ff1b4e25926ed2718153990d73eab Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sun, 27 Jun 2021 19:55:38 +0100 Subject: [PATCH 05/16] fix rust_fmt --- boa/src/builtins/array/mod.rs | 84 +++++++++++++++++------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 24391926784..2836f915764 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1768,52 +1768,52 @@ impl Array { let items = match args.len() { 0 | 1 | 2 => args.split_at(0).0, // empty arr - _ => args.split_at(2).1 + _ => args.split_at(2).1, }; let item_count = items.len(); match item_count { - ic if ic < actual_delete_count => { - for k in actual_start..len-actual_delete_count { - let from = k + actual_delete_count; - let to = k + item_count; - let from_present = this.has_field(from); - if from_present { - let from_value = this.get_field(from, context)?; - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); - } else { - debug_assert!(!from_present); - this.remove_property(to); - } - } - for k in ((len-actual_delete_count+item_count)+1..=len).rev() { - this.remove_property(k-1); - } - }, - ic if ic > actual_delete_count => { - for k in (actual_start+1..=len-actual_delete_count).rev() { - let from = k + actual_delete_count - 1; - let to = k + item_count - 1; - let from_present = this.has_field(from); - if from_present { - let from_value = this.get_field(from, context)?; - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); - } else { - debug_assert!(!from_present); - this.remove_property(to); - } - } - }, - _ => {} + ic if ic < actual_delete_count => { + for k in actual_start..len - actual_delete_count { + let from = k + actual_delete_count; + let to = k + item_count; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + for k in ((len - actual_delete_count + item_count) + 1..=len).rev() { + this.remove_property(k - 1); + } + } + ic if ic > actual_delete_count => { + for k in (actual_start + 1..=len - actual_delete_count).rev() { + let from = k + actual_delete_count - 1; + let to = k + item_count - 1; + let from_present = this.has_field(from); + if from_present { + let from_value = this.get_field(from, context)?; + let fv = DataDescriptor::new( + from_value, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(to, fv); + } else { + debug_assert!(!from_present); + this.remove_property(to); + } + } + } + _ => {} }; let mut k = actual_start; @@ -1828,7 +1828,7 @@ impl Array { } let length = DataDescriptor::new( - Value::from(len-actual_delete_count+item_count), + Value::from(len - actual_delete_count + item_count), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); From 316157eaeb02cd9a15068f6ae6812b59a51a433e Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Thu, 22 Jul 2021 00:06:51 +0100 Subject: [PATCH 06/16] add docs --- boa/src/builtins/array/mod.rs | 85 ++++++++++++++++++++++++++++------- boa/src/value/mod.rs | 13 ++++++ 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 2836f915764..69e7451a7e4 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1745,88 +1745,138 @@ impl Array { return context.throw_type_error("Target splice exceeded max safe integer value"); } + //temp until arrayspecies merges + if actual_delete_count >= u32::MAX 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 as u32, 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 = this.has_field(actual_start + k); + // c. If fromPresent is true, then if from_present { + // i. Let fromValue be ? Get(O, from). let from_value = this.get_field(actual_start + k, context)?; + // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue). let fv = DataDescriptor::new( from_value, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); arr.set_property(actual_start + k, fv); } + // d. Set k to k + 1. } + // 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true). let acd = DataDescriptor::new( - Value::from(len), + Value::from(actual_delete_count), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); arr.set_property("length", acd); - let items = match args.len() { - 0 | 1 | 2 => args.split_at(0).0, // empty arr - _ => args.split_at(2).1, - }; - - let item_count = items.len(); + // 15. Let itemCount be the number of elements in items. + let item_count = args.len().saturating_sub(2); match item_count { + // 16. If itemCount < actualDeleteCount, then ic if ic < actual_delete_count => { + // 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 = this.has_field(from); + // iv. If fromPresent is true, then if from_present { + // 1. Let fromValue be ? Get(O, from). let from_value = this.get_field(from, context)?; + // 2. Perform ? Set(O, to, fromValue, true). let fv = DataDescriptor::new( from_value, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); this.set_property(to, fv); + // v. Else, } else { + // 1. Assert: fromPresent is false. debug_assert!(!from_present); - this.remove_property(to); + // 2. Perform ? DeletePropertyOrThrow(O, to). + this.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) + 1..=len).rev() { - this.remove_property(k - 1); + // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))). + this.delete_property_or_throw(k - 1, context)?; + // ii. Set k to k - 1. } } + // 17. Else if itemCount > actualDeleteCount, then ic if ic > actual_delete_count => { + // a. Set k to (len - actualDeleteCount). + // b. Repeat, while k > actualStart, for k in (actual_start + 1..=len - actual_delete_count).rev() { + // i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)). let from = k + actual_delete_count - 1; + // ii. Let to be ! ToString(𝔽(k + itemCount - 1)). let to = k + item_count - 1; + // iii. Let fromPresent be ? HasProperty(O, from). let from_present = this.has_field(from); + // iv. If fromPresent is true, then if from_present { + // 1. Let fromValue be ? Get(O, from). let from_value = this.get_field(from, context)?; + // 2. Perform ? Set(O, to, fromValue, true). let fv = DataDescriptor::new( from_value, Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); this.set_property(to, fv); + // v. Else, } else { + // 1. Assert: fromPresent is false. debug_assert!(!from_present); - this.remove_property(to); + // 2. Perform ? DeletePropertyOrThrow(O, to). + this.delete_property_or_throw(to, context)?; } + // vi. Set k to k - 1. } } _ => {} }; + // 18. Set k to actualStart. let mut k = actual_start; - for item in items { - let prop = DataDescriptor::new( - item, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(k, prop); - k += 1; + // 19. For each element E of items, do + if item_count > 0 { + let items = args.split_at(2).1; + for item in items { + // a. Perform ? Set(O, ! ToString(𝔽(k)), E, true). + let prop = DataDescriptor::new( + item, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + this.set_property(k, prop); + // b. Set k to k + 1. + k += 1; + } } + // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true). let length = DataDescriptor::new( Value::from(len - actual_delete_count + item_count), Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, @@ -1834,6 +1884,7 @@ impl Array { this.set_property("length", length); + // 21. Return A. Ok(arr) } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index a6fa89c65f4..6700ba4fc9f 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -442,6 +442,19 @@ impl Value { } } + pub fn delete_property_or_throw(&self, key: Key, context: &mut Context) -> Result + where + Key: Into, + { + debug_assert!(self.is_object()); + // debug_assert!(self.get_property(key).is_some()); + if self.remove_property(key) { + Ok(Value::from(true)) + } else { + context.throw_type_error("Unable to delete property") + } + } + /// Resolve the property in the object. /// /// A copy of the Property is returned. From b51752486c3c5b259db034f24e4f50f9f551dde5 Mon Sep 17 00:00:00 2001 From: neeldug <5161147+neeldug@users.noreply.github.com> Date: Fri, 30 Jul 2021 01:51:33 +0100 Subject: [PATCH 07/16] Cleanup due to merge --- boa/src/builtins/array/mod.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 69e7451a7e4..bac99f8b761 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1566,16 +1566,8 @@ impl Array { 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"); } - - //temp until arrayspecies merges - if actual_delete_count >= u32::MAX 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 as u32, context)?; - // 12. Let k be 0. // 13. Repeat, while k < actualDeleteCount, for k in 0..actual_delete_count { From 7212db43bf88539b6df1d08848d58c7e8776e0f0 Mon Sep 17 00:00:00 2001 From: neeldug <5161147+neeldug@users.noreply.github.com> Date: Fri, 30 Jul 2021 01:51:33 +0100 Subject: [PATCH 08/16] Cleanup due to merge --- boa/src/builtins/array/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index bac99f8b761..c9cdfed4253 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1745,7 +1745,6 @@ impl Array { // 11. Let A be ? ArraySpeciesCreate(O, actualDeleteCount). let arr = Self::array_species_create(&o, actual_delete_count as u32, context)?; - // 12. Let k be 0. // 13. Repeat, while k < actualDeleteCount, for k in 0..actual_delete_count { From 861462aa6a30b7f62d72288d42f245d9e4b056ce Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Fri, 30 Jul 2021 19:25:36 +0100 Subject: [PATCH 09/16] changes --- boa/src/builtins/array/mod.rs | 240 ++++------------------------------ boa/src/value/mod.rs | 26 ---- 2 files changed, 24 insertions(+), 242 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index c9cdfed4253..09049033bbc 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1522,7 +1522,7 @@ impl Array { // 1. Let O be ? ToObject(this value). let o = this.to_object(context)?; // 2. Let len be ? LengthOfArrayLike(O). - let len = this.get_field("length", context)?.to_length(context)?; + let len = o.length_of_array_like(context)?; // 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). @@ -1531,8 +1531,8 @@ impl Array { // 7. If start is not present, then let insert_count = if args.get(0).is_none() || args.get(1).is_none() { // 7a. Let insertCount be 0. - // 8a. Let insertCount be 0. 0 + // 9. Else, } else { // 9a. Let insertCount be the number of elements in items. args.len() @@ -1540,11 +1540,13 @@ impl Array { let actual_delete_count = if args.get(0).is_none() { // 7b. Let actualDeleteCount be 0. 0 + // 8. Else if deleteCount is not present, then } else if args.get(1).is_none() { // 8b. Let actualDeleteCount be len - actualStart. len - actual_start + // 9. Else, } else { - // 9b. Let dc be ? ToIntegerOrInfinity(deleteCount). + // b. Let dc be ? ToIntegerOrInfinity(deleteCount). let dc = args.get(1).ok_or(0)?.to_integer_or_infinity(context)?; // c. Let actualDeleteCount be the result of clamping dc between 0 and len - actualStart. let max = len - actual_start; @@ -1562,205 +1564,26 @@ impl Array { IntegerOrInfinity::NegativeInfinity => 0, } }; - // 10. If len + insertCount - actualDeleteCount > 253 - 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 as u32, 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 = this.has_field(actual_start + k); - // c. If fromPresent is true, then - if from_present { - // i. Let fromValue be ? Get(O, from). - let from_value = this.get_field(actual_start + k, context)?; - // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue). - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - arr.set_property(actual_start + k, fv); - } - // d. Set k to k + 1. - } - - // 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true). - let acd = DataDescriptor::new( - Value::from(actual_delete_count), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - - arr.set_property("length", acd); - - // 15. Let itemCount be the number of elements in items. - let item_count = args.len().saturating_sub(2); - - match item_count { - // 16. If itemCount < actualDeleteCount, then - ic if ic < actual_delete_count => { - // 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 = this.has_field(from); - // iv. If fromPresent is true, then - if from_present { - // 1. Let fromValue be ? Get(O, from). - let from_value = this.get_field(from, context)?; - // 2. Perform ? Set(O, to, fromValue, true). - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); - // v. Else, - } else { - // 1. Assert: fromPresent is false. - debug_assert!(!from_present); - // 2. Perform ? DeletePropertyOrThrow(O, to). - this.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) + 1..=len).rev() { - // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))). - this.delete_property_or_throw(k - 1, context)?; - // ii. Set k to k - 1. - } - } - // 17. Else if itemCount > actualDeleteCount, then - ic if ic > actual_delete_count => { - // a. Set k to (len - actualDeleteCount). - // b. Repeat, while k > actualStart, - for k in (actual_start + 1..=len - actual_delete_count).rev() { - // i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)). - let from = k + actual_delete_count - 1; - // ii. Let to be ! ToString(𝔽(k + itemCount - 1)). - let to = k + item_count - 1; - // iii. Let fromPresent be ? HasProperty(O, from). - let from_present = this.has_field(from); - // iv. If fromPresent is true, then - if from_present { - // 1. Let fromValue be ? Get(O, from). - let from_value = this.get_field(from, context)?; - // 2. Perform ? Set(O, to, fromValue, true). - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); - // v. Else, - } else { - // 1. Assert: fromPresent is false. - debug_assert!(!from_present); - // 2. Perform ? DeletePropertyOrThrow(O, to). - this.delete_property_or_throw(to, context)?; - } - // vi. Set k to k - 1. - } - } - _ => {} - }; - - // 18. Set k to actualStart. - let mut k = actual_start; - - // 19. For each element E of items, do - if item_count > 0 { - let items = args.split_at(2).1; - for item in items { - // a. Perform ? Set(O, ! ToString(𝔽(k)), E, true). - let prop = DataDescriptor::new( - item, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(k, prop); - // b. Set k to k + 1. - k += 1; - } - } - - // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true). - let length = DataDescriptor::new( - Value::from(len - actual_delete_count + item_count), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - - this.set_property("length", length); - - // 21. Return A. - Ok(arr) - } - - pub(crate) fn splice(this: &Value, args: &[Value], context: &mut Context) -> Result { - let o = this.to_object(context)?; - let len = this.get_field("length", context)?.to_length(context)?; - let actual_start = Self::get_relative_start(context, args.get(0), len)?; - let insert_count = if args.get(0).is_none() || args.get(1).is_none() { - 0 - } else { - args.len() - }; - let actual_delete_count = if args.get(0).is_none() { - 0 - } else if args.get(1).is_none() { - len - actual_start - } else { - let dc = args.get(1).ok_or(0)?.to_integer_or_infinity(context)?; - let max = len - actual_start; - match dc { - IntegerOrInfinity::Integer(i) => { - if i < 0 { - 0 - } else if i as usize > max { - max - } else { - i as usize - } - } - IntegerOrInfinity::PositiveInfinity => max, - IntegerOrInfinity::NegativeInfinity => 0, - } - }; + // 10. If len + insertCount - actualDeleteCount > 253 - 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"); } - //temp until arrayspecies merges - if actual_delete_count >= u32::MAX 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 as u32, context)?; + 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 = this.has_field(actual_start + k); + let from_present = o.has_property(actual_start + k)?; // c. If fromPresent is true, then if from_present { // i. Let fromValue be ? Get(O, from). - let from_value = this.get_field(actual_start + k, context)?; + let from_value = o.get(actual_start + k, context)?; // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue). - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - arr.set_property(actual_start + k, fv); + arr.create_data_property_or_throw(actual_start + k, from_value, context)?; } // d. Set k to k + 1. } @@ -1773,6 +1596,8 @@ impl Array { arr.set_property("length", acd); + arr.set("length", actual_delete_count, true, context)?; + // 15. Let itemCount be the number of elements in items. let item_count = args.len().saturating_sub(2); @@ -1787,23 +1612,19 @@ impl Array { // ii. Let to be ! ToString(𝔽(k + itemCount)). let to = k + item_count; // iii. Let fromPresent be ? HasProperty(O, from). - let from_present = this.has_field(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 = this.get_field(from, context)?; + let from_value = o.get(from, context)?; // 2. Perform ? Set(O, to, fromValue, true). - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); + o.set(to, from_value, true, context)?; // v. Else, } else { // 1. Assert: fromPresent is false. debug_assert!(!from_present); // 2. Perform ? DeletePropertyOrThrow(O, to). - this.delete_property_or_throw(to, context)?; + o.delete_property_or_throw(to, context)?; } // vi. Set k to k + 1. } @@ -1811,7 +1632,7 @@ impl Array { // d. Repeat, while k > (len - actualDeleteCount + itemCount), for k in ((len - actual_delete_count + item_count) + 1..=len).rev() { // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))). - this.delete_property_or_throw(k - 1, context)?; + o.delete_property_or_throw(k - 1, context)?; // ii. Set k to k - 1. } } @@ -1825,23 +1646,19 @@ impl Array { // ii. Let to be ! ToString(𝔽(k + itemCount - 1)). let to = k + item_count - 1; // iii. Let fromPresent be ? HasProperty(O, from). - let from_present = this.has_field(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 = this.get_field(from, context)?; + let from_value = o.get(from, context)?; // 2. Perform ? Set(O, to, fromValue, true). - let fv = DataDescriptor::new( - from_value, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(to, fv); + o.set(to, from_value, true, context)?; // v. Else, } else { // 1. Assert: fromPresent is false. debug_assert!(!from_present); // 2. Perform ? DeletePropertyOrThrow(O, to). - this.delete_property_or_throw(to, context)?; + o.delete_property_or_throw(to, context)?; } // vi. Set k to k - 1. } @@ -1857,26 +1674,17 @@ impl Array { let items = args.split_at(2).1; for item in items { // a. Perform ? Set(O, ! ToString(𝔽(k)), E, true). - let prop = DataDescriptor::new( - item, - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - this.set_property(k, prop); + o.set(k, item, true, context)?; // b. Set k to k + 1. k += 1; } } // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true). - let length = DataDescriptor::new( - Value::from(len - actual_delete_count + item_count), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - - this.set_property("length", length); + o.set("length", len - actual_delete_count + item_count, true, context)?; // 21. Return A. - Ok(arr) + Ok(Value::from(arr)) } /// `Array.prototype.filter( callback, [ thisArg ] )` diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 6700ba4fc9f..9c9a26a6dea 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -429,32 +429,6 @@ impl Value { self.as_object().map(|x| x.remove(&key.into())).is_some() } - pub fn delete_property_or_throw(&self, key: Key, context: &mut Context) -> Result - where - Key: Into, - { - debug_assert!(self.is_object()); - // debug_assert!(self.get_property(key).is_some()); - if self.remove_property(key) { - Ok(Value::from(true)) - } else { - context.throw_type_error("Unable to delete property") - } - } - - pub fn delete_property_or_throw(&self, key: Key, context: &mut Context) -> Result - where - Key: Into, - { - debug_assert!(self.is_object()); - // debug_assert!(self.get_property(key).is_some()); - if self.remove_property(key) { - Ok(Value::from(true)) - } else { - context.throw_type_error("Unable to delete property") - } - } - /// Resolve the property in the object. /// /// A copy of the Property is returned. From 824b06b8b55d394f7d2f7c7757322402f5def24a Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Fri, 30 Jul 2021 19:30:14 +0100 Subject: [PATCH 10/16] final fixes --- boa/src/builtins/array/mod.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 09049033bbc..bb2e6910eab 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1577,7 +1577,7 @@ impl Array { 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)?; + 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). @@ -1589,13 +1589,6 @@ impl Array { } // 14. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true). - let acd = DataDescriptor::new( - Value::from(actual_delete_count), - Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - - arr.set_property("length", acd); - arr.set("length", actual_delete_count, true, context)?; // 15. Let itemCount be the number of elements in items. From 68cdfbfd9672e66187883d1ab3195b0373795713 Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Thu, 5 Aug 2021 18:24:18 +0100 Subject: [PATCH 11/16] fix clippy --- boa/src/builtins/array/mod.rs | 11 ++++++++--- boa_tester/src/exec/mod.rs | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index bb2e6910eab..f94e422c57a 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -167,7 +167,7 @@ impl Array { // i. Let intLen be ! ToUint32(len). let int_len = len.to_u32(context).unwrap(); // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception. - if !Value::same_value_zero(&int_len.into(), &len) { + if !Value::same_value_zero(&int_len.into(), len) { return Err(context.construct_range_error("invalid array length")); } int_len @@ -1623,7 +1623,7 @@ impl Array { } // c. Set k to len. // d. Repeat, while k > (len - actualDeleteCount + itemCount), - for k in ((len - actual_delete_count + item_count) + 1..=len).rev() { + for k in ((len - actual_delete_count + item_count + 1)..=len).rev() { // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))). o.delete_property_or_throw(k - 1, context)?; // ii. Set k to k - 1. @@ -1674,7 +1674,12 @@ impl Array { } // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true). - o.set("length", len - actual_delete_count + item_count, true, context)?; + o.set( + "length", + len - actual_delete_count + item_count, + true, + context, + )?; // 21. Return A. Ok(Value::from(arr)) diff --git a/boa_tester/src/exec/mod.rs b/boa_tester/src/exec/mod.rs index 82634a69b84..b8be3404396 100644 --- a/boa_tester/src/exec/mod.rs +++ b/boa_tester/src/exec/mod.rs @@ -137,7 +137,7 @@ impl Test { Outcome::Positive => { // TODO: implement async and add `harness/doneprintHandle.js` to the includes. - match self.set_up_env(&harness, strict) { + match self.set_up_env(harness, strict) { Ok(mut context) => { let res = context.eval(&self.content.as_ref()); @@ -183,7 +183,7 @@ impl Test { if let Err(e) = parse(&self.content.as_ref(), strict) { (false, format!("Uncaught {}", e)) } else { - match self.set_up_env(&harness, strict) { + match self.set_up_env(harness, strict) { Ok(mut context) => match context.eval(&self.content.as_ref()) { Ok(res) => (false, format!("{}", res.display())), Err(e) => { From bcf71eebefe190c92b26933f72a41958e21d26b7 Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Fri, 6 Aug 2021 19:16:40 +0100 Subject: [PATCH 12/16] changes --- boa/src/builtins/array/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index f94e422c57a..768f49c902a 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1531,11 +1531,13 @@ impl Array { // 7. If start is not present, then let insert_count = if args.get(0).is_none() || args.get(1).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. - args.len() + args.len().saturating_sub(2) }; let actual_delete_count = if args.get(0).is_none() { // 7b. Let actualDeleteCount be 0. @@ -1599,7 +1601,7 @@ impl Array { ic if ic < actual_delete_count => { // a. Set k to actualStart. // b. Repeat, while k < (len - actualDeleteCount), - for k in actual_start..len - actual_delete_count { + 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)). From 1a0f3395efb2698654c02e2dcf3463a650a3539f Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sat, 4 Sep 2021 13:44:34 +0100 Subject: [PATCH 13/16] (cleanup) fix formatting and unwrap --- boa/src/builtins/array/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 768f49c902a..93aa969e02f 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1512,12 +1512,12 @@ impl Array { new_array.set_field("length", Value::from(new_array_len), true, context)?; Ok(new_array) } + /// `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: &Value, args: &[Value], context: &mut Context) -> Result { // 1. Let O be ? ToObject(this value). let o = this.to_object(context)?; @@ -1549,7 +1549,11 @@ impl Array { // 9. Else, } else { // b. Let dc be ? ToIntegerOrInfinity(deleteCount). - let dc = args.get(1).ok_or(0)?.to_integer_or_infinity(context)?; + let dc = args + .get(1) + .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 { From e992b9227ef55362f1b2a4b260930b42c87a7df3 Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sat, 4 Sep 2021 15:00:21 +0100 Subject: [PATCH 14/16] update submodule --- test262 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test262 b/test262 index e793512b55c..2c4f2665ec8 160000 --- a/test262 +++ b/test262 @@ -1 +1 @@ -Subproject commit e793512b55c199de6abc392d1be4de7325dae544 +Subproject commit 2c4f2665ec86f01bff7e42d3a5b54c9a09f9a362 From 7542ffb8eb6973549afa6f8b7a224491e982a51a Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sat, 4 Sep 2021 17:53:09 +0100 Subject: [PATCH 15/16] (refactor) cleaning up and improving readability --- boa/src/builtins/array/mod.rs | 51 ++++++++++++++++------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index aa5060e1730..bcac302f730 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1797,13 +1797,17 @@ impl Array { 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, args.get(0), len)?; + let actual_start = Self::get_relative_start(context, start, len)?; // 7. If start is not present, then - let insert_count = if args.get(0).is_none() || args.get(1).is_none() { + 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. @@ -1811,41 +1815,32 @@ impl Array { // 9. Else, } else { // 9a. Let insertCount be the number of elements in items. - args.len().saturating_sub(2) + items.len() }; - let actual_delete_count = if args.get(0).is_none() { + let actual_delete_count = if start.is_none() { // 7b. Let actualDeleteCount be 0. 0 // 8. Else if deleteCount is not present, then - } else if args.get(1).is_none() { + } 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 = args - .get(1) + 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) => { - if i < 0 { - 0 - } else if i as usize > max { - max - } else { - i as usize - } - } + IntegerOrInfinity::Integer(i) => (i as usize).clamp(0, max), IntegerOrInfinity::PositiveInfinity => max, IntegerOrInfinity::NegativeInfinity => 0, } }; - // 10. If len + insertCount - actualDeleteCount > 253 - 1, throw a TypeError exception. + // 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"); } @@ -1863,7 +1858,7 @@ impl Array { // 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(actual_start + k, from_value, context)?; + arr.create_data_property_or_throw(k, from_value, context)?; } // d. Set k to k + 1. } @@ -1872,11 +1867,11 @@ impl Array { arr.set("length", actual_delete_count, true, context)?; // 15. Let itemCount be the number of elements in items. - let item_count = args.len().saturating_sub(2); + let item_count = items.len(); - match item_count { + match item_count.cmp(&actual_delete_count) { // 16. If itemCount < actualDeleteCount, then - ic if ic < actual_delete_count => { + Ordering::Less => { // a. Set k to actualStart. // b. Repeat, while k < (len - actualDeleteCount), for k in actual_start..(len - actual_delete_count) { @@ -1910,7 +1905,7 @@ impl Array { } } // 17. Else if itemCount > actualDeleteCount, then - ic if ic > actual_delete_count => { + Ordering::Greater => { // a. Set k to (len - actualDeleteCount). // b. Repeat, while k > actualStart, for k in (actual_start + 1..=len - actual_delete_count).rev() { @@ -1936,20 +1931,20 @@ impl Array { // vi. Set k to k - 1. } } - _ => {} + Ordering::Equal => {} }; // 18. Set k to actualStart. - let mut k = actual_start; - // 19. For each element E of items, do if item_count > 0 { - let items = args.split_at(2).1; - for item in items { + 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. - k += 1; } } From 09a71db0ee69d2acda912b5e19a72cc69fc8448f Mon Sep 17 00:00:00 2001 From: nd419 <5161147+neeldug@users.noreply.github.com> Date: Sun, 5 Sep 2021 23:10:35 +0100 Subject: [PATCH 16/16] fixing weirdness in spec --- boa/src/builtins/array/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index bcac302f730..1a4bebaa44f 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -1898,9 +1898,9 @@ impl Array { } // c. Set k to len. // d. Repeat, while k > (len - actualDeleteCount + itemCount), - for k in ((len - actual_delete_count + item_count + 1)..=len).rev() { + for k in ((len - actual_delete_count + item_count)..len).rev() { // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))). - o.delete_property_or_throw(k - 1, context)?; + o.delete_property_or_throw(k, context)?; // ii. Set k to k - 1. } } @@ -1908,11 +1908,11 @@ impl Array { Ordering::Greater => { // a. Set k to (len - actualDeleteCount). // b. Repeat, while k > actualStart, - for k in (actual_start + 1..=len - actual_delete_count).rev() { + for k in (actual_start..len - actual_delete_count).rev() { // i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)). - let from = k + actual_delete_count - 1; + let from = k + actual_delete_count; // ii. Let to be ! ToString(𝔽(k + itemCount - 1)). - let to = k + item_count - 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