diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp index d4aba04f20..fb8ea48c16 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2020, Linus Groh + * Copyright (c) 2020-2021, Linus Groh * Copyright (c) 2020, Marcin Gasperowicz * All rights reserved. * @@ -81,6 +81,7 @@ void ArrayPrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.fill, fill, 1, attr); define_native_function(vm.names.values, values, 0, attr); define_native_function(vm.names.flat, flat, 0, attr); + define_native_function(vm.names.at, at, 1, attr); // Use define_property here instead of define_native_function so that // Object.is(Array.prototype[Symbol.iterator], Array.prototype.values) @@ -1081,4 +1082,30 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::flat) return {}; return new_array; } + +JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::at) +{ + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) + return {}; + auto length = length_of_array_like(global_object, *this_object); + if (vm.exception()) + return {}; + auto relative_index = vm.argument(0).to_integer_or_infinity(global_object); + if (vm.exception()) + return {}; + if (Value(relative_index).is_infinity()) + return js_undefined(); + Checked index { 0 }; + if (relative_index >= 0) { + index += relative_index; + } else { + index += length; + index -= -relative_index; + } + if (index.has_overflow() || index.value() >= length) + return js_undefined(); + return this_object->get(index.value()); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h index 906e0af39a..e7ab69afd2 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2020, Linus Groh + * Copyright (c) 2020-2021, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,6 +67,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(fill); JS_DECLARE_NATIVE_FUNCTION(values); JS_DECLARE_NATIVE_FUNCTION(flat); + JS_DECLARE_NATIVE_FUNCTION(at); }; } diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index af471bab5e..691939286d 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -68,6 +68,7 @@ namespace JS { P(asUintN) \ P(asin) \ P(asinh) \ + P(at) \ P(atan) \ P(atan2) \ P(atanh) \ diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp index baeeda438e..fa04e21a2c 100644 --- a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling - * Copyright (c) 2020, Linus Groh + * Copyright (c) 2020-2021, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,6 +25,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include @@ -82,7 +83,7 @@ void StringPrototype::initialize(GlobalObject& global_object) StringObject::initialize(global_object); u8 attr = Attribute::Writable | Attribute::Configurable; - define_native_property(vm.names.length, length_getter, {}, 0); + define_native_property(vm.names.length, length_getter, nullptr, 0); define_native_function(vm.names.charAt, char_at, 1, attr); define_native_function(vm.names.charCodeAt, char_code_at, 1, attr); define_native_function(vm.names.repeat, repeat, 1, attr); @@ -104,6 +105,7 @@ void StringPrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.slice, slice, 2, attr); define_native_function(vm.names.split, split, 2, attr); define_native_function(vm.names.lastIndexOf, last_index_of, 1, attr); + define_native_function(vm.names.at, at, 1, attr); define_native_function(vm.well_known_symbol_iterator(), symbol_iterator, 0, attr); } @@ -612,6 +614,29 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::last_index_of) return Value(-1); } +JS_DEFINE_NATIVE_FUNCTION(StringPrototype::at) +{ + auto string = ak_string_from(vm, global_object); + if (string.is_null()) + return {}; + auto length = string.length(); + auto relative_index = vm.argument(0).to_integer_or_infinity(global_object); + if (vm.exception()) + return {}; + if (Value(relative_index).is_infinity()) + return js_undefined(); + Checked index { 0 }; + if (relative_index >= 0) { + index += relative_index; + } else { + index += length; + index -= -relative_index; + } + if (index.has_overflow() || index.value() >= length) + return js_undefined(); + return js_string(vm, String::formatted("{}", string[index.value()])); +} + JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator) { auto this_object = vm.this_value(global_object); diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.h b/Userland/Libraries/LibJS/Runtime/StringPrototype.h index 5847ea2117..d7be6e1f16 100644 --- a/Userland/Libraries/LibJS/Runtime/StringPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.h @@ -39,6 +39,8 @@ public: virtual ~StringPrototype() override; private: + JS_DECLARE_NATIVE_GETTER(length_getter); + JS_DECLARE_NATIVE_FUNCTION(char_at); JS_DECLARE_NATIVE_FUNCTION(char_code_at); JS_DECLARE_NATIVE_FUNCTION(repeat); @@ -52,9 +54,6 @@ private: JS_DECLARE_NATIVE_FUNCTION(pad_end); JS_DECLARE_NATIVE_FUNCTION(substring); JS_DECLARE_NATIVE_FUNCTION(substr); - - JS_DECLARE_NATIVE_GETTER(length_getter); - JS_DECLARE_NATIVE_FUNCTION(trim); JS_DECLARE_NATIVE_FUNCTION(trim_start); JS_DECLARE_NATIVE_FUNCTION(trim_end); @@ -63,6 +62,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(slice); JS_DECLARE_NATIVE_FUNCTION(split); JS_DECLARE_NATIVE_FUNCTION(last_index_of); + JS_DECLARE_NATIVE_FUNCTION(at); JS_DECLARE_NATIVE_FUNCTION(symbol_iterator); }; diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index da0c43faf1..be226b3d68 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Linus Groh + * Copyright (c) 2020-2021, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,8 +39,11 @@ void TypedArrayPrototype::initialize(GlobalObject& object) { auto& vm = this->vm(); Object::initialize(object); + u8 attr = Attribute::Writable | Attribute::Configurable; + // FIXME: This should be an accessor property - define_native_property(vm.names.length, length_getter, {}, Attribute::Configurable); + define_native_property(vm.names.length, length_getter, nullptr, Attribute::Configurable); + define_native_function(vm.names.at, at, 1, attr); } TypedArrayPrototype::~TypedArrayPrototype() @@ -67,4 +70,27 @@ JS_DEFINE_NATIVE_GETTER(TypedArrayPrototype::length_getter) return Value(typed_array->array_length()); } +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::at) +{ + auto typed_array = typed_array_from(vm, global_object); + if (!typed_array) + return {}; + auto length = typed_array->array_length(); + auto relative_index = vm.argument(0).to_integer_or_infinity(global_object); + if (vm.exception()) + return {}; + if (Value(relative_index).is_infinity()) + return js_undefined(); + Checked index { 0 }; + if (relative_index >= 0) { + index += relative_index; + } else { + index += length; + index -= -relative_index; + } + if (index.has_overflow() || index.value() >= length) + return js_undefined(); + return typed_array->get(index.value()); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h index 8c39f31c31..88820344a0 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Linus Groh + * Copyright (c) 2020-2021, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,6 +40,8 @@ public: private: JS_DECLARE_NATIVE_GETTER(length_getter); + + JS_DECLARE_NATIVE_FUNCTION(at); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.at.js b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.at.js new file mode 100644 index 0000000000..4636f2ccc5 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.at.js @@ -0,0 +1,15 @@ +test("basic functionality", () => { + expect(Array.prototype.at).toHaveLength(1); + + const array = ["a", "b", "c"]; + expect(array.at(0)).toBe("a"); + expect(array.at(1)).toBe("b"); + expect(array.at(2)).toBe("c"); + expect(array.at(3)).toBeUndefined(); + expect(array.at(Infinity)).toBeUndefined(); + expect(array.at(-1)).toBe("c"); + expect(array.at(-2)).toBe("b"); + expect(array.at(-3)).toBe("a"); + expect(array.at(-4)).toBeUndefined(); + expect(array.at(-Infinity)).toBeUndefined(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.at.js b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.at.js new file mode 100644 index 0000000000..a551a96b17 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.at.js @@ -0,0 +1,15 @@ +test("basic functionality", () => { + expect(String.prototype.at).toHaveLength(1); + + const string = "abc"; + expect(string.at(0)).toBe("a"); + expect(string.at(1)).toBe("b"); + expect(string.at(2)).toBe("c"); + expect(string.at(3)).toBeUndefined(); + expect(string.at(Infinity)).toBeUndefined(); + expect(string.at(-1)).toBe("c"); + expect(string.at(-2)).toBe("b"); + expect(string.at(-3)).toBe("a"); + expect(string.at(-4)).toBeUndefined(); + expect(string.at(-Infinity)).toBeUndefined(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.at.js b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.at.js new file mode 100644 index 0000000000..fdf756f7ad --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.at.js @@ -0,0 +1,33 @@ +// Update when more typed arrays get added +const TYPED_ARRAYS = [ + Uint8Array, + Uint16Array, + Uint32Array, + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, +]; + +test("basic functionality", () => { + TYPED_ARRAYS.forEach(T => { + expect(T.prototype.at).toHaveLength(1); + + const typedArray = new T(3); + typedArray[0] = 1; + typedArray[1] = 2; + typedArray[2] = 3; + + expect(typedArray.at(0)).toBe(1); + expect(typedArray.at(1)).toBe(2); + expect(typedArray.at(2)).toBe(3); + expect(typedArray.at(3)).toBeUndefined(); + expect(typedArray.at(Infinity)).toBeUndefined(); + expect(typedArray.at(-1)).toBe(3); + expect(typedArray.at(-2)).toBe(2); + expect(typedArray.at(-3)).toBe(1); + expect(typedArray.at(-4)).toBeUndefined(); + expect(typedArray.at(-Infinity)).toBeUndefined(); + }); +});