Skip to content

Commit

Permalink
LibJS: Add spec comments to %TypedArray%.prototype.set
Browse files Browse the repository at this point in the history
  • Loading branch information
IdanHo authored and linusg committed Feb 8, 2022
1 parent 4263dad commit d225b5e
Showing 1 changed file with 138 additions and 36 deletions.
174 changes: 138 additions & 36 deletions Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2020-2021, Linus Groh <[email protected]>
* Copyright (c) 2021, Luke Wilde <[email protected]>
* Copyright (c) 2021, Idan Horowitz <[email protected]>
* Copyright (c) 2021-2022, Idan Horowitz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
Expand Down Expand Up @@ -610,124 +610,226 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::to_string_tag_getter)
// 23.2.3.24 %TypedArray%.prototype.set ( source [ , offset ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.set
JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::set)
{
auto source = vm.argument(0);

// 1. Let target be the this value.
// 2. Perform ? RequireInternalSlot(target, [[TypedArrayName]]).
auto* typed_array = TRY(typed_array_from_this(global_object));

auto source = vm.argument(0);
// 3. Assert: target has a [[ViewedArrayBuffer]] internal slot.

// 4. Let targetOffset be ? ToIntegerOrInfinity(offset).
auto target_offset = TRY(vm.argument(1).to_integer_or_infinity(global_object));

// 5. If targetOffset < 0, throw a RangeError exception.
if (target_offset < 0)
return vm.throw_completion<JS::RangeError>(global_object, "Invalid target offset");
return vm.throw_completion<RangeError>(global_object, "Invalid target offset");

// 6. If source is an Object that has a [[TypedArrayName]] internal slot, then
if (source.is_object() && is<TypedArrayBase>(source.as_object())) {
auto& source_typed_array = static_cast<TypedArrayBase&>(source.as_object());
// a. Perform ? SetTypedArrayFromTypedArray(target, targetOffset, source).

// 23.2.3.23.1 SetTypedArrayFromTypedArray ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray
auto target_buffer = typed_array->viewed_array_buffer();

auto& source_typed_array = static_cast<TypedArrayBase&>(source.as_object());

// 1. Let targetBuffer be target.[[ViewedArrayBuffer]].
auto* target_buffer = typed_array->viewed_array_buffer();

// 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
if (target_buffer->is_detached())
return vm.throw_completion<JS::TypeError>(global_object, ErrorType::DetachedArrayBuffer);
return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer);

// 3. Let targetLength be target.[[ArrayLength]].
auto target_length = typed_array->array_length();
auto target_byte_offset = typed_array->byte_offset();

auto source_buffer = source_typed_array.viewed_array_buffer();
// 4. Let srcBuffer be source.[[ViewedArrayBuffer]].
auto* source_buffer = source_typed_array.viewed_array_buffer();

// 5. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
if (source_buffer->is_detached())
return vm.throw_completion<JS::TypeError>(global_object, ErrorType::DetachedArrayBuffer);
return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer);

// 6. Let targetName be the String value of target.[[TypedArrayName]].
// 7. Let targetType be the Element Type value in Table 69 for targetName.
// 8. Let targetElementSize be the Element Size value specified in Table 69 for targetName.
auto target_element_size = typed_array->element_size();

// 9. Let targetByteOffset be target.[[ByteOffset]].
auto target_byte_offset = typed_array->byte_offset();

// 10. Let srcName be the String value of source.[[TypedArrayName]].
// 11. Let srcType be the Element Type value in Table 69 for srcName.
// 12. Let srcElementSize be the Element Size value specified in Table 69 for srcName.
auto source_element_size = source_typed_array.element_size();

// 13. Let srcLength be source.[[ArrayLength]].
auto source_length = source_typed_array.array_length();

// 14. Let srcByteOffset be source.[[ByteOffset]].
auto source_byte_offset = source_typed_array.byte_offset();

// 15. If targetOffset is +∞, throw a RangeError exception.
if (isinf(target_offset))
return vm.throw_completion<JS::RangeError>(global_object, "Invalid target offset");
return vm.throw_completion<RangeError>(global_object, "Invalid target offset");

