Skip to content

Commit

Permalink
Implement non-erased JsObjects (#3618)
Browse files Browse the repository at this point in the history
  • Loading branch information
jedel1043 authored Jan 29, 2024
1 parent 9ffbc03 commit 0872238
Show file tree
Hide file tree
Showing 14 changed files with 335 additions and 261 deletions.
8 changes: 5 additions & 3 deletions core/engine/src/builtins/array_buffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ impl BuiltInConstructor for ArrayBuffer {
let byte_length = args.get_or_undefined(0).to_index(context)?;

// 3. Return ? AllocateArrayBuffer(NewTarget, byteLength).
Ok(Self::allocate(new_target, byte_length, context)?.into())
Ok(Self::allocate(new_target, byte_length, context)?
.upcast()
.into())
}
}

Expand Down Expand Up @@ -384,7 +386,7 @@ impl ArrayBuffer {
constructor: &JsValue,
byte_length: u64,
context: &mut Context,
) -> JsResult<JsObject> {
) -> JsResult<JsObject<ArrayBuffer>> {
// 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%ArrayBuffer.prototype%", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).
let prototype = get_prototype_from_constructor(
constructor,
Expand All @@ -397,7 +399,7 @@ impl ArrayBuffer {

// 3. Set obj.[[ArrayBufferData]] to block.
// 4. Set obj.[[ArrayBufferByteLength]] to byteLength.
let obj = JsObject::from_proto_and_data_with_shared_shape(
let obj = JsObject::new(
context.root_shape(),
prototype,
Self {
Expand Down
8 changes: 3 additions & 5 deletions core/engine/src/builtins/array_buffer/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl SliceRef<'_> {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-clonearraybuffer
pub(crate) fn clone(&self, context: &mut Context) -> JsResult<JsObject> {
pub(crate) fn clone(&self, context: &mut Context) -> JsResult<JsObject<ArrayBuffer>> {
// 1. Assert: IsDetachedBuffer(srcBuffer) is false.

// 2. Let targetBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, srcLength).
Expand All @@ -140,12 +140,10 @@ impl SliceRef<'_> {

// 4. Let targetBlock be targetBuffer.[[ArrayBufferData]].
{
let mut target_buffer = target_buffer
.downcast_mut::<ArrayBuffer>()
.expect("This must be an ArrayBuffer");
let mut target_buffer = target_buffer.borrow_mut();
let target_block = target_buffer
.data
.as_deref_mut()
.data_mut()
.expect("ArrayBuffer cannot be detached here");

// 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).
Expand Down
32 changes: 24 additions & 8 deletions core/engine/src/builtins/typed_array/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2096,7 +2096,8 @@ impl BuiltinTypedArray {
.subslice(src_byte_offset..src_byte_offset + src_byte_length)
.clone(context)?
};
src_buffer_obj = s;
// TODO: skip this upcast
src_buffer_obj = s.upcast();

// d. Let srcByteIndex be 0.
0
Expand Down Expand Up @@ -3068,7 +3069,14 @@ impl BuiltinTypedArray {
// 9. Set O.[[ArrayLength]] to length.

// 10. Return O.
Ok(TypedArray::new(data, T::ERASED, 0, byte_length, length))
// TODO: skip this upcast.
Ok(TypedArray::new(
data.upcast(),
T::ERASED,
0,
byte_length,
length,
))
}

/// <https://tc39.es/ecma262/#sec-initializetypedarrayfromlist>
Expand Down Expand Up @@ -3209,11 +3217,12 @@ impl BuiltinTypedArray {
context,
)?;
{
let mut data = data_obj
.downcast_mut::<ArrayBuffer>()
.expect("Must be ArrayBuffer");
let mut data =
SliceRefMut::Slice(data.data_mut().expect("a new buffer cannot be detached"));
let mut data = data_obj.borrow_mut();
let mut data = SliceRefMut::Slice(
data.data
.data_mut()
.expect("a new buffer cannot be detached"),
);

// b. If srcArray.[[ContentType]] is not O.[[ContentType]], throw a TypeError exception.
if src_type.content_type() != element_type.content_type() {
Expand Down Expand Up @@ -3279,10 +3288,17 @@ impl BuiltinTypedArray {
// 13. Set O.[[ByteLength]] to byteLength.
// 14. Set O.[[ByteOffset]] to 0.
// 15. Set O.[[ArrayLength]] to elementLength.
// TODO: Skip this upcast.
let obj = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
proto,
TypedArray::new(new_buffer, element_type, 0, byte_length, element_length),
TypedArray::new(
new_buffer.upcast(),
element_type,
0,
byte_length,
element_length,
),
);

// 16. Return unused.
Expand Down
63 changes: 24 additions & 39 deletions core/engine/src/object/builtins/jsarraybuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ use crate::{
builtins::array_buffer::ArrayBuffer,
context::intrinsics::StandardConstructors,
error::JsNativeError,
object::{
internal_methods::get_prototype_from_constructor, ErasedObject, JsObject, JsObjectType,
},
object::{internal_methods::get_prototype_from_constructor, JsObject, JsObjectType, Object},
value::TryFromJs,
Context, JsResult, JsValue,
};
Expand All @@ -14,8 +12,9 @@ use std::ops::Deref;

/// `JsArrayBuffer` provides a wrapper for Boa's implementation of the ECMAScript `ArrayBuffer` object
#[derive(Debug, Clone, Trace, Finalize)]
#[boa_gc(unsafe_no_drop)]
pub struct JsArrayBuffer {
inner: JsObject,
inner: JsObject<ArrayBuffer>,
}

// TODO: Add constructors that also take the `detach_key` as argument.
Expand Down Expand Up @@ -103,7 +102,7 @@ impl JsArrayBuffer {

// 3. Set obj.[[ArrayBufferData]] to block.
// 4. Set obj.[[ArrayBufferByteLength]] to byteLength.
let obj = JsObject::from_proto_and_data_with_shared_shape(
let obj = JsObject::new(
context.root_shape(),
prototype,
ArrayBuffer::from_data(block, JsValue::Undefined),
Expand All @@ -117,13 +116,14 @@ impl JsArrayBuffer {
/// This does not clone the fields of the array buffer, it only does a shallow clone of the object.
#[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.is::<ArrayBuffer>() {
Ok(Self { inner: object })
} else {
Err(JsNativeError::typ()
.with_message("object is not an ArrayBuffer")
.into())
}
object
.downcast::<ArrayBuffer>()
.map(|inner| Self { inner })
.map_err(|_| {
JsNativeError::typ()
.with_message("object is not an ArrayBuffer")
.into()
})
}

/// Returns the byte length of the array buffer.
Expand All @@ -141,18 +141,16 @@ impl JsArrayBuffer {
/// let array_buffer = JsArrayBuffer::from_byte_block(data_block, context)?;
///
/// // Take the inner buffer
/// let buffer_length = array_buffer.byte_length(context);
/// let buffer_length = array_buffer.byte_length();
///
/// assert_eq!(buffer_length, 5);
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn byte_length(&self, context: &mut Context) -> usize {
ArrayBuffer::get_byte_length(&self.inner.clone().into(), &[], context)
.expect("it should not throw")
.as_number()
.expect("expected a number") as usize
#[must_use]
pub fn byte_length(&self) -> usize {
self.inner.borrow().data.len()
}

/// Take the inner `ArrayBuffer`'s `array_buffer_data` field and replace it with `None`
Expand Down Expand Up @@ -188,8 +186,8 @@ impl JsArrayBuffer {
#[inline]
pub fn detach(&self, detach_key: &JsValue) -> JsResult<Vec<u8>> {
self.inner
.downcast_mut::<ArrayBuffer>()
.expect("inner must be an ArrayBuffer")
.borrow_mut()
.data
.detach(detach_key)?
.ok_or_else(|| {
JsNativeError::typ()
Expand Down Expand Up @@ -224,12 +222,7 @@ impl JsArrayBuffer {
#[inline]
#[must_use]
pub fn data(&self) -> Option<GcRef<'_, [u8]>> {
debug_assert!(
self.inner.is::<ArrayBuffer>(),
"inner must be an ArrayBuffer"
);

GcRef::try_map(self.inner.downcast_ref::<ArrayBuffer>()?, ArrayBuffer::data)
GcRef::try_map(self.inner.borrow(), |o| o.data.data())
}

/// Get a mutable reference to the [`JsArrayBuffer`]'s data.
Expand Down Expand Up @@ -261,35 +254,27 @@ impl JsArrayBuffer {
/// ```
#[inline]
#[must_use]
pub fn data_mut(&self) -> Option<GcRefMut<'_, ErasedObject, [u8]>> {
debug_assert!(
self.inner.is::<ArrayBuffer>(),
"inner must be an ArrayBuffer"
);

GcRefMut::try_map(
self.inner.downcast_mut::<ArrayBuffer>()?,
ArrayBuffer::data_mut,
)
pub fn data_mut(&self) -> Option<GcRefMut<'_, Object<ArrayBuffer>, [u8]>> {
GcRefMut::try_map(self.inner.borrow_mut(), |o| o.data.data_mut())
}
}

impl From<JsArrayBuffer> for JsObject {
#[inline]
fn from(o: JsArrayBuffer) -> Self {
o.inner.clone()
o.inner.upcast()
}
}

impl From<JsArrayBuffer> for JsValue {
#[inline]
fn from(o: JsArrayBuffer) -> Self {
o.inner.clone().into()
o.inner.upcast().into()
}
}

impl Deref for JsArrayBuffer {
type Target = JsObject;
type Target = JsObject<ArrayBuffer>;

#[inline]
fn deref(&self) -> &Self::Target {
Expand Down
18 changes: 8 additions & 10 deletions core/engine/src/object/builtins/jsdataview.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! A Rust API wrapper for Boa's `DataView` Builtin ECMAScript Object
use crate::{
builtins::{array_buffer::ArrayBuffer, DataView},
builtins::DataView,
context::intrinsics::StandardConstructors,
object::{
internal_methods::get_prototype_from_constructor, JsArrayBuffer, JsObject, JsObjectType,
Expand All @@ -27,7 +27,7 @@ use std::ops::Deref;
///
/// // Create a new Dataview from pre-existing ArrayBuffer
/// let data_view =
/// JsDataView::from_js_array_buffer(&array_buffer, None, None, context)?;
/// JsDataView::from_js_array_buffer(array_buffer, None, None, context)?;
///
/// # Ok(())
/// # }
Expand All @@ -40,26 +40,23 @@ pub struct JsDataView {
impl JsDataView {
/// Create a new `JsDataView` object from an existing `JsArrayBuffer`.
pub fn from_js_array_buffer(
array_buffer: &JsArrayBuffer,
array_buffer: JsArrayBuffer,
offset: Option<u64>,
byte_length: Option<u64>,
context: &mut Context,
) -> JsResult<Self> {
let (byte_offset, byte_length) = {
let buffer = array_buffer.downcast_ref::<ArrayBuffer>().ok_or_else(|| {
JsNativeError::typ().with_message("buffer must be an ArrayBuffer")
})?;

let buffer = array_buffer.borrow();
let provided_offset = offset.unwrap_or(0_u64);

// Check if buffer is detached.
if buffer.is_detached() {
if buffer.data.is_detached() {
return Err(JsNativeError::typ()
.with_message("ArrayBuffer is detached")
.into());
};

let array_buffer_length = buffer.len() as u64;
let array_buffer_length = buffer.data.len() as u64;

if provided_offset > array_buffer_length {
return Err(JsNativeError::range()
Expand Down Expand Up @@ -97,7 +94,8 @@ impl JsDataView {
context.root_shape(),
prototype,
DataView {
viewed_array_buffer: (**array_buffer).clone(),
// TODO: Remove upcast.
viewed_array_buffer: array_buffer.into(),
byte_length,
byte_offset,
},
Expand Down
4 changes: 2 additions & 2 deletions core/engine/src/object/internal_methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ where
Ok(default(realm.intrinsics().constructors()).prototype())
}

pub(crate) fn non_existant_call(
fn non_existant_call(
_obj: &JsObject,
_argument_count: usize,
context: &mut Context,
Expand All @@ -1054,7 +1054,7 @@ pub(crate) fn non_existant_call(
.into())
}

pub(crate) fn non_existant_construct(
fn non_existant_construct(
_obj: &JsObject,
_argument_count: usize,
context: &mut Context,
Expand Down
Loading

0 comments on commit 0872238

Please sign in to comment.