diff options
author | Luke <luke.wilde@live.co.uk> | 2021-06-18 03:56:57 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-06-18 18:13:31 +0100 |
commit | 65ca2d98af927357bc81c08abdb173179b2acec9 (patch) | |
tree | c71a2a506496106e8b28f10e2f72263eac8e4a82 /Userland/Libraries/LibJS | |
parent | de8aa1b17d25eeeb62da9478ebc4a68354228e5b (diff) | |
download | serenity-65ca2d98af927357bc81c08abdb173179b2acec9.zip |
LibJS: Add %TypedArray%.prototype.every
Diffstat (limited to 'Userland/Libraries/LibJS')
4 files changed, 130 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index 6c6b5fa374..cc18ee3e7c 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -156,6 +156,7 @@ M(TypedArrayInvalidByteOffset, "Invalid byte offset for {}: must be a multiple of {}, got {}") \ M(TypedArrayOutOfRangeByteOffset, "Typed array byte offset {} is out of range for buffer with length {}") \ M(TypedArrayOutOfRangeByteOffsetOrLength, "Typed array range {}:{} is out of range for buffer with length {}") \ + M(TypedArrayPrototypeOneArg, "TypedArray.prototype.{}() requires at least one argument") \ M(TypedArrayFailedSettingIndex, "Failed setting value of index {} of typed array") \ M(UnknownIdentifier, "'{}' is not defined") \ M(URIMalformed, "URI malformed") \ diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index fa4f04bf8e..598339b1ec 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -26,6 +26,7 @@ void TypedArrayPrototype::initialize(GlobalObject& object) 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_function(vm.names.at, at, 1, attr); + define_native_function(vm.names.every, every, 1, attr); } TypedArrayPrototype::~TypedArrayPrototype() @@ -44,6 +45,49 @@ static TypedArrayBase* typed_array_from(VM& vm, GlobalObject& global_object) return static_cast<TypedArrayBase*>(this_object); } +static Function* callback_from_args(GlobalObject& global_object, const String& name) +{ + auto& vm = global_object.vm(); + if (vm.argument_count() < 1) { + vm.throw_exception<TypeError>(global_object, ErrorType::TypedArrayPrototypeOneArg, name); + return nullptr; + } + auto callback = vm.argument(0); + if (!callback.is_function()) { + vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects()); + return nullptr; + } + return &callback.as_function(); +} + +static void for_each_item(VM& vm, GlobalObject& global_object, const String& name, AK::Function<IterationDecision(size_t index, Value value, Value callback_result)> callback) +{ + auto* typed_array = typed_array_from(vm, global_object); + if (!typed_array) + return; + + auto initial_length = typed_array->array_length(); + + auto* callback_function = callback_from_args(global_object, name); + if (!callback_function) + return; + + auto this_value = vm.argument(1); + + for (size_t i = 0; i < initial_length; ++i) { + auto value = typed_array->get(i); + if (vm.exception()) + return; + + auto callback_result = vm.call(*callback_function, this_value, value, Value((i32)i), typed_array); + if (vm.exception()) + return; + + if (callback(i, value, callback_result) == IterationDecision::Break) + break; + } +} + // 23.2.3.18 get %TypedArray%.prototype.length, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length JS_DEFINE_NATIVE_GETTER(TypedArrayPrototype::length_getter) { @@ -81,6 +125,20 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::at) return typed_array->get(index.value()); } +// 23.2.3.7 %TypedArray%.prototype.every ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.every +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::every) +{ + auto result = true; + for_each_item(vm, global_object, "every", [&](auto, auto, auto callback_result) { + if (!callback_result.to_boolean()) { + result = false; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + return Value(result); +} + // 23.2.3.1 get %TypedArray%.prototype.buffer, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer JS_DEFINE_NATIVE_GETTER(TypedArrayPrototype::buffer_getter) { diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h index 6bc3aad1ce..f0e225077f 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h @@ -25,6 +25,7 @@ private: JS_DECLARE_NATIVE_GETTER(byte_offset_getter); JS_DECLARE_NATIVE_FUNCTION(at); + JS_DECLARE_NATIVE_FUNCTION(every); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.every.js b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.every.js new file mode 100644 index 0000000000..b2c4d7d123 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.every.js @@ -0,0 +1,70 @@ +const TYPED_ARRAYS = [ + Uint8Array, + Uint16Array, + Uint32Array, + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, +]; + +const BIGINT_TYPED_ARRAYS = [BigUint64Array, BigInt64Array]; + +test("length is 1", () => { + TYPED_ARRAYS.forEach(T => { + expect(T.prototype.every).toHaveLength(1); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + expect(T.prototype.every).toHaveLength(1); + }); +}); + +describe("errors", () => { + function errorTests(T) { + test(`requires at least one argument (${T.name})`, () => { + expect(() => { + new T().every(); + }).toThrowWithMessage( + TypeError, + "TypedArray.prototype.every() requires at least one argument" + ); + }); + + test(`callback must be a function (${T.name})`, () => { + expect(() => { + new T().every(undefined); + }).toThrowWithMessage(TypeError, "undefined is not a function"); + }); + } + + TYPED_ARRAYS.forEach(T => errorTests(T)); + BIGINT_TYPED_ARRAYS.forEach(T => errorTests(T)); +}); + +test("basic functionality", () => { + TYPED_ARRAYS.forEach(T => { + const typedArray = new T([2, 4, 6]); + expect(typedArray.every(value => value === 2)).toBeFalse(); + expect(typedArray.every(value => value === 4)).toBeFalse(); + expect(typedArray.every(value => value === 6)).toBeFalse(); + expect(typedArray.every(value => value % 2 === 0)).toBeTrue(); + expect(typedArray.every(value => value % 2 === 1)).toBeFalse(); + expect(typedArray.every(value => value < 2)).toBeFalse(); + expect(typedArray.every(value => value > 2)).toBeFalse(); + expect(typedArray.every(value => value >= 2)).toBeTrue(); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + const typedArray = new T([2n, 4n, 6n]); + expect(typedArray.every(value => value === 2n)).toBeFalse(); + expect(typedArray.every(value => value === 4n)).toBeFalse(); + expect(typedArray.every(value => value === 6n)).toBeFalse(); + expect(typedArray.every(value => value % 2n === 0n)).toBeTrue(); + expect(typedArray.every(value => value % 2n === 1n)).toBeFalse(); + expect(typedArray.every(value => value < 2n)).toBeFalse(); + expect(typedArray.every(value => value > 2n)).toBeFalse(); + expect(typedArray.every(value => value >= 2n)).toBeTrue(); + }); +}); |