summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS
diff options
context:
space:
mode:
authorLuke <luke.wilde@live.co.uk>2021-06-18 03:56:57 +0100
committerLinus Groh <mail@linusgroh.de>2021-06-18 18:13:31 +0100
commit65ca2d98af927357bc81c08abdb173179b2acec9 (patch)
treec71a2a506496106e8b28f10e2f72263eac8e4a82 /Userland/Libraries/LibJS
parentde8aa1b17d25eeeb62da9478ebc4a68354228e5b (diff)
downloadserenity-65ca2d98af927357bc81c08abdb173179b2acec9.zip
LibJS: Add %TypedArray%.prototype.every
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorTypes.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp58
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h1
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.every.js70
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();
+ });
+});