xref: /wasmtime-44.0.1/crates/wiggle/src/lib.rs (revision fce17390)
1 use std::borrow::Cow;
2 use std::cell::UnsafeCell;
3 use std::fmt;
4 use std::mem;
5 use std::ops::Range;
6 use std::str;
7 use wasmtime_environ::error::{Result, bail};
8 
9 pub use wiggle_macro::from_witx;
10 
11 pub use wasmtime_environ::error;
12 pub use wiggle_macro::wasmtime_integration;
13 
14 pub use bitflags;
15 
16 #[cfg(feature = "wiggle_metadata")]
17 pub use witx;
18 
19 mod guest_error;
20 mod guest_type;
21 mod region;
22 
23 pub use tracing;
24 
25 pub use guest_error::GuestError;
26 pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent};
27 pub use region::Region;
28 
29 #[cfg(feature = "wasmtime")]
30 pub mod wasmtime_crate {
31     pub use wasmtime::*;
32 }
33 
34 /// Representation of guest memory for `wiggle`-generated trait methods.
35 ///
36 /// Guest memory is represented as an array of bytes. Memories are either
37 /// "unshared" or "shared". Unshared means that the host has exclusive access to
38 /// the entire array of memory. This allows safe borrows into wasm linear
39 /// memory. Shared memories can be modified at any time and are represented as
40 /// an array of `UnsafeCell<u8>`.
41 ///
42 /// This is generated by the `wiggle` bindings macros.
43 pub enum GuestMemory<'a> {
44     Unshared(&'a mut [u8]),
45     Shared(&'a [UnsafeCell<u8>]),
46 }
47 
48 // manual impls are needed because of the `UnsafeCell` in the `Shared` branch
49 // but this otherwise upholds send/sync invariants.
50 unsafe impl Send for GuestMemory<'_> {}
51 unsafe impl Sync for GuestMemory<'_> {}
52 
53 impl<'a> GuestMemory<'a> {
54     /// Read a value from the provided pointer.
55     ///
56     /// This method will delegate to `T`'s implementation of `read` which will
57     /// read a value from the `ptr` provided.
58     ///
59     /// # Errors
60     ///
61     /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
62     /// not valid to read from.
read<T>(&self, ptr: GuestPtr<T>) -> Result<T, GuestError> where T: GuestType,63     pub fn read<T>(&self, ptr: GuestPtr<T>) -> Result<T, GuestError>
64     where
65         T: GuestType,
66     {
67         T::read(self, ptr)
68     }
69 
70     /// Writes the `val` provided to the `ptr` provided.
71     ///
72     /// This commit will write a `val` into a guest's linear memory. This will
73     /// delegate to `T`'s implementation of `write`.
74     ///
75     /// # Errors
76     ///
77     /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
78     /// not valid to read from.
write<T>(&mut self, ptr: GuestPtr<T>, val: T) -> Result<(), GuestError> where T: GuestType,79     pub fn write<T>(&mut self, ptr: GuestPtr<T>, val: T) -> Result<(), GuestError>
80     where
81         T: GuestType,
82     {
83         T::write(self, ptr, val)
84     }
85 
86     /// Acquires a slice or owned copy of the memory pointed to by `ptr`.
87     ///
88     /// This method will attempt to borrow `ptr` directly from linear memory. If
89     /// memory is shared and cannot be borrowed directly then an owned copy is
90     /// returned instead.
91     ///
92     /// # Errors
93     ///
94     /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
95     /// not valid to read from.
as_cow(&self, ptr: GuestPtr<[u8]>) -> Result<Cow<'_, [u8]>, GuestError>96     pub fn as_cow(&self, ptr: GuestPtr<[u8]>) -> Result<Cow<'_, [u8]>, GuestError> {
97         match self {
98             GuestMemory::Unshared(_) => match self.as_slice(ptr)? {
99                 Some(slice) => Ok(Cow::Borrowed(slice)),
100                 None => unreachable!(),
101             },
102             GuestMemory::Shared(_) => Ok(Cow::Owned(self.to_vec(ptr)?)),
103         }
104     }
105 
106     /// Same as [`GuestMemory::as_cow`] but for strings.
107     ///
108     /// # Errors
109     ///
110     /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
111     /// not valid to read from.
as_cow_str(&self, ptr: GuestPtr<str>) -> Result<Cow<'_, str>, GuestError>112     pub fn as_cow_str(&self, ptr: GuestPtr<str>) -> Result<Cow<'_, str>, GuestError> {
113         match self.as_cow(ptr.cast::<[u8]>())? {
114             Cow::Owned(bytes) => Ok(Cow::Owned(
115                 String::from_utf8(bytes).map_err(|e| e.utf8_error())?,
116             )),
117             Cow::Borrowed(bytes) => Ok(Cow::Borrowed(std::str::from_utf8(bytes)?)),
118         }
119     }
120 
121     /// Attempts to borrow a raw guest slice of memory pointed to by `ptr`.
122     ///
123     /// This method will attempt to return a raw pointer into guest memory. This
124     /// can only be done for `Unshared` memories. A `Shared` memory will return
125     /// `Ok(None)` here.
126     ///
127     /// # Errors
128     ///
129     /// An error is returned if `ptr` is out of bounds, misaligned, or otherwise
130     /// not valid to read from.
as_slice(&self, ptr: GuestPtr<[u8]>) -> Result<Option<&[u8]>, GuestError>131     pub fn as_slice(&self, ptr: GuestPtr<[u8]>) -> Result<Option<&[u8]>, GuestError> {
132         let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;
133         match self {
134             GuestMemory::Unshared(slice) => Ok(Some(&slice[range])),
135             GuestMemory::Shared(_) => Ok(None),
136         }
137     }
138 
139     /// Same as [`GuestMemory::as_slice`] but for strings.
as_str(&self, ptr: GuestPtr<str>) -> Result<Option<&str>, GuestError>140     pub fn as_str(&self, ptr: GuestPtr<str>) -> Result<Option<&str>, GuestError> {
141         match self.as_slice(ptr.cast())? {
142             Some(bytes) => Ok(Some(std::str::from_utf8(bytes)?)),
143             None => Ok(None),
144         }
145     }
146 
147     /// Attempts return `ptr` as a raw slice of mutable bytes in wasm linear
148     /// memory.
149     ///
150     /// Like [`GuestMemory::as_slice`] this only works for `Unshared` memories
151     /// and will not work for `Shared` memories.
as_slice_mut(&mut self, ptr: GuestPtr<[u8]>) -> Result<Option<&mut [u8]>, GuestError>152     pub fn as_slice_mut(&mut self, ptr: GuestPtr<[u8]>) -> Result<Option<&mut [u8]>, GuestError> {
153         let range = self.validate_range::<u8>(ptr.pointer.0, ptr.pointer.1)?;
154         match self {
155             GuestMemory::Unshared(slice) => Ok(Some(&mut slice[range])),
156             GuestMemory::Shared(_) => Ok(None),
157         }
158     }
159 
160     /// Copies the data in the guest region into a [`Vec`].
161     ///
162     /// This is useful when one cannot use [`GuestMemory::as_slice`], e.g., when
163     /// pointing to a region of WebAssembly shared memory.
to_vec<T>(&self, ptr: GuestPtr<[T]>) -> Result<Vec<T>, GuestError> where T: GuestTypeTransparent + Copy,164     pub fn to_vec<T>(&self, ptr: GuestPtr<[T]>) -> Result<Vec<T>, GuestError>
165     where
166         T: GuestTypeTransparent + Copy,
167     {
168         let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;
169         let mut host = Vec::with_capacity(guest.len());
170 
171         // SAFETY: The `guest_slice` variable is already a valid pointer into
172         // the guest's memory, and it may or may not be a pointer into shared
173         // memory. We can't naively use `.to_vec(..)` which could introduce data
174         // races but all that needs to happen is to copy data into our local
175         // `vec` as all the data is `Copy` and transparent anyway. For this
176         // purpose the `ptr::copy` function should be sufficient for copying
177         // over all the data.
178         //
179         // TODO: audit that this use of `std::ptr::copy` is safe with shared
180         // memory (https://github.com/bytecodealliance/wasmtime/issues/4203)
181         unsafe {
182             std::ptr::copy(guest.as_ptr().cast(), host.as_mut_ptr(), guest.len());
183             host.set_len(guest.len());
184         }
185         Ok(host)
186     }
187 
188     /// Copies the data pointed to by `slice` into this guest region.
189     ///
190     /// This method is a *safe* method to copy data from the host to the guest.
191     /// This requires that `self` and `slice` have the same length. The pointee
192     /// type `T` requires the [`GuestTypeTransparent`] trait which is an
193     /// assertion that the representation on the host and on the guest is the
194     /// same.
195     ///
196     /// # Errors
197     ///
198     /// Returns an error if this guest pointer is out of bounds or if the length
199     /// of this guest pointer is not equal to the length of the slice provided.
copy_from_slice<T>(&mut self, slice: &[T], ptr: GuestPtr<[T]>) -> Result<(), GuestError> where T: GuestTypeTransparent + Copy,200     pub fn copy_from_slice<T>(&mut self, slice: &[T], ptr: GuestPtr<[T]>) -> Result<(), GuestError>
201     where
202         T: GuestTypeTransparent + Copy,
203     {
204         if usize::try_from(ptr.len())? != slice.len() {
205             return Err(GuestError::SliceLengthsDiffer);
206         }
207         if slice.is_empty() {
208             return Ok(());
209         }
210 
211         let guest = self.validate_size_align::<T>(ptr.pointer.0, ptr.pointer.1)?;
212 
213         // SAFETY: in the shared memory case, we copy and accept that
214         // the guest data may be concurrently modified. TODO: audit that
215         // this use of `std::ptr::copy` is safe with shared memory
216         // (https://github.com/bytecodealliance/wasmtime/issues/4203)
217         //
218         // Also note that the validity of `guest_slice` has already been
219         // determined by the `as_unsafe_slice_mut` call above.
220         assert_eq!(guest.len(), slice.len());
221         unsafe {
222             let guest: &[UnsafeCell<T>] = guest;
223             let guest: *const UnsafeCell<T> = guest.as_ptr();
224             let guest = guest.cast_mut().cast::<T>();
225             std::ptr::copy(slice.as_ptr(), guest, slice.len());
226         }
227         Ok(())
228     }
229 
230     /// Validates a guest-relative pointer given various attributes, and returns
231     /// the corresponding host pointer.
232     ///
233     /// * `mem` - this is the guest memory being accessed.
234     /// * `offset` - this is the guest-relative pointer, an offset from the
235     ///   base.
236     /// * `len` - this is the number of length, in units of `T`, to return
237     ///   in the resulting slice.
238     ///
239     /// If the parameters are valid then this function will return a slice into
240     /// `mem` for units of `T`, assuming everything is in-bounds and properly
241     /// aligned. Additionally the byte-based `Region` is returned, used for borrows
242     /// later on.
validate_size_align<T>(&self, offset: u32, len: u32) -> Result<&[UnsafeCell<T>], GuestError> where T: GuestTypeTransparent,243     fn validate_size_align<T>(&self, offset: u32, len: u32) -> Result<&[UnsafeCell<T>], GuestError>
244     where
245         T: GuestTypeTransparent,
246     {
247         let range = self.validate_range::<T>(offset, len)?;
248         let cells = match self {
249             GuestMemory::Unshared(s) => {
250                 let s: &[u8] = s;
251                 unsafe { &*(s as *const [u8] as *const [UnsafeCell<u8>]) }
252             }
253             GuestMemory::Shared(s) => s,
254         };
255         let memory = &cells[range.clone()];
256 
257         // ... and then align it to `T`, failing if either the head or tail slices
258         // are nonzero in length. This `unsafe` here is from the standard library
259         // and should be ok since the input slice is `UnsafeCell<u8>` and the output
260         // slice is `UnsafeCell<T>`, meaning the only guarantee of the output is
261         // that it's valid addressable memory, still unsafe to actually access.
262         assert!(mem::align_of::<T>() <= T::guest_align());
263         let (start, mid, end) = unsafe { memory.align_to() };
264         if start.len() > 0 || end.len() > 0 {
265             let region = Region {
266                 start: range.start as u32,
267                 len: range.len() as u32,
268             };
269             return Err(GuestError::PtrNotAligned(region, T::guest_align() as u32));
270         }
271         Ok(mid)
272     }
273 
validate_range<T>(&self, offset: u32, len: u32) -> Result<Range<usize>, GuestError> where T: GuestTypeTransparent,274     fn validate_range<T>(&self, offset: u32, len: u32) -> Result<Range<usize>, GuestError>
275     where
276         T: GuestTypeTransparent,
277     {
278         let byte_len = len
279             .checked_mul(T::guest_size())
280             .ok_or(GuestError::PtrOverflow)?;
281         let region = Region {
282             start: offset,
283             len: byte_len,
284         };
285         let offset = usize::try_from(offset)?;
286         let byte_len = usize::try_from(byte_len)?;
287 
288         let range = offset..offset + byte_len;
289         let oob = match self {
290             GuestMemory::Unshared(b) => b.get(range.clone()).is_none(),
291             GuestMemory::Shared(b) => b.get(range.clone()).is_none(),
292         };
293         if oob {
294             Err(GuestError::PtrOutOfBounds(region))
295         } else {
296             Ok(range)
297         }
298     }
299 
300     /// Returns whether this is a shared memory or not.
is_shared_memory(&self) -> bool301     pub fn is_shared_memory(&self) -> bool {
302         match self {
303             GuestMemory::Shared(_) => true,
304             GuestMemory::Unshared(_) => false,
305         }
306     }
307 }
308 
309 /// A *guest* pointer.
310 ///
311 /// This type represents a pointer from the guest that points into host memory.
312 /// Internally a `GuestPtr` the offset into the memory that the pointer is
313 /// pointing at. At this time this is always a 32-bit offset so this is not
314 /// suitable for bindings where wasm has 64-bit addresses.
315 ///
316 /// Presence of a [`GuestPtr`] does not imply any form of validity. Pointers can
317 /// be out-of-bounds, misaligned, etc. It is safe to construct a `GuestPtr` with
318 /// any offset at any time. Consider a `GuestPtr<T>` roughly equivalent to `*mut
319 /// T`.
320 ///
321 /// ## Slices and Strings
322 ///
323 /// Note that the type parameter does not need to implement the `Sized` trait,
324 /// so you can implement types such as this:
325 ///
326 /// * `GuestPtr<str>` - a pointer to a guest string.
327 /// * `GuestPtr<[T]>` - a pointer to a guest array.
328 ///
329 /// Note that generated bindings won't use these types so you'll have to
330 /// otherwise construct the types with `.cast()` or `.as_array()`. Unsized types
331 /// track both the pointer and length in guest memory.
332 ///
333 /// ## Type parameter and pointee
334 ///
335 /// The `T` type parameter is largely intended for more static safety in Rust as
336 /// well as having a better handle on what we're pointing to. A `GuestPtr<T>`,
337 /// however, does not necessarily literally imply a guest pointer pointing to
338 /// type `T`. Instead the [`GuestType`] trait is a layer of abstraction where
339 /// `GuestPtr<T>` may actually be a pointer to `U` in guest memory, but you can
340 /// construct a `T` from a `U`.
341 ///
342 /// For example `GuestPtr<GuestPtr<T>>` is a valid type, but this is actually
343 /// more equivalent to `GuestPtr<u32>` because guest pointers are always
344 /// 32-bits. That being said you can create a `GuestPtr<T>` from a `u32`.
345 ///
346 /// Additionally `GuestPtr<MyEnum>` will actually delegate, typically, to and
347 /// implementation which loads the underlying data as `GuestPtr<u8>` (or
348 /// similar) and then the bytes loaded are validated to fit within the
349 /// definition of `MyEnum` before `MyEnum` is returned.
350 ///
351 /// For more information see the [`GuestMemory::read`] and
352 /// [`GuestMemory::write`] methods. In general though be extremely careful about
353 /// writing `unsafe` code when working with a `GuestPtr` if you're not using one
354 /// of the already-attached helper methods.
355 #[repr(transparent)]
356 pub struct GuestPtr<T: ?Sized + Pointee> {
357     pointer: T::Pointer,
358 }
359 
360 impl<T: ?Sized + Pointee> GuestPtr<T> {
361     /// Creates a new `GuestPtr` from the given `mem` and `pointer` values.
362     ///
363     /// Note that for sized types like `u32`, `GuestPtr<T>`, etc, the `pointer`
364     /// value is a `u32` offset into guest memory. For slices and strings,
365     /// `pointer` is a `(u32, u32)` offset/length pair.
new(pointer: T::Pointer) -> GuestPtr<T>366     pub fn new(pointer: T::Pointer) -> GuestPtr<T> {
367         GuestPtr { pointer }
368     }
369 
370     /// Returns the offset of this pointer in guest memory.
371     ///
372     /// Note that for sized types this returns a `u32`, but for slices and
373     /// strings it returns a `(u32, u32)` pointer/length pair.
offset(&self) -> T::Pointer374     pub fn offset(&self) -> T::Pointer {
375         self.pointer
376     }
377 
378     /// Casts this `GuestPtr` type to a different type.
379     ///
380     /// This is a safe method which is useful for simply reinterpreting the type
381     /// parameter on this `GuestPtr`. Note that this is a safe method, where
382     /// again there's no guarantees about alignment, validity, in-bounds-ness,
383     /// etc of the returned pointer.
cast<U>(&self) -> GuestPtr<U> where U: Pointee<Pointer = T::Pointer> + ?Sized,384     pub fn cast<U>(&self) -> GuestPtr<U>
385     where
386         U: Pointee<Pointer = T::Pointer> + ?Sized,
387     {
388         GuestPtr::new(self.pointer)
389     }
390 
391     /// Performs pointer arithmetic on this pointer, moving the pointer forward
392     /// `amt` slots.
393     ///
394     /// This will either return the resulting pointer or `Err` if the pointer
395     /// arithmetic calculation would overflow around the end of the address
396     /// space.
add(&self, amt: u32) -> Result<GuestPtr<T>, GuestError> where T: GuestType + Pointee<Pointer = u32>,397     pub fn add(&self, amt: u32) -> Result<GuestPtr<T>, GuestError>
398     where
399         T: GuestType + Pointee<Pointer = u32>,
400     {
401         let offset = amt
402             .checked_mul(T::guest_size())
403             .and_then(|o| self.pointer.checked_add(o));
404         let offset = match offset {
405             Some(o) => o,
406             None => return Err(GuestError::PtrOverflow),
407         };
408         Ok(GuestPtr::new(offset))
409     }
410 
411     /// Returns a `GuestPtr` for an array of `T`s using this pointer as the
412     /// base.
as_array(&self, elems: u32) -> GuestPtr<[T]> where T: GuestType + Pointee<Pointer = u32>,413     pub fn as_array(&self, elems: u32) -> GuestPtr<[T]>
414     where
415         T: GuestType + Pointee<Pointer = u32>,
416     {
417         GuestPtr::new((self.pointer, elems))
418     }
419 }
420 
421 impl<T> GuestPtr<[T]> {
422     /// For slices, specifically returns the relative pointer to the base of the
423     /// array.
424     ///
425     /// This is similar to `<[T]>::as_ptr()`
offset_base(&self) -> u32426     pub fn offset_base(&self) -> u32 {
427         self.pointer.0
428     }
429 
430     /// For slices, returns the length of the slice, in elements.
len(&self) -> u32431     pub fn len(&self) -> u32 {
432         self.pointer.1
433     }
434 
435     /// Returns an iterator over interior pointers.
436     ///
437     /// Each item is a `Result` indicating whether it overflowed past the end of
438     /// the address space or not.
iter(&self) -> impl ExactSizeIterator<Item = Result<GuestPtr<T>, GuestError>> + '_ where T: GuestType,439     pub fn iter(&self) -> impl ExactSizeIterator<Item = Result<GuestPtr<T>, GuestError>> + '_
440     where
441         T: GuestType,
442     {
443         let base = self.as_ptr();
444         (0..self.len()).map(move |i| base.add(i))
445     }
446 
447     /// Returns a `GuestPtr` pointing to the base of the array for the interior
448     /// type `T`.
as_ptr(&self) -> GuestPtr<T>449     pub fn as_ptr(&self) -> GuestPtr<T> {
450         GuestPtr::new(self.offset_base())
451     }
452 
get(&self, index: u32) -> Option<GuestPtr<T>> where T: GuestType,453     pub fn get(&self, index: u32) -> Option<GuestPtr<T>>
454     where
455         T: GuestType,
456     {
457         if index < self.len() {
458             Some(
459                 self.as_ptr()
460                     .add(index)
461                     .expect("just performed bounds check"),
462             )
463         } else {
464             None
465         }
466     }
467 
get_range(&self, r: std::ops::Range<u32>) -> Option<GuestPtr<[T]>> where T: GuestType,468     pub fn get_range(&self, r: std::ops::Range<u32>) -> Option<GuestPtr<[T]>>
469     where
470         T: GuestType,
471     {
472         if r.end < r.start {
473             return None;
474         }
475         let range_length = r.end - r.start;
476         if r.start <= self.len() && r.end <= self.len() {
477             Some(
478                 self.as_ptr()
479                     .add(r.start)
480                     .expect("just performed bounds check")
481                     .as_array(range_length),
482             )
483         } else {
484             None
485         }
486     }
487 }
488 
489 impl GuestPtr<str> {
490     /// For strings, returns the relative pointer to the base of the string
491     /// allocation.
offset_base(&self) -> u32492     pub fn offset_base(&self) -> u32 {
493         self.pointer.0
494     }
495 
496     /// Returns the length, in bytes, of the string.
len(&self) -> u32497     pub fn len(&self) -> u32 {
498         self.pointer.1
499     }
500 
501     /// Returns a raw pointer for the underlying slice of bytes that this
502     /// pointer points to.
as_bytes(&self) -> GuestPtr<[u8]>503     pub fn as_bytes(&self) -> GuestPtr<[u8]> {
504         GuestPtr::new(self.pointer)
505     }
506 }
507 
508 impl<T: ?Sized + Pointee> Clone for GuestPtr<T> {
clone(&self) -> Self509     fn clone(&self) -> Self {
510         *self
511     }
512 }
513 
514 impl<T: ?Sized + Pointee> Copy for GuestPtr<T> {}
515 
516 impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result517     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
518         T::debug(self.pointer, f)
519     }
520 }
521 
522 impl<T: ?Sized + Pointee> PartialEq for GuestPtr<T> {
eq(&self, other: &Self) -> bool523     fn eq(&self, other: &Self) -> bool {
524         self.pointer == other.pointer
525     }
526 }
527 
528 mod private {
529     pub trait Sealed {}
530     impl<T> Sealed for T {}
531     impl<T> Sealed for [T] {}
532     impl Sealed for str {}
533 }
534 
535 /// Types that can be pointed to by `GuestPtr<T>`.
536 ///
537 /// In essence everything can, and the only special-case is unsized types like
538 /// `str` and `[T]` which have special implementations.
539 pub trait Pointee: private::Sealed {
540     #[doc(hidden)]
541     type Pointer: Copy + PartialEq;
542     #[doc(hidden)]
debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result543     fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result;
544 }
545 
546 impl<T> Pointee for T {
547     type Pointer = u32;
debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result548     fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
549         write!(f, "*guest {pointer:#x}")
550     }
551 }
552 
553 impl<T> Pointee for [T] {
554     type Pointer = (u32, u32);
debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result555     fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
556         write!(f, "*guest {:#x}/{}", pointer.0, pointer.1)
557     }
558 }
559 
560 impl Pointee for str {
561     type Pointer = (u32, u32);
debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result562     fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
563         <[u8]>::debug(pointer, f)
564     }
565 }
566 
run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Output>567 pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Output> {
568     use std::pin::Pin;
569     use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
570 
571     let mut f = Pin::from(Box::new(future));
572     let waker = dummy_waker();
573     let mut cx = Context::from_waker(&waker);
574     match f.as_mut().poll(&mut cx) {
575         Poll::Ready(val) => return Ok(val),
576         Poll::Pending => bail!(
577             "Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store"
578         ),
579     }
580 
581     fn dummy_waker() -> Waker {
582         return unsafe { Waker::from_raw(clone(5 as *const _)) };
583 
584         unsafe fn clone(ptr: *const ()) -> RawWaker {
585             assert_eq!(ptr as usize, 5);
586             const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
587             RawWaker::new(ptr, &VTABLE)
588         }
589 
590         unsafe fn wake(ptr: *const ()) {
591             assert_eq!(ptr as usize, 5);
592         }
593 
594         unsafe fn wake_by_ref(ptr: *const ()) {
595             assert_eq!(ptr as usize, 5);
596         }
597 
598         unsafe fn drop(ptr: *const ()) {
599             assert_eq!(ptr as usize, 5);
600         }
601     }
602 }
603