mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibJS: Fill all available space when TA.copyWithin shrinks the array
This is a normative change in the ECMA-262 spec. See: https://github.com/tc39/ecma262/commit/d228070p
This commit is contained in:
parent
a7e55f3024
commit
400300945f
|
|
@ -231,35 +231,35 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
// 4. Let relativeTarget be ? ToIntegerOrInfinity(target).
|
// 4. Let relativeTarget be ? ToIntegerOrInfinity(target).
|
||||||
auto relative_target = TRY(target.to_integer_or_infinity(vm));
|
auto relative_target = TRY(target.to_integer_or_infinity(vm));
|
||||||
|
|
||||||
double to;
|
double target_index;
|
||||||
// 5. If relativeTarget = -∞, let to be 0.
|
// 5. If relativeTarget = -∞, let targetIndex be 0.
|
||||||
if (Value { relative_target }.is_negative_infinity()) {
|
if (Value { relative_target }.is_negative_infinity()) {
|
||||||
to = 0.0;
|
target_index = 0.0;
|
||||||
}
|
}
|
||||||
// 6. Else if relativeTarget < 0, let to be max(len + relativeTarget, 0).
|
// 6. Else if relativeTarget < 0, let targetIndex be max(len + relativeTarget, 0).
|
||||||
else if (relative_target < 0) {
|
else if (relative_target < 0) {
|
||||||
to = max(length + relative_target, 0.0);
|
target_index = max(length + relative_target, 0.0);
|
||||||
}
|
}
|
||||||
// 7. Else, let to be min(relativeTarget, len).
|
// 7. Else, let targetIndex be min(relativeTarget, len).
|
||||||
else {
|
else {
|
||||||
to = min(relative_target, (double)length);
|
target_index = min(relative_target, (double)length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. Let relativeStart be ? ToIntegerOrInfinity(start).
|
// 8. Let relativeStart be ? ToIntegerOrInfinity(start).
|
||||||
auto relative_start = TRY(start.to_integer_or_infinity(vm));
|
auto relative_start = TRY(start.to_integer_or_infinity(vm));
|
||||||
|
|
||||||
double from;
|
double start_index;
|
||||||
// 9. If relativeStart = -∞, let from be 0.
|
// 9. If relativeStart = -∞, let startIndex be 0.
|
||||||
if (Value { relative_start }.is_negative_infinity()) {
|
if (Value { relative_start }.is_negative_infinity()) {
|
||||||
from = 0.0;
|
start_index = 0.0;
|
||||||
}
|
}
|
||||||
// 10. Else if relativeStart < 0, let from be max(len + relativeStart, 0).
|
// 10. Else if relativeStart < 0, let startIndex be max(len + relativeStart, 0).
|
||||||
else if (relative_start < 0) {
|
else if (relative_start < 0) {
|
||||||
from = max(length + relative_start, 0.0);
|
start_index = max(length + relative_start, 0.0);
|
||||||
}
|
}
|
||||||
// 11. Else, let from be min(relativeStart, len).
|
// 11. Else, let startIndex be min(relativeStart, len).
|
||||||
else {
|
else {
|
||||||
from = min(relative_start, (double)length);
|
start_index = min(relative_start, (double)length);
|
||||||
}
|
}
|
||||||
|
|
||||||
double relative_end;
|
double relative_end;
|
||||||
|
|
@ -269,22 +269,22 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
else
|
else
|
||||||
relative_end = TRY(end.to_integer_or_infinity(vm));
|
relative_end = TRY(end.to_integer_or_infinity(vm));
|
||||||
|
|
||||||
double final;
|
double end_index;
|
||||||
// 13. If relativeEnd = -∞, let final be 0.
|
// 13. If relativeEnd = -∞, let endIndex be 0.
|
||||||
if (Value { relative_end }.is_negative_infinity()) {
|
if (Value { relative_end }.is_negative_infinity()) {
|
||||||
final = 0.0;
|
end_index = 0.0;
|
||||||
}
|
}
|
||||||
// 14. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).
|
// 14. Else if relativeEnd < 0, let endIndex be max(len + relativeEnd, 0).
|
||||||
else if (relative_end < 0) {
|
else if (relative_end < 0) {
|
||||||
final = max(length + relative_end, 0.0);
|
end_index = max(length + relative_end, 0.0);
|
||||||
}
|
}
|
||||||
// 15. Else, let final be min(relativeEnd, len).
|
// 15. Else, let endIndex be min(relativeEnd, len).
|
||||||
else {
|
else {
|
||||||
final = min(relative_end, (double)length);
|
end_index = min(relative_end, (double)length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 16. Let count be min(final - from, len - to).
|
// 16. Let count be min(endIndex - startIndex, len - targetIndex).
|
||||||
double count = min(final - from, length - to);
|
double count = min(end_index - start_index, length - target_index);
|
||||||
|
|
||||||
// 17. If count > 0, then
|
// 17. If count > 0, then
|
||||||
if (count > 0.0) {
|
if (count > 0.0) {
|
||||||
|
|
@ -293,7 +293,7 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
// b. Let buffer be O.[[ViewedArrayBuffer]].
|
// b. Let buffer be O.[[ViewedArrayBuffer]].
|
||||||
auto* buffer = typed_array->viewed_array_buffer();
|
auto* buffer = typed_array->viewed_array_buffer();
|
||||||
|
|
||||||
// c. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).
|
// c. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, SEQ-CST).
|
||||||
typed_array_record = make_typed_array_with_buffer_witness_record(*typed_array, ArrayBuffer::Order::SeqCst);
|
typed_array_record = make_typed_array_with_buffer_witness_record(*typed_array, ArrayBuffer::Order::SeqCst);
|
||||||
|
|
||||||
// d. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
|
// d. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
|
||||||
|
|
@ -303,25 +303,22 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
// e. Set len to TypedArrayLength(taRecord).
|
// e. Set len to TypedArrayLength(taRecord).
|
||||||
length = typed_array_length(typed_array_record);
|
length = typed_array_length(typed_array_record);
|
||||||
|
|
||||||
// f. Let elementSize be TypedArrayElementSize(O).
|
// f. NOTE: Side-effects of the above steps may have reduced the size of O, in which case copying should proceed
|
||||||
|
// with the longest still-applicable prefix.
|
||||||
|
|
||||||
|
// g. Set count to min(count, len - startIndex, len - targetIndex).
|
||||||
|
count = min(count, min(length - start_index, length - target_index));
|
||||||
|
|
||||||
|
// h. Let elementSize be TypedArrayElementSize(O).
|
||||||
auto element_size = typed_array->element_size();
|
auto element_size = typed_array->element_size();
|
||||||
|
|
||||||
// g. Let byteOffset be O.[[ByteOffset]].
|
// i. Let byteOffset be O.[[ByteOffset]].
|
||||||
auto byte_offset = typed_array->byte_offset();
|
auto byte_offset = typed_array->byte_offset();
|
||||||
|
|
||||||
// FIXME: Not exactly sure what we should do when overflow occurs. Just return as if succeeded for now.
|
// FIXME: Not exactly sure what we should do when overflow occurs. Just return as if succeeded for now.
|
||||||
|
|
||||||
// h. Let bufferByteLimit be len × elementSize + byteOffset.
|
// j. Let toByteIndex be (targetIndex × elementSize) + byteOffset.
|
||||||
Checked<size_t> buffer_byte_limit_checked = static_cast<size_t>(length);
|
Checked<size_t> to_byte_index_checked = static_cast<size_t>(target_index);
|
||||||
buffer_byte_limit_checked *= element_size;
|
|
||||||
buffer_byte_limit_checked += byte_offset;
|
|
||||||
if (buffer_byte_limit_checked.has_overflow()) {
|
|
||||||
dbgln("TypedArrayPrototype::copy_within: buffer_byte_limit overflowed, returning as if succeeded.");
|
|
||||||
return typed_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
// i. Let toByteIndex be to × elementSize + byteOffset.
|
|
||||||
Checked<size_t> to_byte_index_checked = static_cast<size_t>(to);
|
|
||||||
to_byte_index_checked *= element_size;
|
to_byte_index_checked *= element_size;
|
||||||
to_byte_index_checked += byte_offset;
|
to_byte_index_checked += byte_offset;
|
||||||
if (to_byte_index_checked.has_overflow()) {
|
if (to_byte_index_checked.has_overflow()) {
|
||||||
|
|
@ -329,8 +326,8 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
return typed_array;
|
return typed_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
// j. Let fromByteIndex be from × elementSize + byteOffset.
|
// k. Let fromByteIndex be (startIndex × elementSize) + byteOffset.
|
||||||
Checked<size_t> from_byte_index_checked = static_cast<size_t>(from);
|
Checked<size_t> from_byte_index_checked = static_cast<size_t>(start_index);
|
||||||
from_byte_index_checked *= element_size;
|
from_byte_index_checked *= element_size;
|
||||||
from_byte_index_checked += byte_offset;
|
from_byte_index_checked += byte_offset;
|
||||||
if (from_byte_index_checked.has_overflow()) {
|
if (from_byte_index_checked.has_overflow()) {
|
||||||
|
|
@ -338,7 +335,7 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
return typed_array;
|
return typed_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
// k. Let countBytes be count × elementSize.
|
// l. Let countBytes be count × elementSize.
|
||||||
Checked<size_t> count_bytes_checked = static_cast<size_t>(count);
|
Checked<size_t> count_bytes_checked = static_cast<size_t>(count);
|
||||||
count_bytes_checked *= element_size;
|
count_bytes_checked *= element_size;
|
||||||
if (count_bytes_checked.has_overflow()) {
|
if (count_bytes_checked.has_overflow()) {
|
||||||
|
|
@ -346,7 +343,6 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
return typed_array;
|
return typed_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto buffer_byte_limit = buffer_byte_limit_checked.value();
|
|
||||||
auto to_byte_index = to_byte_index_checked.value();
|
auto to_byte_index = to_byte_index_checked.value();
|
||||||
auto from_byte_index = from_byte_index_checked.value();
|
auto from_byte_index = from_byte_index_checked.value();
|
||||||
auto count_bytes = count_bytes_checked.value();
|
auto count_bytes = count_bytes_checked.value();
|
||||||
|
|
@ -362,13 +358,11 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
if (!buffer->is_shared_array_buffer()) {
|
if (!buffer->is_shared_array_buffer()) {
|
||||||
Checked<size_t> from_end = from_byte_index;
|
Checked<size_t> from_end = from_byte_index;
|
||||||
from_end += count_bytes;
|
from_end += count_bytes;
|
||||||
|
|
||||||
Checked<size_t> to_end = to_byte_index;
|
Checked<size_t> to_end = to_byte_index;
|
||||||
to_end += count_bytes;
|
to_end += count_bytes;
|
||||||
|
|
||||||
if (!from_end.has_overflow()
|
if (!from_end.has_overflow() && !to_end.has_overflow()) {
|
||||||
&& !to_end.has_overflow()
|
|
||||||
&& from_end.value() <= buffer_byte_limit
|
|
||||||
&& to_end.value() <= buffer_byte_limit) {
|
|
||||||
auto* base = buffer->buffer().data();
|
auto* base = buffer->buffer().data();
|
||||||
void const* src = base + from_byte_index;
|
void const* src = base + from_byte_index;
|
||||||
void* dst = base + to_byte_index;
|
void* dst = base + to_byte_index;
|
||||||
|
|
@ -383,6 +377,7 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
if (from_byte_index < to_byte_index && to_byte_index < from_plus_count.value()) {
|
if (from_byte_index < to_byte_index && to_byte_index < from_plus_count.value()) {
|
||||||
// i. Let direction be -1.
|
// i. Let direction be -1.
|
||||||
direction = -1;
|
direction = -1;
|
||||||
|
|
||||||
// ii. Set fromByteIndex to fromByteIndex + countBytes - 1.
|
// ii. Set fromByteIndex to fromByteIndex + countBytes - 1.
|
||||||
from_byte_index = from_plus_count.value() - 1;
|
from_byte_index = from_plus_count.value() - 1;
|
||||||
|
|
||||||
|
|
@ -402,30 +397,22 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::copy_within)
|
||||||
direction = 1;
|
direction = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// n. Repeat, while countBytes > 0,
|
// o. Repeat, while countBytes > 0,
|
||||||
while (count_bytes > 0) {
|
while (count_bytes > 0) {
|
||||||
// i. If fromByteIndex < bufferByteLimit and toByteIndex < bufferByteLimit, then
|
// i. Let value be GetValueFromBuffer(buffer, fromByteIndex, UINT8, true, UNORDERED).
|
||||||
if (from_byte_index < buffer_byte_limit && to_byte_index < buffer_byte_limit) {
|
auto value = buffer->get_value<u8>(from_byte_index, true, ArrayBuffer::Order::Unordered);
|
||||||
// 1. Let value be GetValueFromBuffer(buffer, fromByteIndex, uint8, true, unordered).
|
|
||||||
auto value = buffer->get_value<u8>(from_byte_index, true, ArrayBuffer::Order::Unordered);
|
|
||||||
|
|
||||||
// 2. Perform SetValueInBuffer(buffer, toByteIndex, uint8, value, true, unordered).
|
// ii. Perform SetValueInBuffer(buffer, toByteIndex, UINT8, value, true, UNORDERED).
|
||||||
buffer->set_value<u8>(to_byte_index, value, true, ArrayBuffer::Order::Unordered);
|
buffer->set_value<u8>(to_byte_index, value, true, ArrayBuffer::Order::Unordered);
|
||||||
|
|
||||||
// 3. Set fromByteIndex to fromByteIndex + direction.
|
// iii. Set fromByteIndex to fromByteIndex + direction.
|
||||||
from_byte_index += direction;
|
from_byte_index += direction;
|
||||||
|
|
||||||
// 4. Set toByteIndex to toByteIndex + direction.
|
// iv. Set toByteIndex to toByteIndex + direction.
|
||||||
to_byte_index += direction;
|
to_byte_index += direction;
|
||||||
|
|
||||||
// 5. Set countBytes to countBytes - 1.
|
// v. Set countBytes to countBytes - 1.
|
||||||
--count_bytes;
|
--count_bytes;
|
||||||
}
|
|
||||||
// ii. Else,
|
|
||||||
else {
|
|
||||||
// 1. Set countBytes to 0.
|
|
||||||
count_bytes = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,4 +133,26 @@ describe("normal behavior", () => {
|
||||||
expect(array).toEqual(new T([0n, 54n, 999n, 999n, 1000n, 0n]));
|
expect(array).toEqual(new T([0n, 54n, 999n, 999n, 1000n, 0n]));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("resizing during copyWithin", () => {
|
||||||
|
TYPED_ARRAYS.forEach(T => {
|
||||||
|
let arrayBuffer = new ArrayBuffer(T.BYTES_PER_ELEMENT * 4, {
|
||||||
|
maxByteLength: T.BYTES_PER_ELEMENT * 8,
|
||||||
|
});
|
||||||
|
|
||||||
|
let typedArray = new T(arrayBuffer);
|
||||||
|
typedArray[0] = 1;
|
||||||
|
typedArray[1] = 2;
|
||||||
|
typedArray[2] = 3;
|
||||||
|
typedArray[3] = 4;
|
||||||
|
|
||||||
|
const resize = () => {
|
||||||
|
arrayBuffer.resize(3 * T.BYTES_PER_ELEMENT);
|
||||||
|
return 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedArray.copyWithin({ valueOf: resize }, 1);
|
||||||
|
expect(typedArray).toEqual(new T([1, 2, 2]));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user