Browse Source

Improve typing of `DataView` and related objects (#3626)

pull/3633/head
José Julián Espina 10 months ago committed by GitHub
parent
commit
20dba13ae3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 114
      core/engine/src/builtins/array_buffer/mod.rs
  2. 12
      core/engine/src/builtins/array_buffer/shared.rs
  3. 18
      core/engine/src/builtins/array_buffer/utils.rs
  4. 37
      core/engine/src/builtins/atomics/mod.rs
  5. 59
      core/engine/src/builtins/dataview/mod.rs
  6. 93
      core/engine/src/builtins/typed_array/builtin.rs
  7. 6
      core/engine/src/builtins/typed_array/element/mod.rs
  8. 124
      core/engine/src/builtins/typed_array/mod.rs
  9. 136
      core/engine/src/builtins/typed_array/object.rs
  10. 16
      core/engine/src/object/builtins/jsarraybuffer.rs
  11. 91
      core/engine/src/object/builtins/jsdataview.rs
  12. 49
      core/engine/src/object/builtins/jssharedarraybuffer.rs
  13. 60
      core/engine/src/object/jsobject.rs
  14. 28
      core/engine/src/object/mod.rs

114
core/engine/src/builtins/array_buffer/mod.rs

@ -16,6 +16,8 @@ pub(crate) mod utils;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use std::ops::{Deref, DerefMut};
pub use shared::SharedArrayBuffer; pub use shared::SharedArrayBuffer;
use crate::{ use crate::{
@ -23,7 +25,7 @@ use crate::{
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError, error::JsNativeError,
js_string, js_string,
object::{internal_methods::get_prototype_from_constructor, JsObject}, object::{internal_methods::get_prototype_from_constructor, JsObject, Object},
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::common::StaticJsStrings, string::common::StaticJsStrings,
@ -31,7 +33,7 @@ use crate::{
value::IntegerOrInfinity, value::IntegerOrInfinity,
Context, JsArgs, JsData, JsResult, JsString, JsValue, Context, JsArgs, JsData, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, GcRef, GcRefMut, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use self::utils::{SliceRef, SliceRefMut}; use self::utils::{SliceRef, SliceRefMut};
@ -41,16 +43,20 @@ use super::{
}; };
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) enum BufferRef<'a> { pub(crate) enum BufferRef<B, S> {
Buffer(&'a ArrayBuffer), Buffer(B),
SharedBuffer(&'a SharedArrayBuffer), SharedBuffer(S),
} }
impl BufferRef<'_> { impl<B, S> BufferRef<B, S>
where
B: Deref<Target = ArrayBuffer>,
S: Deref<Target = SharedArrayBuffer>,
{
pub(crate) fn data(&self) -> Option<SliceRef<'_>> { pub(crate) fn data(&self) -> Option<SliceRef<'_>> {
match self { match self {
Self::Buffer(buf) => buf.data().map(SliceRef::Slice), Self::Buffer(buf) => buf.deref().data().map(SliceRef::Slice),
Self::SharedBuffer(buf) => Some(SliceRef::AtomicSlice(buf.data())), Self::SharedBuffer(buf) => Some(SliceRef::AtomicSlice(buf.deref().data())),
} }
} }
@ -60,16 +66,96 @@ impl BufferRef<'_> {
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum BufferRefMut<'a> { pub(crate) enum BufferRefMut<B, S> {
Buffer(&'a mut ArrayBuffer), Buffer(B),
SharedBuffer(&'a mut SharedArrayBuffer), SharedBuffer(S),
} }
impl BufferRefMut<'_> { impl<B, S> BufferRefMut<B, S>
where
B: DerefMut<Target = ArrayBuffer>,
S: DerefMut<Target = SharedArrayBuffer>,
{
pub(crate) fn data_mut(&mut self) -> Option<SliceRefMut<'_>> { pub(crate) fn data_mut(&mut self) -> Option<SliceRefMut<'_>> {
match self { match self {
Self::Buffer(buf) => buf.data_mut().map(SliceRefMut::Slice), Self::Buffer(buf) => buf.deref_mut().data_mut().map(SliceRefMut::Slice),
Self::SharedBuffer(buf) => Some(SliceRefMut::AtomicSlice(buf.data())), Self::SharedBuffer(buf) => Some(SliceRefMut::AtomicSlice(buf.deref_mut().data())),
}
}
}
/// A `JsObject` containing a bytes buffer as its inner data.
#[derive(Debug, Clone, Trace, Finalize)]
#[boa_gc(unsafe_no_drop)]
pub(crate) enum BufferObject {
Buffer(JsObject<ArrayBuffer>),
SharedBuffer(JsObject<SharedArrayBuffer>),
}
impl From<BufferObject> for JsObject {
fn from(value: BufferObject) -> Self {
match value {
BufferObject::Buffer(buf) => buf.upcast(),
BufferObject::SharedBuffer(buf) => buf.upcast(),
}
}
}
impl From<BufferObject> for JsValue {
fn from(value: BufferObject) -> Self {
JsValue::from(JsObject::from(value))
}
}
impl BufferObject {
/// Gets the buffer data of the object.
#[inline]
#[must_use]
pub(crate) fn as_buffer(
&self,
) -> BufferRef<GcRef<'_, ArrayBuffer>, GcRef<'_, SharedArrayBuffer>> {
match self {
Self::Buffer(buf) => BufferRef::Buffer(GcRef::map(buf.borrow(), |o| &o.data)),
Self::SharedBuffer(buf) => {
BufferRef::SharedBuffer(GcRef::map(buf.borrow(), |o| &o.data))
}
}
}
/// Gets the mutable buffer data of the object
#[inline]
pub(crate) fn as_buffer_mut(
&self,
) -> BufferRefMut<
GcRefMut<'_, Object<ArrayBuffer>, ArrayBuffer>,
GcRefMut<'_, Object<SharedArrayBuffer>, SharedArrayBuffer>,
> {
match self {
Self::Buffer(buf) => {
BufferRefMut::Buffer(GcRefMut::map(buf.borrow_mut(), |o| &mut o.data))
}
Self::SharedBuffer(buf) => {
BufferRefMut::SharedBuffer(GcRefMut::map(buf.borrow_mut(), |o| &mut o.data))
}
}
}
/// Returns `true` if the buffer objects point to the same buffer.
#[inline]
pub(crate) fn equals(lhs: &Self, rhs: &Self) -> bool {
match (lhs, rhs) {
(BufferObject::Buffer(lhs), BufferObject::Buffer(rhs)) => JsObject::equals(lhs, rhs),
(BufferObject::SharedBuffer(lhs), BufferObject::SharedBuffer(rhs)) => {
if JsObject::equals(lhs, rhs) {
return true;
}
let lhs = lhs.borrow();
let rhs = rhs.borrow();
std::ptr::eq(lhs.data.data().as_ptr(), rhs.data.data().as_ptr())
}
_ => false,
} }
} }
} }

12
core/engine/src/builtins/array_buffer/shared.rs

@ -125,7 +125,9 @@ impl BuiltInConstructor for SharedArrayBuffer {
let byte_length = args.get_or_undefined(0).to_index(context)?; let byte_length = args.get_or_undefined(0).to_index(context)?;
// 3. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength, requestedMaxByteLength). // 3. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).
Ok(Self::allocate(new_target, byte_length, context)?.into()) Ok(Self::allocate(new_target, byte_length, context)?
.upcast()
.into())
} }
} }
@ -256,7 +258,7 @@ impl SharedArrayBuffer {
constructor: &JsValue, constructor: &JsValue,
byte_length: u64, byte_length: u64,
context: &mut Context, context: &mut Context,
) -> JsResult<JsObject> { ) -> JsResult<JsObject<SharedArrayBuffer>> {
// TODO: // TODO:
// 1. Let slots be « [[ArrayBufferData]] ». // 1. Let slots be « [[ArrayBufferData]] ».
// 2. If maxByteLength is present and maxByteLength is not empty, let allocatingGrowableBuffer // 2. If maxByteLength is present and maxByteLength is not empty, let allocatingGrowableBuffer
@ -291,11 +293,7 @@ impl SharedArrayBuffer {
// 10. Else, // 10. Else,
// a. Set obj.[[ArrayBufferByteLength]] to byteLength. // a. Set obj.[[ArrayBufferByteLength]] to byteLength.
let obj = JsObject::from_proto_and_data_with_shared_shape( let obj = JsObject::new(context.root_shape(), prototype, Self { data });
context.root_shape(),
prototype,
Self { data },
);
// 11. Return obj. // 11. Return obj.
Ok(obj) Ok(obj)

18
core/engine/src/builtins/array_buffer/utils.rs

@ -3,7 +3,6 @@
use std::{ptr, slice::SliceIndex, sync::atomic}; use std::{ptr, slice::SliceIndex, sync::atomic};
use portable_atomic::AtomicU8; use portable_atomic::AtomicU8;
use sptr::Strict;
use crate::{ use crate::{
builtins::typed_array::{ClampedU8, Element, TypedArrayElement, TypedArrayKind}, builtins::typed_array::{ClampedU8, Element, TypedArrayElement, TypedArrayKind},
@ -41,7 +40,9 @@ impl SliceRef<'_> {
} }
/// Gets the starting address of this `SliceRef`. /// Gets the starting address of this `SliceRef`.
#[cfg(debug_assertions)]
pub(crate) fn addr(&self) -> usize { pub(crate) fn addr(&self) -> usize {
use sptr::Strict;
match self { match self {
Self::Slice(buf) => buf.as_ptr().addr(), Self::Slice(buf) => buf.as_ptr().addr(),
Self::AtomicSlice(buf) => buf.as_ptr().addr(), Self::AtomicSlice(buf) => buf.as_ptr().addr(),
@ -68,7 +69,8 @@ impl SliceRef<'_> {
// 1. Assert: IsDetachedBuffer(arrayBuffer) is false. // 1. Assert: IsDetachedBuffer(arrayBuffer) is false.
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type. // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
if cfg!(debug_assertions) { #[cfg(debug_assertions)]
{
assert!(buffer.len() >= std::mem::size_of::<T>()); assert!(buffer.len() >= std::mem::size_of::<T>());
assert_eq!(buffer.addr() % std::mem::align_of::<T>(), 0); assert_eq!(buffer.addr() % std::mem::align_of::<T>(), 0);
} }
@ -178,6 +180,7 @@ pub(crate) enum SliceRefMut<'a> {
impl SliceRefMut<'_> { impl SliceRefMut<'_> {
/// Gets the byte length of this `SliceRefMut`. /// Gets the byte length of this `SliceRefMut`.
#[cfg(debug_assertions)]
pub(crate) fn len(&self) -> usize { pub(crate) fn len(&self) -> usize {
match self { match self {
Self::Slice(buf) => buf.len(), Self::Slice(buf) => buf.len(),
@ -201,7 +204,9 @@ impl SliceRefMut<'_> {
} }
/// Gets the starting address of this `SliceRefMut`. /// Gets the starting address of this `SliceRefMut`.
#[cfg(debug_assertions)]
pub(crate) fn addr(&self) -> usize { pub(crate) fn addr(&self) -> usize {
use sptr::Strict;
match self { match self {
Self::Slice(buf) => buf.as_ptr().addr(), Self::Slice(buf) => buf.as_ptr().addr(),
Self::AtomicSlice(buf) => buf.as_ptr().addr(), Self::AtomicSlice(buf) => buf.as_ptr().addr(),
@ -236,7 +241,8 @@ impl SliceRefMut<'_> {
// 1. Assert: IsDetachedBuffer(arrayBuffer) is false. // 1. Assert: IsDetachedBuffer(arrayBuffer) is false.
// 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type. // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
// 3. Assert: value is a BigInt if IsBigIntElementType(type) is true; otherwise, value is a Number. // 3. Assert: value is a BigInt if IsBigIntElementType(type) is true; otherwise, value is a Number.
if cfg!(debug_assertions) { #[cfg(debug_assertions)]
{
assert!(buffer.len() >= std::mem::size_of::<T>()); assert!(buffer.len() >= std::mem::size_of::<T>());
assert_eq!(buffer.addr() % std::mem::align_of::<T>(), 0); assert_eq!(buffer.addr() % std::mem::align_of::<T>(), 0);
} }
@ -345,7 +351,8 @@ unsafe fn copy_shared_to_shared_backwards(src: &[AtomicU8], dest: &[AtomicU8], c
/// (you cannot borrow and mutably borrow a slice at the same time), but cannot be guaranteed /// (you cannot borrow and mutably borrow a slice at the same time), but cannot be guaranteed
/// for atomic slices. /// for atomic slices.
pub(crate) unsafe fn memcpy(src: SliceRef<'_>, dest: SliceRefMut<'_>, count: usize) { pub(crate) unsafe fn memcpy(src: SliceRef<'_>, dest: SliceRefMut<'_>, count: usize) {
if cfg!(debug_assertions) { #[cfg(debug_assertions)]
{
assert!(src.len() >= count); assert!(src.len() >= count);
assert!(dest.len() >= count); assert!(dest.len() >= count);
let src_range = src.addr()..src.addr() + src.len(); let src_range = src.addr()..src.addr() + src.len();
@ -387,7 +394,8 @@ pub(crate) unsafe fn memcpy(src: SliceRef<'_>, dest: SliceRefMut<'_>, count: usi
/// - `buffer` must contain at least `from + count` bytes to be read. /// - `buffer` must contain at least `from + count` bytes to be read.
/// - `buffer` must contain at least `to + count` bytes to be written. /// - `buffer` must contain at least `to + count` bytes to be written.
pub(crate) unsafe fn memmove(buffer: SliceRefMut<'_>, from: usize, to: usize, count: usize) { pub(crate) unsafe fn memmove(buffer: SliceRefMut<'_>, from: usize, to: usize, count: usize) {
if cfg!(debug_assertions) { #[cfg(debug_assertions)]
{
assert!(from + count <= buffer.len()); assert!(from + count <= buffer.len());
assert!(to + count <= buffer.len()); assert!(to + count <= buffer.len());
} }

37
core/engine/src/builtins/atomics/mod.rs

@ -24,7 +24,7 @@ use boa_gc::GcRef;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use super::{ use super::{
array_buffer::{BufferRef, SharedArrayBuffer}, array_buffer::BufferRef,
typed_array::{Atomic, ContentType, Element, TypedArray, TypedArrayElement, TypedArrayKind}, typed_array::{Atomic, ContentType, Element, TypedArray, TypedArrayElement, TypedArrayKind},
BuiltInBuilder, IntrinsicObject, BuiltInBuilder, IntrinsicObject,
}; };
@ -80,10 +80,7 @@ macro_rules! atomic_op {
let value = ii.kind().get_element(value, context)?; let value = ii.kind().get_element(value, context)?;
// revalidate // revalidate
let mut buffer = ii.viewed_array_buffer().borrow_mut(); let mut buffer = ii.viewed_array_buffer().as_buffer_mut();
let mut buffer = buffer
.as_buffer_mut()
.expect("integer indexed object must contain a valid buffer");
let Some(mut data) = buffer.data_mut() else { let Some(mut data) = buffer.data_mut() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("cannot execute atomic operation in detached buffer") .with_message("cannot execute atomic operation in detached buffer")
@ -168,10 +165,7 @@ impl Atomics {
let pos = validate_atomic_access(&ii, index, context)?; let pos = validate_atomic_access(&ii, index, context)?;
// 2. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition). // 2. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition).
let buffer = ii.viewed_array_buffer().borrow(); let buffer = ii.viewed_array_buffer().as_buffer();
let buffer = buffer
.as_buffer()
.expect("integer indexed object must contain a valid buffer");
let Some(data) = buffer.data() else { let Some(data) = buffer.data() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("cannot execute atomic operation in detached buffer") .with_message("cannot execute atomic operation in detached buffer")
@ -217,10 +211,7 @@ impl Atomics {
let value = ii.kind().get_element(&converted, context)?; let value = ii.kind().get_element(&converted, context)?;
// 4. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition). // 4. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition).
let mut buffer = ii.viewed_array_buffer().borrow_mut(); let mut buffer = ii.viewed_array_buffer().as_buffer_mut();
let mut buffer = buffer
.as_buffer_mut()
.expect("integer indexed object must contain a valid buffer");
let Some(mut buffer) = buffer.data_mut() else { let Some(mut buffer) = buffer.data_mut() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("cannot execute atomic operation in detached buffer") .with_message("cannot execute atomic operation in detached buffer")
@ -269,10 +260,7 @@ impl Atomics {
.to_bytes(); .to_bytes();
// 6. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition). // 6. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition).
let mut buffer = ii.viewed_array_buffer().borrow_mut(); let mut buffer = ii.viewed_array_buffer().as_buffer_mut();
let mut buffer = buffer
.as_buffer_mut()
.expect("integer indexed object must contain a valid buffer");
let Some(mut data) = buffer.data_mut() else { let Some(mut data) = buffer.data_mut() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("cannot execute atomic operation in detached buffer") .with_message("cannot execute atomic operation in detached buffer")
@ -403,10 +391,7 @@ impl Atomics {
// 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true). // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray, true).
let ii = validate_integer_typed_array(array, true)?; let ii = validate_integer_typed_array(array, true)?;
let buffer = ii.viewed_array_buffer().borrow(); let buffer = ii.viewed_array_buffer().as_buffer();
let buffer = buffer
.as_buffer()
.expect("integer indexed object must contain a valid buffer");
// 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception. // 2. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
let BufferRef::SharedBuffer(buffer) = buffer else { let BufferRef::SharedBuffer(buffer) = buffer else {
@ -451,10 +436,10 @@ impl Atomics {
// SAFETY: the validity of `addr` is verified by our call to `validate_atomic_access`. // SAFETY: the validity of `addr` is verified by our call to `validate_atomic_access`.
let result = unsafe { let result = unsafe {
if ii.kind() == TypedArrayKind::BigInt64 { if ii.kind() == TypedArrayKind::BigInt64 {
futex::wait(buffer, offset, value, timeout)? futex::wait(&buffer, offset, value, timeout)?
} else { } else {
// value must fit into `i32` since it came from an `i32` above. // value must fit into `i32` since it came from an `i32` above.
futex::wait(buffer, offset, value as i32, timeout)? futex::wait(&buffer, offset, value as i32, timeout)?
} }
}; };
@ -496,13 +481,11 @@ impl Atomics {
// 4. Let buffer be typedArray.[[ViewedArrayBuffer]]. // 4. Let buffer be typedArray.[[ViewedArrayBuffer]].
// 5. Let block be buffer.[[ArrayBufferData]]. // 5. Let block be buffer.[[ArrayBufferData]].
// 6. If IsSharedArrayBuffer(buffer) is false, return +0𝔽. // 6. If IsSharedArrayBuffer(buffer) is false, return +0𝔽.
let buffer = ii.viewed_array_buffer(); let BufferRef::SharedBuffer(shared) = ii.viewed_array_buffer().as_buffer() else {
let buffer = buffer.borrow();
let Some(shared) = buffer.downcast_ref::<SharedArrayBuffer>() else {
return Ok(0.into()); return Ok(0.into());
}; };
let count = futex::notify(shared, offset, count)?; let count = futex::notify(&shared, offset, count)?;
// 12. Let n be the number of elements in S. // 12. Let n be the number of elements in S.
// 13. Return 𝔽(n). // 13. Return 𝔽(n).

59
core/engine/src/builtins/dataview/mod.rs

@ -26,7 +26,10 @@ use boa_gc::{Finalize, Trace};
use bytemuck::{bytes_of, bytes_of_mut}; use bytemuck::{bytes_of, bytes_of_mut};
use super::{ use super::{
array_buffer::utils::{memcpy, SliceRef, SliceRefMut}, array_buffer::{
utils::{memcpy, SliceRef, SliceRefMut},
BufferObject,
},
typed_array::{self, TypedArrayElement}, typed_array::{self, TypedArrayElement},
BuiltInBuilder, BuiltInConstructor, IntrinsicObject, BuiltInBuilder, BuiltInConstructor, IntrinsicObject,
}; };
@ -34,7 +37,7 @@ use super::{
/// The internal representation of a `DataView` object. /// The internal representation of a `DataView` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)] #[derive(Debug, Clone, Trace, Finalize, JsData)]
pub struct DataView { pub struct DataView {
pub(crate) viewed_array_buffer: JsObject, pub(crate) viewed_array_buffer: BufferObject,
pub(crate) byte_length: u64, pub(crate) byte_length: u64,
pub(crate) byte_offset: u64, pub(crate) byte_offset: u64,
} }
@ -135,11 +138,6 @@ impl BuiltInConstructor for DataView {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
let byte_length = args.get_or_undefined(2); let byte_length = args.get_or_undefined(2);
let buffer_obj = args
.get_or_undefined(0)
.as_object()
.ok_or_else(|| JsNativeError::typ().with_message("buffer must be an ArrayBuffer"))?;
// 1. If NewTarget is undefined, throw a TypeError exception. // 1. If NewTarget is undefined, throw a TypeError exception.
if new_target.is_undefined() { if new_target.is_undefined() {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
@ -147,15 +145,19 @@ impl BuiltInConstructor for DataView {
.into()); .into());
} }
// 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]).
let buffer_obj = args
.get_or_undefined(0)
.as_object()
.and_then(|o| o.clone().into_buffer_object().ok())
.ok_or_else(|| JsNativeError::typ().with_message("buffer must be an ArrayBuffer"))?;
let (offset, view_byte_length) = { let (offset, view_byte_length) = {
// 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]). let buffer = buffer_obj.as_buffer();
let buffer_borrow = buffer_obj.borrow();
let buffer = buffer_borrow.as_buffer().ok_or_else(|| {
JsNativeError::typ().with_message("buffer must be an ArrayBuffer")
})?;
// 3. Let offset be ? ToIndex(byteOffset). // 3. Let offset be ? ToIndex(byteOffset).
let offset = args.get_or_undefined(1).to_index(context)?; let offset = args.get_or_undefined(1).to_index(context)?;
// 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
let Some(buffer) = buffer.data() else { let Some(buffer) = buffer.data() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
@ -195,12 +197,7 @@ impl BuiltInConstructor for DataView {
get_prototype_from_constructor(new_target, StandardConstructors::data_view, context)?; get_prototype_from_constructor(new_target, StandardConstructors::data_view, context)?;
// 10. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 10. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if buffer_obj if buffer_obj.as_buffer().is_detached() {
.borrow()
.as_buffer()
.expect("already checked that `buffer_obj` was a buffer")
.is_detached()
{
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("ArrayBuffer can't be detached") .with_message("ArrayBuffer can't be detached")
.into()); .into());
@ -211,7 +208,7 @@ impl BuiltInConstructor for DataView {
prototype, prototype,
Self { Self {
// 11. Set O.[[ViewedArrayBuffer]] to buffer. // 11. Set O.[[ViewedArrayBuffer]] to buffer.
viewed_array_buffer: buffer_obj.clone(), viewed_array_buffer: buffer_obj,
// 12. Set O.[[ByteLength]] to viewByteLength. // 12. Set O.[[ByteLength]] to viewByteLength.
byte_length: view_byte_length, byte_length: view_byte_length,
// 13. Set O.[[ByteOffset]] to offset. // 13. Set O.[[ByteOffset]] to offset.
@ -277,12 +274,9 @@ impl DataView {
.ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?;
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
// 4. Let buffer be O.[[ViewedArrayBuffer]]. // 4. Let buffer be O.[[ViewedArrayBuffer]].
let buffer_borrow = view.viewed_array_buffer.borrow(); let buffer = view.viewed_array_buffer.as_buffer();
let borrow = buffer_borrow
.as_buffer()
.expect("DataView must be constructed with a Buffer");
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if borrow.is_detached() { if buffer.is_detached() {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("ArrayBuffer is detached") .with_message("ArrayBuffer is detached")
.into()); .into());
@ -317,12 +311,9 @@ impl DataView {
.ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?; .ok_or_else(|| JsNativeError::typ().with_message("`this` is not a DataView"))?;
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
// 4. Let buffer be O.[[ViewedArrayBuffer]]. // 4. Let buffer be O.[[ViewedArrayBuffer]].
let buffer_borrow = view.viewed_array_buffer.borrow(); let buffer = view.viewed_array_buffer.as_buffer();
let borrow = buffer_borrow
.as_buffer()
.expect("DataView must be constructed with a Buffer");
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
if borrow.is_detached() { if buffer.is_detached() {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("Buffer is detached") .with_message("Buffer is detached")
.into()); .into());
@ -362,9 +353,7 @@ impl DataView {
let is_little_endian = is_little_endian.to_boolean(); let is_little_endian = is_little_endian.to_boolean();
// 5. Let buffer be view.[[ViewedArrayBuffer]]. // 5. Let buffer be view.[[ViewedArrayBuffer]].
let buffer = &view.viewed_array_buffer; let buffer = view.viewed_array_buffer.as_buffer();
let buffer_borrow = buffer.borrow();
let buffer = buffer_borrow.as_buffer().expect("Should be unreachable");
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
let Some(data) = buffer.data() else { let Some(data) = buffer.data() else {
@ -676,11 +665,7 @@ impl DataView {
// 6. Set isLittleEndian to ! ToBoolean(isLittleEndian). // 6. Set isLittleEndian to ! ToBoolean(isLittleEndian).
let is_little_endian = is_little_endian.to_boolean(); let is_little_endian = is_little_endian.to_boolean();
// 7. Let buffer be view.[[ViewedArrayBuffer]]. // 7. Let buffer be view.[[ViewedArrayBuffer]].
let buffer = &view.viewed_array_buffer; let mut buffer = view.viewed_array_buffer.as_buffer_mut();
let mut buffer_borrow = buffer.borrow_mut();
let mut buffer = buffer_borrow
.as_buffer_mut()
.expect("Should be unreachable");
// 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
let Some(mut data) = buffer.data_mut() else { let Some(mut data) = buffer.data_mut() else {

93
core/engine/src/builtins/typed_array/builtin.rs

@ -1,14 +1,17 @@
use std::{cmp, ptr, sync::atomic}; use std::{cmp, sync::atomic};
use boa_macros::utf16; use boa_macros::utf16;
use num_traits::Zero; use num_traits::Zero;
use super::{
object::typed_array_set_element, ContentType, TypedArray, TypedArrayKind, TypedArrayMarker,
};
use crate::{ use crate::{
builtins::{ builtins::{
array::{find_via_predicate, ArrayIterator, Direction}, array::{find_via_predicate, ArrayIterator, Direction},
array_buffer::{ array_buffer::{
utils::{memcpy, memmove, SliceRefMut}, utils::{memcpy, memmove, SliceRefMut},
ArrayBuffer, BufferRef, ArrayBuffer, BufferObject,
}, },
iterable::iterable_to_list, iterable::iterable_to_list,
Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
@ -23,11 +26,6 @@ use crate::{
Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, Context, JsArgs, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
}; };
use super::{
integer_indexed_object::integer_indexed_element_set, ContentType, TypedArray, TypedArrayKind,
TypedArrayMarker,
};
/// The JavaScript `%TypedArray%` object. /// The JavaScript `%TypedArray%` object.
/// ///
/// <https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object> /// <https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object>
@ -427,7 +425,7 @@ impl BuiltinTypedArray {
// 4. Let buffer be O.[[ViewedArrayBuffer]]. // 4. Let buffer be O.[[ViewedArrayBuffer]].
// 5. Return buffer. // 5. Return buffer.
Ok(typed_array.viewed_array_buffer().clone().into()) Ok(JsObject::from(typed_array.viewed_array_buffer().clone()).into())
} }
/// `23.2.3.3 get %TypedArray%.prototype.byteLength` /// `23.2.3.3 get %TypedArray%.prototype.byteLength`
@ -574,10 +572,7 @@ impl BuiltinTypedArray {
// b. Let buffer be O.[[ViewedArrayBuffer]]. // b. Let buffer be O.[[ViewedArrayBuffer]].
// c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
let buffer_obj = o.viewed_array_buffer(); let buffer_obj = o.viewed_array_buffer();
let mut buffer_obj_borrow = buffer_obj.borrow_mut(); let mut buffer = buffer_obj.as_buffer_mut();
let mut buffer = buffer_obj_borrow
.as_buffer_mut()
.expect("Already checked for detached buffer");
let Some(buffer) = buffer.data_mut() else { let Some(buffer) = buffer.data_mut() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("Buffer of the typed array is detached") .with_message("Buffer of the typed array is detached")
@ -2060,34 +2055,14 @@ impl BuiltinTypedArray {
// 18. If IsSharedArrayBuffer(srcBuffer) is true, IsSharedArrayBuffer(targetBuffer) is true, // 18. If IsSharedArrayBuffer(srcBuffer) is true, IsSharedArrayBuffer(targetBuffer) is true,
// and srcBuffer.[[ArrayBufferData]] is targetBuffer.[[ArrayBufferData]], let // and srcBuffer.[[ArrayBufferData]] is targetBuffer.[[ArrayBufferData]], let
// sameSharedArrayBuffer be true; otherwise, let sameSharedArrayBuffer be false. // sameSharedArrayBuffer be true; otherwise, let sameSharedArrayBuffer be false.
let same = if JsObject::equals(&src_buffer_obj, &target_buffer_obj) {
true
} else {
let src_buffer_obj = src_buffer_obj.borrow();
let src_buffer = src_buffer_obj.as_buffer().expect("Must be an array buffer");
let target_buffer_obj = target_buffer_obj.borrow();
let target_buffer = target_buffer_obj
.as_buffer()
.expect("Must be an array buffer");
match (src_buffer, target_buffer) {
(BufferRef::SharedBuffer(src), BufferRef::SharedBuffer(dest)) => {
ptr::eq(src.data(), dest.data())
}
(_, _) => false,
}
};
// 19. If SameValue(srcBuffer, targetBuffer) is true or sameSharedArrayBuffer is true, then // 19. If SameValue(srcBuffer, targetBuffer) is true or sameSharedArrayBuffer is true, then
let src_byte_index = if same { let src_byte_index = if BufferObject::equals(&src_buffer_obj, &target_buffer_obj) {
// a. Let srcByteLength be source.[[ByteLength]]. // a. Let srcByteLength be source.[[ByteLength]].
let src_byte_offset = src_byte_offset as usize; let src_byte_offset = src_byte_offset as usize;
let src_byte_length = source_array.byte_length() as usize; let src_byte_length = source_array.byte_length() as usize;
let s = { let s = {
let slice = src_buffer_obj.borrow(); let slice = src_buffer_obj.as_buffer();
let slice = slice.as_buffer().expect("Must be an array buffer");
let slice = slice.data().expect("Already checked for detached buffer"); let slice = slice.data().expect("Already checked for detached buffer");
// b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%). // b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%).
@ -2096,8 +2071,7 @@ impl BuiltinTypedArray {
.subslice(src_byte_offset..src_byte_offset + src_byte_length) .subslice(src_byte_offset..src_byte_offset + src_byte_length)
.clone(context)? .clone(context)?
}; };
// TODO: skip this upcast src_buffer_obj = BufferObject::Buffer(s);
src_buffer_obj = s.upcast();
// d. Let srcByteIndex be 0. // d. Let srcByteIndex be 0.
0 0
@ -2112,16 +2086,12 @@ impl BuiltinTypedArray {
// 22. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset. // 22. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
let target_byte_index = target_offset * target_element_size + target_byte_offset; let target_byte_index = target_offset * target_element_size + target_byte_offset;
let src_buffer = src_buffer_obj.borrow(); let src_buffer = src_buffer_obj.as_buffer();
let src_buffer = src_buffer.as_buffer().expect("Must be an array buffer");
let src_buffer = src_buffer let src_buffer = src_buffer
.data() .data()
.expect("Already checked for detached buffer"); .expect("Already checked for detached buffer");
let mut target_buffer = target_buffer_obj.borrow_mut(); let mut target_buffer = target_buffer_obj.as_buffer_mut();
let mut target_buffer = target_buffer
.as_buffer_mut()
.expect("Must be an array buffer");
let mut target_buffer = target_buffer let mut target_buffer = target_buffer
.data_mut() .data_mut()
.expect("Already checked for detached buffer"); .expect("Already checked for detached buffer");
@ -2255,7 +2225,7 @@ impl BuiltinTypedArray {
let target_index = target_offset + k; let target_index = target_offset + k;
// d. Perform ? IntegerIndexedElementSet(target, targetIndex, value). // d. Perform ? IntegerIndexedElementSet(target, targetIndex, value).
integer_indexed_element_set(target, target_index as f64, &value, &mut context.into())?; typed_array_set_element(target, target_index as f64, &value, &mut context.into())?;
// e. Set k to k + 1. // e. Set k to k + 1.
} }
@ -2377,18 +2347,12 @@ impl BuiltinTypedArray {
} else { } else {
// i. Let srcBuffer be O.[[ViewedArrayBuffer]]. // i. Let srcBuffer be O.[[ViewedArrayBuffer]].
let src_buffer_obj = o.viewed_array_buffer(); let src_buffer_obj = o.viewed_array_buffer();
let src_buffer_obj_borrow = src_buffer_obj.borrow(); let src_buffer = src_buffer_obj.as_buffer();
let src_buffer = src_buffer_obj_borrow
.as_buffer()
.expect("view must be a buffer");
let src_buffer = src_buffer.data().expect("cannot be detached here"); let src_buffer = src_buffer.data().expect("cannot be detached here");
// ii. Let targetBuffer be A.[[ViewedArrayBuffer]]. // ii. Let targetBuffer be A.[[ViewedArrayBuffer]].
let target_buffer_obj = a_array.viewed_array_buffer(); let target_buffer_obj = a_array.viewed_array_buffer();
let mut target_buffer_obj_borrow = target_buffer_obj.borrow_mut(); let mut target_buffer = target_buffer_obj.as_buffer_mut();
let mut target_buffer = target_buffer_obj_borrow
.as_buffer_mut()
.expect("view must be a buffer");
let mut target_buffer = target_buffer.data_mut().expect("cannot be detached here"); let mut target_buffer = target_buffer.data_mut().expect("cannot be detached here");
// iii. Let elementSize be the Element Size value specified in Table 73 for Element Type srcType. // iii. Let elementSize be the Element Size value specified in Table 73 for Element Type srcType.
@ -2713,7 +2677,7 @@ impl BuiltinTypedArray {
obj, obj,
o.kind(), o.kind(),
&[ &[
buffer.clone().into(), JsObject::from(buffer.clone()).into(),
begin_byte_offset.into(), begin_byte_offset.into(),
new_length.into(), new_length.into(),
], ],
@ -3069,9 +3033,8 @@ impl BuiltinTypedArray {
// 9. Set O.[[ArrayLength]] to length. // 9. Set O.[[ArrayLength]] to length.
// 10. Return O. // 10. Return O.
// TODO: skip this upcast.
Ok(TypedArray::new( Ok(TypedArray::new(
data.upcast(), BufferObject::Buffer(data),
T::ERASED, T::ERASED,
0, 0,
byte_length, byte_length,
@ -3153,18 +3116,14 @@ impl BuiltinTypedArray {
/// [spec]: https://tc39.es/ecma262/#sec-initializetypedarrayfromtypedarray /// [spec]: https://tc39.es/ecma262/#sec-initializetypedarrayfromtypedarray
pub(super) fn initialize_from_typed_array<T: TypedArrayMarker>( pub(super) fn initialize_from_typed_array<T: TypedArrayMarker>(
proto: JsObject, proto: JsObject,
src_array: &JsObject, src_array: &JsObject<TypedArray>,
context: &mut Context, context: &mut Context,
) -> JsResult<JsObject> { ) -> JsResult<JsObject> {
let src_array = src_array.borrow(); let src_array = src_array.borrow();
let src_array = src_array let src_array = &src_array.data;
.downcast_ref::<TypedArray>()
.expect("this must be a typed array");
let src_data = src_array.viewed_array_buffer(); let src_data = src_array.viewed_array_buffer();
let src_data = src_data.borrow(); let src_data = src_data.as_buffer();
let src_data = src_data
.as_buffer()
.expect("integer indexed must have a buffer");
// 1. Let srcData be srcArray.[[ViewedArrayBuffer]]. // 1. Let srcData be srcArray.[[ViewedArrayBuffer]].
// 2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception. // 2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
@ -3288,12 +3247,11 @@ impl BuiltinTypedArray {
// 13. Set O.[[ByteLength]] to byteLength. // 13. Set O.[[ByteLength]] to byteLength.
// 14. Set O.[[ByteOffset]] to 0. // 14. Set O.[[ByteOffset]] to 0.
// 15. Set O.[[ArrayLength]] to elementLength. // 15. Set O.[[ArrayLength]] to elementLength.
// TODO: Skip this upcast.
let obj = JsObject::from_proto_and_data_with_shared_shape( let obj = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(), context.root_shape(),
proto, proto,
TypedArray::new( TypedArray::new(
new_buffer.upcast(), BufferObject::Buffer(new_buffer),
element_type, element_type,
0, 0,
byte_length, byte_length,
@ -3313,7 +3271,7 @@ impl BuiltinTypedArray {
/// [spec]: https://tc39.es/ecma262/#sec-initializetypedarrayfromarraybuffer /// [spec]: https://tc39.es/ecma262/#sec-initializetypedarrayfromarraybuffer
pub(super) fn initialize_from_array_buffer<T: TypedArrayMarker>( pub(super) fn initialize_from_array_buffer<T: TypedArrayMarker>(
proto: JsObject, proto: JsObject,
buffer: JsObject, buffer: BufferObject,
byte_offset: &JsValue, byte_offset: &JsValue,
length: &JsValue, length: &JsValue,
context: &mut Context, context: &mut Context,
@ -3342,10 +3300,9 @@ impl BuiltinTypedArray {
// 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
// 6. Let bufferByteLength be buffer.[[ArrayBufferByteLength]]. // 6. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
let buffer_byte_length = { let buffer_byte_length = {
let buffer_borrow = buffer.borrow(); let buffer = buffer.as_buffer();
let buffer_array = buffer_borrow.as_buffer().expect("Must be a buffer");
let Some(data) = buffer_array.data() else { let Some(data) = buffer.data() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("Cannot construct typed array from detached buffer") .with_message("Cannot construct typed array from detached buffer")
.into()); .into());

6
core/engine/src/builtins/typed_array/element/mod.rs

@ -277,7 +277,8 @@ macro_rules! element {
} }
unsafe fn read(buffer: SliceRef<'_>) -> ElementRef<'_, Self> { unsafe fn read(buffer: SliceRef<'_>) -> ElementRef<'_, Self> {
if cfg!(debug_assertions) { #[cfg(debug_assertions)]
{
assert!(buffer.len() >= std::mem::size_of::<Self>()); assert!(buffer.len() >= std::mem::size_of::<Self>());
assert!(buffer.addr() % std::mem::align_of::<Self>() == 0); assert!(buffer.addr() % std::mem::align_of::<Self>() == 0);
} }
@ -293,7 +294,8 @@ macro_rules! element {
} }
unsafe fn read_mut(buffer: SliceRefMut<'_>) -> ElementRefMut<'_, Self> { unsafe fn read_mut(buffer: SliceRefMut<'_>) -> ElementRefMut<'_, Self> {
if cfg!(debug_assertions) { #[cfg(debug_assertions)]
{
assert!(buffer.len() >= std::mem::size_of::<Self>()); assert!(buffer.len() >= std::mem::size_of::<Self>());
assert!(buffer.addr() % std::mem::align_of::<Self>() == 0); assert!(buffer.addr() % std::mem::align_of::<Self>() == 0);
} }

124
core/engine/src/builtins/typed_array/mod.rs

@ -28,15 +28,16 @@ use crate::{
value::{JsValue, Numeric}, value::{JsValue, Numeric},
Context, JsArgs, JsResult, JsString, Context, JsArgs, JsResult, JsString,
}; };
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
mod builtin; mod builtin;
mod element; mod element;
mod integer_indexed_object; mod object;
pub(crate) use builtin::{is_valid_integer_index, BuiltinTypedArray}; pub(crate) use builtin::{is_valid_integer_index, BuiltinTypedArray};
pub(crate) use element::{Atomic, ClampedU8, Element}; pub(crate) use element::{Atomic, ClampedU8, Element};
pub use integer_indexed_object::{IntegerIndexed, TypedArray}; pub use object::TypedArray;
pub(crate) trait TypedArrayMarker { pub(crate) trait TypedArrayMarker {
type Element: Element; type Element: Element;
@ -137,18 +138,39 @@ impl<T: TypedArrayMarker> BuiltInConstructor for T {
let first_argument = &args[0]; let first_argument = &args[0];
// b. If Type(firstArgument) is Object, then // b. If Type(firstArgument) is Object, then
if let Some(first_argument) = first_argument.as_object() { let Some(first_argument) = first_argument.as_object() else {
// i. Let O be ? AllocateTypedArray(constructorName, NewTarget, proto). // c. Else,
let proto = // i. Assert: firstArgument is not an Object.
get_prototype_from_constructor(new_target, T::STANDARD_CONSTRUCTOR, context)?; // Ensured by the let-else
// ii. Let elementLength be ? ToIndex(firstArgument).
let element_length = first_argument.to_index(context)?;
// iii. Return ? AllocateTypedArray(constructorName, NewTarget, proto, elementLength).
return BuiltinTypedArray::allocate::<T>(new_target, element_length, context)
.map(JsValue::from);
};
// ii. If firstArgument has a [[TypedArrayName]] internal slot, then let first_argument = first_argument.clone();
let o = if first_argument.is::<TypedArray>() {
// i. Let O be ? AllocateTypedArray(constructorName, NewTarget, proto).
let proto = get_prototype_from_constructor(new_target, T::STANDARD_CONSTRUCTOR, context)?;
// ii. If firstArgument has a [[TypedArrayName]] internal slot, then
let first_argument = match first_argument.downcast::<TypedArray>() {
Ok(arr) => {
// 1. Perform ? InitializeTypedArrayFromTypedArray(O, firstArgument). // 1. Perform ? InitializeTypedArrayFromTypedArray(O, firstArgument).
BuiltinTypedArray::initialize_from_typed_array::<T>(proto, first_argument, context)?
} else if first_argument.is_buffer() {
// iii. Else if firstArgument has an [[ArrayBufferData]] internal slot, then
// v. Return O.
return BuiltinTypedArray::initialize_from_typed_array::<T>(proto, &arr, context)
.map(JsValue::from);
}
Err(obj) => obj,
};
// iii. Else if firstArgument has an [[ArrayBufferData]] internal slot, then
let first_argument = match first_argument.into_buffer_object() {
Ok(buf) => {
// 1. If numberOfArgs > 1, let byteOffset be args[1]; else let byteOffset be undefined. // 1. If numberOfArgs > 1, let byteOffset be args[1]; else let byteOffset be undefined.
let byte_offset = args.get_or_undefined(1); let byte_offset = args.get_or_undefined(1);
@ -156,59 +178,46 @@ impl<T: TypedArrayMarker> BuiltInConstructor for T {
let length = args.get_or_undefined(2); let length = args.get_or_undefined(2);
// 3. Perform ? InitializeTypedArrayFromArrayBuffer(O, firstArgument, byteOffset, length). // 3. Perform ? InitializeTypedArrayFromArrayBuffer(O, firstArgument, byteOffset, length).
BuiltinTypedArray::initialize_from_array_buffer::<T>(
// v. Return O.
return BuiltinTypedArray::initialize_from_array_buffer::<T>(
proto, proto,
first_argument.clone(), buf,
byte_offset, byte_offset,
length, length,
context, context,
)? )
} else { .map(JsValue::from);
// iv. Else, }
Err(obj) => obj,
// 1. Assert: Type(firstArgument) is Object and firstArgument does not have };
// either a [[TypedArrayName]] or an [[ArrayBufferData]] internal slot.
// 2. Let usingIterator be ? GetMethod(firstArgument, @@iterator).
let first_argument_v = JsValue::from(first_argument.clone());
let using_iterator = first_argument_v.get_method(JsSymbol::iterator(), context)?;
// 3. If usingIterator is not undefined, then
if let Some(using_iterator) = using_iterator {
// a. Let values be ? IterableToList(firstArgument, usingIterator).
let values =
iterable_to_list(context, &first_argument_v, Some(using_iterator))?;
// b. Perform ? InitializeTypedArrayFromList(O, values).
BuiltinTypedArray::initialize_from_list::<T>(proto, values, context)?
} else {
// 4. Else,
// a. NOTE: firstArgument is not an Iterable so assume it is already an array-like object.
// b. Perform ? InitializeTypedArrayFromArrayLike(O, firstArgument).
BuiltinTypedArray::initialize_from_array_like::<T>(
proto,
first_argument,
context,
)?
}
};
// v. Return O.
Ok(o.into())
} else {
// c. Else,
// i. Assert: firstArgument is not an Object. // iv. Else,
assert!(!first_argument.is_object(), "firstArgument was an object");
// ii. Let elementLength be ? ToIndex(firstArgument). // 1. Assert: Type(firstArgument) is Object and firstArgument does not have
let element_length = first_argument.to_index(context)?; // either a [[TypedArrayName]] or an [[ArrayBufferData]] internal slot.
// iii. Return ? AllocateTypedArray(constructorName, NewTarget, proto, elementLength). // 2. Let usingIterator be ? GetMethod(firstArgument, @@iterator).
Ok(BuiltinTypedArray::allocate::<T>(new_target, element_length, context)?.into())
let using_iterator = first_argument.get_method(JsSymbol::iterator(), context)?;
// 3. If usingIterator is not undefined, then
if let Some(using_iterator) = using_iterator {
// a. Let values be ? IterableToList(firstArgument, usingIterator).
let values = iterable_to_list(context, &first_argument.into(), Some(using_iterator))?;
// b. Perform ? InitializeTypedArrayFromList(O, values).
BuiltinTypedArray::initialize_from_list::<T>(proto, values, context)
} else {
// 4. Else,
// a. NOTE: firstArgument is not an Iterable so assume it is already an array-like object.
// b. Perform ? InitializeTypedArrayFromArrayLike(O, firstArgument).
BuiltinTypedArray::initialize_from_array_like::<T>(proto, &first_argument, context)
} }
.map(JsValue::from)
// v. Return O.
} }
} }
@ -330,7 +339,8 @@ pub(crate) enum ContentType {
} }
/// List of all typed array kinds. /// List of all typed array kinds.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Trace, Finalize)]
#[boa_gc(empty_trace)]
pub(crate) enum TypedArrayKind { pub(crate) enum TypedArrayKind {
Int8, Int8,
Uint8, Uint8,

136
core/engine/src/builtins/typed_array/integer_indexed_object.rs → core/engine/src/builtins/typed_array/object.rs

@ -1,9 +1,9 @@
//! This module implements the `Integer-Indexed` exotic object. //! This module implements the `TypedArray` exotic object.
use std::sync::atomic; use std::sync::atomic;
use crate::{ use crate::{
builtins::Number, builtins::{array_buffer::BufferObject, Number},
object::{ object::{
internal_methods::{ internal_methods::{
ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property, ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property,
@ -20,22 +20,16 @@ use boa_macros::utf16;
use super::{is_valid_integer_index, TypedArrayKind}; use super::{is_valid_integer_index, TypedArrayKind};
/// An `IntegerIndexed` object is just an alias for a `TypedArray` object. /// A `TypedArray` object is an exotic object that performs special handling of integer
pub type IntegerIndexed = TypedArray;
/// A `TypedArrayObject` is an exotic object that performs special handling of integer
/// index property keys. /// index property keys.
/// ///
/// This is also called an `IntegerIndexed` object in the specification.
///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects /// [spec]: https://tc39.es/ecma262/#sec-typedarray-exotic-objects
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Clone, Trace, Finalize)]
pub struct TypedArray { pub struct TypedArray {
viewed_array_buffer: JsObject, viewed_array_buffer: BufferObject,
#[unsafe_ignore_trace]
kind: TypedArrayKind, kind: TypedArrayKind,
byte_offset: u64, byte_offset: u64,
byte_length: u64, byte_length: u64,
@ -45,13 +39,13 @@ pub struct TypedArray {
impl JsData for TypedArray { impl JsData for TypedArray {
fn internal_methods(&self) -> &'static InternalObjectMethods { fn internal_methods(&self) -> &'static InternalObjectMethods {
static METHODS: InternalObjectMethods = InternalObjectMethods { static METHODS: InternalObjectMethods = InternalObjectMethods {
__get_own_property__: integer_indexed_exotic_get_own_property, __get_own_property__: typed_array_exotic_get_own_property,
__has_property__: integer_indexed_exotic_has_property, __has_property__: typed_array_exotic_has_property,
__define_own_property__: integer_indexed_exotic_define_own_property, __define_own_property__: typed_array_exotic_define_own_property,
__get__: integer_indexed_exotic_get, __get__: typed_array_exotic_get,
__set__: integer_indexed_exotic_set, __set__: typed_array_exotic_set,
__delete__: integer_indexed_exotic_delete, __delete__: typed_array_exotic_delete,
__own_property_keys__: integer_indexed_exotic_own_property_keys, __own_property_keys__: typed_array_exotic_own_property_keys,
..ORDINARY_INTERNAL_METHODS ..ORDINARY_INTERNAL_METHODS
}; };
@ -61,7 +55,7 @@ impl JsData for TypedArray {
impl TypedArray { impl TypedArray {
pub(crate) const fn new( pub(crate) const fn new(
viewed_array_buffer: JsObject, viewed_array_buffer: BufferObject,
kind: TypedArrayKind, kind: TypedArrayKind,
byte_offset: u64, byte_offset: u64,
byte_length: u64, byte_length: u64,
@ -85,37 +79,33 @@ impl TypedArray {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-isdetachedbuffer /// [spec]: https://tc39.es/ecma262/#sec-isdetachedbuffer
pub(crate) fn is_detached(&self) -> bool { pub(crate) fn is_detached(&self) -> bool {
self.viewed_array_buffer self.viewed_array_buffer.as_buffer().is_detached()
.borrow()
.as_buffer()
.expect("Typed array must have internal array buffer object")
.is_detached()
} }
/// Get the integer indexed object's byte offset. /// Get the `TypedArray` object's byte offset.
#[must_use] #[must_use]
pub const fn byte_offset(&self) -> u64 { pub const fn byte_offset(&self) -> u64 {
self.byte_offset self.byte_offset
} }
/// Get the integer indexed object's typed array kind. /// Get the `TypedArray` object's typed array kind.
pub(crate) const fn kind(&self) -> TypedArrayKind { pub(crate) const fn kind(&self) -> TypedArrayKind {
self.kind self.kind
} }
/// Get a reference to the integer indexed object's viewed array buffer. /// Get a reference to the `TypedArray` object's viewed array buffer.
#[must_use] #[must_use]
pub const fn viewed_array_buffer(&self) -> &JsObject { pub(crate) const fn viewed_array_buffer(&self) -> &BufferObject {
&self.viewed_array_buffer &self.viewed_array_buffer
} }
/// Get the integer indexed object's byte length. /// Get the `TypedArray` object's byte length.
#[must_use] #[must_use]
pub const fn byte_length(&self) -> u64 { pub const fn byte_length(&self) -> u64 {
self.byte_length self.byte_length
} }
/// Get the integer indexed object's array length. /// Get the `TypedArray` object's array length.
#[must_use] #[must_use]
pub const fn array_length(&self) -> u64 { pub const fn array_length(&self) -> u64 {
self.array_length self.array_length
@ -146,13 +136,13 @@ fn canonical_numeric_index_string(argument: &JsString) -> Option<f64> {
None None
} }
/// `[[GetOwnProperty]]` internal method for Integer-Indexed exotic objects. /// `[[GetOwnProperty]]` internal method for `TypedArray` exotic objects.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-getownproperty-p /// [spec]: https://tc39.es/ecma262/#sec-typedarray-getownproperty
pub(crate) fn integer_indexed_exotic_get_own_property( pub(crate) fn typed_array_exotic_get_own_property(
obj: &JsObject, obj: &JsObject,
key: &PropertyKey, key: &PropertyKey,
context: &mut InternalMethodContext<'_>, context: &mut InternalMethodContext<'_>,
@ -170,7 +160,7 @@ pub(crate) fn integer_indexed_exotic_get_own_property(
// 1.b. If numericIndex is not undefined, then // 1.b. If numericIndex is not undefined, then
if let Some(numeric_index) = p { if let Some(numeric_index) = p {
// i. Let value be IntegerIndexedElementGet(O, numericIndex). // i. Let value be IntegerIndexedElementGet(O, numericIndex).
let value = integer_indexed_element_get(obj, numeric_index); let value = typed_array_get_element(obj, numeric_index);
// ii. If value is undefined, return undefined. // ii. If value is undefined, return undefined.
// iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }. // iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
@ -188,13 +178,13 @@ pub(crate) fn integer_indexed_exotic_get_own_property(
ordinary_get_own_property(obj, key, context) ordinary_get_own_property(obj, key, context)
} }
/// `[[HasProperty]]` internal method for Integer-Indexed exotic objects. /// `[[HasProperty]]` internal method for `TypedArray` exotic objects.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-hasproperty-p /// [spec]: https://tc39.es/ecma262/#sec-typedarray-hasproperty
pub(crate) fn integer_indexed_exotic_has_property( pub(crate) fn typed_array_exotic_has_property(
obj: &JsObject, obj: &JsObject,
key: &PropertyKey, key: &PropertyKey,
context: &mut InternalMethodContext<'_>, context: &mut InternalMethodContext<'_>,
@ -218,13 +208,13 @@ pub(crate) fn integer_indexed_exotic_has_property(
ordinary_has_property(obj, key, context) ordinary_has_property(obj, key, context)
} }
/// `[[DefineOwnProperty]]` internal method for Integer-Indexed exotic objects. /// `[[DefineOwnProperty]]` internal method for `TypedArray` exotic objects.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-defineownproperty-p-desc /// [spec]: https://tc39.es/ecma262/#sec-typedarray-defineownproperty
pub(crate) fn integer_indexed_exotic_define_own_property( pub(crate) fn typed_array_exotic_define_own_property(
obj: &JsObject, obj: &JsObject,
key: &PropertyKey, key: &PropertyKey,
desc: PropertyDescriptor, desc: PropertyDescriptor,
@ -269,7 +259,7 @@ pub(crate) fn integer_indexed_exotic_define_own_property(
// vi. If Desc has a [[Value]] field, perform ? IntegerIndexedElementSet(O, numericIndex, Desc.[[Value]]). // vi. If Desc has a [[Value]] field, perform ? IntegerIndexedElementSet(O, numericIndex, Desc.[[Value]]).
if let Some(value) = desc.value() { if let Some(value) = desc.value() {
integer_indexed_element_set(obj, numeric_index, value, context)?; typed_array_set_element(obj, numeric_index, value, context)?;
} }
// vii. Return true. // vii. Return true.
@ -280,13 +270,13 @@ pub(crate) fn integer_indexed_exotic_define_own_property(
ordinary_define_own_property(obj, key, desc, context) ordinary_define_own_property(obj, key, desc, context)
} }
/// Internal method `[[Get]]` for Integer-Indexed exotic objects. /// Internal method `[[Get]]` for `TypedArray` exotic objects.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-get-p-receiver /// [spec]: https://tc39.es/ecma262/#sec-typedarray-get
pub(crate) fn integer_indexed_exotic_get( pub(crate) fn typed_array_exotic_get(
obj: &JsObject, obj: &JsObject,
key: &PropertyKey, key: &PropertyKey,
receiver: JsValue, receiver: JsValue,
@ -305,20 +295,20 @@ pub(crate) fn integer_indexed_exotic_get(
// 1.b. If numericIndex is not undefined, then // 1.b. If numericIndex is not undefined, then
if let Some(numeric_index) = p { if let Some(numeric_index) = p {
// i. Return IntegerIndexedElementGet(O, numericIndex). // i. Return IntegerIndexedElementGet(O, numericIndex).
return Ok(integer_indexed_element_get(obj, numeric_index).unwrap_or_default()); return Ok(typed_array_get_element(obj, numeric_index).unwrap_or_default());
} }
// 2. Return ? OrdinaryGet(O, P, Receiver). // 2. Return ? OrdinaryGet(O, P, Receiver).
ordinary_get(obj, key, receiver, context) ordinary_get(obj, key, receiver, context)
} }
/// Internal method `[[Set]]` for Integer-Indexed exotic objects. /// Internal method `[[Set]]` for `TypedArray` exotic objects.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver /// [spec]: https://tc39.es/ecma262/#sec-typedarray-set
pub(crate) fn integer_indexed_exotic_set( pub(crate) fn typed_array_exotic_set(
obj: &JsObject, obj: &JsObject,
key: PropertyKey, key: PropertyKey,
value: JsValue, value: JsValue,
@ -340,7 +330,7 @@ pub(crate) fn integer_indexed_exotic_set(
// i. If SameValue(O, Receiver) is true, then // i. If SameValue(O, Receiver) is true, then
if JsValue::same_value(&obj.clone().into(), &receiver) { if JsValue::same_value(&obj.clone().into(), &receiver) {
// 1. Perform ? IntegerIndexedElementSet(O, numericIndex, V). // 1. Perform ? IntegerIndexedElementSet(O, numericIndex, V).
integer_indexed_element_set(obj, numeric_index, &value, context)?; typed_array_set_element(obj, numeric_index, &value, context)?;
// 2. Return true. // 2. Return true.
return Ok(true); return Ok(true);
@ -356,13 +346,13 @@ pub(crate) fn integer_indexed_exotic_set(
ordinary_set(obj, key, value, receiver, context) ordinary_set(obj, key, value, receiver, context)
} }
/// Internal method `[[Delete]]` for Integer-Indexed exotic objects. /// Internal method `[[Delete]]` for `TypedArray` exotic objects.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-delete-p /// [spec]: https://tc39.es/ecma262/#sec-typedarray-delete
pub(crate) fn integer_indexed_exotic_delete( pub(crate) fn typed_array_exotic_delete(
obj: &JsObject, obj: &JsObject,
key: &PropertyKey, key: &PropertyKey,
context: &mut InternalMethodContext<'_>, context: &mut InternalMethodContext<'_>,
@ -387,21 +377,21 @@ pub(crate) fn integer_indexed_exotic_delete(
ordinary_delete(obj, key, context) ordinary_delete(obj, key, context)
} }
/// Internal method `[[OwnPropertyKeys]]` for Integer-Indexed exotic objects. /// Internal method `[[OwnPropertyKeys]]` for `TypedArray` exotic objects.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys /// [spec]: https://tc39.es/ecma262/#sec-typedarray-ownpropertykeys
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
pub(crate) fn integer_indexed_exotic_own_property_keys( pub(crate) fn typed_array_exotic_own_property_keys(
obj: &JsObject, obj: &JsObject,
_context: &mut Context, _context: &mut Context,
) -> JsResult<Vec<PropertyKey>> { ) -> JsResult<Vec<PropertyKey>> {
let obj = obj.borrow(); let obj = obj.borrow();
let inner = obj.downcast_ref::<TypedArray>().expect( let inner = obj
"integer indexed exotic method should only be callable from integer indexed objects", .downcast_ref::<TypedArray>()
); .expect("TypedArray exotic method should only be callable from TypedArray objects");
// 1. Let keys be a new empty List. // 1. Let keys be a new empty List.
let mut keys = if inner.is_detached() { let mut keys = if inner.is_detached() {
@ -424,13 +414,13 @@ pub(crate) fn integer_indexed_exotic_own_property_keys(
Ok(keys) Ok(keys)
} }
/// Abstract operation `IntegerIndexedElementGet ( O, index )`. /// Abstract operation `TypedArrayGetElement ( O, index )`.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integerindexedelementget /// [spec]: https://tc39.es/ecma262/sec-typedarraygetelement
fn integer_indexed_element_get(obj: &JsObject, index: f64) -> Option<JsValue> { fn typed_array_get_element(obj: &JsObject, index: f64) -> Option<JsValue> {
// 1. If ! IsValidIntegerIndex(O, index) is false, return undefined. // 1. If ! IsValidIntegerIndex(O, index) is false, return undefined.
if !is_valid_integer_index(obj, index) { if !is_valid_integer_index(obj, index) {
return None; return None;
@ -438,10 +428,9 @@ fn integer_indexed_element_get(obj: &JsObject, index: f64) -> Option<JsValue> {
let inner = obj let inner = obj
.downcast_ref::<TypedArray>() .downcast_ref::<TypedArray>()
.expect("Must be an integer indexed object"); .expect("Must be an TypedArray object");
let buffer = inner.viewed_array_buffer(); let buffer = inner.viewed_array_buffer();
let buffer = buffer.borrow(); let buffer = buffer.as_buffer();
let buffer = buffer.as_buffer().expect("Must be a buffer");
let buffer = buffer let buffer = buffer
.data() .data()
.expect("already checked that it's not detached"); .expect("already checked that it's not detached");
@ -461,7 +450,7 @@ fn integer_indexed_element_get(obj: &JsObject, index: f64) -> Option<JsValue> {
// 7. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, true, Unordered). // 7. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, true, Unordered).
// SAFETY: The integer indexed object guarantees that the buffer is aligned. // SAFETY: The TypedArray object guarantees that the buffer is aligned.
// The call to `is_valid_integer_index` guarantees that the index is in-bounds. // The call to `is_valid_integer_index` guarantees that the index is in-bounds.
let value = unsafe { let value = unsafe {
buffer buffer
@ -472,22 +461,22 @@ fn integer_indexed_element_get(obj: &JsObject, index: f64) -> Option<JsValue> {
Some(value.into()) Some(value.into())
} }
/// Abstract operation `IntegerIndexedElementSet ( O, index, value )`. /// Abstract operation `TypedArraySetElement ( O, index, value )`.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-integerindexedelementset /// [spec]: https://tc39.es/ecma262/#sec-typedarraysetelement
pub(crate) fn integer_indexed_element_set( pub(crate) fn typed_array_set_element(
obj: &JsObject, obj: &JsObject,
index: f64, index: f64,
value: &JsValue, value: &JsValue,
context: &mut InternalMethodContext<'_>, context: &mut InternalMethodContext<'_>,
) -> JsResult<()> { ) -> JsResult<()> {
let obj_borrow = obj.borrow(); let obj_borrow = obj.borrow();
let inner = obj_borrow.downcast_ref::<TypedArray>().expect( let inner = obj_borrow
"integer indexed exotic method should only be callable from integer indexed objects", .downcast_ref::<TypedArray>()
); .expect("TypedArray exotic method should only be callable from TypedArray objects");
// 1. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value). // 1. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
// 2. Otherwise, let numValue be ? ToNumber(value). // 2. Otherwise, let numValue be ? ToNumber(value).
@ -512,15 +501,14 @@ pub(crate) fn integer_indexed_element_set(
let indexed_position = ((index as u64 * size) + offset) as usize; let indexed_position = ((index as u64 * size) + offset) as usize;
let buffer = inner.viewed_array_buffer(); let buffer = inner.viewed_array_buffer();
let mut buffer = buffer.borrow_mut(); let mut buffer = buffer.as_buffer_mut();
let mut buffer = buffer.as_buffer_mut().expect("Must be a buffer");
let mut buffer = buffer let mut buffer = buffer
.data_mut() .data_mut()
.expect("already checked that it's not detached"); .expect("already checked that it's not detached");
// f. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, numValue, true, Unordered). // f. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], indexedPosition, elementType, numValue, true, Unordered).
// SAFETY: The integer indexed object guarantees that the buffer is aligned. // SAFETY: The TypedArray object guarantees that the buffer is aligned.
// The call to `is_valid_integer_index` guarantees that the index is in-bounds. // The call to `is_valid_integer_index` guarantees that the index is in-bounds.
unsafe { unsafe {
buffer buffer

16
core/engine/src/object/builtins/jsarraybuffer.rs

@ -17,6 +17,20 @@ pub struct JsArrayBuffer {
inner: JsObject<ArrayBuffer>, inner: JsObject<ArrayBuffer>,
} }
impl From<JsArrayBuffer> for JsObject<ArrayBuffer> {
#[inline]
fn from(value: JsArrayBuffer) -> Self {
value.inner
}
}
impl From<JsObject<ArrayBuffer>> for JsArrayBuffer {
#[inline]
fn from(value: JsObject<ArrayBuffer>) -> Self {
Self { inner: value }
}
}
// TODO: Add constructors that also take the `detach_key` as argument. // TODO: Add constructors that also take the `detach_key` as argument.
impl JsArrayBuffer { impl JsArrayBuffer {
/// Create a new array buffer with byte length. /// Create a new array buffer with byte length.
@ -157,7 +171,7 @@ impl JsArrayBuffer {
/// ///
/// # Note /// # Note
/// ///
/// This tries to detach the pre-existing `JsArrayBuffer`, meaning the original detached /// This tries to detach the pre-existing `JsArrayBuffer`, meaning the original detach
/// key is required. By default, the key is set to `undefined`. /// key is required. By default, the key is set to `undefined`.
/// ///
/// ``` /// ```

91
core/engine/src/object/builtins/jsdataview.rs

@ -1,6 +1,6 @@
//! A Rust API wrapper for Boa's `DataView` Builtin ECMAScript Object //! A Rust API wrapper for Boa's `DataView` Builtin ECMAScript Object
use crate::{ use crate::{
builtins::DataView, builtins::{array_buffer::BufferObject, DataView},
context::intrinsics::StandardConstructors, context::intrinsics::StandardConstructors,
object::{ object::{
internal_methods::get_prototype_from_constructor, JsArrayBuffer, JsObject, JsObjectType, internal_methods::get_prototype_from_constructor, JsArrayBuffer, JsObject, JsObjectType,
@ -33,8 +33,23 @@ use std::ops::Deref;
/// # } /// # }
/// ``` /// ```
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Clone, Trace, Finalize)]
#[boa_gc(unsafe_no_drop)]
pub struct JsDataView { pub struct JsDataView {
inner: JsObject, inner: JsObject<DataView>,
}
impl From<JsDataView> for JsObject<DataView> {
#[inline]
fn from(value: JsDataView) -> Self {
value.inner
}
}
impl From<JsObject<DataView>> for JsDataView {
#[inline]
fn from(value: JsObject<DataView>) -> Self {
Self { inner: value }
}
} }
impl JsDataView { impl JsDataView {
@ -90,12 +105,11 @@ impl JsDataView {
let prototype = let prototype =
get_prototype_from_constructor(&constructor, StandardConstructors::data_view, context)?; get_prototype_from_constructor(&constructor, StandardConstructors::data_view, context)?;
let obj = JsObject::from_proto_and_data_with_shared_shape( let obj = JsObject::new(
context.root_shape(), context.root_shape(),
prototype, prototype,
DataView { DataView {
// TODO: Remove upcast. viewed_array_buffer: BufferObject::Buffer(array_buffer.into()),
viewed_array_buffer: array_buffer.into(),
byte_length, byte_length,
byte_offset, byte_offset,
}, },
@ -107,32 +121,33 @@ impl JsDataView {
/// Create a new `JsDataView` object from an existing object. /// Create a new `JsDataView` object from an existing object.
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.is::<DataView>() { object
Ok(Self { inner: object }) .downcast::<DataView>()
} else { .map(|inner| Self { inner })
Err(JsNativeError::typ() .map_err(|_| {
.with_message("object is not a DataView") JsNativeError::typ()
.into()) .with_message("object is not a DataView")
} .into()
})
} }
/// Returns the `viewed_array_buffer` field for [`JsDataView`] /// Returns the `viewed_array_buffer` field for [`JsDataView`]
#[inline] #[inline]
pub fn buffer(&self, context: &mut Context) -> JsResult<JsValue> { pub fn buffer(&self, context: &mut Context) -> JsResult<JsValue> {
DataView::get_buffer(&self.inner.clone().into(), &[], context) DataView::get_buffer(&self.inner.clone().upcast().into(), &[], context)
} }
/// Returns the `byte_length` property of [`JsDataView`] as a u64 integer /// Returns the `byte_length` property of [`JsDataView`] as a u64 integer
#[inline] #[inline]
pub fn byte_length(&self, context: &mut Context) -> JsResult<u64> { pub fn byte_length(&self, context: &mut Context) -> JsResult<u64> {
DataView::get_byte_length(&self.inner.clone().into(), &[], context) DataView::get_byte_length(&self.inner.clone().upcast().into(), &[], context)
.map(|v| v.as_number().expect("value should be a number") as u64) .map(|v| v.as_number().expect("value should be a number") as u64)
} }
/// Returns the `byte_offset` field property of [`JsDataView`] as a u64 integer /// Returns the `byte_offset` field property of [`JsDataView`] as a u64 integer
#[inline] #[inline]
pub fn byte_offset(&self, context: &mut Context) -> JsResult<u64> { pub fn byte_offset(&self, context: &mut Context) -> JsResult<u64> {
DataView::get_byte_offset(&self.inner.clone().into(), &[], context) DataView::get_byte_offset(&self.inner.clone().upcast().into(), &[], context)
.map(|v| v.as_number().expect("byte_offset value must be a number") as u64) .map(|v| v.as_number().expect("byte_offset value must be a number") as u64)
} }
@ -145,7 +160,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<i64> { ) -> JsResult<i64> {
DataView::get_big_int64( DataView::get_big_int64(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -161,7 +176,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<u64> { ) -> JsResult<u64> {
DataView::get_big_uint64( DataView::get_big_uint64(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -177,7 +192,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<f32> { ) -> JsResult<f32> {
DataView::get_float32( DataView::get_float32(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -193,7 +208,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<f64> { ) -> JsResult<f64> {
DataView::get_float64( DataView::get_float64(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -209,7 +224,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<i8> { ) -> JsResult<i8> {
DataView::get_int8( DataView::get_int8(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -225,7 +240,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<i16> { ) -> JsResult<i16> {
DataView::get_int16( DataView::get_int16(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -241,7 +256,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<i32> { ) -> JsResult<i32> {
DataView::get_int32( DataView::get_int32(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -257,7 +272,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<u8> { ) -> JsResult<u8> {
DataView::get_uint8( DataView::get_uint8(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -273,7 +288,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<u16> { ) -> JsResult<u16> {
DataView::get_uint16( DataView::get_uint16(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -289,7 +304,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<u32> { ) -> JsResult<u32> {
DataView::get_uint32( DataView::get_uint32(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), is_little_endian.into()], &[byte_offset.into(), is_little_endian.into()],
context, context,
) )
@ -306,7 +321,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_big_int64( DataView::set_big_int64(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -322,7 +337,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_big_uint64( DataView::set_big_uint64(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -338,7 +353,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_float32( DataView::set_float32(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -354,7 +369,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_float64( DataView::set_float64(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -370,7 +385,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_int8( DataView::set_int8(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -386,7 +401,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_int16( DataView::set_int16(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -402,7 +417,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_int32( DataView::set_int32(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -418,7 +433,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_uint8( DataView::set_uint8(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -434,7 +449,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_uint16( DataView::set_uint16(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -450,7 +465,7 @@ impl JsDataView {
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
DataView::set_uint32( DataView::set_uint32(
&self.inner.clone().into(), &self.inner.clone().upcast().into(),
&[byte_offset.into(), value.into(), is_little_endian.into()], &[byte_offset.into(), value.into(), is_little_endian.into()],
context, context,
) )
@ -460,19 +475,19 @@ impl JsDataView {
impl From<JsDataView> for JsObject { impl From<JsDataView> for JsObject {
#[inline] #[inline]
fn from(o: JsDataView) -> Self { fn from(o: JsDataView) -> Self {
o.inner.clone() o.inner.upcast()
} }
} }
impl From<JsDataView> for JsValue { impl From<JsDataView> for JsValue {
#[inline] #[inline]
fn from(o: JsDataView) -> Self { fn from(o: JsDataView) -> Self {
o.inner.clone().into() o.inner.upcast().into()
} }
} }
impl Deref for JsDataView { impl Deref for JsDataView {
type Target = JsObject; type Target = JsObject<DataView>;
#[inline] #[inline]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {

49
core/engine/src/object/builtins/jssharedarraybuffer.rs

@ -11,8 +11,23 @@ use std::ops::Deref;
/// `JsSharedArrayBuffer` provides a wrapper for Boa's implementation of the ECMAScript `ArrayBuffer` object /// `JsSharedArrayBuffer` provides a wrapper for Boa's implementation of the ECMAScript `ArrayBuffer` object
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Clone, Trace, Finalize)]
#[boa_gc(unsafe_no_drop)]
pub struct JsSharedArrayBuffer { pub struct JsSharedArrayBuffer {
inner: JsObject, inner: JsObject<SharedArrayBuffer>,
}
impl From<JsSharedArrayBuffer> for JsObject<SharedArrayBuffer> {
#[inline]
fn from(value: JsSharedArrayBuffer) -> Self {
value.inner
}
}
impl From<JsObject<SharedArrayBuffer>> for JsSharedArrayBuffer {
#[inline]
fn from(value: JsObject<SharedArrayBuffer>) -> Self {
JsSharedArrayBuffer { inner: value }
}
} }
impl JsSharedArrayBuffer { impl JsSharedArrayBuffer {
@ -42,8 +57,7 @@ impl JsSharedArrayBuffer {
.shared_array_buffer() .shared_array_buffer()
.prototype(); .prototype();
let inner = let inner = JsObject::new(context.root_shape(), proto, buffer);
JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, buffer);
Self { inner } Self { inner }
} }
@ -55,50 +69,47 @@ impl JsSharedArrayBuffer {
/// the object. /// the object.
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.is::<SharedArrayBuffer>() { object
Ok(Self { inner: object }) .downcast::<SharedArrayBuffer>()
} else { .map(|inner| Self { inner })
Err(JsNativeError::typ() .map_err(|_| {
.with_message("object is not an ArrayBuffer") JsNativeError::typ()
.into()) .with_message("object is not a SharedArrayBuffer")
} .into()
})
} }
/// Returns the byte length of the array buffer. /// Returns the byte length of the array buffer.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn byte_length(&self) -> usize { pub fn byte_length(&self) -> usize {
self.downcast_ref::<SharedArrayBuffer>() self.borrow().data.len()
.expect("should be an array buffer")
.len()
} }
/// Gets the raw buffer of this `JsSharedArrayBuffer`. /// Gets the raw buffer of this `JsSharedArrayBuffer`.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn inner(&self) -> SharedArrayBuffer { pub fn inner(&self) -> SharedArrayBuffer {
self.downcast_ref::<SharedArrayBuffer>() self.borrow().data.clone()
.expect("should be an array buffer")
.clone()
} }
} }
impl From<JsSharedArrayBuffer> for JsObject { impl From<JsSharedArrayBuffer> for JsObject {
#[inline] #[inline]
fn from(o: JsSharedArrayBuffer) -> Self { fn from(o: JsSharedArrayBuffer) -> Self {
o.inner.clone() o.inner.upcast()
} }
} }
impl From<JsSharedArrayBuffer> for JsValue { impl From<JsSharedArrayBuffer> for JsValue {
#[inline] #[inline]
fn from(o: JsSharedArrayBuffer) -> Self { fn from(o: JsSharedArrayBuffer) -> Self {
o.inner.clone().into() o.inner.upcast().into()
} }
} }
impl Deref for JsSharedArrayBuffer { impl Deref for JsSharedArrayBuffer {
type Target = JsObject; type Target = JsObject<SharedArrayBuffer>;
#[inline] #[inline]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {

60
core/engine/src/object/jsobject.rs

@ -8,7 +8,11 @@ use super::{
JsPrototype, NativeObject, Object, PrivateName, PropertyMap, JsPrototype, NativeObject, Object, PrivateName, PropertyMap,
}; };
use crate::{ use crate::{
builtins::{array::ARRAY_EXOTIC_INTERNAL_METHODS, object::OrdinaryObject}, builtins::{
array::ARRAY_EXOTIC_INTERNAL_METHODS,
array_buffer::{ArrayBuffer, BufferObject, SharedArrayBuffer},
object::OrdinaryObject,
},
context::intrinsics::Intrinsics, context::intrinsics::Intrinsics,
error::JsNativeError, error::JsNativeError,
js_string, js_string,
@ -212,6 +216,24 @@ impl JsObject {
} }
} }
/// Downcasts the object's inner data to `T` without verifying the inner type of `T`.
///
/// # Safety
///
/// For this cast to be sound, `self` must contain an instance of `T` inside its inner data.
#[must_use]
pub unsafe fn downcast_unchecked<T: NativeObject>(self) -> JsObject<T> {
let ptr: NonNull<GcBox<VTableObject<T>>> = Gc::into_raw(self.inner).cast();
// SAFETY: The caller guarantees `T` is the original inner data type of the underlying
// object.
unsafe {
JsObject {
inner: Gc::from_raw(ptr),
}
}
}
/// Downcasts a reference to the object, /// Downcasts a reference to the object,
/// if the object is of type `T`. /// if the object is of type `T`.
/// ///
@ -268,18 +290,6 @@ impl JsObject {
std::ptr::eq(self.vtable(), &ARRAY_EXOTIC_INTERNAL_METHODS) std::ptr::eq(self.vtable(), &ARRAY_EXOTIC_INTERNAL_METHODS)
} }
/// Checks if it's an `ArrayBuffer` or `SharedArrayBuffer` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[must_use]
#[track_caller]
pub fn is_buffer(&self) -> bool {
self.borrow().as_buffer().is_some()
}
/// Converts an object to a primitive. /// Converts an object to a primitive.
/// ///
/// Diverges from the spec to prevent a stack overflow when the object is recursive. /// Diverges from the spec to prevent a stack overflow when the object is recursive.
@ -535,6 +545,30 @@ Cannot both specify accessors and a value or writable attribute",
} }
None None
} }
/// Casts to a `BufferObject` if the object is an `ArrayBuffer` or a `SharedArrayBuffer`.
#[inline]
pub(crate) fn into_buffer_object(self) -> Result<BufferObject, JsObject> {
let obj = self.borrow();
if obj.is::<ArrayBuffer>() {
drop(obj);
// SAFETY: We have verified that the inner data of `self` is of type `ArrayBuffer`.
return Ok(BufferObject::Buffer(unsafe {
self.downcast_unchecked::<ArrayBuffer>()
}));
}
if obj.is::<SharedArrayBuffer>() {
drop(obj);
// SAFETY: We have verified that the inner data of `self` is of type `SharedArrayBuffer`.
return Ok(BufferObject::SharedBuffer(unsafe {
self.downcast_unchecked::<SharedArrayBuffer>()
}));
}
drop(obj);
Err(self)
}
} }
impl<T: NativeObject + ?Sized> JsObject<T> { impl<T: NativeObject + ?Sized> JsObject<T> {

28
core/engine/src/object/mod.rs

@ -10,7 +10,6 @@ use thin_vec::ThinVec;
use self::{internal_methods::ORDINARY_INTERNAL_METHODS, shape::Shape}; use self::{internal_methods::ORDINARY_INTERNAL_METHODS, shape::Shape};
use crate::{ use crate::{
builtins::{ builtins::{
array_buffer::{ArrayBuffer, BufferRef, BufferRefMut, SharedArrayBuffer},
function::{ function::{
arguments::{MappedArguments, UnmappedArguments}, arguments::{MappedArguments, UnmappedArguments},
ConstructorKind, ConstructorKind,
@ -352,33 +351,6 @@ impl Object<dyn NativeObject> {
self.data.downcast_mut::<T>() self.data.downcast_mut::<T>()
} }
/// Gets the buffer data if the object is an `ArrayBuffer` or a `SharedArrayBuffer`.
#[inline]
#[must_use]
pub(crate) fn as_buffer(&self) -> Option<BufferRef<'_>> {
if let Some(buffer) = self.downcast_ref::<ArrayBuffer>() {
return Some(BufferRef::Buffer(buffer));
}
self.downcast_ref::<SharedArrayBuffer>()
.map(BufferRef::SharedBuffer)
}
/// Gets the mutable buffer data if the object is an `ArrayBuffer` or a `SharedArrayBuffer`.
#[inline]
pub(crate) fn as_buffer_mut(&mut self) -> Option<BufferRefMut<'_>> {
// Workaround for Problem case 3 of the current borrow checker.
// https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions
if self.is::<ArrayBuffer>() {
match self.downcast_mut::<ArrayBuffer>() {
Some(buffer) => return Some(BufferRefMut::Buffer(buffer)),
None => unreachable!(),
}
}
self.downcast_mut::<SharedArrayBuffer>()
.map(BufferRefMut::SharedBuffer)
}
/// Checks if this object is an `Arguments` object. /// Checks if this object is an `Arguments` object.
pub(crate) fn is_arguments(&self) -> bool { pub(crate) fn is_arguments(&self) -> bool {
self.is::<UnmappedArguments>() || self.is::<MappedArguments>() self.is::<UnmappedArguments>() || self.is::<MappedArguments>()

Loading…
Cancel
Save