diff --git a/nova_vm/src/heap/array_buffer.rs b/nova_vm/src/heap/array_buffer.rs index 99c209fc..57ee17da 100644 --- a/nova_vm/src/heap/array_buffer.rs +++ b/nova_vm/src/heap/array_buffer.rs @@ -25,7 +25,7 @@ use super::{ /// the capacity or length. The pointer can be None /// if the length of the buffer is zero. #[derive(Debug, Copy, Clone)] -pub(crate) struct BackingStore { +pub struct BackingStore { ptr: Option>, cap: u32, byte_length: u32, @@ -129,7 +129,7 @@ impl BackingStore { } fn as_ptr(&self, byte_offset: u32) -> Option<*const u8> { - if byte_offset > self.byte_length { + if byte_offset >= self.byte_length { None } else if let Some(data) = self.ptr { // SAFETY: The data is properly initialized, and the T being read is @@ -141,7 +141,7 @@ impl BackingStore { } fn as_mut_ptr(&mut self, byte_offset: u32) -> Option<*mut u8> { - if byte_offset > self.byte_length { + if byte_offset >= self.byte_length { None } else if let Some(data) = self.ptr { // SAFETY: The data is properly initialized, and the T being read is @@ -155,7 +155,7 @@ impl BackingStore { pub fn get(&self, offset: u32) -> Option { let size = std::mem::size_of::() as u32; let byte_offset = offset * size; - if byte_offset > self.byte_length { + if byte_offset >= self.byte_length { None } else if let Some(data) = self.ptr { // SAFETY: The data is properly initialized, and the T being read is @@ -192,20 +192,20 @@ impl BackingStore { // SAFETY: Source buffer length is valid, destination buffer // is likewise at least equal in length to source, and both // are properly aligned for bytes. - unsafe { std::ptr::copy::(src, dst, byte_length) } + unsafe { std::ptr::copy::(src, dst, byte_length as usize) } } } } #[derive(Debug)] -pub(crate) enum InternalBuffer { +pub enum InternalBuffer { Detached, Growable(BackingStore), Static(BackingStore), } #[derive(Debug)] -pub(crate) struct ArrayBufferHeapData { +pub struct ArrayBufferHeapData { pub(super) object_index: Option, pub(super) buffer: InternalBuffer, // detach_key @@ -300,3 +300,84 @@ fn array_buffer_species(_heap: &mut Heap, this: Value, _args: &[Value]) -> JsRes fn array_buffer_todo(_heap: &mut Heap, _this: Value, _args: &[Value]) -> JsResult { todo!() } + +#[test] +fn new_backing_store() { + let bs = BackingStore::new(0); + assert_eq!(bs.len(), 0); + assert_eq!(bs.capacity(), 0); + assert_eq!(bs.get::(0), None); + + let bs = BackingStore::new(8); + assert_eq!(bs.len(), 8); + assert_eq!(bs.capacity(), 8); + for i in 0..8 { + assert_eq!(bs.get::(i as u32), Some(0)); + } +} + +#[test] +fn new_backing_store_with_capacity() { + let bs = BackingStore::new_with_capacity(0, 8); + assert_eq!(bs.len(), 0); + assert_eq!(bs.capacity(), 8); + for i in 0..8 { + assert_eq!(bs.get::(i as u32), None); + } + + let bs = BackingStore::new_with_capacity(8, 16); + assert_eq!(bs.len(), 8); + assert_eq!(bs.capacity(), 16); + for i in 0..8 { + assert_eq!(bs.get::(i as u32), Some(0)); + } + for i in 8..16 { + assert_eq!(bs.get::(i as u32), None); + } +} + +#[test] +fn backing_store_set() { + let mut bs = BackingStore::new(8); + assert_eq!(bs.len(), 8); + assert_eq!(bs.capacity(), 8); + for i in 0..8 { + assert_eq!(bs.get::(i as u32), Some(0)); + } + + for i in 0..8 { + bs.set::(i as u32, i + 1); + } + + for i in 0..8 { + assert_eq!(bs.get::(i as u32), Some(i + 1)); + } +} + +#[test] +fn backing_store_resize() { + let mut bs = BackingStore::new_with_capacity(0, 8); + bs.resize(8); + assert_eq!(bs.len(), 8); + assert_eq!(bs.capacity(), 8); + for i in 0..8 { + assert_eq!(bs.get::(i as u32), Some(0)); + } + + for i in 0..8 { + bs.set::(i as u32, i + 1); + } + + let ptr = bs.as_ptr(0).unwrap(); + bs.resize(0); + + // SAFETY: Backing store is not deallocated: Zero index pointer is still valid + // and is not read beyond its allocated capacity. The only usual safety requirement + // broken is to read beyond the buffer length, which is safe as the outside is still + // properly initialized u8s. + unsafe { + for i in 0..8 { + assert_eq!(ptr.add(i).read(), 0); + } + } +}