// 16. If srcLength + targetOffset > targetLength, throw a RangeError exception.
Checked<size_t> checked = source_length;
checked += static_cast<u32>(target_offset);
if (checked.has_overflow() || checked.value() > target_length)
return vm.throw_completion<JS::RangeError>(global_object, "Overflow or out of bounds in target length");
return vm.throw_completion<RangeError>(global_object, "Overflow or out of bounds in target length");

// 17. If target.[[ContentType]] ≠ source.[[ContentType]], throw a TypeError exception.
if (typed_array->content_type() != source_typed_array.content_type())
return vm.throw_completion<JS::TypeError>(global_object, "Copy between arrays of different content types is prohibited");
return vm.throw_completion<TypeError>(global_object, "Copy between arrays of different content types is prohibited");

// FIXME: 18. If both IsSharedArrayBuffer(srcBuffer) and IsSharedArrayBuffer(targetBuffer) are true, then
// FIXME: a. If srcBuffer.[[ArrayBufferData]] and targetBuffer.[[ArrayBufferData]] are the same Shared Data Block values, let same be true; else let same be false.

// 19. Else, let same be SameValue(srcBuffer, targetBuffer).
auto same = same_value(source_buffer, target_buffer);

size_t source_byte_index;
bool same = false;
// FIXME: Step 19: If both IsSharedArrayBuffer(srcBuffer) and IsSharedArrayBuffer(targetBuffer) are true...
same = same_value(source_buffer, target_buffer);

// 20. If same is true, then
if (same) {
// FIXME: Implement this: Step 21
// a. Let srcByteLength be source.[[ByteLength]].
// b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%).
// c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects.
// d. Let srcByteIndex be 0.
TODO();
} else {
// 21. Else, let srcByteIndex be srcByteOffset.
source_byte_index = source_byte_offset;
}

// 22. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
Checked<size_t> checked_target_byte_index(static_cast<size_t>(target_offset));
checked_target_byte_index *= typed_array->element_size();
checked_target_byte_index *= target_element_size;
checked_target_byte_index += target_byte_offset;
if (checked_target_byte_index.has_overflow())
return vm.throw_completion<JS::RangeError>(global_object, "Overflow in target byte index");
return vm.throw_completion<RangeError>(global_object, "Overflow in target byte index");
auto target_byte_index = checked_target_byte_index.value();

// 23. Let limit be targetByteIndex + targetElementSize × srcLength.
Checked<size_t> checked_limit(source_length);
checked_limit *= typed_array->element_size();
checked_limit *= target_element_size;
checked_limit += target_byte_index;
if (checked_limit.has_overflow())
return vm.throw_completion<JS::RangeError>(global_object, "Overflow in target limit");
return vm.throw_completion<RangeError>(global_object, "Overflow in target limit");
auto limit = checked_limit.value();

// 24. If srcType is the same as targetType, then
if (source_typed_array.element_size() == typed_array->element_size()) {
// FIXME: SharedBuffers use a different mechanism, implement that when SharedBuffers are implemented.
// a. NOTE: If srcType and targetType are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data.
// b. Repeat, while targetByteIndex < limit,
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, true, Unordered).
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered).
// iii. Set srcByteIndex to srcByteIndex + 1.
// iv. Set targetByteIndex to targetByteIndex + 1.
target_buffer->buffer().overwrite(target_byte_index, source_buffer->buffer().data(), limit - target_byte_index);
} else {
// a. Repeat, while targetByteIndex < limit,
while (target_byte_index < limit) {
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, Unordered).
auto value = source_typed_array.get_value_from_buffer(source_byte_index, ArrayBuffer::Unordered);
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered).
typed_array->set_value_in_buffer(target_byte_index, value, ArrayBuffer::Unordered);
source_byte_index += source_typed_array.element_size();
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
source_byte_index += source_element_size;
// iv. Set targetByteIndex to targetByteIndex + targetElementSize.
target_byte_index += typed_array->element_size();
}
}
} else {
}
// 7. Else,
else {
// a. Perform ? SetTypedArrayFromArrayLike(target, targetOffset, source).

// 23.2.3.23.2 SetTypedArrayFromArrayLike ( target, targetOffset, source ), https://tc39.es/ecma262/#sec-settypedarrayfromarraylike
auto target_buffer = typed_array->viewed_array_buffer();

// 1. Let targetBuffer be target.[[ViewedArrayBuffer]].
auto* target_buffer = typed_array->viewed_array_buffer();

// 2. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
if (target_buffer->is_detached())
return vm.throw_completion<JS::TypeError>(global_object, ErrorType::DetachedArrayBuffer);
return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer);

