LibJS: Implement Array.prototype.with()
This commit is contained in:
parent
ae81793f20
commit
698062b831
Notes:
sideshowbarker
2024-07-17 10:16:56 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/698062b831 Pull-request: https://github.com/SerenityOS/serenity/pull/14270 Reviewed-by: https://github.com/Lubrsi Reviewed-by: https://github.com/davidot ✅
4 changed files with 110 additions and 0 deletions
|
@ -78,6 +78,7 @@ void ArrayPrototype::initialize(GlobalObject& global_object)
|
|||
define_native_function(vm.names.toReversed, to_reversed, 0, attr);
|
||||
define_native_function(vm.names.toSorted, to_sorted, 1, attr);
|
||||
define_native_function(vm.names.toSpliced, to_spliced, 2, attr);
|
||||
define_native_function(vm.names.with, with, 2, attr);
|
||||
|
||||
// Use define_direct_property here instead of define_native_function so that
|
||||
// Object.is(Array.prototype[Symbol.iterator], Array.prototype.values)
|
||||
|
@ -1991,4 +1992,60 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_spliced)
|
|||
return array;
|
||||
}
|
||||
|
||||
// 1.1.1.7 Array.prototype.with ( index, value ), https://tc39.es/proposal-change-array-by-copy/#sec-array.prototype.with
|
||||
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::with)
|
||||
{
|
||||
auto index = vm.argument(0);
|
||||
auto value = vm.argument(1);
|
||||
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
auto* object = TRY(vm.this_value(global_object).to_object(global_object));
|
||||
|
||||
// 2. Let len be ? LengthOfArrayLike(O).
|
||||
auto length = TRY(length_of_array_like(global_object, *object));
|
||||
|
||||
// 3. Let relativeIndex be ? ToIntegerOrInfinity(index).
|
||||
auto relative_index = TRY(index.to_integer_or_infinity(global_object));
|
||||
|
||||
double actual_index;
|
||||
|
||||
// 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
|
||||
if (relative_index >= 0)
|
||||
actual_index = relative_index;
|
||||
// 5. Else, let actualIndex be len + relativeIndex.
|
||||
else
|
||||
actual_index = static_cast<double>(length) + relative_index;
|
||||
|
||||
// 6. If actualIndex ≥ len or actualIndex < 0, throw a RangeError exception.
|
||||
if (actual_index >= static_cast<double>(length) || actual_index < 0)
|
||||
return vm.throw_completion<RangeError>(global_object, ErrorType::IndexOutOfRange, actual_index, length);
|
||||
|
||||
// 7. Let A be ? ArrayCreate(𝔽(len)).
|
||||
auto* array = TRY(Array::create(global_object, length));
|
||||
|
||||
// 8. Let k be 0.
|
||||
// 9. Repeat, while k < len,
|
||||
for (size_t k = 0; k < length; ++k) {
|
||||
// a. Let Pk be ! ToString(𝔽(k)).
|
||||
auto property_key = PropertyKey { k };
|
||||
|
||||
Value from_value;
|
||||
|
||||
// b. If k is actualIndex, let fromValue be value.
|
||||
if (k == static_cast<size_t>(actual_index))
|
||||
from_value = value;
|
||||
// c. Else, let fromValue be ? Get(O, Pk).
|
||||
else
|
||||
from_value = TRY(object->get(property_key));
|
||||
|
||||
// d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
|
||||
MUST(array->create_data_property_or_throw(property_key, from_value));
|
||||
|
||||
// e. Set k to k + 1.
|
||||
}
|
||||
|
||||
// 10. Return A.
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(to_reversed);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_sorted);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_spliced);
|
||||
JS_DECLARE_NATIVE_FUNCTION(with);
|
||||
};
|
||||
|
||||
ThrowCompletionOr<void> array_merge_sort(GlobalObject&, FunctionObject* compare_func, MarkedVector<Value>& arr_to_sort);
|
||||
|
|
|
@ -360,4 +360,10 @@ describe("ability to work with generic non-array objects", () => {
|
|||
expect(result).toEqual(["foo", "hello", "friends", "baz", undefined]);
|
||||
expect(result).not.toBe(o);
|
||||
});
|
||||
|
||||
test("with", () => {
|
||||
const result = Array.prototype.with.call(o, 2, "hello");
|
||||
expect(result).toEqual(["foo", "bar", "hello", "baz", undefined]);
|
||||
expect(result).not.toBe(o);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
describe("normal behavior", () => {
|
||||
test("length is 2", () => {
|
||||
expect(Array.prototype.with).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const a = [1, 2, 3, 4, 5];
|
||||
const values = [
|
||||
[0, "foo", ["foo", 2, 3, 4, 5]],
|
||||
[-5, "foo", ["foo", 2, 3, 4, 5]],
|
||||
[4, "foo", [1, 2, 3, 4, "foo"]],
|
||||
[-1, "foo", [1, 2, 3, 4, "foo"]],
|
||||
];
|
||||
for (const [index, value, expected] of values) {
|
||||
const b = a.with(index, value);
|
||||
expect(a).not.toBe(b);
|
||||
expect(a).toEqual([1, 2, 3, 4, 5]);
|
||||
expect(b).toEqual(expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("null or undefined this value", () => {
|
||||
expect(() => {
|
||||
Array.prototype.with.call();
|
||||
}).toThrowWithMessage(TypeError, "ToObject on null or undefined");
|
||||
|
||||
expect(() => {
|
||||
Array.prototype.with.call(undefined);
|
||||
}).toThrowWithMessage(TypeError, "ToObject on null or undefined");
|
||||
|
||||
expect(() => {
|
||||
Array.prototype.with.call(null);
|
||||
}).toThrowWithMessage(TypeError, "ToObject on null or undefined");
|
||||
});
|
||||
|
||||
test("out of range index", () => {
|
||||
expect(() => {
|
||||
[].with(0, "foo");
|
||||
}).toThrowWithMessage(RangeError, "Index 0 is out of range of array length 0");
|
||||
expect(() => {
|
||||
[].with(-1, "foo");
|
||||
}).toThrowWithMessage(RangeError, "Index -1 is out of range of array length 0");
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue