Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibJS: Add %TypedArray%.prototype.with #14644

Merged
merged 2 commits into from Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 64 additions & 0 deletions Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp
Expand Up @@ -60,6 +60,7 @@ void TypedArrayPrototype::initialize(GlobalObject& 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);
define_native_function(vm.names.values, values, 0, attr);

define_native_accessor(*vm.well_known_symbol_to_string_tag(), to_string_tag_getter, nullptr, Attribute::Configurable);
Expand Down Expand Up @@ -1715,6 +1716,69 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::to_spliced)
return return_array;
}

// 1.2.2.1.6 %TypedArray%.prototype.with ( index, value ), https://tc39.es/proposal-change-array-by-copy/#sec-%typedarray%.prototype.with
JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::with)
{
auto index = vm.argument(0);
linusg marked this conversation as resolved.
Show resolved Hide resolved
auto value = vm.argument(1);

// 1. Let O be the this value.
// 2. Perform ? ValidateTypedArray(O).
auto* typed_array = TRY(validate_typed_array_from_this(global_object));

// 3. Let len be O.[[ArrayLength]].
auto length = typed_array->array_length();

// 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
auto relative_index = TRY(index.to_integer_or_infinity(global_object));

double actual_index = 0;
// 5. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
if (relative_index >= 0)
actual_index = relative_index;
else
actual_index = length + relative_index;

// 7. If O.[[ContentType]] is BigInt, set value to ? ToBigInt(value).
if (typed_array->content_type() == TypedArrayBase::ContentType::BigInt)
value = TRY(value.to_bigint(global_object));
// 8. Else, set value to ? ToNumber(value).
else
value = TRY(value.to_number(global_object));

// 9. If ! IsValidIntegerIndex(O, 𝔽(actualIndex)) is false, throw a RangeError exception.
if (!is_valid_integer_index(*typed_array, CanonicalIndex(CanonicalIndex::Type::Index, actual_index)))
return vm.throw_completion<RangeError>(global_object, ErrorType::InvalidIndex);

// 10. Let A be ? TypedArrayCreateSameType(O, « 𝔽(len) »).
MarkedVector<Value> arguments(vm.heap());
arguments.empend(length);
auto* return_array = TRY(typed_array_create_same_type(global_object, *typed_array, move(arguments)));

// 11. Let k be 0.
// 12. 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 == actual_index)
from_value = value;
// c. Else, let fromValue be ! Get(O, Pk).
else
from_value = MUST(typed_array->get(property_key));

// d. Perform ! Set(A, Pk, fromValue, true).
MUST(return_array->set(property_key, from_value, Object::ShouldThrowExceptions::Yes));

// e. Set k to k + 1.
}

// 13. Return A.
return return_array;
}

// 23.2.3.33 %TypedArray%.prototype.values ( ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.values
JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::values)
{
Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h
Expand Up @@ -54,6 +54,7 @@ class TypedArrayPrototype final : public Object {
JS_DECLARE_NATIVE_FUNCTION(to_reversed);
JS_DECLARE_NATIVE_FUNCTION(to_sorted);
JS_DECLARE_NATIVE_FUNCTION(to_spliced);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(values);
JS_DECLARE_NATIVE_FUNCTION(to_string_tag_getter);
};
Expand Down
@@ -0,0 +1,59 @@
const TYPED_ARRAYS = [
Uint8Array,
Uint8ClampedArray,
Uint16Array,
Uint32Array,
Int8Array,
Int16Array,
Int32Array,
Float32Array,
Float64Array,
];

const BIGINT_TYPED_ARRAYS = [BigUint64Array, BigInt64Array];

describe("normal behavior", () => {
test("length is 2", () => {
TYPED_ARRAYS.forEach(T => {
expect(T.prototype.with).toHaveLength(2);
});

BIGINT_TYPED_ARRAYS.forEach(T => {
expect(T.prototype.with).toHaveLength(2);
});
});

test("basic functionality", () => {
TYPED_ARRAYS.forEach(T => {
const a = new T([1, 2, 3, 4, 5]);
const values = [
[0, 10, [10, 2, 3, 4, 5]],
[-5, 10, [10, 2, 3, 4, 5]],
[4, 10, [1, 2, 3, 4, 10]],
[-1, 10, [1, 2, 3, 4, 10]],
];
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);
}
});

BIGINT_TYPED_ARRAYS.forEach(T => {
const a = new T([1n, 2n, 3n, 4n, 5n]);
const values = [
[0, 10n, [10n, 2n, 3n, 4n, 5n]],
[-5, 10n, [10n, 2n, 3n, 4n, 5n]],
[4, 10n, [1n, 2n, 3n, 4n, 10n]],
[-1, 10n, [1n, 2n, 3n, 4n, 10n]],
];
for (const [index, value, expected] of values) {
const b = a.with(index, value);
expect(a).not.toBe(b);
expect(a).toEqual([1n, 2n, 3n, 4n, 5n]);
expect(b).toEqual(expected);
}
});
});
});