// 3. Let targetLength be target.[[ArrayLength]].
auto target_length = typed_array->array_length();

// 4. Let targetName be the String value of target.[[TypedArrayName]].
// 5. Let targetElementSize be the Element Size value specified in Table 69 for targetName.
// 6. Let targetType be the Element Type value in Table 69 for targetName.
auto target_element_size = typed_array->element_size();

// 7. Let targetByteOffset be target.[[ByteOffset]].
auto target_byte_offset = typed_array->byte_offset();

auto src = TRY(source.to_object(global_object));
// 8. Let src be ? ToObject(source).
auto* src = TRY(source.to_object(global_object));

// 9. Let srcLength be ? LengthOfArrayLike(src).
auto source_length = TRY(length_of_array_like(global_object, *src));

// 10. If targetOffset is +∞, throw a RangeError exception.
if (isinf(target_offset))
return vm.throw_completion<JS::RangeError>(global_object, "Invalid target offset");
return vm.throw_completion<RangeError>(global_object, "Invalid target offset");

// 11. If srcLength + targetOffset > targetLength, throw a RangeError exception.
Checked<size_t> checked = source_length;
checked += static_cast<u32>(target_offset);
if (checked.has_overflow() || checked.value() > target_length)
return vm.throw_completion<JS::RangeError>(global_object, "Overflow or out of bounds in target length");
return vm.throw_completion<RangeError>(global_object, "Overflow or out of bounds in target length");

// 12. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
Checked<size_t> checked_target_byte_index(static_cast<size_t>(target_offset));
checked_target_byte_index *= typed_array->element_size();
checked_target_byte_index *= target_element_size;
checked_target_byte_index += target_byte_offset;
if (checked_target_byte_index.has_overflow())
return vm.throw_completion<JS::RangeError>(global_object, "Overflow in target byte index");
return vm.throw_completion<RangeError>(global_object, "Overflow in target byte index");
auto target_byte_index = checked_target_byte_index.value();

// 13. Let k be 0.
auto k = 0;

// 14. Let limit be targetByteIndex + targetElementSize × srcLength.
Checked<size_t> checked_limit(source_length);
checked_limit *= typed_array->element_size();
checked_limit *= target_element_size;
checked_limit += target_byte_index;
if (checked_limit.has_overflow())
return vm.throw_completion<JS::RangeError>(global_object, "Overflow in target limit");

return vm.throw_completion<RangeError>(global_object, "Overflow in target limit");
auto limit = checked_limit.value();
auto k = 0;

// 15. Repeat, while targetByteIndex < limit,
while (target_byte_index < limit) {
// a. Let Pk be ! ToString(𝔽(k)).
// b. Let value be ? Get(src, Pk).
auto value = TRY(src->get(k));

// c. If target.[[ContentType]] is BigInt, set value to ? ToBigInt(value).
if (typed_array->content_type() == TypedArrayBase::ContentType::BigInt)
value = TRY(value.to_bigint(global_object));
// d. Otherwise, set value to ? ToNumber(value).
else
value = TRY(value.to_number(global_object));

// e. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
if (target_buffer->is_detached())
return vm.throw_completion<JS::TypeError>(global_object, ErrorType::DetachedArrayBuffer);
return vm.throw_completion<TypeError>(global_object, ErrorType::DetachedArrayBuffer);

// f. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered).
typed_array->set_value_in_buffer(target_byte_index, value, ArrayBuffer::Unordered);

// g. Set k to k + 1.
++k;
target_byte_index += typed_array->element_size();

// h. Set targetByteIndex to targetByteIndex + targetElementSize.
target_byte_index += target_element_size;
}
}

// 8. Return undefined.
return js_undefined();
}

Expand Down

0 comments on commit d225b5e

Please sign in to comment.