From 999ad734ecedb124cfaa520813ad029b21d61c4a Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 15 Jul 2022 08:03:57 -0400 Subject: [PATCH] LibJS: Sort TypedArray.prototype methods in spec order Makes it much easier to scroll through the file while comparing to the spec. Proposals are inserted alphabetically as that's where they will likely end up once merged into the main spec. --- .../LibJS/Runtime/TypedArrayPrototype.cpp | 861 +++++++++--------- .../LibJS/Runtime/TypedArrayPrototype.h | 25 +- 2 files changed, 444 insertions(+), 442 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index 9407062efe..0138f7dd18 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -26,13 +26,17 @@ void TypedArrayPrototype::initialize(GlobalObject& object) Object::initialize(object); u8 attr = Attribute::Writable | Attribute::Configurable; - define_native_accessor(vm.names.length, length_getter, nullptr, Attribute::Configurable); define_native_accessor(vm.names.buffer, buffer_getter, nullptr, Attribute::Configurable); define_native_accessor(vm.names.byteLength, byte_length_getter, nullptr, Attribute::Configurable); define_native_accessor(vm.names.byteOffset, byte_offset_getter, nullptr, Attribute::Configurable); + define_native_accessor(vm.names.length, length_getter, nullptr, Attribute::Configurable); + define_native_function(vm.names.at, at, 1, attr); + define_native_function(vm.names.copyWithin, copy_within, 2, attr); + define_native_function(vm.names.entries, entries, 0, attr); define_native_function(vm.names.every, every, 1, attr); define_native_function(vm.names.fill, fill, 1, attr); + define_native_function(vm.names.filter, filter, 1, attr); define_native_function(vm.names.find, find, 1, attr); define_native_function(vm.names.findIndex, find_index, 1, attr); define_native_function(vm.names.findLast, find_last, 1, attr); @@ -40,26 +44,23 @@ void TypedArrayPrototype::initialize(GlobalObject& object) define_native_function(vm.names.forEach, for_each, 1, attr); define_native_function(vm.names.includes, includes, 1, attr); define_native_function(vm.names.indexOf, index_of, 1, attr); - define_native_function(vm.names.lastIndexOf, last_index_of, 1, attr); - define_native_function(vm.names.reduce, reduce, 1, attr); - define_native_function(vm.names.reduceRight, reduce_right, 1, attr); - define_native_function(vm.names.some, some, 1, attr); define_native_function(vm.names.join, join, 1, attr); define_native_function(vm.names.keys, keys, 0, attr); - define_native_function(vm.names.values, values, 0, attr); - define_native_function(vm.names.entries, entries, 0, attr); + define_native_function(vm.names.lastIndexOf, last_index_of, 1, attr); + define_native_function(vm.names.map, map, 1, attr); + define_native_function(vm.names.reduce, reduce, 1, attr); + define_native_function(vm.names.reduceRight, reduce_right, 1, attr); + define_native_function(vm.names.reverse, reverse, 0, attr); define_native_function(vm.names.set, set, 1, attr); define_native_function(vm.names.slice, slice, 2, attr); + define_native_function(vm.names.some, some, 1, attr); define_native_function(vm.names.sort, sort, 1, attr); define_native_function(vm.names.subarray, subarray, 2, attr); - define_native_function(vm.names.reverse, reverse, 0, attr); - define_native_function(vm.names.copyWithin, copy_within, 2, attr); - define_native_function(vm.names.filter, filter, 1, attr); - define_native_function(vm.names.map, map, 1, attr); define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr); define_native_function(vm.names.toReversed, to_reversed, 0, attr); define_native_function(vm.names.toSorted, to_sorted, 1, attr); define_native_function(vm.names.toSpliced, to_spliced, 2, attr); + define_native_function(vm.names.values, values, 0, attr); define_native_accessor(*vm.well_known_symbol_to_string_tag(), to_string_tag_getter, nullptr, Attribute::Configurable); @@ -163,17 +164,6 @@ static ThrowCompletionOr typed_array_species_create(GlobalObjec return result; } -// 23.2.3.19 get %TypedArray%.prototype.length, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::length_getter) -{ - auto* typed_array = TRY(typed_array_from_this(global_object)); - auto* array_buffer = typed_array->viewed_array_buffer(); - VERIFY(array_buffer); - if (array_buffer->is_detached()) - return Value(0); - return Value(typed_array->array_length()); -} - // 23.2.3.1 %TypedArray%.prototype.at ( index ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.at JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::at) { @@ -194,6 +184,212 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::at) return TRY(typed_array->get(index.value())); } +// 23.2.3.2 get %TypedArray%.prototype.buffer, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::buffer_getter) +{ + auto* typed_array = TRY(typed_array_from_this(global_object)); + auto* array_buffer = typed_array->viewed_array_buffer(); + VERIFY(array_buffer); + return Value(array_buffer); +} + +// 23.2.3.3 get %TypedArray%.prototype.byteLength, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.bytelength +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::byte_length_getter) +{ + auto* typed_array = TRY(typed_array_from_this(global_object)); + auto* array_buffer = typed_array->viewed_array_buffer(); + VERIFY(array_buffer); + if (array_buffer->is_detached()) + return Value(0); + return Value(typed_array->byte_length()); +} + +// 23.2.3.4 get %TypedArray%.prototype.byteOffset, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.byteoffset +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::byte_offset_getter) +{ + auto* typed_array = TRY(typed_array_from_this(global_object)); + auto* array_buffer = typed_array->viewed_array_buffer(); + VERIFY(array_buffer); + if (array_buffer->is_detached()) + return Value(0); + return Value(typed_array->byte_offset()); +} + +// 23.2.3.6 %TypedArray%.prototype.copyWithin ( target, start [ , end ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within) +{ + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). + auto* typed_array = TRY(validate_typed_array_from_this(global_object)); + + // 3. Let len be O.[[ArrayLength]]. + auto length = typed_array->array_length(); + + // 4. Let relativeTarget be ? ToIntegerOrInfinity(target). + auto relative_target = TRY(vm.argument(0).to_integer_or_infinity(global_object)); + + double to; + if (Value(relative_target).is_negative_infinity()) { + // 5. If relativeTarget is -∞, let to be 0. + to = 0.0; + } else if (relative_target < 0) { + // 6. Else if relativeTarget < 0, let to be max(len + relativeTarget, 0) + to = max(length + relative_target, 0.0); + } else { + // 7. Else, let to be min(relativeTarget, len). + to = min(relative_target, (double)length); + } + + // 8. Let relativeStart be ? ToIntegerOrInfinity(start). + auto relative_start = TRY(vm.argument(1).to_integer_or_infinity(global_object)); + + double from; + if (Value(relative_start).is_negative_infinity()) { + // 9. If relativeStart is -∞, let from be 0. + from = 0.0; + } else if (relative_start < 0) { + // 10. Else if relativeStart < 0, let from be max(len + relativeStart, 0). + from = max(length + relative_start, 0.0); + } else { + // 11. Else, let from be min(relativeStart, len). + from = min(relative_start, (double)length); + } + + double relative_end; + + // 12. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). + if (vm.argument(2).is_undefined()) + relative_end = length; + else + relative_end = TRY(vm.argument(2).to_integer_or_infinity(global_object)); + + double final; + if (Value(relative_end).is_negative_infinity()) { + // 13. If relativeEnd is -∞, let final be 0. + final = 0.0; + } else if (relative_end < 0) { + // 14. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). + final = max(length + relative_end, 0.0); + } else { + // 15. Else, let final be min(relativeEnd, len). + final = min(relative_end, (double)length); + } + + // 16. Let count be min(final - from, len - to). + double count = min(final - from, length - to); + + // 17. If count > 0, then + if (count > 0.0) { + // a. NOTE: The copying must be performed in a manner that preserves the bit-level encoding of the source data. + + // b. Let buffer be O.[[ViewedArrayBuffer]]. + auto buffer = typed_array->viewed_array_buffer(); + + // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. + if (buffer->is_detached()) + return vm.throw_completion(global_object, ErrorType::DetachedArrayBuffer); + + // d. Let elementSize be TypedArrayElementSize(O). + auto element_size = typed_array->element_size(); + + // e. Let byteOffset be O.[[ByteOffset]]. + auto byte_offset = typed_array->byte_offset(); + + // FIXME: Not exactly sure what we should do when overflow occurs. + // Just return as if succeeded for now. (This goes for steps g to j) + + // f. Let toByteIndex be to × elementSize + byteOffset. + Checked to_byte_index_checked = static_cast(to); + to_byte_index_checked *= element_size; + to_byte_index_checked += byte_offset; + if (to_byte_index_checked.has_overflow()) { + dbgln("TypedArrayPrototype::copy_within: to_byte_index overflowed, returning as if succeeded."); + return typed_array; + } + + // g. Let fromByteIndex be from × elementSize + byteOffset. + Checked from_byte_index_checked = static_cast(from); + from_byte_index_checked *= element_size; + from_byte_index_checked += byte_offset; + if (from_byte_index_checked.has_overflow()) { + dbgln("TypedArrayPrototype::copy_within: from_byte_index_checked overflowed, returning as if succeeded."); + return typed_array; + } + + // h. Let countBytes be count × elementSize. + Checked count_bytes_checked = static_cast(count); + count_bytes_checked *= element_size; + if (count_bytes_checked.has_overflow()) { + dbgln("TypedArrayPrototype::copy_within: count_bytes_checked overflowed, returning as if succeeded."); + return typed_array; + } + + auto to_byte_index = to_byte_index_checked.value(); + auto from_byte_index = from_byte_index_checked.value(); + auto count_bytes = count_bytes_checked.value(); + + Checked from_plus_count = from_byte_index; + from_plus_count += count_bytes; + if (from_plus_count.has_overflow()) { + dbgln("TypedArrayPrototype::copy_within: from_plus_count overflowed, returning as if succeeded."); + return typed_array; + } + + i8 direction; + + // i. If fromByteIndex < toByteIndex and toByteIndex < fromByteIndex + countBytes, then + if (from_byte_index < to_byte_index && to_byte_index < from_plus_count.value()) { + // i. Let direction be -1. + direction = -1; + + // ii. Set fromByteIndex to fromByteIndex + countBytes - 1. + from_byte_index = from_plus_count.value() - 1; + + Checked to_plus_count = to_byte_index; + to_plus_count += count_bytes; + if (to_plus_count.has_overflow()) { + dbgln("TypedArrayPrototype::copy_within: to_plus_count overflowed, returning as if succeeded."); + return typed_array; + } + + // iii. Set toByteIndex to toByteIndex + countBytes - 1. + to_byte_index = to_plus_count.value() - 1; + } + // j. Else, + else { + // i. Let direction be 1. + direction = 1; + } + + // k. Repeat, while countBytes > 0, + for (; count_bytes > 0; --count_bytes) { + // i. Let value be GetValueFromBuffer(buffer, fromByteIndex, Uint8, true, Unordered). + auto value = buffer->get_value(from_byte_index, true, ArrayBuffer::Order::Unordered); + + // ii. Perform SetValueInBuffer(buffer, toByteIndex, Uint8, value, true, Unordered). + buffer->set_value(to_byte_index, value, true, ArrayBuffer::Order::Unordered); + + // iii. Set fromByteIndex to fromByteIndex + direction. + from_byte_index += direction; + + // iv. Set toByteIndex to toByteIndex + direction. + to_byte_index += direction; + + // v. Set countBytes to countBytes - 1. + } + } + + // 18. Return O. + return typed_array; +} + +// 23.2.3.7 %TypedArray%.prototype.entries ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::entries) +{ + auto* typed_array = TRY(validate_typed_array_from_this(global_object)); + return ArrayIterator::create(global_object, typed_array, Object::PropertyKind::KeyAndValue); +} + // 23.2.3.8 %TypedArray%.prototype.every ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.every JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::every) { @@ -254,6 +450,70 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::fill) return typed_array; } +// 23.2.3.10 %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::filter) +{ + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). + auto* typed_array = TRY(validate_typed_array_from_this(global_object)); + + // 3. Let len be O.[[ArrayLength]]. + auto initial_length = typed_array->array_length(); + + // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. + auto* callback_function = TRY(callback_from_args(global_object, "filter")); + + // 5. Let kept be a new empty List. + MarkedVector kept(vm.heap()); + + // 7. Let captured be 0. + size_t captured = 0; + + auto this_value = vm.argument(1); + + // 5. Let k be 0. + // 8. Repeat, while k < len, + for (size_t i = 0; i < initial_length; ++i) { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kValue be ! Get(O, Pk). + auto value = MUST(typed_array->get(i)); + + // c. Let selected be ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). + auto callback_result = TRY(call(global_object, *callback_function, this_value, value, Value((i32)i), typed_array)).to_boolean(); + + // d. If selected is true, then + if (callback_result) { + // i. Append kValue to the end of kept. + kept.append(value); + + // ii. Set captured to captured + 1. + ++captured; + } + + // e. Set k to k + 1. + } + + // 9. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(captured) »). + MarkedVector arguments(vm.heap()); + arguments.empend(captured); + auto* filter_array = TRY(typed_array_species_create(global_object, *typed_array, move(arguments))); + + // 10. Let n be 0. + size_t index = 0; + + // 11. For each element e of kept, do + for (auto& value : kept) { + // a. Perform ! Set(A, ! ToString(𝔽(n)), e, true). + MUST(filter_array->set(index, value, Object::ShouldThrowExceptions::Yes)); + + // b. Set n to n + 1. + ++index; + } + + // 12. Return A. + return filter_array; +} + // 23.2.3.11 %TypedArray%.prototype.find ( predicate [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.find JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::find) { @@ -400,6 +660,36 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::index_of) return Value(-1); } +// 23.2.3.16 %TypedArray%.prototype.join ( separator ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.join +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::join) +{ + auto* typed_array = TRY(validate_typed_array_from_this(global_object)); + auto length = typed_array->array_length(); + String separator = ","; + if (!vm.argument(0).is_undefined()) + separator = TRY(vm.argument(0).to_string(global_object)); + + StringBuilder builder; + for (size_t i = 0; i < length; ++i) { + if (i > 0) + builder.append(separator); + auto value = TRY(typed_array->get(i)); + if (value.is_nullish()) + continue; + auto string = TRY(value.to_string(global_object)); + builder.append(string); + } + + return js_string(vm, builder.to_string()); +} + +// 23.2.3.17 %TypedArray%.prototype.keys ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::keys) +{ + auto* typed_array = TRY(validate_typed_array_from_this(global_object)); + return ArrayIterator::create(global_object, typed_array, Object::PropertyKind::Key); +} + // 23.2.3.18 %TypedArray%.prototype.lastIndexOf ( searchElement [ , fromIndex ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::last_index_of) { @@ -443,6 +733,57 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::last_index_of) return Value(-1); } +// 23.2.3.19 get %TypedArray%.prototype.length, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::length_getter) +{ + auto* typed_array = TRY(typed_array_from_this(global_object)); + auto* array_buffer = typed_array->viewed_array_buffer(); + VERIFY(array_buffer); + if (array_buffer->is_detached()) + return Value(0); + return Value(typed_array->array_length()); +} + +// 23.2.3.20 %TypedArray%.prototype.map ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.map +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::map) +{ + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). + auto* typed_array = TRY(validate_typed_array_from_this(global_object)); + + // 3. Let len be O.[[ArrayLength]]. + auto initial_length = typed_array->array_length(); + + // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. + auto* callback_function = TRY(callback_from_args(global_object, "map")); + + // 5. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(len) »). + MarkedVector arguments(vm.heap()); + arguments.empend(initial_length); + auto* return_array = TRY(typed_array_species_create(global_object, *typed_array, move(arguments))); + + auto this_value = vm.argument(1); + + // 6. Let k be 0. + // 7. Repeat, while k < len, + for (size_t i = 0; i < initial_length; ++i) { + // a. Let Pk be ! ToString(𝔽(k)). + // b. Let kValue be ! Get(O, Pk). + auto value = MUST(typed_array->get(i)); + + // c. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »). + auto mapped_value = TRY(call(global_object, *callback_function, this_value, value, Value((i32)i), typed_array)); + + // d. Perform ? Set(A, Pk, mappedValue, true). + TRY(return_array->set(i, mapped_value, Object::ShouldThrowExceptions::Yes)); + + // e. Set k to k + 1. + } + + // 8. Return A. + return return_array; +} + // 23.2.3.21 %TypedArray%.prototype.reduce ( callbackfn [ , initialValue ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::reduce) { @@ -503,108 +844,47 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::reduce_right) return accumulator; } -// 23.2.3.26 %TypedArray%.prototype.some ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.some -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::some) -{ - auto result = false; - TRY(for_each_item(vm, global_object, "some", [&](auto, auto, auto callback_result) { - if (callback_result.to_boolean()) { - result = true; - return IterationDecision::Break; - } - return IterationDecision::Continue; - })); - return Value(result); -} - -// 23.2.3.16 %TypedArray%.prototype.join ( separator ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.join -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::join) +// 23.2.3.23 %TypedArray%.prototype.reverse ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::reverse) { + // 1. Let O be the this value. + // 2. Perform ? ValidateTypedArray(O). auto* typed_array = TRY(validate_typed_array_from_this(global_object)); - auto length = typed_array->array_length(); - String separator = ","; - if (!vm.argument(0).is_undefined()) - separator = TRY(vm.argument(0).to_string(global_object)); - StringBuilder builder; - for (size_t i = 0; i < length; ++i) { - if (i > 0) - builder.append(separator); - auto value = TRY(typed_array->get(i)); - if (value.is_nullish()) - continue; - auto string = TRY(value.to_string(global_object)); - builder.append(string); + // 3. Let len be O.[[ArrayLength]]. + auto length = typed_array->array_length(); + + // 4. Let middle be floor(len / 2). + auto middle = length / 2; + + // 5. Let lower be 0. + // 6. Repeat, while lower ≠ middle, + for (size_t lower = 0; lower != middle; ++lower) { + // a. Let upper be len - lower - 1. + auto upper = length - lower - 1; + + // b. Let upperP be ! ToString(𝔽(upper)). + // d. Let lowerValue be ! Get(O, lowerP). + auto lower_value = MUST(typed_array->get(lower)); + + // c. Let lowerP be ! ToString(𝔽(lower)). + // e. Let upperValue be ! Get(O, upperP). + auto upper_value = MUST(typed_array->get(upper)); + + // f. Perform ! Set(O, lowerP, upperValue, true). + MUST(typed_array->set(lower, upper_value, Object::ShouldThrowExceptions::Yes)); + + // g. Perform ! Set(O, upperP, lowerValue, true). + MUST(typed_array->set(upper, lower_value, Object::ShouldThrowExceptions::Yes)); + + // h. Set lower to lower + 1. } - return js_string(vm, builder.to_string()); + // 7. Return O. + return typed_array; } -// 23.2.3.17 %TypedArray%.prototype.keys ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::keys) -{ - auto* typed_array = TRY(validate_typed_array_from_this(global_object)); - return ArrayIterator::create(global_object, typed_array, Object::PropertyKind::Key); -} - -// 23.2.3.31 %TypedArray%.prototype.values ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.values -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::values) -{ - auto* typed_array = TRY(typed_array_from_this(global_object)); - return ArrayIterator::create(global_object, typed_array, Object::PropertyKind::Value); -} - -// 23.2.3.7 %TypedArray%.prototype.entries ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::entries) -{ - auto* typed_array = TRY(validate_typed_array_from_this(global_object)); - return ArrayIterator::create(global_object, typed_array, Object::PropertyKind::KeyAndValue); -} - -// 23.2.3.2 get %TypedArray%.prototype.buffer, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::buffer_getter) -{ - auto* typed_array = TRY(typed_array_from_this(global_object)); - auto* array_buffer = typed_array->viewed_array_buffer(); - VERIFY(array_buffer); - return Value(array_buffer); -} - -// 23.2.3.3 get %TypedArray%.prototype.byteLength, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.bytelength -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::byte_length_getter) -{ - auto* typed_array = TRY(typed_array_from_this(global_object)); - auto* array_buffer = typed_array->viewed_array_buffer(); - VERIFY(array_buffer); - if (array_buffer->is_detached()) - return Value(0); - return Value(typed_array->byte_length()); -} - -// 23.2.3.4 get %TypedArray%.prototype.byteOffset, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.byteoffset -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::byte_offset_getter) -{ - auto* typed_array = TRY(typed_array_from_this(global_object)); - auto* array_buffer = typed_array->viewed_array_buffer(); - VERIFY(array_buffer); - if (array_buffer->is_detached()) - return Value(0); - return Value(typed_array->byte_offset()); -} - -// 23.2.3.33 get %TypedArray%.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::to_string_tag_getter) -{ - auto this_value = vm.this_value(global_object); - if (!this_value.is_object()) - return js_undefined(); - auto& this_object = this_value.as_object(); - if (!this_object.is_typed_array()) - return js_undefined(); - return js_string(vm, static_cast(this_object).element_name()); -} - -// 23.2.3.23.1 SetTypedArrayFromTypedArray ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray +// 23.2.3.24.1 SetTypedArrayFromTypedArray ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray static ThrowCompletionOr set_typed_array_from_typed_array(GlobalObject& global_object, TypedArrayBase& target, double target_offset, TypedArrayBase& source) { auto& vm = global_object.vm(); @@ -726,7 +1006,7 @@ static ThrowCompletionOr set_typed_array_from_typed_array(GlobalObject& gl return {}; } -// 23.2.3.23.2 SetTypedArrayFromArrayLike ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromarraylike +// 23.2.3.24.2 SetTypedArrayFromArrayLike ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromarraylike static ThrowCompletionOr set_typed_array_from_array_like(GlobalObject& global_object, TypedArrayBase& target, double target_offset, Value source) { auto& vm = global_object.vm(); @@ -956,6 +1236,20 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::slice) return new_array; } +// 23.2.3.26 %TypedArray%.prototype.some ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.some +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::some) +{ + auto result = false; + TRY(for_each_item(vm, global_object, "some", [&](auto, auto, auto callback_result) { + if (callback_result.to_boolean()) { + result = true; + return IterationDecision::Break; + } + return IterationDecision::Continue; + })); + return Value(result); +} + static ThrowCompletionOr typed_array_merge_sort(GlobalObject& global_object, FunctionObject* compare_function, ArrayBuffer& buffer, MarkedVector& arr_to_sort) { auto& vm = global_object.vm(); @@ -1148,318 +1442,6 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::subarray) return TRY(typed_array_species_create(global_object, *typed_array, move(arguments))); } -// 23.2.3.23 %TypedArray%.prototype.reverse ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::reverse) -{ - // 1. Let O be the this value. - // 2. Perform ? ValidateTypedArray(O). - auto* typed_array = TRY(validate_typed_array_from_this(global_object)); - - // 3. Let len be O.[[ArrayLength]]. - auto length = typed_array->array_length(); - - // 4. Let middle be floor(len / 2). - auto middle = length / 2; - - // 5. Let lower be 0. - // 6. Repeat, while lower ≠ middle, - for (size_t lower = 0; lower != middle; ++lower) { - // a. Let upper be len - lower - 1. - auto upper = length - lower - 1; - - // b. Let upperP be ! ToString(𝔽(upper)). - // d. Let lowerValue be ! Get(O, lowerP). - auto lower_value = MUST(typed_array->get(lower)); - - // c. Let lowerP be ! ToString(𝔽(lower)). - // e. Let upperValue be ! Get(O, upperP). - auto upper_value = MUST(typed_array->get(upper)); - - // f. Perform ! Set(O, lowerP, upperValue, true). - MUST(typed_array->set(lower, upper_value, Object::ShouldThrowExceptions::Yes)); - - // g. Perform ! Set(O, upperP, lowerValue, true). - MUST(typed_array->set(upper, lower_value, Object::ShouldThrowExceptions::Yes)); - - // h. Set lower to lower + 1. - } - - // 7. Return O. - return typed_array; -} - -// 23.2.3.6 %TypedArray%.prototype.copyWithin ( target, start [ , end ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within) -{ - // 1. Let O be the this value. - // 2. Perform ? ValidateTypedArray(O). - auto* typed_array = TRY(validate_typed_array_from_this(global_object)); - - // 3. Let len be O.[[ArrayLength]]. - auto length = typed_array->array_length(); - - // 4. Let relativeTarget be ? ToIntegerOrInfinity(target). - auto relative_target = TRY(vm.argument(0).to_integer_or_infinity(global_object)); - - double to; - if (Value(relative_target).is_negative_infinity()) { - // 5. If relativeTarget is -∞, let to be 0. - to = 0.0; - } else if (relative_target < 0) { - // 6. Else if relativeTarget < 0, let to be max(len + relativeTarget, 0) - to = max(length + relative_target, 0.0); - } else { - // 7. Else, let to be min(relativeTarget, len). - to = min(relative_target, (double)length); - } - - // 8. Let relativeStart be ? ToIntegerOrInfinity(start). - auto relative_start = TRY(vm.argument(1).to_integer_or_infinity(global_object)); - - double from; - if (Value(relative_start).is_negative_infinity()) { - // 9. If relativeStart is -∞, let from be 0. - from = 0.0; - } else if (relative_start < 0) { - // 10. Else if relativeStart < 0, let from be max(len + relativeStart, 0). - from = max(length + relative_start, 0.0); - } else { - // 11. Else, let from be min(relativeStart, len). - from = min(relative_start, (double)length); - } - - double relative_end; - - // 12. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end). - if (vm.argument(2).is_undefined()) - relative_end = length; - else - relative_end = TRY(vm.argument(2).to_integer_or_infinity(global_object)); - - double final; - if (Value(relative_end).is_negative_infinity()) { - // 13. If relativeEnd is -∞, let final be 0. - final = 0.0; - } else if (relative_end < 0) { - // 14. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0). - final = max(length + relative_end, 0.0); - } else { - // 15. Else, let final be min(relativeEnd, len). - final = min(relative_end, (double)length); - } - - // 16. Let count be min(final - from, len - to). - double count = min(final - from, length - to); - - // 17. If count > 0, then - if (count > 0.0) { - // a. NOTE: The copying must be performed in a manner that preserves the bit-level encoding of the source data. - - // b. Let buffer be O.[[ViewedArrayBuffer]]. - auto buffer = typed_array->viewed_array_buffer(); - - // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. - if (buffer->is_detached()) - return vm.throw_completion(global_object, ErrorType::DetachedArrayBuffer); - - // d. Let elementSize be TypedArrayElementSize(O). - auto element_size = typed_array->element_size(); - - // e. Let byteOffset be O.[[ByteOffset]]. - auto byte_offset = typed_array->byte_offset(); - - // FIXME: Not exactly sure what we should do when overflow occurs. - // Just return as if succeeded for now. (This goes for steps g to j) - - // f. Let toByteIndex be to × elementSize + byteOffset. - Checked to_byte_index_checked = static_cast(to); - to_byte_index_checked *= element_size; - to_byte_index_checked += byte_offset; - if (to_byte_index_checked.has_overflow()) { - dbgln("TypedArrayPrototype::copy_within: to_byte_index overflowed, returning as if succeeded."); - return typed_array; - } - - // g. Let fromByteIndex be from × elementSize + byteOffset. - Checked from_byte_index_checked = static_cast(from); - from_byte_index_checked *= element_size; - from_byte_index_checked += byte_offset; - if (from_byte_index_checked.has_overflow()) { - dbgln("TypedArrayPrototype::copy_within: from_byte_index_checked overflowed, returning as if succeeded."); - return typed_array; - } - - // h. Let countBytes be count × elementSize. - Checked count_bytes_checked = static_cast(count); - count_bytes_checked *= element_size; - if (count_bytes_checked.has_overflow()) { - dbgln("TypedArrayPrototype::copy_within: count_bytes_checked overflowed, returning as if succeeded."); - return typed_array; - } - - auto to_byte_index = to_byte_index_checked.value(); - auto from_byte_index = from_byte_index_checked.value(); - auto count_bytes = count_bytes_checked.value(); - - Checked from_plus_count = from_byte_index; - from_plus_count += count_bytes; - if (from_plus_count.has_overflow()) { - dbgln("TypedArrayPrototype::copy_within: from_plus_count overflowed, returning as if succeeded."); - return typed_array; - } - - i8 direction; - - // i. If fromByteIndex < toByteIndex and toByteIndex < fromByteIndex + countBytes, then - if (from_byte_index < to_byte_index && to_byte_index < from_plus_count.value()) { - // i. Let direction be -1. - direction = -1; - - // ii. Set fromByteIndex to fromByteIndex + countBytes - 1. - from_byte_index = from_plus_count.value() - 1; - - Checked to_plus_count = to_byte_index; - to_plus_count += count_bytes; - if (to_plus_count.has_overflow()) { - dbgln("TypedArrayPrototype::copy_within: to_plus_count overflowed, returning as if succeeded."); - return typed_array; - } - - // iii. Set toByteIndex to toByteIndex + countBytes - 1. - to_byte_index = to_plus_count.value() - 1; - } - // j. Else, - else { - // i. Let direction be 1. - direction = 1; - } - - // k. Repeat, while countBytes > 0, - for (; count_bytes > 0; --count_bytes) { - // i. Let value be GetValueFromBuffer(buffer, fromByteIndex, Uint8, true, Unordered). - auto value = buffer->get_value(from_byte_index, true, ArrayBuffer::Order::Unordered); - - // ii. Perform SetValueInBuffer(buffer, toByteIndex, Uint8, value, true, Unordered). - buffer->set_value(to_byte_index, value, true, ArrayBuffer::Order::Unordered); - - // iii. Set fromByteIndex to fromByteIndex + direction. - from_byte_index += direction; - - // iv. Set toByteIndex to toByteIndex + direction. - to_byte_index += direction; - - // v. Set countBytes to countBytes - 1. - } - } - - // 18. Return O. - return typed_array; -} - -// 23.2.3.10 %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::filter) -{ - // 1. Let O be the this value. - // 2. Perform ? ValidateTypedArray(O). - auto* typed_array = TRY(validate_typed_array_from_this(global_object)); - - // 3. Let len be O.[[ArrayLength]]. - auto initial_length = typed_array->array_length(); - - // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. - auto* callback_function = TRY(callback_from_args(global_object, "filter")); - - // 5. Let kept be a new empty List. - MarkedVector kept(vm.heap()); - - // 7. Let captured be 0. - size_t captured = 0; - - auto this_value = vm.argument(1); - - // 5. Let k be 0. - // 8. Repeat, while k < len, - for (size_t i = 0; i < initial_length; ++i) { - // a. Let Pk be ! ToString(𝔽(k)). - // b. Let kValue be ! Get(O, Pk). - auto value = MUST(typed_array->get(i)); - - // c. Let selected be ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)). - auto callback_result = TRY(call(global_object, *callback_function, this_value, value, Value((i32)i), typed_array)).to_boolean(); - - // d. If selected is true, then - if (callback_result) { - // i. Append kValue to the end of kept. - kept.append(value); - - // ii. Set captured to captured + 1. - ++captured; - } - - // e. Set k to k + 1. - } - - // 9. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(captured) »). - MarkedVector arguments(vm.heap()); - arguments.empend(captured); - auto* filter_array = TRY(typed_array_species_create(global_object, *typed_array, move(arguments))); - - // 10. Let n be 0. - size_t index = 0; - - // 11. For each element e of kept, do - for (auto& value : kept) { - // a. Perform ! Set(A, ! ToString(𝔽(n)), e, true). - MUST(filter_array->set(index, value, Object::ShouldThrowExceptions::Yes)); - - // b. Set n to n + 1. - ++index; - } - - // 12. Return A. - return filter_array; -} - -// 23.2.3.20 %TypedArray%.prototype.map ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.map -JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::map) -{ - // 1. Let O be the this value. - // 2. Perform ? ValidateTypedArray(O). - auto* typed_array = TRY(validate_typed_array_from_this(global_object)); - - // 3. Let len be O.[[ArrayLength]]. - auto initial_length = typed_array->array_length(); - - // 4. If IsCallable(callbackfn) is false, throw a TypeError exception. - auto* callback_function = TRY(callback_from_args(global_object, "map")); - - // 5. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(len) »). - MarkedVector arguments(vm.heap()); - arguments.empend(initial_length); - auto* return_array = TRY(typed_array_species_create(global_object, *typed_array, move(arguments))); - - auto this_value = vm.argument(1); - - // 6. Let k be 0. - // 7. Repeat, while k < len, - for (size_t i = 0; i < initial_length; ++i) { - // a. Let Pk be ! ToString(𝔽(k)). - // b. Let kValue be ! Get(O, Pk). - auto value = MUST(typed_array->get(i)); - - // c. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »). - auto mapped_value = TRY(call(global_object, *callback_function, this_value, value, Value((i32)i), typed_array)); - - // d. Perform ? Set(A, Pk, mappedValue, true). - TRY(return_array->set(i, mapped_value, Object::ShouldThrowExceptions::Yes)); - - // e. Set k to k + 1. - } - - // 8. Return A. - return return_array; -} - // 23.2.3.29 %TypedArray%.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring // 19.5.1 Array.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sup-array.prototype.tolocalestring JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::to_locale_string) @@ -1733,4 +1715,23 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::to_spliced) return return_array; } +// 23.2.3.31 %TypedArray%.prototype.values ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.values +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::values) +{ + auto* typed_array = TRY(typed_array_from_this(global_object)); + return ArrayIterator::create(global_object, typed_array, Object::PropertyKind::Value); +} + +// 23.2.3.33 get %TypedArray%.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::to_string_tag_getter) +{ + auto this_value = vm.this_value(global_object); + if (!this_value.is_object()) + return js_undefined(); + auto& this_object = this_value.as_object(); + if (!this_object.is_typed_array()) + return js_undefined(); + return js_string(vm, static_cast(this_object).element_name()); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h index 857dfa8a46..06ac8ab85e 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h @@ -20,14 +20,17 @@ public: virtual ~TypedArrayPrototype() override = default; private: - JS_DECLARE_NATIVE_FUNCTION(length_getter); JS_DECLARE_NATIVE_FUNCTION(buffer_getter); JS_DECLARE_NATIVE_FUNCTION(byte_length_getter); JS_DECLARE_NATIVE_FUNCTION(byte_offset_getter); - JS_DECLARE_NATIVE_FUNCTION(to_string_tag_getter); + JS_DECLARE_NATIVE_FUNCTION(length_getter); + JS_DECLARE_NATIVE_FUNCTION(at); + JS_DECLARE_NATIVE_FUNCTION(copy_within); + JS_DECLARE_NATIVE_FUNCTION(entries); JS_DECLARE_NATIVE_FUNCTION(every); JS_DECLARE_NATIVE_FUNCTION(fill); + JS_DECLARE_NATIVE_FUNCTION(filter); JS_DECLARE_NATIVE_FUNCTION(find); JS_DECLARE_NATIVE_FUNCTION(find_index); JS_DECLARE_NATIVE_FUNCTION(find_last); @@ -35,26 +38,24 @@ private: JS_DECLARE_NATIVE_FUNCTION(for_each); JS_DECLARE_NATIVE_FUNCTION(includes); JS_DECLARE_NATIVE_FUNCTION(index_of); - JS_DECLARE_NATIVE_FUNCTION(last_index_of); - JS_DECLARE_NATIVE_FUNCTION(reduce); - JS_DECLARE_NATIVE_FUNCTION(reduce_right); - JS_DECLARE_NATIVE_FUNCTION(some); JS_DECLARE_NATIVE_FUNCTION(join); JS_DECLARE_NATIVE_FUNCTION(keys); - JS_DECLARE_NATIVE_FUNCTION(values); - JS_DECLARE_NATIVE_FUNCTION(entries); + JS_DECLARE_NATIVE_FUNCTION(last_index_of); + JS_DECLARE_NATIVE_FUNCTION(map); + JS_DECLARE_NATIVE_FUNCTION(reduce); + JS_DECLARE_NATIVE_FUNCTION(reduce_right); + JS_DECLARE_NATIVE_FUNCTION(reverse); JS_DECLARE_NATIVE_FUNCTION(set); JS_DECLARE_NATIVE_FUNCTION(slice); + JS_DECLARE_NATIVE_FUNCTION(some); JS_DECLARE_NATIVE_FUNCTION(sort); JS_DECLARE_NATIVE_FUNCTION(subarray); - JS_DECLARE_NATIVE_FUNCTION(reverse); - JS_DECLARE_NATIVE_FUNCTION(copy_within); - JS_DECLARE_NATIVE_FUNCTION(filter); - JS_DECLARE_NATIVE_FUNCTION(map); JS_DECLARE_NATIVE_FUNCTION(to_locale_string); JS_DECLARE_NATIVE_FUNCTION(to_reversed); JS_DECLARE_NATIVE_FUNCTION(to_sorted); JS_DECLARE_NATIVE_FUNCTION(to_spliced); + JS_DECLARE_NATIVE_FUNCTION(values); + JS_DECLARE_NATIVE_FUNCTION(to_string_tag_getter); }; }