Browse Source

Fix TypedArrayConstructors tests (#3171)

pull/3176/head
raskad 1 year ago committed by GitHub
parent
commit
33e8c51fc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 68
      boa_engine/src/builtins/array_buffer/mod.rs
  2. 281
      boa_engine/src/builtins/typed_array/mod.rs
  3. 343
      boa_engine/src/object/internal_methods/integer_indexed.rs

68
boa_engine/src/builtins/array_buffer/mod.rs

@ -379,7 +379,7 @@ impl ArrayBuffer {
self.array_buffer_data.is_none()
}
/// `25.1.2.4 CloneArrayBuffer ( srcBuffer, srcByteOffset, srcLength, cloneConstructor )`
/// `25.1.2.4 CloneArrayBuffer ( srcBuffer, srcByteOffset, srcLength )`
///
/// More information:
/// - [ECMAScript reference][spec]
@ -389,39 +389,51 @@ impl ArrayBuffer {
&self,
src_byte_offset: u64,
src_length: u64,
clone_constructor: &JsValue,
context: &mut Context<'_>,
) -> JsResult<JsObject> {
// 1. Let targetBuffer be ? AllocateArrayBuffer(cloneConstructor, srcLength).
let target_buffer = Self::allocate(clone_constructor, src_length, context)?;
// 1. Assert: IsDetachedBuffer(srcBuffer) is false.
debug_assert!(!self.is_detached_buffer());
// 2. Let targetBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, srcLength).
let target_buffer = Self::allocate(
&context
.realm()
.intrinsics()
.constructors()
.array_buffer()
.constructor()
.into(),
src_length,
context,
)?;
// 2. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
// 3. Let srcBlock be srcBuffer.[[ArrayBufferData]].
let src_block = self.array_buffer_data.as_deref().ok_or_else(|| {
JsNativeError::syntax().with_message("Cannot clone detached array buffer")
})?;
{
// 4. Let targetBlock be targetBuffer.[[ArrayBufferData]].
let mut target_buffer_mut = target_buffer.borrow_mut();
let target_block = target_buffer_mut
.as_array_buffer_mut()
.expect("This must be an ArrayBuffer");
// 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
copy_data_block_bytes(
target_block
.array_buffer_data
.as_mut()
.expect("ArrayBuffer cannot me detached here"),
0,
src_block,
src_byte_offset as usize,
src_length as usize,
);
}
let src_block = self
.array_buffer_data
.as_deref()
.expect("ArrayBuffer cannot be detached");
// 4. Let targetBlock be targetBuffer.[[ArrayBufferData]].
let mut target_buffer_mut = target_buffer.borrow_mut();
let target_array_buffer = target_buffer_mut
.as_array_buffer_mut()
.expect("This must be an ArrayBuffer");
let target_block = target_array_buffer
.array_buffer_data
.as_mut()
.expect("ArrayBuffer cannot me detached here");
// 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
copy_data_block_bytes(
target_block,
0,
src_block,
src_byte_offset as usize,
src_length as usize,
);
// 6. Return targetBuffer.
drop(target_buffer_mut);
Ok(target_buffer)
}

281
boa_engine/src/builtins/typed_array/mod.rs

@ -184,7 +184,7 @@ macro_rules! typed_array {
let first_argument_v = JsValue::from(first_argument.clone());
let using_iterator =
first_argument_v.get_method(JsSymbol::replace(), context)?;
first_argument_v.get_method(JsSymbol::iterator(), context)?;
// 3. If usingIterator is not undefined, then
if let Some(using_iterator) = using_iterator {
@ -567,6 +567,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length() as i64;
drop(obj_borrow);
// 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
let relative_index = args.get_or_undefined(0).to_integer_or_infinity(context)?;
@ -919,6 +921,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback_fn = match args.get_or_undefined(0).as_object() {
Some(obj) if obj.is_callable() => obj,
@ -1030,6 +1034,8 @@ impl TypedArray {
.into());
}
drop(obj_borrow);
// 15. Repeat, while k < final,
while k < r#final {
// a. Let Pk be ! ToString(𝔽(k)).
@ -1071,9 +1077,13 @@ impl TypedArray {
.into());
}
let typed_array_name = o.typed_array_name();
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback_fn =
match args.get_or_undefined(0).as_object() {
@ -1118,7 +1128,7 @@ impl TypedArray {
}
// 9. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(captured) »).
let a = Self::species_create(obj, o.typed_array_name(), &[captured.into()], context)?;
let a = Self::species_create(obj, typed_array_name, &[captured.into()], context)?;
// 10. Let n be 0.
// 11. For each element e of kept, do
@ -1162,6 +1172,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
let predicate = args.get_or_undefined(0);
let this_arg = args.get_or_undefined(1);
@ -1209,6 +1221,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
let predicate = args.get_or_undefined(0);
let this_arg = args.get_or_undefined(1);
@ -1350,6 +1364,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback_fn =
match args.get_or_undefined(0).as_object() {
@ -1409,6 +1425,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length() as i64;
drop(obj_borrow);
// 4. If len is 0, return false.
if len == 0 {
return Ok(false.into());
@ -1488,6 +1506,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length() as i64;
drop(obj_borrow);
// 4. If len is 0, return -1𝔽.
if len == 0 {
return Ok((-1).into());
@ -1576,6 +1596,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
// 4. If separator is undefined, let sep be the single-element String ",".
let separator = args.get_or_undefined(0);
let sep = if separator.is_undefined() {
@ -1673,6 +1695,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length() as i64;
drop(obj_borrow);
// 4. If len is 0, return -1𝔽.
if len == 0 {
return Ok((-1).into());
@ -1779,9 +1803,13 @@ impl TypedArray {
.into());
}
let typed_array_name = o.typed_array_name();
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback_fn = match args.get_or_undefined(0).as_object() {
Some(obj) if obj.is_callable() => obj,
@ -1795,7 +1823,7 @@ impl TypedArray {
};
// 5. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(len) »).
let a = Self::species_create(obj, o.typed_array_name(), &[len.into()], context)?;
let a = Self::species_create(obj, typed_array_name, &[len.into()], context)?;
// 6. Let k be 0.
// 7. Repeat, while k < len,
@ -1848,6 +1876,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback_fn =
match args.get_or_undefined(0).as_object() {
@ -1933,6 +1963,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length() as i64;
drop(obj_borrow);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback_fn = match args.get_or_undefined(0).as_object() {
Some(obj) if obj.is_callable() => obj,
@ -2021,6 +2053,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length() as f64;
drop(obj_borrow);
// 4. Let middle be floor(len / 2).
let middle = (len / 2.0).floor();
@ -2143,7 +2177,8 @@ impl TypedArray {
}
let target_buffer_obj = target_array
.viewed_array_buffer()
.expect("Already checked for detached buffer");
.expect("Already checked for detached buffer")
.clone();
// 3. Let targetLength be target.[[ArrayLength]].
let target_length = target_array.array_length();
@ -2170,6 +2205,8 @@ impl TypedArray {
// 9. Let targetByteOffset be target.[[ByteOffset]].
let target_byte_offset = target_array.byte_offset();
drop(target_borrow);
// 10. Let srcName be the String value of source.[[TypedArrayName]].
// 11. Let srcType be the Element Type value in Table 73 for srcName.
let src_name = source_array.typed_array_name();
@ -2216,7 +2253,7 @@ impl TypedArray {
// a. If srcBuffer.[[ArrayBufferData]] and targetBuffer.[[ArrayBufferData]] are the same Shared Data Block values, let same be true; else let same be false.
// 19. Else, let same be SameValue(srcBuffer, targetBuffer).
let same = JsObject::equals(&src_buffer_obj, target_buffer_obj);
let same = JsObject::equals(&src_buffer_obj, &target_buffer_obj);
// 20. If same is true, then
let mut src_byte_index = if same {
@ -2225,22 +2262,11 @@ impl TypedArray {
// b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%).
// c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects.
let array_buffer_constructor = context
.intrinsics()
.constructors()
.array_buffer()
.constructor()
.into();
let s = src_buffer_obj
.borrow()
.as_array_buffer()
.expect("Already checked for detached buffer")
.clone_array_buffer(
src_byte_offset,
src_byte_length,
&array_buffer_constructor,
context,
)?;
.clone_array_buffer(src_byte_offset, src_byte_length, context)?;
src_buffer_obj = s;
// d. Let srcByteIndex be 0.
@ -2251,6 +2277,8 @@ impl TypedArray {
src_byte_offset
};
drop(source_borrow);
// 22. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
let mut target_byte_index = target_offset * target_element_size + target_byte_offset;
@ -2373,6 +2401,8 @@ impl TypedArray {
// 7. Let targetByteOffset be target.[[ByteOffset]].
let target_byte_offset = target_array.byte_offset();
drop(target_borrow);
// 8. Let src be ? ToObject(source).
let src = source.to_object(context)?;
@ -2420,6 +2450,10 @@ impl TypedArray {
value.to_number(context)?.into()
};
let target_borrow = target.borrow();
let target_array = target_borrow
.as_typed_array()
.expect("Target must be a typed array");
let target_buffer_obj = target_array
.viewed_array_buffer()
.expect("Already checked for detached buffer");
@ -2534,13 +2568,21 @@ impl TypedArray {
// b. Let srcName be the String value of O.[[TypedArrayName]].
// c. Let srcType be the Element Type value in Table 73 for srcName.
let src_type = o.typed_array_name();
// d. Let targetName be the String value of A.[[TypedArrayName]].
// e. Let targetType be the Element Type value in Table 73 for targetName.
let target_type = a_array.typed_array_name();
// f. If srcType is different from targetType, then
#[allow(clippy::if_not_else)]
if o.typed_array_name() != a_array.typed_array_name() {
if src_type != target_type {
drop(obj_borrow);
drop(a_borrow);
// i. Let n be 0.
let mut n = 0;
// ii. Repeat, while k < final,
while k < r#final {
// 1. Let Pk be ! ToString(𝔽(k)).
@ -2619,11 +2661,14 @@ impl TypedArray {
// 4. Set targetByteIndex to targetByteIndex + 1.
target_byte_index += 1;
}
drop(target_buffer_obj_borrow);
drop(a_borrow);
}
} else {
drop(a_borrow);
}
drop(a_borrow);
// 15. Return A.
Ok(a.into())
}
@ -2657,6 +2702,8 @@ impl TypedArray {
// 3. Let len be O.[[ArrayLength]].
let len = o.array_length();
drop(obj_borrow);
// 4. If IsCallable(callbackfn) is false, throw a TypeError exception.
let callback_fn = match args.get_or_undefined(0).as_object() {
Some(obj) if obj.is_callable() => obj,
@ -3291,86 +3338,82 @@ impl TypedArray {
.with_message("Cannot initialize typed array from detached buffer")
.into());
}
let src_data_obj = src_array
.viewed_array_buffer()
.expect("Already checked for detached buffer");
// 3. Let constructorName be the String value of O.[[TypedArrayName]].
// 4. Let elementType be the Element Type value in Table 73 for constructorName.
// 10. Let elementSize be the Element Size value specified in Table 73 for constructorName.
let constructor_name = o_array.typed_array_name();
// 3. Let elementType be TypedArrayElementType(O).
let element_type = o_array.typed_array_name();
// 5. Let elementLength be srcArray.[[ArrayLength]].
let element_length = src_array.array_length();
// 4. Let elementSize be TypedArrayElementSize(O).
let element_size = element_type.element_size();
// 6. Let srcName be the String value of srcArray.[[TypedArrayName]].
// 7. Let srcType be the Element Type value in Table 73 for srcName.
// 8. Let srcElementSize be the Element Size value specified in Table 73 for srcName.
let src_name = src_array.typed_array_name();
// 5. Let srcType be TypedArrayElementType(srcArray).
let src_type = src_array.typed_array_name();
// 9. Let srcByteOffset be srcArray.[[ByteOffset]].
// 6. Let srcElementSize be TypedArrayElementSize(srcArray).
let src_element_size = src_type.element_size();
// 7. Let srcByteOffset be srcArray.[[ByteOffset]].
let src_byte_offset = src_array.byte_offset();
// 11. Let byteLength be elementSize × elementLength.
let byte_length = constructor_name.element_size() * element_length;
// 8. Let elementLength be srcArray.[[ArrayLength]].
let element_length = src_array.array_length();
// 12. If IsSharedArrayBuffer(srcData) is false, then
// a. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%).
// TODO: Shared Array Buffer
// 13. Else,
// a. Let bufferConstructor be %ArrayBuffer%.
let buffer_constructor =
src_data_obj.species_constructor(StandardConstructors::array_buffer, context)?;
// 9. Let byteLength be elementSize × elementLength.
let byte_length = element_size * element_length;
let src_data_obj = src_array
.viewed_array_buffer()
.expect("Already checked for detached buffer");
let src_data_obj_b = src_data_obj.borrow();
let src_data = src_data_obj_b
.as_array_buffer()
.expect("Already checked for detached buffer");
// 14. If elementType is the same as srcType, then
let data = if constructor_name == src_name {
// a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor).
src_data.clone_array_buffer(
src_byte_offset,
// 10. If elementType is srcType, then
let data = if element_type == src_type {
// a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength).
src_data.clone_array_buffer(src_byte_offset, byte_length, context)?
}
// 11. Else,
else {
// a. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
let data_obj = ArrayBuffer::allocate(
&context
.realm()
.intrinsics()
.constructors()
.array_buffer()
.constructor()
.into(),
byte_length,
&buffer_constructor.into(),
context,
)?
// 15. Else,
} else {
// a. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength).
let data_obj = ArrayBuffer::allocate(&buffer_constructor.into(), byte_length, context)?;
)?;
let mut data_obj_b = data_obj.borrow_mut();
let data = data_obj_b
.as_array_buffer_mut()
.expect("Must be ArrayBuffer");
// b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
if src_data.is_detached_buffer() {
return Err(JsNativeError::typ()
.with_message("Cannot initialize typed array from detached buffer")
.into());
}
// c. If srcArray.[[ContentType]] ≠ O.[[ContentType]], throw a TypeError exception.
if src_name.content_type() != constructor_name.content_type() {
// b. If srcArray.[[ContentType]] is not O.[[ContentType]], throw a TypeError exception.
if src_type.content_type() != element_type.content_type() {
return Err(JsNativeError::typ()
.with_message("Cannot initialize typed array from different content type")
.into());
}
// d. Let srcByteIndex be srcByteOffset.
// c. Let srcByteIndex be srcByteOffset.
let mut src_byte_index = src_byte_offset;
// e. Let targetByteIndex be 0.
// d. Let targetByteIndex be 0.
let mut target_byte_index = 0;
// f. Let count be elementLength.
// e. Let count be elementLength.
let mut count = element_length;
// g. Repeat, while count > 0,
// f. Repeat, while count > 0,
while count > 0 {
// i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, Unordered).
let value = src_data.get_value_from_buffer(
src_byte_index,
src_name,
src_type,
true,
SharedMemoryOrder::Unordered,
None,
@ -3379,7 +3422,7 @@ impl TypedArray {
// ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, Unordered).
data.set_value_in_buffer(
target_byte_index,
constructor_name,
element_type,
&value,
SharedMemoryOrder::Unordered,
None,
@ -3387,31 +3430,33 @@ impl TypedArray {
)?;
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
src_byte_index += src_name.element_size();
src_byte_index += src_element_size;
// iv. Set targetByteIndex to targetByteIndex + elementSize.
target_byte_index += constructor_name.element_size();
target_byte_index += element_size;
// v. Set count to count - 1.
count -= 1;
}
drop(data_obj_b);
data_obj
};
// 16. Set O.[[ViewedArrayBuffer]] to data.
// 17. Set O.[[ByteLength]] to byteLength.
// 18. Set O.[[ByteOffset]] to 0.
// 19. Set O.[[ArrayLength]] to elementLength.
// 12. Set O.[[ViewedArrayBuffer]] to data.
// 13. Set O.[[ByteLength]] to byteLength.
// 14. Set O.[[ByteOffset]] to 0.
// 15. Set O.[[ArrayLength]] to elementLength.
drop(o_obj);
*o.borrow_mut().kind_mut() = ObjectKind::IntegerIndexed(IntegerIndexed::new(
Some(data),
constructor_name,
element_type,
0,
byte_length,
element_length,
));
// 16. Return unused.
Ok(())
}
@ -3428,45 +3473,66 @@ impl TypedArray {
length: &JsValue,
context: &mut Context<'_>,
) -> JsResult<()> {
// 1. Let constructorName be the String value of O.[[TypedArrayName]].
// 2. Let elementSize be the Element Size value specified in Table 73 for constructorName.
let constructor_name = o
// 1. Let elementSize be TypedArrayElementSize(O).
let element_size = o
.borrow()
.as_typed_array()
.expect("This must be a typed array")
.typed_array_name();
.expect("Must be a typed array")
.typed_array_name()
.element_size();
// 3. Let offset be ? ToIndex(byteOffset).
// 2. Let offset be ? ToIndex(byteOffset).
let offset = byte_offset.to_index(context)?;
// 4. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if offset % constructor_name.element_size() != 0 {
// 3. If offset modulo elementSize ≠ 0, throw a RangeError exception.
if offset % element_size != 0 {
return Err(JsNativeError::range()
.with_message("Invalid length for typed array")
.into());
}
// 4. If length is not undefined, then
let new_length = if length.is_undefined() {
None
} else {
// a. Let newLength be ? ToIndex(length).
Some(length.to_index(context)?)
};
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 6. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
let buffer_byte_length = {
let buffer_obj_b = buffer.borrow();
let buffer_array = buffer_obj_b
let buffer_borrow = buffer.borrow();
let buffer_array = buffer_borrow
.as_array_buffer()
.expect("This must be an ArrayBuffer");
.expect("Must be an ArrayBuffer");
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if buffer_array.is_detached_buffer() {
return Err(JsNativeError::typ()
.with_message("Cannot construct typed array from detached buffer")
.into());
}
// 7. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
buffer_array.array_buffer_byte_length()
};
// 8. If length is undefined, then
let new_byte_length = if length.is_undefined() {
// 7. If length is undefined, then
// 8. Else,
let new_byte_length = if let Some(new_length) = new_length {
// a. Let newByteLength be newLength × elementSize.
let new_byte_length = new_length * element_size;
// b. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
if offset + new_byte_length > buffer_byte_length {
return Err(JsNativeError::range()
.with_message("Invalid length for typed array")
.into());
}
new_byte_length
} else {
// a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.
if buffer_byte_length % constructor_name.element_size() != 0 {
if buffer_byte_length % element_size != 0 {
return Err(JsNativeError::range()
.with_message("Invalid length for typed array")
.into());
@ -3483,38 +3549,23 @@ impl TypedArray {
}
new_byte_length as u64
// 9. Else,
} else {
// 5. If length is not undefined, then
// a. Let newLength be ? ToIndex(length).
// a. Let newByteLength be newLength × elementSize.
let new_byte_length = length.to_index(context)? * constructor_name.element_size();
// b. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
if offset + new_byte_length > buffer_byte_length {
return Err(JsNativeError::range()
.with_message("Invalid length for typed array")
.into());
}
new_byte_length
};
let mut o_obj_borrow = o.borrow_mut();
let o = o_obj_borrow
let mut o_borrow = o.borrow_mut();
let o = o_borrow
.as_typed_array_mut()
.expect("This must be an ArrayBuffer");
// 10. Set O.[[ViewedArrayBuffer]] to buffer.
// 9. Set O.[[ViewedArrayBuffer]] to buffer.
o.set_viewed_array_buffer(Some(buffer));
// 11. Set O.[[ByteLength]] to newByteLength.
// 10. Set O.[[ByteLength]] to newByteLength.
o.set_byte_length(new_byte_length);
// 12. Set O.[[ByteOffset]] to offset.
// 11. Set O.[[ByteOffset]] to offset.
o.set_byte_offset(offset);
// 13. Set O.[[ArrayLength]] to newByteLength / elementSize.
o.set_array_length(new_byte_length / constructor_name.element_size());
// 12. Set O.[[ArrayLength]] to newByteLength / elementSize.
o.set_array_length(new_byte_length / element_size);
// 13. Return unused.
Ok(())
}

343
boa_engine/src/object/internal_methods/integer_indexed.rs

@ -1,10 +1,12 @@
use boa_macros::utf16;
use crate::{
builtins::{array_buffer::SharedMemoryOrder, typed_array::integer_indexed_object::ContentType},
builtins::{
array_buffer::SharedMemoryOrder, typed_array::integer_indexed_object::ContentType, Number,
},
object::JsObject,
property::{PropertyDescriptor, PropertyKey},
Context, JsResult, JsValue,
Context, JsResult, JsString, JsValue,
};
use super::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS};
@ -27,6 +29,30 @@ pub(crate) static INTEGER_INDEXED_EXOTIC_INTERNAL_METHODS: InternalObjectMethods
..ORDINARY_INTERNAL_METHODS
};
/// `CanonicalNumericIndexString ( argument )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-canonicalnumericindexstring
fn canonical_numeric_index_string(argument: &JsString) -> Option<f64> {
// 1. If argument is "-0", return -0𝔽.
if argument == utf16!("-0") {
return Some(-0.0);
}
// 2. Let n be ! ToNumber(argument).
let n = argument.to_number();
// 3. If ! ToString(n) is argument, return n.
if &JsString::from(Number::to_native_string(n)) == argument {
return Some(n);
}
// 4. Return undefined.
None
}
/// `[[GetOwnProperty]]` internal method for Integer-Indexed exotic objects.
///
/// More information:
@ -38,33 +64,35 @@ pub(crate) fn integer_indexed_exotic_get_own_property(
key: &PropertyKey,
context: &mut Context<'_>,
) -> JsResult<Option<PropertyDescriptor>> {
// 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
// b. If numericIndex is not undefined, then
match key {
PropertyKey::Index(index) => {
// i. Let value be ! IntegerIndexedElementGet(O, numericIndex).
// ii. If value is undefined, return undefined.
// iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
Ok(
integer_indexed_element_get(obj, u64::from(*index)).map(|v| {
PropertyDescriptor::builder()
.value(v)
.writable(true)
.enumerable(true)
.configurable(true)
.build()
}),
)
}
// The following step is taken from https://tc39.es/ecma262/#sec-isvalidintegerindex :
// Step 3. If index is -0𝔽, return false.
PropertyKey::String(string) if string == utf16!("-0") => Ok(None),
key => {
// 2. Return OrdinaryGetOwnProperty(O, P).
super::ordinary_get_own_property(obj, key, context)
let p = match key {
PropertyKey::String(key) => {
// 1.a. Let numericIndex be CanonicalNumericIndexString(P).
canonical_numeric_index_string(key)
}
PropertyKey::Index(index) => Some((*index).into()),
PropertyKey::Symbol(_) => None,
};
// 1. If P is a String, then
// 1.b. If numericIndex is not undefined, then
if let Some(numeric_index) = p {
// i. Let value be IntegerIndexedElementGet(O, numericIndex).
let value = integer_indexed_element_get(obj, numeric_index);
// ii. If value is undefined, return undefined.
// iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
return Ok(value.map(|v| {
PropertyDescriptor::builder()
.value(v)
.writable(true)
.enumerable(true)
.configurable(true)
.build()
}));
}
// 2. Return OrdinaryGetOwnProperty(O, P).
super::ordinary_get_own_property(obj, key, context)
}
/// `[[HasProperty]]` internal method for Integer-Indexed exotic objects.
@ -78,21 +106,23 @@ pub(crate) fn integer_indexed_exotic_has_property(
key: &PropertyKey,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
match key {
PropertyKey::Index(index) => {
// b. If numericIndex is not undefined, return ! IsValidIntegerIndex(O, numericIndex).
Ok(is_valid_integer_index(obj, u64::from(*index)))
}
// The following step is taken from https://tc39.es/ecma262/#sec-isvalidintegerindex :
// Step 3. If index is -0𝔽, return false.
PropertyKey::String(string) if string == utf16!("-0") => Ok(false),
key => {
// 2. Return ? OrdinaryHasProperty(O, P).
super::ordinary_has_property(obj, key, context)
let p = match key {
PropertyKey::String(key) => {
// 1.a. Let numericIndex be CanonicalNumericIndexString(P).
canonical_numeric_index_string(key)
}
PropertyKey::Index(index) => Some((*index).into()),
PropertyKey::Symbol(_) => None,
};
// 1. If P is a String, then
// 1.b. If numericIndex is not undefined, return IsValidIntegerIndex(O, numericIndex).
if let Some(numeric_index) = p {
return Ok(is_valid_integer_index(obj, numeric_index));
}
// 2. Return ? OrdinaryHasProperty(O, P).
super::ordinary_has_property(obj, key, context)
}
/// `[[DefineOwnProperty]]` internal method for Integer-Indexed exotic objects.
@ -107,41 +137,54 @@ pub(crate) fn integer_indexed_exotic_define_own_property(
desc: PropertyDescriptor,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
// b. If numericIndex is not undefined, then
match key {
&PropertyKey::Index(index) => {
// i. If ! IsValidIntegerIndex(O, numericIndex) is false, return false.
// ii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, return false.
// iii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false.
// v. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false.
// iv. If ! IsAccessorDescriptor(Desc) is true, return false.
if !is_valid_integer_index(obj, u64::from(index))
|| !desc
.configurable()
.or_else(|| desc.enumerable())
.or_else(|| desc.writable())
.unwrap_or(true)
|| desc.is_accessor_descriptor()
{
return Ok(false);
}
// vi. If Desc has a [[Value]] field, perform ? IntegerIndexedElementSet(O, numericIndex, Desc.[[Value]]).
if let Some(value) = desc.value() {
integer_indexed_element_set(obj, index as usize, value, context)?;
}
// vii. Return true.
Ok(true)
let p = match key {
PropertyKey::String(key) => {
// 1.a. Let numericIndex be CanonicalNumericIndexString(P).
canonical_numeric_index_string(key)
}
PropertyKey::Index(index) => Some((*index).into()),
PropertyKey::Symbol(_) => None,
};
// 1. If P is a String, then
// 1.b. If numericIndex is not undefined, then
if let Some(numeric_index) = p {
// i. If IsValidIntegerIndex(O, numericIndex) is false, return false.
if !is_valid_integer_index(obj, numeric_index) {
return Ok(false);
}
// ii. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is false, return false.
if desc.configurable() == Some(false) {
return Ok(false);
}
// iii. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.
if desc.enumerable() == Some(false) {
return Ok(false);
}
// iv. If IsAccessorDescriptor(Desc) is true, return false.
if desc.is_accessor_descriptor() {
return Ok(false);
}
// v. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.
if desc.writable() == Some(false) {
return Ok(false);
}
PropertyKey::String(string) if string == utf16!("-0") => Ok(false),
key => {
// 2. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
super::ordinary_define_own_property(obj, key, desc, context)
// vi. If Desc has a [[Value]] field, perform ? IntegerIndexedElementSet(O, numericIndex, Desc.[[Value]]).
if let Some(value) = desc.value() {
integer_indexed_element_set(obj, numeric_index, value, context)?;
}
// vii. Return true.
return Ok(true);
}
// 2. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
super::ordinary_define_own_property(obj, key, desc, context)
}
/// Internal method `[[Get]]` for Integer-Indexed exotic objects.
@ -156,22 +199,24 @@ pub(crate) fn integer_indexed_exotic_get(
receiver: JsValue,
context: &mut Context<'_>,
) -> JsResult<JsValue> {
// 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
// b. If numericIndex is not undefined, then
match key {
PropertyKey::Index(index) => {
// i. Return ! IntegerIndexedElementGet(O, numericIndex).
Ok(integer_indexed_element_get(obj, u64::from(*index)).unwrap_or_default())
}
// The following step is taken from https://tc39.es/ecma262/#sec-isvalidintegerindex :
// Step 3. If index is -0𝔽, return false.
PropertyKey::String(string) if string == utf16!("-0") => Ok(JsValue::undefined()),
key => {
// 2. Return ? OrdinaryGet(O, P, Receiver).
super::ordinary_get(obj, key, receiver, context)
let p = match key {
PropertyKey::String(key) => {
// 1.a. Let numericIndex be CanonicalNumericIndexString(P).
canonical_numeric_index_string(key)
}
PropertyKey::Index(index) => Some((*index).into()),
PropertyKey::Symbol(_) => None,
};
// 1. If P is a String, then
// 1.b. If numericIndex is not undefined, then
if let Some(numeric_index) = p {
// i. Return IntegerIndexedElementGet(O, numericIndex).
return Ok(integer_indexed_element_get(obj, numeric_index).unwrap_or_default());
}
// 2. Return ? OrdinaryGet(O, P, Receiver).
super::ordinary_get(obj, key, receiver, context)
}
/// Internal method `[[Set]]` for Integer-Indexed exotic objects.
@ -187,25 +232,35 @@ pub(crate) fn integer_indexed_exotic_set(
receiver: JsValue,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
// b. If numericIndex is not undefined, then
match key {
PropertyKey::Index(index) => {
// i. Perform ? IntegerIndexedElementSet(O, numericIndex, V).
integer_indexed_element_set(obj, index as usize, &value, context)?;
// ii. Return true.
Ok(true)
let p = match &key {
PropertyKey::String(key) => {
// 1.a. Let numericIndex be CanonicalNumericIndexString(P).
canonical_numeric_index_string(key)
}
// The following step is taken from https://tc39.es/ecma262/#sec-isvalidintegerindex :
// Step 3. If index is -0𝔽, return false.
PropertyKey::String(string) if &string == utf16!("-0") => Ok(false),
key => {
// 2. Return ? OrdinarySet(O, P, V, Receiver).
super::ordinary_set(obj, key, value, receiver, context)
PropertyKey::Index(index) => Some((*index).into()),
PropertyKey::Symbol(_) => None,
};
// 1. If P is a String, then
// 1.b. If numericIndex is not undefined, then
if let Some(numeric_index) = p {
// i. If SameValue(O, Receiver) is true, then
if JsValue::same_value(&obj.clone().into(), &receiver) {
// 1. Perform ? IntegerIndexedElementSet(O, numericIndex, V).
integer_indexed_element_set(obj, numeric_index, &value, context)?;
// 2. Return true.
return Ok(true);
}
// ii. If IsValidIntegerIndex(O, numericIndex) is false, return true.
if !is_valid_integer_index(obj, numeric_index) {
return Ok(true);
}
}
// 2. Return ? OrdinarySet(O, P, V, Receiver).
super::ordinary_set(obj, key, value, receiver, context)
}
/// Internal method `[[Delete]]` for Integer-Indexed exotic objects.
@ -219,37 +274,24 @@ pub(crate) fn integer_indexed_exotic_delete(
key: &PropertyKey,
context: &mut Context<'_>,
) -> JsResult<bool> {
// 1. If Type(P) is String, then
// a. Let numericIndex be ! CanonicalNumericIndexString(P).
// b. If numericIndex is not undefined, then
match key {
PropertyKey::Index(index) => {
// i. If ! IsValidIntegerIndex(O, numericIndex) is false, return true; else return false.
Ok(!is_valid_integer_index(obj, u64::from(*index)))
}
// The following step is taken from https://tc39.es/ecma262/#sec-isvalidintegerindex :
// Step 3. If index is -0𝔽, return false.
PropertyKey::String(string) if string == utf16!("-0") => {
let obj = obj.borrow();
let inner = obj.as_typed_array().expect(
"integer indexed exotic method should only be callable from integer indexed objects",
);
// 1. If IsValidIntegerIndex(O, numericIndex) is false, return true; else return false.
// From IsValidIntegerIndex:
// 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
// 3. If index is -0𝔽, return false.
//
// NOTE: They are negated, so it should return true.
if inner.is_detached() {
return Ok(true);
}
Ok(true)
}
key => {
// 2. Return ? OrdinaryDelete(O, P).
super::ordinary_delete(obj, key, context)
let p = match &key {
PropertyKey::String(key) => {
// 1.a. Let numericIndex be CanonicalNumericIndexString(P).
canonical_numeric_index_string(key)
}
PropertyKey::Index(index) => Some((*index).into()),
PropertyKey::Symbol(_) => None,
};
// 1. If P is a String, then
// 1.b. If numericIndex is not undefined, then
if let Some(numeric_index) = p {
// i. If IsValidIntegerIndex(O, numericIndex) is false, return true; else return false.
return Ok(!is_valid_integer_index(obj, numeric_index));
}
// 2. Return ! OrdinaryDelete(O, P).
super::ordinary_delete(obj, key, context)
}
/// Internal method `[[OwnPropertyKeys]]` for Integer-Indexed exotic objects.
@ -297,25 +339,34 @@ pub(crate) fn integer_indexed_exotic_own_property_keys(
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-isvalidintegerindex
pub(crate) fn is_valid_integer_index(obj: &JsObject, index: u64) -> bool {
pub(crate) fn is_valid_integer_index(obj: &JsObject, index: f64) -> bool {
let obj = obj.borrow();
let inner = obj.as_typed_array().expect(
"integer indexed exotic method should only be callable from integer indexed objects",
);
// 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
//
// SKIPPED: 2. If ! IsIntegralNumber(index) is false, return false.
// NOTE: This step has already been done when we construct a PropertyKey.
//
// MOVED: 3. If index is -0𝔽, return false.
// NOTE: This step has been moved into the callers of this functions,
// once we get the index it is already converted into unsigned integer
// index, it cannot be `-0`.
if inner.is_detached() {
return false;
}
// 2. If IsIntegralNumber(index) is false, return false.
if index.is_nan() || index.is_infinite() || index.fract() != 0.0 {
return false;
}
// 3. If index is -0𝔽, return false.
if index == 0.0 && index.is_sign_negative() {
return false;
}
// 4. If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false.
// 5. Return true.
if index < 0.0 || index >= inner.array_length() as f64 {
return false;
}
!inner.is_detached() && index < inner.array_length()
// 5. Return true.
true
}
/// Abstract operation `IntegerIndexedElementGet ( O, index )`.
@ -324,7 +375,7 @@ pub(crate) fn is_valid_integer_index(obj: &JsObject, index: u64) -> bool {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-integerindexedelementget
fn integer_indexed_element_get(obj: &JsObject, index: u64) -> Option<JsValue> {
fn integer_indexed_element_get(obj: &JsObject, index: f64) -> Option<JsValue> {
// 1. If ! IsValidIntegerIndex(O, index) is false, return undefined.
if !is_valid_integer_index(obj, index) {
return None;
@ -353,7 +404,7 @@ fn integer_indexed_element_get(obj: &JsObject, index: u64) -> Option<JsValue> {
let size = elem_type.element_size();
// 5. Let indexedPosition be (ℝ(index) × elementSize) + offset.
let indexed_position = (index * size) + offset;
let indexed_position = (index as u64 * size) + offset;
// 7. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, true, Unordered).
Some(buffer.get_value_from_buffer(
@ -373,7 +424,7 @@ fn integer_indexed_element_get(obj: &JsObject, index: u64) -> Option<JsValue> {
/// [spec]: https://tc39.es/ecma262/#sec-integerindexedelementset
fn integer_indexed_element_set(
obj: &JsObject,
index: usize,
index: f64,
value: &JsValue,
context: &mut Context<'_>,
) -> JsResult<()> {
@ -391,7 +442,7 @@ fn integer_indexed_element_set(
};
// 3. If ! IsValidIntegerIndex(O, index) is true, then
if is_valid_integer_index(obj, index as u64) {
if is_valid_integer_index(obj, index) {
// a. Let offset be O.[[ByteOffset]].
let offset = inner.byte_offset();

Loading…
Cancel
Save