mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:37:35 +00:00
LibJS: Implement 'Relative Indexing Method' proposal (.at())
Still stage 3, but already implemented in major engines and unlikely to change - there isn't much to change here anyway. :^) See: - https://github.com/tc39/proposal-relative-indexing-method - https://tc39.es/proposal-relative-indexing-method/ - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at
This commit is contained in:
parent
9769542bc2
commit
2d8362cceb
10 changed files with 155 additions and 10 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||||
* Copyright (c) 2020, Marcin Gasperowicz <xnooga@gmail.com>
|
* Copyright (c) 2020, Marcin Gasperowicz <xnooga@gmail.com>
|
||||||
* All rights reserved.
|
* 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.fill, fill, 1, attr);
|
||||||
define_native_function(vm.names.values, values, 0, attr);
|
define_native_function(vm.names.values, values, 0, attr);
|
||||||
define_native_function(vm.names.flat, flat, 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
|
// Use define_property here instead of define_native_function so that
|
||||||
// Object.is(Array.prototype[Symbol.iterator], Array.prototype.values)
|
// Object.is(Array.prototype[Symbol.iterator], Array.prototype.values)
|
||||||
|
@ -1081,4 +1082,30 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::flat)
|
||||||
return {};
|
return {};
|
||||||
return new_array;
|
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<size_t> 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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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(fill);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(values);
|
JS_DECLARE_NATIVE_FUNCTION(values);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(flat);
|
JS_DECLARE_NATIVE_FUNCTION(flat);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(at);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ namespace JS {
|
||||||
P(asUintN) \
|
P(asUintN) \
|
||||||
P(asin) \
|
P(asin) \
|
||||||
P(asinh) \
|
P(asinh) \
|
||||||
|
P(at) \
|
||||||
P(atan) \
|
P(atan) \
|
||||||
P(atan2) \
|
P(atan2) \
|
||||||
P(atanh) \
|
P(atanh) \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/Checked.h>
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibJS/Heap/Heap.h>
|
#include <LibJS/Heap/Heap.h>
|
||||||
|
@ -82,7 +83,7 @@ void StringPrototype::initialize(GlobalObject& global_object)
|
||||||
StringObject::initialize(global_object);
|
StringObject::initialize(global_object);
|
||||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
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.charAt, char_at, 1, attr);
|
||||||
define_native_function(vm.names.charCodeAt, char_code_at, 1, attr);
|
define_native_function(vm.names.charCodeAt, char_code_at, 1, attr);
|
||||||
define_native_function(vm.names.repeat, repeat, 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.slice, slice, 2, attr);
|
||||||
define_native_function(vm.names.split, split, 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.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);
|
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);
|
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<size_t> 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)
|
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator)
|
||||||
{
|
{
|
||||||
auto this_object = vm.this_value(global_object);
|
auto this_object = vm.this_value(global_object);
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
virtual ~StringPrototype() override;
|
virtual ~StringPrototype() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
JS_DECLARE_NATIVE_GETTER(length_getter);
|
||||||
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(char_at);
|
JS_DECLARE_NATIVE_FUNCTION(char_at);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(char_code_at);
|
JS_DECLARE_NATIVE_FUNCTION(char_code_at);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(repeat);
|
JS_DECLARE_NATIVE_FUNCTION(repeat);
|
||||||
|
@ -52,9 +54,6 @@ private:
|
||||||
JS_DECLARE_NATIVE_FUNCTION(pad_end);
|
JS_DECLARE_NATIVE_FUNCTION(pad_end);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(substring);
|
JS_DECLARE_NATIVE_FUNCTION(substring);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(substr);
|
JS_DECLARE_NATIVE_FUNCTION(substr);
|
||||||
|
|
||||||
JS_DECLARE_NATIVE_GETTER(length_getter);
|
|
||||||
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(trim);
|
JS_DECLARE_NATIVE_FUNCTION(trim);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(trim_start);
|
JS_DECLARE_NATIVE_FUNCTION(trim_start);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(trim_end);
|
JS_DECLARE_NATIVE_FUNCTION(trim_end);
|
||||||
|
@ -63,6 +62,7 @@ private:
|
||||||
JS_DECLARE_NATIVE_FUNCTION(slice);
|
JS_DECLARE_NATIVE_FUNCTION(slice);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(split);
|
JS_DECLARE_NATIVE_FUNCTION(split);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(last_index_of);
|
JS_DECLARE_NATIVE_FUNCTION(last_index_of);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(at);
|
||||||
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
|
JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -39,8 +39,11 @@ void TypedArrayPrototype::initialize(GlobalObject& object)
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
Object::initialize(object);
|
Object::initialize(object);
|
||||||
|
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||||
|
|
||||||
// FIXME: This should be an accessor property
|
// 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()
|
TypedArrayPrototype::~TypedArrayPrototype()
|
||||||
|
@ -67,4 +70,27 @@ JS_DEFINE_NATIVE_GETTER(TypedArrayPrototype::length_getter)
|
||||||
return Value(typed_array->array_length());
|
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<size_t> 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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -40,6 +40,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JS_DECLARE_NATIVE_GETTER(length_getter);
|
JS_DECLARE_NATIVE_GETTER(length_getter);
|
||||||
|
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(at);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
});
|
|
@ -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();
|
||||||
|
});
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue