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); }; }