diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index d25bc5735b..1332b3894b 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -45,6 +45,7 @@ void TypedArrayPrototype::initialize(GlobalObject& object) define_native_function(vm.names.values, values, 0, attr); define_native_function(vm.names.entries, entries, 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.reverse, reverse, 0, attr); define_native_function(vm.names.copyWithin, copy_within, 2, attr); define_native_function(vm.names.filter, filter, 1, attr); @@ -816,6 +817,96 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::set) return js_undefined(); } +// 23.2.3.24 %TypedArray%.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::slice) +{ + auto* typed_array = validate_typed_array(global_object); + if (!typed_array) + return {}; + + auto length = typed_array->array_length(); + + auto relative_start = vm.argument(0).to_integer_or_infinity(global_object); + if (vm.exception()) + return {}; + + i32 k; + if (Value(relative_start).is_negative_infinity()) + k = 0; + else if (relative_start < 0) + k = max(length + relative_start, 0); + else + k = min(relative_start, length); + + double relative_end; + if (vm.argument(1).is_undefined()) { + relative_end = length; + } else { + relative_end = vm.argument(1).to_integer_or_infinity(global_object); + if (vm.exception()) + return {}; + } + + i32 final; + if (Value(relative_end).is_negative_infinity()) + final = 0; + else if (relative_end < 0) + final = max(length + relative_end, 0); + else + final = min(relative_end, length); + + auto count = max(final - k, 0); + + MarkedValueList arguments(vm.heap()); + arguments.empend(count); + auto new_array = typed_array_species_create(global_object, *typed_array, move(arguments)); + if (vm.exception()) + return {}; + + if (count > 0) { + if (typed_array->viewed_array_buffer()->is_detached()) { + vm.throw_exception(global_object, ErrorType::DetachedArrayBuffer); + return {}; + } + + if (typed_array->element_name() != new_array->element_name()) { + for (i32 n = 0; k < final; ++k, ++n) { + auto k_value = typed_array->get(k); + new_array->set(n, k_value, true); + } + } else { + auto element_size = typed_array->element_size(); + + Checked source_byte_index = k; + source_byte_index *= element_size; + source_byte_index += typed_array->byte_offset(); + if (source_byte_index.has_overflow()) { + dbgln("TypedArrayPrototype::slice: source_byte_index overflowed, returning as if succeeded."); + return new_array; + } + + auto target_byte_index = new_array->byte_offset(); + + Checked limit = count; + limit *= element_size; + limit += target_byte_index; + if (limit.has_overflow()) { + dbgln("TypedArrayPrototype::slice: limit overflowed, returning as if succeeded."); + return new_array; + } + + auto& source_buffer = *typed_array->viewed_array_buffer(); + auto& target_buffer = *new_array->viewed_array_buffer(); + for (; target_byte_index < limit.value(); ++source_byte_index, ++target_byte_index) { + auto value = source_buffer.get_value(source_byte_index.value(), true, ArrayBuffer::Unordered); + target_buffer.set_value(target_byte_index, value, true, ArrayBuffer::Unordered); + } + } + } + + return new_array; +} + // 23.2.3.22 %TypedArray%.prototype.reverse ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::reverse) { diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h index 245239dd35..9ccb095da7 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h @@ -42,6 +42,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(values); JS_DECLARE_NATIVE_FUNCTION(entries); JS_DECLARE_NATIVE_FUNCTION(set); + JS_DECLARE_NATIVE_FUNCTION(slice); JS_DECLARE_NATIVE_FUNCTION(reverse); JS_DECLARE_NATIVE_FUNCTION(copy_within); JS_DECLARE_NATIVE_FUNCTION(filter); diff --git a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.slice.js b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.slice.js new file mode 100644 index 0000000000..92a8eaa8d5 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.slice.js @@ -0,0 +1,47 @@ +const TYPED_ARRAYS = [ + Uint8Array, + Uint8ClampedArray, + Uint16Array, + Uint32Array, + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, +]; + +const BIGINT_TYPED_ARRAYS = [BigUint64Array, BigInt64Array]; + +test("basic functionality", () => { + TYPED_ARRAYS.forEach(T => { + expect(T.prototype.slice).toHaveLength(2); + + const typedArray = new T(3); + typedArray[0] = 1; + typedArray[1] = 2; + typedArray[2] = 3; + + const first_slice = typedArray.slice(1, 2); + expect(first_slice).toHaveLength(1); + expect(first_slice[0]).toBe(2); + + const second_slice = typedArray.slice(3, 1); + expect(second_slice).toHaveLength(0); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + expect(T.prototype.slice).toHaveLength(2); + + const typedArray = new T(3); + typedArray[0] = 1n; + typedArray[1] = 2n; + typedArray[2] = 3n; + + const first_slice = typedArray.slice(1, 2); + expect(first_slice).toHaveLength(1); + expect(first_slice[0]).toBe(2n); + + const second_slice = typedArray.slice(3, 1); + expect(second_slice).toHaveLength(0); + }); +});