Added ptr api

This commit is contained in:
Syrus Akbary
2021-06-23 22:17:26 -07:00
parent 0db591a1d3
commit 756a2a2926
4 changed files with 502 additions and 89 deletions

371
lib/js-api/src/cell.rs Normal file
View File

@@ -0,0 +1,371 @@
use core::cmp::Ordering;
use core::fmt::{self, Debug, Display};
use core::mem;
use core::ops::{Deref, DerefMut};
use core::ptr;
use std::marker::PhantomData;
use js_sys::Uint8Array;
use wasm_bindgen::JsValue;
/// A mutable memory location.
///
/// # Examples
///
/// In this example, you can see that `WasmCell<T>` enables mutation inside an
/// immutable struct. In other words, it enables "interior mutability".
///
/// ```
/// use wasmer::WasmCell;
///
/// struct SomeStruct {
/// regular_field: u8,
/// special_field: WasmCell<u8>,
/// }
///
/// let my_struct = SomeStruct {
/// regular_field: 0,
/// special_field: WasmCell::new(1),
/// };
///
/// let new_value = 100;
///
/// // ERROR: `my_struct` is immutable
/// // my_struct.regular_field = new_value;
///
/// // WORKS: although `my_struct` is immutable, `special_field` is a `WasmCell`,
/// // which can always be mutated
/// my_struct.special_field.set(new_value);
/// assert_eq!(my_struct.special_field.get(), new_value);
/// ```
///
/// See the [module-level documentation](self) for more.
#[derive(Clone)]
pub struct WasmCell<T: ?Sized> {
pub(crate) memory: Uint8Array,
phantom: PhantomData<T>,
}
unsafe impl<T: ?Sized> Send for WasmCell<T> where T: Send {}
unsafe impl<T: ?Sized> Sync for WasmCell<T> {}
// impl<T: Copy> Clone for WasmCell<T> {
// #[inline]
// fn clone(&self) -> WasmCell<T> {
// WasmCell::new(self.get())
// }
// }
impl<T: Default> Default for WasmCell<T> {
/// Creates a `WasmCell<T>`, with the `Default` value for T.
#[inline]
fn default() -> WasmCell<T> {
unimplemented!()
// WasmCell::new(Default::default())
}
}
impl<T: PartialEq + Copy> PartialEq for WasmCell<T> {
#[inline]
fn eq(&self, other: &WasmCell<T>) -> bool {
true
}
}
impl<T: Eq + Copy> Eq for WasmCell<T> {}
impl<T: PartialOrd + Copy> PartialOrd for WasmCell<T> {
#[inline]
fn partial_cmp(&self, other: &WasmCell<T>) -> Option<Ordering> {
self.get().partial_cmp(&other.get())
}
#[inline]
fn lt(&self, other: &WasmCell<T>) -> bool {
self.get() < other.get()
}
#[inline]
fn le(&self, other: &WasmCell<T>) -> bool {
self.get() <= other.get()
}
#[inline]
fn gt(&self, other: &WasmCell<T>) -> bool {
self.get() > other.get()
}
#[inline]
fn ge(&self, other: &WasmCell<T>) -> bool {
self.get() >= other.get()
}
}
impl<T: Ord + Copy> Ord for WasmCell<T> {
#[inline]
fn cmp(&self, other: &WasmCell<T>) -> Ordering {
self.get().cmp(&other.get())
}
}
impl<T> From<T> for WasmCell<T> {
fn from(t: T) -> WasmCell<T> {
unimplemented!();
// WasmCell::new(t)
}
}
impl<T> WasmCell<T> {
/// Creates a new `WasmCell` containing the given value.
///
/// # Examples
///
/// ```
/// use wasmer::WasmCell;
///
/// let c = WasmCell::new(5);
/// ```
#[inline]
pub const fn new(memory: Uint8Array) -> WasmCell<T> {
// WasmCell { value: UnsafeWasmCell::new(value) }
WasmCell {
memory,
phantom: PhantomData,
}
}
/// Swaps the values of two WasmCells.
/// Difference with `std::mem::swap` is that this function doesn't require `&mut` reference.
///
/// # Examples
///
/// ```
/// use wasmer::WasmCell;
///
/// let c1 = WasmCell::new(5i32);
/// let c2 = WasmCell::new(10i32);
/// c1.swap(&c2);
/// assert_eq!(10, c1.get());
/// assert_eq!(5, c2.get());
/// ```
#[inline]
pub fn swap(&self, other: &Self) {
unimplemented!();
// if ptr::eq(self, other) {
// return;
// }
// // SAFETY: This can be risky if called from separate threads, but `WasmCell`
// // is `!Sync` so this won't happen. This also won't invalidate any
// // pointers since `WasmCell` makes sure nothing else will be pointing into
// // either of these `WasmCell`s.
// unsafe {
// ptr::swap(self.value.get(), other.value.get());
// }
}
/// Replaces the contained value with `val`, and returns the old contained value.
///
/// # Examples
///
/// ```
/// use wasmer::WasmCell;
///
/// let cell = WasmCell::new(5);
/// assert_eq!(cell.get(), 5);
/// assert_eq!(cell.replace(10), 5);
/// assert_eq!(cell.get(), 10);
/// ```
pub fn replace(&self, val: T) -> T {
unimplemented!();
// SAFETY: This can cause data races if called from a separate thread,
// but `WasmCell` is `!Sync` so this won't happen.
// mem::replace(unsafe { &mut *self.value.get() }, val)
}
// /// Unwraps the value.
// ///
// /// # Examples
// ///
// /// ```
// /// use wasmer::WasmCell;
// ///
// /// let c = WasmCell::new(5);
// /// let five = c.into_inner();
// ///
// /// assert_eq!(five, 5);
// /// ```
// pub const fn into_inner(self) -> T {
// // This will get the item out of the MemoryView and into
// // Rust memory allocator
// unimplemented!()
// // self.get()
// }
}
impl<T: Copy> WasmCell<T> {
/// Returns a copy of the contained value.
///
/// # Examples
///
/// ```
/// use wasmer::WasmCell;
///
/// let c = WasmCell::new(5);
///
/// let five = c.get();
/// ```
#[inline]
pub fn get(&self) -> T {
let vec = self.memory.to_vec();
unsafe { *(vec.as_slice().as_ptr() as *const T) }
// unimplemented!();
}
/// Updates the contained value using a function and returns the new value.
///
/// # Examples
///
/// ```
/// #![feature(cell_update)]
///
/// use wasmer::WasmCell;
///
/// let c = WasmCell::new(5);
/// let new = c.update(|x| x + 1);
///
/// assert_eq!(new, 6);
/// assert_eq!(c.get(), 6);
/// ```
#[inline]
pub fn update<F>(&self, f: F) -> T
where
F: FnOnce(T) -> T,
{
let old = self.get();
let new = f(old);
self.set(new);
new
}
}
impl<T: Sized> WasmCell<T> {
/// Sets the contained value.
///
/// # Examples
///
/// ```
/// use wasmer::WasmCell;
///
/// let c = WasmCell::new(5);
///
/// c.set(10);
/// ```
#[inline]
pub fn set(&self, val: T) {
let size = std::mem::size_of::<T>();
let ptr = &val as *const T as *const u8;
let slice = unsafe { std::slice::from_raw_parts(ptr, size) };
self.memory.copy_from(slice);
// p.as_ptr();
// let ptr = (&val) as usize;
// unimplemented!();
// let old = self.replace(val);
// drop(old);
}
}
// /// Returns a raw pointer to the underlying data in this cell.
// ///
// /// # Examples
// ///
// /// ```
// /// use wasmer::WasmCell;
// ///
// /// let c = WasmCell::new(5);
// ///
// /// let ptr = c.as_ptr();
// /// ```
// #[inline]
// pub const fn as_ptr(&self) -> *mut T {
// self.value.get()
// }
// /// Returns a mutable reference to the underlying data.
// ///
// /// This call borrows `WasmCell` mutably (at compile-time) which guarantees
// /// that we possess the only reference.
// ///
// /// # Examples
// ///
// /// ```
// /// use wasmer::WasmCell;
// ///
// /// let mut c = WasmCell::new(5);
// /// *c.get_mut() += 1;
// ///
// /// assert_eq!(c.get(), 6);
// /// ```
// #[inline]
// pub fn get_mut(&mut self) -> &mut T {
// self.value.get_mut()
// }
// /// Returns a `&WasmCell<T>` from a `&mut T`
// ///
// /// # Examples
// ///
// /// ```
// /// use wasmer::WasmCell;
// ///
// /// let slice: &mut [i32] = &mut [1, 2, 3];
// /// let cell_slice: &WasmCell<[i32]> = WasmCell::from_mut(slice);
// /// let slice_cell: &[WasmCell<i32>] = cell_slice.as_slice_of_cells();
// ///
// /// assert_eq!(slice_cell.len(), 3);
// /// ```
// #[inline]
// pub fn from_mut(t: &mut T) -> &WasmCell<T> {
// // SAFETY: `&mut` ensures unique access.
// unsafe { &*(t as *mut T as *const WasmCell<T>) }
// }
// }
// impl<T: Default> WasmCell<T> {
// /// Takes the value of the cell, leaving `Default::default()` in its place.
// ///
// /// # Examples
// ///
// /// ```
// /// use wasmer::WasmCell;
// ///
// /// let c = WasmCell::new(5);
// /// let five = c.take();
// ///
// /// assert_eq!(five, 5);
// /// assert_eq!(c.into_inner(), 0);
// /// ```
// pub fn take(&self) -> T {
// self.replace(Default::default())
// }
// }
impl<T> WasmCell<[T]> {
/// Returns a `&[WasmCell<T>]` from a `&WasmCell<[T]>`
///
/// # Examples
///
/// ```
/// use wasmer::WasmCell;
///
/// let slice: &mut [i32] = &mut [1, 2, 3];
/// let cell_slice: &WasmCell<[i32]> = WasmCell::from_mut(slice);
/// let slice_cell: &[WasmCell<i32>] = cell_slice.as_slice_of_cells();
///
/// assert_eq!(slice_cell.len(), 3);
/// ```
pub fn as_slice_of_cells(&self) -> &[WasmCell<T>] {
unimplemented!();
// SAFETY: `WasmCell<T>` has the same memory layout as `T`.
// unsafe { &*(self as *const WasmCell<[T]> as *const [WasmCell<T>]) }
}
}

