Skip to content

Commit b6b295c

Browse files
yossydevaapoalas
andauthored
feat(ecmascript): %TypedArray%.prototype.set (#744)
Co-authored-by: Aapo Alasuutari <[email protected]>
1 parent b617571 commit b6b295c

File tree

4 files changed

+408
-133
lines changed

4 files changed

+408
-133
lines changed

nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/abstract_operations.rs

Lines changed: 252 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
operations_on_objects::{
1212
construct, get, length_of_array_like, set, species_constructor,
1313
},
14-
type_conversion::{to_big_int, to_index, to_number},
14+
type_conversion::{IntegerOrInfinity, to_big_int, to_index, to_number, to_object},
1515
},
1616
builtins::{
1717
ArgumentsList, ArrayBuffer, BuiltinFunction,
@@ -20,7 +20,10 @@ use crate::{
2020
array_buffer_byte_length, clone_array_buffer, get_value_from_buffer,
2121
is_detached_buffer, is_fixed_length_array_buffer, set_value_in_buffer,
2222
},
23-
indexed_collections::typed_array_objects::typed_array_intrinsic_object::require_internal_slot_typed_array,
23+
indexed_collections::typed_array_objects::typed_array_intrinsic_object::{
24+
byte_slice_to_viewable, byte_slice_to_viewable_mut,
25+
require_internal_slot_typed_array, split_typed_array_buffers,
26+
},
2427
ordinary::get_prototype_from_constructor,
2528
typed_array::{
2629
TypedArray,
@@ -41,6 +44,8 @@ use crate::{
4144
heap::CreateHeapData,
4245
};
4346

47+
use super::typed_array_intrinsic_object::copy_between_different_type_typed_arrays;
48+
4449
/// Matches a TypedArray and defines a type T in the expression which
4550
/// is the generic type of the viewable.
4651
#[macro_export]
@@ -1482,3 +1487,248 @@ pub(crate) fn typed_array_species_create_with_buffer<'a, T: Viewable>(
14821487
// 6. Return result.
14831488
Ok(result.unbind())
14841489
}
1490+
1491+
/// [23.2.3.26.1 SetTypedArrayFromTypedArray ( target, targetOffset, source )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-settypedarrayfromtypedarray)
1492+
/// The abstract operation SetTypedArrayFromTypedArray takes arguments target
1493+
/// (a TypedArray), targetOffset (a non-negative integer or +∞), and source
1494+
/// (a TypedArray) and returns either a normal completion containing unused
1495+
/// or a throw completion. It sets multiple values in target, starting at index
1496+
/// targetOffset, reading the values from source.
1497+
pub(crate) fn set_typed_array_from_typed_array<'a, TargetType: Viewable, SrcType: Viewable>(
1498+
agent: &mut Agent,
1499+
target: TypedArray,
1500+
target_offset: IntegerOrInfinity,
1501+
source: TypedArray,
1502+
gc: NoGcScope<'a, '_>,
1503+
) -> JsResult<'a, ()> {
1504+
let target = target.bind(gc);
1505+
let source = source.bind(gc);
1506+
// 1. Let targetBuffer be target.[[ViewedArrayBuffer]].
1507+
let target_buffer = target.get_viewed_array_buffer(agent, gc);
1508+
// 2. Let targetRecord be MakeTypedArrayWithBufferWitnessRecord(target, seq-cst).
1509+
let target_record =
1510+
make_typed_array_with_buffer_witness_record(agent, target, Ordering::SeqCst, gc);
1511+
// 3. If IsTypedArrayOutOfBounds(targetRecord) is true, throw a TypeError exception.
1512+
if is_typed_array_out_of_bounds::<TargetType>(agent, &target_record, gc) {
1513+
return Err(agent.throw_exception_with_static_message(
1514+
ExceptionType::TypeError,
1515+
"TypedArray out of bounds",
1516+
gc,
1517+
));
1518+
};
1519+
// 4. Let targetLength be TypedArrayLength(targetRecord).
1520+
let target_length = typed_array_length::<TargetType>(agent, &target_record, gc);
1521+
// 5. Let srcBuffer be source.[[ViewedArrayBuffer]].
1522+
let mut src_buffer = source.get_viewed_array_buffer(agent, gc);
1523+
// 6. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(source, seq-cst).
1524+
let src_record =
1525+
make_typed_array_with_buffer_witness_record(agent, source, Ordering::SeqCst, gc);
1526+
// 7. If IsTypedArrayOutOfBounds(srcRecord) is true, throw a TypeError exception.
1527+
if is_typed_array_out_of_bounds::<SrcType>(agent, &src_record, gc) {
1528+
return Err(agent.throw_exception_with_static_message(
1529+
ExceptionType::TypeError,
1530+
"TypedArray out of bounds",
1531+
gc,
1532+
));
1533+
}
1534+
// 8. Let srcLength be TypedArrayLength(srcRecord).
1535+
let src_length = typed_array_length::<SrcType>(agent, &src_record, gc);
1536+
// 9. Let targetType be TypedArrayElementType(target).
1537+
// 10. Let targetElementSize be TypedArrayElementSize(target).
1538+
let target_element_size = size_of::<TargetType>();
1539+
// 11. Let targetByteOffset be target.[[ByteOffset]].
1540+
let target_byte_offset = target.byte_offset(agent);
1541+
// 12. Let srcType be TypedArrayElementType(source).
1542+
// 13. Let srcElementSize be TypedArrayElementSize(source).
1543+
// 14. Let srcByteOffset be source.[[ByteOffset]].
1544+
let src_byte_offset = source.byte_offset(agent);
1545+
// 15. If targetOffset = +∞, throw a RangeError exception.
1546+
if target_offset.is_pos_infinity() {
1547+
return Err(agent.throw_exception_with_static_message(
1548+
ExceptionType::RangeError,
1549+
"count must be less than infinity",
1550+
gc,
1551+
));
1552+
};
1553+
// 16. If srcLength + targetOffset > targetLength, throw a RangeError exception.
1554+
let target_offset = target_offset.into_i64() as u64;
1555+
if src_length as u64 + target_offset > target_length as u64 {
1556+
return Err(agent.throw_exception_with_static_message(
1557+
ExceptionType::RangeError,
1558+
"source length out of target bounds",
1559+
gc,
1560+
));
1561+
};
1562+
let target_offset = target_offset as usize;
1563+
// 17. If target.[[ContentType]] is not source.[[ContentType]], throw a TypeError exception.
1564+
let is_type_match = has_matching_content_type::<TargetType>(source);
1565+
if !is_type_match {
1566+
return Err(agent.throw_exception_with_static_message(
1567+
ExceptionType::TypeError,
1568+
"TypedArray species did not match source",
1569+
gc,
1570+
));
1571+
};
1572+
// 18. If IsSharedArrayBuffer(srcBuffer) is true,
1573+
// IsSharedArrayBuffer(targetBuffer) is true, and
1574+
// srcBuffer.[[ArrayBufferData]] is targetBuffer.[[ArrayBufferData]],
1575+
// let sameSharedArrayBuffer be true; otherwise, let
1576+
// sameSharedArrayBuffer be false.
1577+
// 19. If SameValue(srcBuffer, targetBuffer) is true or
1578+
// sameSharedArrayBuffer is true, then
1579+
let src_byte_index = if src_buffer == target_buffer {
1580+
// a. Let srcByteLength be TypedArrayByteLength(srcRecord).
1581+
let src_byte_length = typed_array_byte_length::<SrcType>(agent, &src_record, gc);
1582+
// b. Set srcBuffer to
1583+
// ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength).
1584+
src_buffer = clone_array_buffer(agent, src_buffer, src_byte_offset, src_byte_length, gc)
1585+
.unbind()?
1586+
.bind(gc);
1587+
// c. Let srcByteIndex be 0.
1588+
0
1589+
} else {
1590+
src_byte_offset
1591+
};
1592+
debug_assert_ne!(src_buffer, target_buffer);
1593+
debug_assert_ne!(
1594+
src_buffer.as_slice(agent).as_ptr(),
1595+
target_buffer.as_slice(agent).as_ptr()
1596+
);
1597+
// 21. Let targetByteIndex be (targetOffset × targetElementSize) + targetByteOffset.
1598+
let target_byte_index = (target_offset * target_element_size) + target_byte_offset;
1599+
// 22. Let limit be targetByteIndex + (targetElementSize × srcLength).
1600+
let limit = target_byte_index + (target_element_size * src_length);
1601+
// 23. If srcType is targetType, then
1602+
if core::any::TypeId::of::<SrcType>() == core::any::TypeId::of::<TargetType>() {
1603+
// a. NOTE: The transfer must be performed in a manner that preserves
1604+
// the bit-level encoding of the source data.
1605+
// Repeat, while targetByteIndex < limit,
1606+
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, uint8,
1607+
// true, unordered).
1608+
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, uint8,
1609+
// value, true, unordered).
1610+
let (target_slice, src_slice) = split_typed_array_buffers::<SrcType>(
1611+
agent,
1612+
target_buffer,
1613+
target_byte_index,
1614+
src_buffer,
1615+
src_byte_index,
1616+
limit,
1617+
);
1618+
target_slice.copy_from_slice(src_slice);
1619+
// iii. Set srcByteIndex to srcByteIndex + 1.
1620+
// iv. Set targetByteIndex to targetByteIndex + 1.
1621+
} else {
1622+
// 24. Else,
1623+
// a. Repeat, while targetByteIndex < limit,
1624+
// i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, unordered).
1625+
// ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, unordered).
1626+
let target_slice = byte_slice_to_viewable_mut::<TargetType>(
1627+
target_buffer.as_mut_slice(agent),
1628+
target_byte_index,
1629+
limit,
1630+
);
1631+
let target_ptr = target_slice.as_mut_ptr();
1632+
let target_len = target_slice.len();
1633+
let src_slice = byte_slice_to_viewable::<SrcType>(
1634+
src_buffer.as_slice(agent),
1635+
src_byte_index,
1636+
// Note: source buffer is limited by the target buffer length.
1637+
src_byte_index + target_len * core::mem::size_of::<SrcType>(),
1638+
);
1639+
// SAFETY: Confirmed beforehand that the two ArrayBuffers are in separate memory regions.
1640+
let target_slice = unsafe { std::slice::from_raw_parts_mut(target_ptr, target_len) };
1641+
copy_between_different_type_typed_arrays::<SrcType, TargetType>(src_slice, target_slice);
1642+
// iii. Set srcByteIndex to srcByteIndex + srcElementSize.
1643+
// iv. Set targetByteIndex to targetByteIndex + targetElementSize.
1644+
}
1645+
// 25. Return unused.
1646+
Ok(())
1647+
}
1648+
1649+
/// ### [23.2.3.26.2 SetTypedArrayFromArrayLike ( target, targetOffset, source )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-settypedarrayfromarraylike)
1650+
/// The abstract operation SetTypedArrayFromArrayLike takes arguments target
1651+
/// (a TypedArray), targetOffset (a non-negative integer or +∞), and source
1652+
/// (an ECMAScript language value, but not a TypedArray) and returns either
1653+
/// a normal completion containing unused or a throw completion. It sets
1654+
/// multiple values in target, starting at index targetOffset, reading the
1655+
/// values from source.
1656+
pub(crate) fn set_typed_array_from_array_like<'a, T: Viewable>(
1657+
agent: &mut Agent,
1658+
target: Scoped<TypedArray>,
1659+
target_offset: IntegerOrInfinity,
1660+
source: Scoped<Value>,
1661+
mut gc: GcScope<'a, '_>,
1662+
) -> JsResult<'a, ()> {
1663+
// 1. Let targetRecord be MakeTypedArrayWithBufferWitnessRecord(target, seq-cst).
1664+
let target_record = make_typed_array_with_buffer_witness_record(
1665+
agent,
1666+
target.get(agent),
1667+
Ordering::SeqCst,
1668+
gc.nogc(),
1669+
);
1670+
// 2. If IsTypedArrayOutOfBounds(targetRecord) is true, throw a TypeError exception.
1671+
if is_typed_array_out_of_bounds::<T>(agent, &target_record, gc.nogc()) {
1672+
return Err(agent.throw_exception_with_static_message(
1673+
ExceptionType::TypeError,
1674+
"TypedArray out of bounds",
1675+
gc.into_nogc(),
1676+
));
1677+
};
1678+
// 3. Let targetLength be TypedArrayLength(targetRecord).
1679+
let target_length = typed_array_length::<T>(agent, &target_record, gc.nogc()) as u64;
1680+
// 4. Let src be ? ToObject(source).
1681+
let src = to_object(agent, source.get(agent), gc.nogc())
1682+
.unbind()?
1683+
.bind(gc.nogc());
1684+
// SAFETY: source is not shared.
1685+
let source = unsafe { source.replace_self(agent, src.unbind()) };
1686+
// 5. Let srcLength be ? LengthOfArrayLike(src).
1687+
let src_length = length_of_array_like(agent, src.unbind(), gc.reborrow()).unbind()? as u64;
1688+
let src = source;
1689+
// 6. If targetOffset = +∞, throw a RangeError exception.
1690+
if target_offset.is_pos_infinity() {
1691+
return Err(agent.throw_exception_with_static_message(
1692+
ExceptionType::RangeError,
1693+
"count must be less than infinity",
1694+
gc.into_nogc(),
1695+
));
1696+
};
1697+
let target_offset = target_offset.into_i64() as u64;
1698+
// 7. If srcLength + targetOffset > targetLength, throw a RangeError exception.
1699+
if src_length + target_offset > target_length {
1700+
return Err(agent.throw_exception_with_static_message(
1701+
ExceptionType::RangeError,
1702+
"count must be less than infinity",
1703+
gc.into_nogc(),
1704+
));
1705+
};
1706+
let target_offset = target_offset as usize;
1707+
let src_length = src_length as usize;
1708+
// 8. Let k be 0.
1709+
let mut k = 0;
1710+
// 9. Repeat, while k < srcLength,
1711+
while k < src_length {
1712+
// a. Let Pk be ! ToString(𝔽(k)).
1713+
let pk = PropertyKey::Integer(k.try_into().unwrap());
1714+
// b. Let value be ? Get(src, Pk).
1715+
let value = get(agent, src.get(agent), pk, gc.reborrow())
1716+
.unbind()?
1717+
.bind(gc.nogc());
1718+
// c. Let targetIndex be 𝔽(targetOffset + k).
1719+
let target_index = target_offset + k;
1720+
// d. Perform ? TypedArraySetElement(target, targetIndex, value).
1721+
typed_array_set_element::<T>(
1722+
agent,
1723+
target.get(agent),
1724+
target_index as i64,
1725+
value.unbind(),
1726+
gc.reborrow(),
1727+
)
1728+
.unbind()?;
1729+
// e. Set k to k + 1.
1730+
k += 1;
1731+
}
1732+
// 10. Return unused.
1733+
Ok(())
1734+
}

0 commit comments

Comments
 (0)