|
@@ -28,25 +28,6 @@ ThrowCompletionOr<TypedArrayBase*> typed_array_from(VM& vm, Value typed_array_va
|
|
|
return static_cast<TypedArrayBase*>(this_object.ptr());
|
|
|
}
|
|
|
|
|
|
-// 23.2.4.4 ValidateTypedArray ( O ), https://tc39.es/ecma262/#sec-validatetypedarray
|
|
|
-ThrowCompletionOr<void> validate_typed_array(VM& vm, TypedArrayBase& typed_array)
|
|
|
-{
|
|
|
- // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
|
|
|
- if (!typed_array.is_typed_array())
|
|
|
- return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "TypedArray");
|
|
|
-
|
|
|
- // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
|
|
|
-
|
|
|
- // 3. Let buffer be O.[[ViewedArrayBuffer]].
|
|
|
- auto* buffer = typed_array.viewed_array_buffer();
|
|
|
-
|
|
|
- // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
|
|
- if (buffer->is_detached())
|
|
|
- return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
|
|
|
-
|
|
|
- return {};
|
|
|
-}
|
|
|
-
|
|
|
// 22.2.5.1.3 InitializeTypedArrayFromArrayBuffer, https://tc39.es/ecma262/#sec-initializetypedarrayfromarraybuffer
|
|
|
static ThrowCompletionOr<void> initialize_typed_array_from_array_buffer(VM& vm, TypedArrayBase& typed_array, ArrayBuffer& array_buffer, Value byte_offset, Value length)
|
|
|
{
|
|
@@ -60,101 +41,122 @@ static ThrowCompletionOr<void> initialize_typed_array_from_array_buffer(VM& vm,
|
|
|
if (offset % element_size != 0)
|
|
|
return vm.throw_completion<RangeError>(ErrorType::TypedArrayInvalidByteOffset, typed_array.class_name(), element_size, offset);
|
|
|
|
|
|
- size_t new_length { 0 };
|
|
|
+ // 4. Let bufferIsFixedLength be IsFixedLengthArrayBuffer(buffer).
|
|
|
+ auto buffer_is_fixed_length = array_buffer.is_fixed_length();
|
|
|
|
|
|
- // 4. If length is not undefined, then
|
|
|
+ size_t new_length { 0 };
|
|
|
+ // 5. If length is not undefined, then
|
|
|
if (!length.is_undefined()) {
|
|
|
// a. Let newLength be ? ToIndex(length).
|
|
|
new_length = TRY(length.to_index(vm));
|
|
|
}
|
|
|
|
|
|
- // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
|
|
+ // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
|
|
if (array_buffer.is_detached())
|
|
|
return vm.throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
|
|
|
|
|
|
- // 6. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
|
|
|
- auto buffer_byte_length = array_buffer.byte_length();
|
|
|
-
|
|
|
- Checked<size_t> new_byte_length;
|
|
|
-
|
|
|
- // 7. If length is undefined, then
|
|
|
- if (length.is_undefined()) {
|
|
|
- // a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.
|
|
|
- if (buffer_byte_length % element_size != 0)
|
|
|
- return vm.throw_completion<RangeError>(ErrorType::TypedArrayInvalidBufferLength, typed_array.class_name(), element_size, buffer_byte_length);
|
|
|
+ // 7. Let bufferByteLength be ArrayBufferByteLength(buffer, seq-cst).
|
|
|
+ auto buffer_byte_length = array_buffer_byte_length(array_buffer, ArrayBuffer::Order::SeqCst);
|
|
|
|
|
|
- // b. Let newByteLength be bufferByteLength - offset.
|
|
|
- // c. If newByteLength < 0, throw a RangeError exception.
|
|
|
+ // 8. If length is undefined and bufferIsFixedLength is false, then
|
|
|
+ if (length.is_undefined() && !buffer_is_fixed_length) {
|
|
|
+ // a. If offset > bufferByteLength, throw a RangeError exception.
|
|
|
if (offset > buffer_byte_length)
|
|
|
return vm.throw_completion<RangeError>(ErrorType::TypedArrayOutOfRangeByteOffset, offset, buffer_byte_length);
|
|
|
- new_byte_length = buffer_byte_length;
|
|
|
- new_byte_length -= offset;
|
|
|
+
|
|
|
+ // b. Set O.[[ByteLength]] to auto.
|
|
|
+ typed_array.set_byte_length(ByteLength::auto_());
|
|
|
+
|
|
|
+ // c. Set O.[[ArrayLength]] to auto.
|
|
|
+ typed_array.set_array_length(ByteLength::auto_());
|
|
|
}
|
|
|
- // 8. Else,
|
|
|
+ // 9. Else,
|
|
|
else {
|
|
|
- // a. Let newByteLength be newLength × elementSize.
|
|
|
- new_byte_length = new_length;
|
|
|
- new_byte_length *= element_size;
|
|
|
+ Checked<u32> new_byte_length;
|
|
|
|
|
|
- // b. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
|
|
|
- Checked<size_t> new_byte_end = new_byte_length;
|
|
|
- new_byte_end += offset;
|
|
|
+ // a. If length is undefined, then
|
|
|
+ if (length.is_undefined()) {
|
|
|
+ // i. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.
|
|
|
+ if (modulo(buffer_byte_length, element_size) != 0)
|
|
|
+ return vm.throw_completion<RangeError>(ErrorType::TypedArrayInvalidBufferLength, typed_array.class_name(), element_size, buffer_byte_length);
|
|
|
|
|
|
- if (new_byte_end.has_overflow())
|
|
|
- return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array");
|
|
|
+ // ii. Let newByteLength be bufferByteLength - offset.
|
|
|
+ new_byte_length = buffer_byte_length;
|
|
|
+ new_byte_length -= offset;
|
|
|
|
|
|
- if (new_byte_end.value() > buffer_byte_length)
|
|
|
- return vm.throw_completion<RangeError>(ErrorType::TypedArrayOutOfRangeByteOffsetOrLength, offset, new_byte_end.value(), buffer_byte_length);
|
|
|
- }
|
|
|
+ // iii. If newByteLength < 0, throw a RangeError exception.
|
|
|
+ if (new_byte_length.has_overflow())
|
|
|
+ return vm.throw_completion<RangeError>(ErrorType::TypedArrayOutOfRangeByteOffset, offset, buffer_byte_length);
|
|
|
+ }
|
|
|
+ // b. Else,
|
|
|
+ else {
|
|
|
+ // i. Let newByteLength be newLength × elementSize.
|
|
|
+ new_byte_length = new_length;
|
|
|
+ new_byte_length *= element_size;
|
|
|
+
|
|
|
+ // ii. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
|
|
|
+ Checked<u32> new_byte_end = offset;
|
|
|
+ new_byte_end += new_byte_length;
|
|
|
+
|
|
|
+ if (new_byte_end.has_overflow())
|
|
|
+ return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array");
|
|
|
+ if (new_byte_end.value() > buffer_byte_length)
|
|
|
+ return vm.throw_completion<RangeError>(ErrorType::TypedArrayOutOfRangeByteOffsetOrLength, offset, new_byte_end.value(), buffer_byte_length);
|
|
|
+ }
|
|
|
|
|
|
- if (new_byte_length.has_overflow())
|
|
|
- return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "typed array");
|
|
|
+ // c. Set O.[[ByteLength]] to newByteLength.
|
|
|
+ typed_array.set_byte_length(new_byte_length.value());
|
|
|
|
|
|
- // 9. Set O.[[ViewedArrayBuffer]] to buffer.
|
|
|
- typed_array.set_viewed_array_buffer(&array_buffer);
|
|
|
+ // d. Set O.[[ArrayLength]] to newByteLength / elementSize.
|
|
|
+ typed_array.set_array_length(new_byte_length.value() / element_size);
|
|
|
+ }
|
|
|
|
|
|
- // 10. Set O.[[ByteLength]] to newByteLength.
|
|
|
- typed_array.set_byte_length(new_byte_length.value());
|
|
|
+ // 10. Set O.[[ViewedArrayBuffer]] to buffer.
|
|
|
+ typed_array.set_viewed_array_buffer(&array_buffer);
|
|
|
|
|
|
// 11. Set O.[[ByteOffset]] to offset.
|
|
|
typed_array.set_byte_offset(offset);
|
|
|
|
|
|
- // 12. Set O.[[ArrayLength]] to newByteLength / elementSize.
|
|
|
- typed_array.set_array_length(new_byte_length.value() / element_size);
|
|
|
-
|
|
|
- // 13. Return unused.
|
|
|
+ // 12. Return unused.
|
|
|
return {};
|
|
|
}
|
|
|
|
|
|
// 23.2.5.1.2 InitializeTypedArrayFromTypedArray ( O, srcArray ), https://tc39.es/ecma262/#sec-initializetypedarrayfromtypedarray
|
|
|
template<typename T>
|
|
|
-static ThrowCompletionOr<void> initialize_typed_array_from_typed_array(VM& vm, TypedArray<T>& dest_array, TypedArrayBase& src_array)
|
|
|
+static ThrowCompletionOr<void> initialize_typed_array_from_typed_array(VM& vm, TypedArray<T>& typed_array, TypedArrayBase& source_array)
|
|
|
{
|
|
|
auto& realm = *vm.current_realm();
|
|
|
|
|
|
// 1. Let srcData be srcArray.[[ViewedArrayBuffer]].
|
|
|
- auto* src_data = src_array.viewed_array_buffer();
|
|
|
- VERIFY(src_data);
|
|
|
+ auto* source_data = source_array.viewed_array_buffer();
|
|
|
+ VERIFY(source_data);
|
|
|
|
|
|
- // 2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
|
|
|
- if (src_data->is_detached())
|
|
|
- return vm.template throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
|
|
|
+ // 2. Let elementType be TypedArrayElementType(O).
|
|
|
+ auto const& element_type = typed_array.element_name();
|
|
|
|
|
|
- // 3. Let elementType be TypedArrayElementType(O).
|
|
|
- // 4. Let elementSize be TypedArrayElementSize(O).
|
|
|
- auto element_size = dest_array.element_size();
|
|
|
+ // 3. Let elementSize be TypedArrayElementSize(O).
|
|
|
+ auto element_size = typed_array.element_size();
|
|
|
+
|
|
|
+ // 4. Let srcType be TypedArrayElementType(srcArray).
|
|
|
+ auto const& source_type = source_array.element_name();
|
|
|
+
|
|
|
+ // 5. Let srcElementSize be TypedArrayElementSize(srcArray).
|
|
|
+ auto source_element_size = source_array.element_size();
|
|
|
+
|
|
|
+ // 6. Let srcByteOffset be srcArray.[[ByteOffset]].
|
|
|
+ auto source_byte_offset = source_array.byte_offset();
|
|
|
|
|
|
- // 5. Let srcType be TypedArrayElementType(srcArray).
|
|
|
- // 6. Let srcElementSize be TypedArrayElementSize(srcArray).
|
|
|
- auto src_element_size = src_array.element_size();
|
|
|
+ // 7. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(srcArray, seq-cst).
|
|
|
+ auto source_record = make_typed_array_with_buffer_witness_record(source_array, ArrayBuffer::Order::SeqCst);
|
|
|
|
|
|
- // 7. Let srcByteOffset be srcArray.[[ByteOffset]].
|
|
|
- auto src_byte_offset = src_array.byte_offset();
|
|
|
+ // 8. If IsTypedArrayOutOfBounds(srcRecord) is true, throw a TypeError exception.
|
|
|
+ if (is_typed_array_out_of_bounds(source_record))
|
|
|
+ return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
|
|
|
|
|
|
- // 8. Let elementLength be srcArray.[[ArrayLength]].
|
|
|
- auto element_length = src_array.array_length();
|
|
|
+ // 9. Let elementLength be TypedArrayLength(srcRecord).
|
|
|
+ auto element_length = typed_array_length(source_record);
|
|
|
|
|
|
- // 9. Let byteLength be elementSize × elementLength.
|
|
|
+ // 10. Let byteLength be elementSize × elementLength.
|
|
|
Checked<size_t> byte_length = element_size;
|
|
|
byte_length *= element_length;
|
|
|
if (byte_length.has_overflow())
|
|
@@ -162,41 +164,37 @@ static ThrowCompletionOr<void> initialize_typed_array_from_typed_array(VM& vm, T
|
|
|
|
|
|
ArrayBuffer* data = nullptr;
|
|
|
|
|
|
- // 10. If elementType is the same as srcType, then
|
|
|
- if (dest_array.element_name() == src_array.element_name()) {
|
|
|
+ // 11. If elementType is srcType, then
|
|
|
+ if (element_type == source_type) {
|
|
|
// a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength).
|
|
|
- data = TRY(clone_array_buffer(vm, *src_data, src_byte_offset, byte_length.value()));
|
|
|
+ data = TRY(clone_array_buffer(vm, *source_data, source_byte_offset, byte_length.value()));
|
|
|
}
|
|
|
- // 11. Else,
|
|
|
+ // 12. Else,
|
|
|
else {
|
|
|
- // a. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength).
|
|
|
+ // a. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
|
|
|
data = TRY(allocate_array_buffer(vm, realm.intrinsics().array_buffer_constructor(), byte_length.value()));
|
|
|
|
|
|
- // b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
|
|
|
- if (src_data->is_detached())
|
|
|
- return vm.template throw_completion<TypeError>(ErrorType::DetachedArrayBuffer);
|
|
|
+ // b. If srcArray.[[ContentType]] is not O.[[ContentType]], throw a TypeError exception.
|
|
|
+ if (source_array.content_type() != typed_array.content_type())
|
|
|
+ return vm.template throw_completion<TypeError>(ErrorType::TypedArrayContentTypeMismatch, typed_array.class_name(), source_array.class_name());
|
|
|
|
|
|
- // c. If srcArray.[[ContentType]] ≠ O.[[ContentType]], throw a TypeError exception.
|
|
|
- if (src_array.content_type() != dest_array.content_type())
|
|
|
- return vm.template throw_completion<TypeError>(ErrorType::TypedArrayContentTypeMismatch, dest_array.class_name(), src_array.class_name());
|
|
|
+ // c. Let srcByteIndex be srcByteOffset.
|
|
|
+ u64 source_byte_index = source_byte_offset;
|
|
|
|
|
|
- // d. Let srcByteIndex be srcByteOffset.
|
|
|
- u64 src_byte_index = src_byte_offset;
|
|
|
-
|
|
|
- // e. Let targetByteIndex be 0.
|
|
|
+ // d. Let targetByteIndex be 0.
|
|
|
u64 target_byte_index = 0;
|
|
|
|
|
|
- // f. Let count be elementLength.
|
|
|
- // g. Repeat, while count > 0,
|
|
|
+ // e. Let count be elementLength.
|
|
|
+ // f. Repeat, while count > 0,
|
|
|
for (u32 i = 0; i < element_length; ++i) {
|
|
|
- // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, Unordered).
|
|
|
- auto value = src_array.get_value_from_buffer(src_byte_index, ArrayBuffer::Order::Unordered);
|
|
|
+ // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, unordered).
|
|
|
+ auto value = source_array.get_value_from_buffer(source_byte_index, ArrayBuffer::Order::Unordered);
|
|
|
|
|
|
- // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, Unordered).
|
|
|
+ // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, unordered).
|
|
|
data->template set_value<T>(target_byte_index, value, true, ArrayBuffer::Order::Unordered);
|
|
|
|
|
|
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
|
|
|
- src_byte_index += src_element_size;
|
|
|
+ source_byte_index += source_element_size;
|
|
|
|
|
|
// iv. Set targetByteIndex to targetByteIndex + elementSize.
|
|
|
target_byte_index += element_size;
|
|
@@ -205,19 +203,19 @@ static ThrowCompletionOr<void> initialize_typed_array_from_typed_array(VM& vm, T
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 12. Set O.[[ViewedArrayBuffer]] to data.
|
|
|
- dest_array.set_viewed_array_buffer(data);
|
|
|
+ // 13. Set O.[[ViewedArrayBuffer]] to data.
|
|
|
+ typed_array.set_viewed_array_buffer(data);
|
|
|
|
|
|
- // 13. Set O.[[ByteLength]] to byteLength.
|
|
|
- dest_array.set_byte_length(byte_length.value());
|
|
|
+ // 14. Set O.[[ByteLength]] to byteLength.
|
|
|
+ typed_array.set_byte_length(byte_length.value());
|
|
|
|
|
|
- // 14. Set O.[[ByteOffset]] to 0.
|
|
|
- dest_array.set_byte_offset(0);
|
|
|
+ // 15. Set O.[[ByteOffset]] to 0.
|
|
|
+ typed_array.set_byte_offset(0);
|
|
|
|
|
|
- // 15. Set O.[[ArrayLength]] to elementLength.
|
|
|
- dest_array.set_array_length(element_length);
|
|
|
+ // 16. Set O.[[ArrayLength]] to elementLength.
|
|
|
+ typed_array.set_array_length(element_length);
|
|
|
|
|
|
- // 16. Return unused.
|
|
|
+ // 17. Return unused.
|
|
|
return {};
|
|
|
}
|
|
|
|
|
@@ -318,27 +316,32 @@ static ThrowCompletionOr<void> initialize_typed_array_from_list(VM& vm, TypedArr
|
|
|
// 23.2.4.2 TypedArrayCreate ( constructor, argumentList ), https://tc39.es/ecma262/#typedarray-create
|
|
|
ThrowCompletionOr<TypedArrayBase*> typed_array_create(VM& vm, FunctionObject& constructor, MarkedVector<Value> arguments)
|
|
|
{
|
|
|
- Optional<Value> first_argument;
|
|
|
- if (!arguments.is_empty())
|
|
|
- first_argument = arguments[0];
|
|
|
+ Optional<double> first_argument;
|
|
|
+ if (arguments.size() == 1 && arguments[0].is_number())
|
|
|
+ first_argument = arguments[0].as_double();
|
|
|
+
|
|
|
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
|
|
|
auto new_typed_array = TRY(construct(vm, constructor, arguments.span()));
|
|
|
|
|
|
- // 2. Perform ? ValidateTypedArray(newTypedArray).
|
|
|
- if (!new_typed_array->is_typed_array())
|
|
|
- return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "TypedArray");
|
|
|
- auto& typed_array = *static_cast<TypedArrayBase*>(new_typed_array.ptr());
|
|
|
- TRY(validate_typed_array(vm, typed_array));
|
|
|
+ // 2. Let taRecord be ? ValidateTypedArray(newTypedArray, seq-cst).
|
|
|
+ auto typed_array_record = TRY(validate_typed_array(vm, *new_typed_array, ArrayBuffer::Order::SeqCst));
|
|
|
+
|
|
|
+ // 3. If the number of elements in argumentList is 1 and argumentList[0] is a Number, then
|
|
|
+ if (first_argument.has_value()) {
|
|
|
+ // a. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
|
|
|
+ if (is_typed_array_out_of_bounds(typed_array_record))
|
|
|
+ return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
|
|
|
|
|
|
- // 3. If argumentList is a List of a single Number, then
|
|
|
- if (first_argument.has_value() && first_argument->is_number()) {
|
|
|
- // a. If newTypedArray.[[ArrayLength]] < ℝ(argumentList[0]), throw a TypeError exception.
|
|
|
- if (typed_array.array_length() < first_argument->as_double())
|
|
|
+ // b. Let length be TypedArrayLength(taRecord).
|
|
|
+ auto length = typed_array_length(typed_array_record);
|
|
|
+
|
|
|
+ // c. If length < ℝ(argumentList[0]), throw a TypeError exception.
|
|
|
+ if (length < *first_argument)
|
|
|
return vm.throw_completion<TypeError>(ErrorType::InvalidLength, "typed array");
|
|
|
}
|
|
|
|
|
|
// 4. Return newTypedArray.
|
|
|
- return &typed_array;
|
|
|
+ return static_cast<TypedArrayBase*>(new_typed_array.ptr());
|
|
|
}
|
|
|
|
|
|
// 23.2.4.3 TypedArrayCreateSameType ( exemplar, argumentList ), https://tc39.es/ecma262/#sec-typedarray-create-same-type
|
|
@@ -358,6 +361,27 @@ ThrowCompletionOr<TypedArrayBase*> typed_array_create_same_type(VM& vm, TypedArr
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+// 23.2.4.4 ValidateTypedArray ( O ), https://tc39.es/ecma262/#sec-validatetypedarray
|
|
|
+ThrowCompletionOr<TypedArrayWithBufferWitness> validate_typed_array(VM& vm, Object const& object, ArrayBuffer::Order order)
|
|
|
+{
|
|
|
+ // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
|
|
|
+ if (!object.is_typed_array())
|
|
|
+ return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "TypedArray");
|
|
|
+
|
|
|
+ // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
|
|
|
+ auto const& typed_array = static_cast<TypedArrayBase const&>(object);
|
|
|
+
|
|
|
+ // 3. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, order).
|
|
|
+ auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, order);
|
|
|
+
|
|
|
+ // 4. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
|
|
|
+ if (is_typed_array_out_of_bounds(typed_array_record))
|
|
|
+ return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
|
|
|
+
|
|
|
+ // 5. Return taRecord.
|
|
|
+ return typed_array_record;
|
|
|
+}
|
|
|
+
|
|
|
// 23.2.4.7 CompareTypedArrayElements ( x, y, comparefn ), https://tc39.es/ecma262/#sec-typedarray-create-same-type
|
|
|
ThrowCompletionOr<double> compare_typed_array_elements(VM& vm, Value x, Value y, FunctionObject* comparefn)
|
|
|
{
|
|
@@ -563,4 +587,162 @@ void TypedArrayBase::visit_edges(Visitor& visitor)
|
|
|
JS_ENUMERATE_TYPED_ARRAYS
|
|
|
#undef __JS_ENUMERATE
|
|
|
|
|
|
+// 10.4.5.9 MakeTypedArrayWithBufferWitnessRecord ( obj, order ), https://tc39.es/ecma262/#sec-maketypedarraywithbufferwitnessrecord
|
|
|
+TypedArrayWithBufferWitness make_typed_array_with_buffer_witness_record(TypedArrayBase const& typed_array, ArrayBuffer::Order order)
|
|
|
+{
|
|
|
+ // 1. Let buffer be obj.[[ViewedArrayBuffer]].
|
|
|
+ auto* buffer = typed_array.viewed_array_buffer();
|
|
|
+
|
|
|
+ ByteLength byte_length { 0 };
|
|
|
+
|
|
|
+ // 2. If IsDetachedBuffer(buffer) is true, then
|
|
|
+ if (buffer->is_detached()) {
|
|
|
+ // a. Let byteLength be detached.
|
|
|
+ byte_length = ByteLength::detached();
|
|
|
+ }
|
|
|
+ // 3. Else,
|
|
|
+ else {
|
|
|
+ // a. Let byteLength be ArrayBufferByteLength(buffer, order).
|
|
|
+ byte_length = array_buffer_byte_length(*buffer, order);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. Return the TypedArray With Buffer Witness Record { [[Object]]: obj, [[CachedBufferByteLength]]: byteLength }.
|
|
|
+ return { .object = typed_array, .cached_buffer_byte_length = move(byte_length) };
|
|
|
+}
|
|
|
+
|
|
|
+// 10.4.5.11 TypedArrayByteLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraybytelength
|
|
|
+u32 typed_array_byte_length(TypedArrayWithBufferWitness const& typed_array_record)
|
|
|
+{
|
|
|
+ // 1. If IsTypedArrayOutOfBounds(taRecord) is true, return 0.
|
|
|
+ if (is_typed_array_out_of_bounds(typed_array_record))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ // 2. Let length be TypedArrayLength(taRecord).
|
|
|
+ auto length = typed_array_length(typed_array_record);
|
|
|
+
|
|
|
+ // 3. If length = 0, return 0.
|
|
|
+ if (length == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ // 4. Let O be taRecord.[[Object]].
|
|
|
+ auto object = typed_array_record.object;
|
|
|
+
|
|
|
+ // 5. If O.[[ByteLength]] is not auto, return O.[[ByteLength]].
|
|
|
+ if (!object->byte_length().is_auto())
|
|
|
+ return object->byte_length().length();
|
|
|
+
|
|
|
+ // 6. Let elementSize be TypedArrayElementSize(O).
|
|
|
+ auto element_size = object->element_size();
|
|
|
+
|
|
|
+ // 7. Return length × elementSize.
|
|
|
+ return length * element_size;
|
|
|
+}
|
|
|
+
|
|
|
+// 10.4.5.12 TypedArrayLength ( taRecord ), https://tc39.es/ecma262/#sec-typedarraylength
|
|
|
+u32 typed_array_length(TypedArrayWithBufferWitness const& typed_array_record)
|
|
|
+{
|
|
|
+ // 1. Assert: IsTypedArrayOutOfBounds(taRecord) is false.
|
|
|
+ VERIFY(!is_typed_array_out_of_bounds(typed_array_record));
|
|
|
+
|
|
|
+ // 2. Let O be taRecord.[[Object]].
|
|
|
+ auto object = typed_array_record.object;
|
|
|
+
|
|
|
+ // 3. If O.[[ArrayLength]] is not auto, return O.[[ArrayLength]].
|
|
|
+ if (!object->array_length().is_auto())
|
|
|
+ return object->array_length().length();
|
|
|
+
|
|
|
+ // 4. Assert: IsFixedLengthArrayBuffer(O.[[ViewedArrayBuffer]]) is false.
|
|
|
+ VERIFY(!object->viewed_array_buffer()->is_fixed_length());
|
|
|
+
|
|
|
+ // 5. Let byteOffset be O.[[ByteOffset]].
|
|
|
+ auto byte_offset = object->byte_offset();
|
|
|
+
|
|
|
+ // 6. Let elementSize be TypedArrayElementSize(O).
|
|
|
+ auto element_size = object->element_size();
|
|
|
+
|
|
|
+ // 7. Let byteLength be taRecord.[[CachedBufferByteLength]].
|
|
|
+ auto const& byte_length = typed_array_record.cached_buffer_byte_length;
|
|
|
+
|
|
|
+ // 8. Assert: byteLength is not detached.
|
|
|
+ VERIFY(!byte_length.is_detached());
|
|
|
+
|
|
|
+ // 9. Return floor((byteLength - byteOffset) / elementSize).
|
|
|
+ return (byte_length.length() - byte_offset) / element_size;
|
|
|
+}
|
|
|
+
|
|
|
+// 10.4.5.13 IsTypedArrayOutOfBounds ( taRecord ), https://tc39.es/ecma262/#sec-istypedarrayoutofbounds
|
|
|
+bool is_typed_array_out_of_bounds(TypedArrayWithBufferWitness const& typed_array_record)
|
|
|
+{
|
|
|
+ // 1. Let O be taRecord.[[Object]].
|
|
|
+ auto object = typed_array_record.object;
|
|
|
+
|
|
|
+ // 2. Let bufferByteLength be taRecord.[[CachedBufferByteLength]].
|
|
|
+ auto const& buffer_byte_length = typed_array_record.cached_buffer_byte_length;
|
|
|
+
|
|
|
+ // 3. Assert: IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true if and only if bufferByteLength is detached.
|
|
|
+ VERIFY(object->viewed_array_buffer()->is_detached() == buffer_byte_length.is_detached());
|
|
|
+
|
|
|
+ // 4. If bufferByteLength is detached, return true.
|
|
|
+ if (buffer_byte_length.is_detached())
|
|
|
+ return true;
|
|
|
+
|
|
|
+ // 5. Let byteOffsetStart be O.[[ByteOffset]].
|
|
|
+ auto byte_offset_start = object->byte_offset();
|
|
|
+ u32 byte_offset_end = 0;
|
|
|
+
|
|
|
+ // 6. If O.[[ArrayLength]] is auto, then
|
|
|
+ if (object->array_length().is_auto()) {
|
|
|
+ // a. Let byteOffsetEnd be bufferByteLength.
|
|
|
+ byte_offset_end = buffer_byte_length.length();
|
|
|
+ }
|
|
|
+ // 7. Else,
|
|
|
+ else {
|
|
|
+ // a. Let elementSize be TypedArrayElementSize(O).
|
|
|
+ auto element_size = object->element_size();
|
|
|
+
|
|
|
+ // b. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize.
|
|
|
+ byte_offset_end = byte_offset_start + object->array_length().length() * element_size;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 8. If byteOffsetStart > bufferByteLength or byteOffsetEnd > bufferByteLength, return true.
|
|
|
+ if ((byte_offset_start > buffer_byte_length.length()) || (byte_offset_end > buffer_byte_length.length()))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ // 9. NOTE: 0-length TypedArrays are not considered out-of-bounds.
|
|
|
+ // 10. Return false.
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+// 10.4.5.14 IsValidIntegerIndex ( O, index ), https://tc39.es/ecma262/#sec-isvalidintegerindex
|
|
|
+bool is_valid_integer_index(TypedArrayBase const& typed_array, CanonicalIndex property_index)
|
|
|
+{
|
|
|
+ // 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
|
|
|
+ if (typed_array.viewed_array_buffer()->is_detached())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 2. If IsIntegralNumber(index) is false, return false.
|
|
|
+ // 3. If index is -0𝔽, return false.
|
|
|
+ if (!property_index.is_index())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, unordered).
|
|
|
+ auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, ArrayBuffer::Unordered);
|
|
|
+
|
|
|
+ // NOTE: Bounds checking is not a synchronizing operation when O's backing buffer is a growable SharedArrayBuffer.
|
|
|
+
|
|
|
+ // 6. If IsTypedArrayOutOfBounds(taRecord) is true, return false.
|
|
|
+ if (is_typed_array_out_of_bounds(typed_array_record))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 7. Let length be TypedArrayLength(taRecord).
|
|
|
+ auto length = typed_array_length(typed_array_record);
|
|
|
+
|
|
|
+ // 8. If ℝ(index) < 0 or ℝ(index) ≥ length, return false.
|
|
|
+ if (property_index.as_index() >= length)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 9. Return true.
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
}
|