View File

@@ -116,8 +116,7 @@ impl Memory {
/// assert_eq!(m.store(), &store);
/// ```
pub fn store(&self) -> &Store {
unimplemented!();
// &self.store
&self.store
}
/// Retrieve a slice of the memory contents.
@@ -270,6 +269,11 @@ impl Memory {
// unsafe { MemoryView::new(base as _, length as u32) }
}
/// example view
pub fn uint8view(&self) -> js_sys::Uint8Array {
js_sys::Uint8Array::new(&self.vm_memory.buffer())
}
pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self {
Self {
store: store.clone(),

View File

@@ -278,6 +278,7 @@ mod lib {
}
}
mod cell;
mod env;
mod error;
mod export;

View File

@@ -6,6 +6,7 @@
//! Therefore, you should use this abstraction whenever possible to avoid memory
//! related bugs when implementing an ABI.
use crate::cell::WasmCell;
use crate::{externals::Memory, FromToNativeWasmType};
use std::{cell::Cell, fmt, marker::PhantomData, mem};
use wasmer_types::ValueType;
@@ -103,19 +104,21 @@ impl<T: Copy + ValueType> WasmPtr<T, Item> {
/// If you're unsure what that means, it likely does not apply to you.
/// This invariant will be enforced in the future.
#[inline]
pub fn deref<'a>(self, memory: &'a Memory) -> Option<&'a Cell<T>> {
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|| mem::size_of::<T>() == 0
{
pub fn deref<'a>(self, memory: &'a Memory) -> Option<WasmCell<T>> {
let total_len = (self.offset as usize) + mem::size_of::<T>();
if total_len > memory.size().bytes().0 || mem::size_of::<T>() == 0 {
return None;
}
unsafe {
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *const Cell<T>;
Some(&*cell_ptr)
}
let subarray = memory.uint8view().subarray(self.offset, total_len as u32);
Some(WasmCell::new(subarray))
// unimplemented!();
// unsafe {
// let cell_ptr = align_pointer(
// memory.view::<u8>()[self.offset as usize] as usize,
// mem::align_of::<T>(),
// ) as *const Cell<T>;
// Some(&*cell_ptr)
// }
}
/// Mutably dereference this `WasmPtr` getting a `&mut Cell<T>` allowing for
@@ -126,17 +129,18 @@ impl<T: Copy + ValueType> WasmPtr<T, Item> {
/// `&mut T` that point to the same memory. You should ensure that you have
/// exclusive access to Wasm linear memory before calling this method.
#[inline]
pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<&'a mut Cell<T>> {
if (self.offset as usize) + mem::size_of::<T>() > memory.size().bytes().0
|| mem::size_of::<T>() == 0
{
pub unsafe fn deref_mut<'a>(self, memory: &'a Memory) -> Option<WasmCell<T>> {
let total_len = (self.offset as usize) + mem::size_of::<T>();
if total_len > memory.size().bytes().0 || mem::size_of::<T>() == 0 {
return None;
}
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *mut Cell<T>;
Some(&mut *cell_ptr)
let subarray = memory.uint8view().subarray(self.offset, total_len as u32);
Some(WasmCell::new(subarray))
// let cell_ptr = align_pointer(
// memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
// mem::align_of::<T>(),
// ) as *mut Cell<T>;
// Some(&mut *cell_ptr)
}
}
@@ -164,16 +168,20 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
{
return None;
}
// let subarray = &memory.uint8view().subarray(self.offset, total_len as u32);
// let subarray_static = unsafe { std::mem::transmute::<&js_sys::Uint8Array, &'static js_sys::Uint8Array>(&subarray) };
// Some(WasmCell::new(subarray_static))
unsafe {
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *const Cell<T>;
let cell_ptrs = &std::slice::from_raw_parts(cell_ptr, slice_full_len)
[index as usize..slice_full_len];
Some(cell_ptrs)
}
unimplemented!();
// unsafe {
// let cell_ptr = align_pointer(
// memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
// mem::align_of::<T>(),
// ) as *const Cell<T>;
// let cell_ptrs = &std::slice::from_raw_parts(cell_ptr, slice_full_len)
// [index as usize..slice_full_len];
// Some(cell_ptrs)
// }
}
/// Mutably dereference this `WasmPtr` getting a `&mut [Cell<T>]` allowing for
@@ -190,26 +198,27 @@ impl<T: Copy + ValueType> WasmPtr<T, Array> {
index: u32,
length: u32,
) -> Option<&mut [Cell<T>]> {
// gets the size of the item in the array with padding added such that
// for any index, we will always result an aligned memory access
let item_size = mem::size_of::<T>();
let slice_full_len = index as usize + length as usize;
let memory_size = memory.size().bytes().0;
unimplemented!();
// // gets the size of the item in the array with padding added such that
// // for any index, we will always result an aligned memory access
// let item_size = mem::size_of::<T>();
// let slice_full_len = index as usize + length as usize;
// let memory_size = memory.size().bytes().0;
if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
|| self.offset as usize >= memory_size
|| mem::size_of::<T>() == 0
{
return None;
}
// if (self.offset as usize) + (item_size * slice_full_len) > memory.size().bytes().0
// || self.offset as usize >= memory_size
// || mem::size_of::<T>() == 0
// {
// return None;
// }
let cell_ptr = align_pointer(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *mut Cell<T>;
let cell_ptrs = &mut std::slice::from_raw_parts_mut(cell_ptr, slice_full_len)
[index as usize..slice_full_len];
Some(cell_ptrs)
// let cell_ptr = align_pointer(
// memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
// mem::align_of::<T>(),
// ) as *mut Cell<T>;
// let cell_ptrs = &mut std::slice::from_raw_parts_mut(cell_ptr, slice_full_len)
// [index as usize..slice_full_len];
// Some(cell_ptrs)
}
/// Get a UTF-8 string from the `WasmPtr` with the given length.
@@ -337,10 +346,38 @@ impl<T: Copy, Ty> fmt::Debug for WasmPtr<T, Ty> {
mod test {
use super::*;
use crate::{Memory, MemoryType, Store};
use wasm_bindgen_test::*;
/// Ensure that memory accesses work on the edges of memory and that out of
/// bounds errors are caught with both `deref` and `deref_mut`.
#[test]
#[wasm_bindgen_test]
fn wasm_ptr_is_functional() {
let store = Store::default();
let memory_descriptor = MemoryType::new(1, Some(1), false);
let memory = Memory::new(&store, memory_descriptor).unwrap();
let start_wasm_ptr: WasmPtr<u64> = WasmPtr::new(2);
let mut val = start_wasm_ptr.deref(&memory).unwrap();
assert_eq!(val.memory.to_vec(), vec![0; 8]);
val.set(1200);
assert_eq!(val.memory.to_vec(), vec![176, 4, 0, 0, 0, 0, 0, 0]);
// Let's make sure the main memory is changed
assert_eq!(
memory.uint8view().subarray(0, 10).to_vec(),
vec![0, 0, 176, 4, 0, 0, 0, 0, 0, 0]
);
val.memory.copy_from(&[10, 0, 0, 0, 0, 0, 0, 0]);
let value = val.get();
assert_eq!(value, 10);
}
/// Ensure that memory accesses work on the edges of memory and that out of
/// bounds errors are caught with both `deref` and `deref_mut`.
#[wasm_bindgen_test]
fn wasm_ptr_memory_bounds_checks_hold() {
// create a memory
let store = Store::default();
@@ -349,16 +386,16 @@ mod test {
// test that basic access works and that len = 0 works, but oob does not
let start_wasm_ptr: WasmPtr<u8> = WasmPtr::new(0);
let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
// let start_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(0);
assert!(start_wasm_ptr.deref(&memory).is_some());
assert!(unsafe { start_wasm_ptr.deref_mut(&memory).is_some() });
assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some());
assert!(unsafe { start_wasm_ptr_array.get_utf8_str(&memory, 0).is_some() });
assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some());
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() });
assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
// assert!(start_wasm_ptr_array.deref(&memory, 0, 0).is_some());
// assert!(unsafe { start_wasm_ptr_array.get_utf8_str(&memory, 0).is_some() });
// assert!(start_wasm_ptr_array.get_utf8_string(&memory, 0).is_some());
// assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 0).is_some() });
// assert!(start_wasm_ptr_array.deref(&memory, 0, 1).is_some());
// assert!(unsafe { start_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
// test that accessing the last valid memory address works correctly and OOB is caught
let last_valid_address_for_u8 = (memory.size().bytes().0 - 1) as u32;
@@ -366,18 +403,18 @@ mod test {
assert!(end_wasm_ptr.deref(&memory).is_some());
assert!(unsafe { end_wasm_ptr.deref_mut(&memory).is_some() });
let end_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(last_valid_address_for_u8);
// let end_wasm_ptr_array: WasmPtr<u8, Array> = WasmPtr::new(last_valid_address_for_u8);
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
let invalid_idx_len_combos: [(u32, u32); 3] =
[(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)];
for &(idx, len) in invalid_idx_len_combos.iter() {
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
}
assert!(unsafe { end_wasm_ptr_array.get_utf8_str(&memory, 2).is_none() });
assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none());
// assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
// assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
// let invalid_idx_len_combos: [(u32, u32); 3] =
// [(last_valid_address_for_u8 + 1, 0), (0, 2), (1, 1)];
// for &(idx, len) in invalid_idx_len_combos.iter() {
// assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
// assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
// }
// assert!(unsafe { end_wasm_ptr_array.get_utf8_str(&memory, 2).is_none() });
// assert!(end_wasm_ptr_array.get_utf8_string(&memory, 2).is_none());
// test that accesing the last valid memory address for a u32 is valid
// (same as above test but with more edge cases to assert on)
@@ -398,29 +435,29 @@ mod test {
assert!(oob_end_ptr.deref(&memory).is_none());
assert!(unsafe { oob_end_ptr.deref_mut(&memory).is_none() });
}
let end_wasm_ptr_array: WasmPtr<u32, Array> = WasmPtr::new(last_valid_address_for_u32);
assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
// let end_wasm_ptr_array: WasmPtr<u32, Array> = WasmPtr::new(last_valid_address_for_u32);
// assert!(end_wasm_ptr_array.deref(&memory, 0, 1).is_some());
// assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, 0, 1).is_some() });
let invalid_idx_len_combos: [(u32, u32); 3] =
[(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)];
for &(idx, len) in invalid_idx_len_combos.iter() {
assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
}
// let invalid_idx_len_combos: [(u32, u32); 3] =
// [(last_valid_address_for_u32 + 1, 0), (0, 2), (1, 1)];
// for &(idx, len) in invalid_idx_len_combos.iter() {
// assert!(end_wasm_ptr_array.deref(&memory, idx, len).is_none());
// assert!(unsafe { end_wasm_ptr_array.deref_mut(&memory, idx, len).is_none() });
// }
let end_wasm_ptr_array_oob_array: [WasmPtr<u32, Array>; 4] = [
WasmPtr::new(last_valid_address_for_u32 + 1),
WasmPtr::new(last_valid_address_for_u32 + 2),
WasmPtr::new(last_valid_address_for_u32 + 3),
WasmPtr::new(last_valid_address_for_u32 + 4),
];
// let end_wasm_ptr_array_oob_array: [WasmPtr<u32, Array>; 4] = [
// WasmPtr::new(last_valid_address_for_u32 + 1),
// WasmPtr::new(last_valid_address_for_u32 + 2),
// WasmPtr::new(last_valid_address_for_u32 + 3),
// WasmPtr::new(last_valid_address_for_u32 + 4),
// ];
for oob_end_array_ptr in end_wasm_ptr_array_oob_array.iter() {
assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
}
// for oob_end_array_ptr in end_wasm_ptr_array_oob_array.iter() {
// assert!(oob_end_array_ptr.deref(&memory, 0, 1).is_none());
// assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 0, 1).is_none() });
// assert!(oob_end_array_ptr.deref(&memory, 1, 0).is_none());
// assert!(unsafe { oob_end_array_ptr.deref_mut(&memory, 1, 0).is_none() });
// }
}
}