diff --git a/Cargo.lock b/Cargo.lock index 46384b2d6a..542b28795e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2680,6 +2680,7 @@ dependencies = [ "wasmtime", "wasmtime-wiggle-macro", "wiggle", + "wiggle-borrow", "witx", ] @@ -2752,6 +2753,13 @@ dependencies = [ "witx", ] +[[package]] +name = "wiggle-borrow" +version = "0.21.0" +dependencies = [ + "wiggle", +] + [[package]] name = "wiggle-generate" version = "0.21.0" @@ -2778,7 +2786,7 @@ dependencies = [ [[package]] name = "wiggle-test" -version = "0.19.0" +version = "0.21.0" dependencies = [ "env_logger 0.8.1", "proptest", @@ -2786,6 +2794,7 @@ dependencies = [ "tracing", "tracing-subscriber", "wiggle", + "wiggle-borrow", ] [[package]] diff --git a/crates/wiggle/borrow/Cargo.toml b/crates/wiggle/borrow/Cargo.toml new file mode 100644 index 0000000000..4c029c9005 --- /dev/null +++ b/crates/wiggle/borrow/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "wiggle-borrow" +version = "0.21.0" +authors = ["Pat Hickey ", "Jakub Konka ", "Alex Crichton "] +edition = "2018" +license = "Apache-2.0 WITH LLVM-exception" +description = "A run-time borrow checker for use with Wiggle" +categories = ["wasm"] +keywords = ["webassembly", "wasm"] +repository = "https://github.com/bytecodealliance/wasmtime" +include = ["src/**/*", "LICENSE"] + +[dependencies] +wiggle = { path = "..", version = "0.21.0" } + +[badges] +maintenance = { status = "actively-developed" } diff --git a/crates/wiggle/test-helpers/src/borrow.rs b/crates/wiggle/borrow/src/lib.rs similarity index 56% rename from crates/wiggle/test-helpers/src/borrow.rs rename to crates/wiggle/borrow/src/lib.rs index 5ed16e271b..ff712e3d10 100644 --- a/crates/wiggle/test-helpers/src/borrow.rs +++ b/crates/wiggle/borrow/src/lib.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use wiggle::{BorrowHandle, GuestError, Region}; pub struct BorrowChecker { + /// Unfortunately, since the terminology of std::cell and the problem domain of borrow checking + /// overlap, the method calls on this member will be confusing. bc: RefCell, } @@ -23,14 +25,16 @@ impl BorrowChecker { pub fn has_outstanding_borrows(&self) -> bool { self.bc.borrow().has_outstanding_borrows() } - - pub(crate) fn borrow(&self, r: Region) -> Result { - self.bc.borrow_mut().borrow(r) + pub fn immut_borrow(&self, r: Region) -> Result { + self.bc.borrow_mut().immut_borrow(r) } - pub(crate) fn unborrow(&self, h: BorrowHandle) { + pub fn mut_borrow(&self, r: Region) -> Result { + self.bc.borrow_mut().mut_borrow(r) + } + pub fn unborrow(&self, h: BorrowHandle) { self.bc.borrow_mut().unborrow(h) } - pub(crate) fn is_borrowed(&self, r: Region) -> bool { + pub fn is_borrowed(&self, r: Region) -> bool { self.bc.borrow().is_borrowed(r) } } @@ -39,11 +43,12 @@ impl BorrowChecker { /// This is a pretty naive way to account for borrows. This datastructure /// could be made a lot more efficient with some effort. struct InnerBorrowChecker { - /// Map from handle to region borrowed. A HashMap is probably not ideal + /// Maps from handle to region borrowed. A HashMap is probably not ideal /// for this but it works. It would be more efficient if we could /// check `is_borrowed` without an O(n) iteration, by organizing borrows /// by an ordering of Region. - borrows: HashMap, + immut_borrows: HashMap, + mut_borrows: HashMap, /// Handle to give out for the next borrow. This is the bare minimum of /// bookkeeping of free handles, and in a pathological case we could run /// out, hence [`GuestError::BorrowCheckerOutOfHandles`] @@ -53,22 +58,27 @@ struct InnerBorrowChecker { impl InnerBorrowChecker { fn new() -> Self { InnerBorrowChecker { - borrows: HashMap::new(), + immut_borrows: HashMap::new(), + mut_borrows: HashMap::new(), next_handle: BorrowHandle(0), } } fn has_outstanding_borrows(&self) -> bool { - !self.borrows.is_empty() + !(self.immut_borrows.is_empty() && self.mut_borrows.is_empty()) } fn is_borrowed(&self, r: Region) -> bool { - !self.borrows.values().all(|b| !b.overlaps(r)) + !self + .immut_borrows + .values() + .chain(self.mut_borrows.values()) + .all(|b| !b.overlaps(r)) } fn new_handle(&mut self) -> Result { // Reset handles to 0 if all handles have been returned. - if self.borrows.is_empty() { + if self.immut_borrows.is_empty() && self.mut_borrows.is_empty() { self.next_handle = BorrowHandle(0); } let h = self.next_handle; @@ -84,17 +94,29 @@ impl InnerBorrowChecker { Ok(h) } - fn borrow(&mut self, r: Region) -> Result { + fn immut_borrow(&mut self, r: Region) -> Result { + if !self.mut_borrows.values().all(|b| !b.overlaps(r)) { + return Err(GuestError::PtrBorrowed(r)); + } + let h = self.new_handle()?; + self.immut_borrows.insert(h, r); + Ok(h) + } + + fn mut_borrow(&mut self, r: Region) -> Result { if self.is_borrowed(r) { return Err(GuestError::PtrBorrowed(r)); } let h = self.new_handle()?; - self.borrows.insert(h, r); + self.mut_borrows.insert(h, r); Ok(h) } fn unborrow(&mut self, h: BorrowHandle) { - let _ = self.borrows.remove(&h); + let removed = self.mut_borrows.remove(&h); + if removed.is_none() { + let _ = self.immut_borrows.remove(&h); + } } } @@ -107,15 +129,15 @@ mod test { let r1 = Region::new(0, 10); let r2 = Region::new(10, 10); assert!(!r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - bs.borrow(r2).expect("can borrow r2"); + bs.mut_borrow(r1).expect("can borrow r1"); + bs.mut_borrow(r2).expect("can borrow r2"); let mut bs = InnerBorrowChecker::new(); let r1 = Region::new(10, 10); let r2 = Region::new(0, 10); assert!(!r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - bs.borrow(r2).expect("can borrow r2"); + bs.mut_borrow(r1).expect("can borrow r1"); + bs.mut_borrow(r2).expect("can borrow r2"); } #[test] @@ -124,29 +146,33 @@ mod test { let r1 = Region::new(0, 10); let r2 = Region::new(9, 10); assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + bs.immut_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant mut borrow r2"); + bs.immut_borrow(r2).expect("can immut borrow r2"); let mut bs = InnerBorrowChecker::new(); let r1 = Region::new(0, 10); let r2 = Region::new(2, 5); assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + bs.immut_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); + bs.immut_borrow(r2).expect("can immut borrow r2"); let mut bs = InnerBorrowChecker::new(); let r1 = Region::new(9, 10); let r2 = Region::new(0, 10); assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + bs.immut_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); + bs.immut_borrow(r2).expect("can immut borrow r2"); let mut bs = InnerBorrowChecker::new(); let r1 = Region::new(2, 5); let r2 = Region::new(0, 10); assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); + bs.immut_borrow(r1).expect("can borrow r1"); + assert!(bs.mut_borrow(r2).is_err(), "cant borrow r2"); + bs.immut_borrow(r2).expect("can immut borrow r2"); let mut bs = InnerBorrowChecker::new(); let r1 = Region::new(2, 5); @@ -154,10 +180,11 @@ mod test { let r3 = Region::new(15, 5); let r4 = Region::new(0, 10); assert!(r1.overlaps(r4)); - bs.borrow(r1).expect("can borrow r1"); - bs.borrow(r2).expect("can borrow r2"); - bs.borrow(r3).expect("can borrow r3"); - assert!(bs.borrow(r4).is_err(), "cant borrow r4"); + bs.immut_borrow(r1).expect("can borrow r1"); + bs.immut_borrow(r2).expect("can borrow r2"); + bs.immut_borrow(r3).expect("can borrow r3"); + assert!(bs.mut_borrow(r4).is_err(), "cant mut borrow r4"); + bs.immut_borrow(r4).expect("can immut borrow r4"); } #[test] @@ -167,11 +194,11 @@ mod test { let r2 = Region::new(10, 10); assert!(!r1.overlaps(r2)); assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); - let h1 = bs.borrow(r1).expect("can borrow r1"); + let h1 = bs.mut_borrow(r1).expect("can borrow r1"); assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); - let h2 = bs.borrow(r2).expect("can borrow r2"); + let h2 = bs.mut_borrow(r2).expect("can borrow r2"); - assert!(bs.borrow(r2).is_err(), "can't borrow r2 twice"); + assert!(bs.mut_borrow(r2).is_err(), "can't borrow r2 twice"); bs.unborrow(h2); assert_eq!( bs.has_outstanding_borrows(), @@ -182,7 +209,29 @@ mod test { assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); let _h3 = bs - .borrow(r2) + .mut_borrow(r2) .expect("can borrow r2 again now that its been unborrowed"); + + // Lets try again with immut: + + let mut bs = InnerBorrowChecker::new(); + let r1 = Region::new(0, 10); + let r2 = Region::new(10, 10); + assert!(!r1.overlaps(r2)); + assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); + let h1 = bs.immut_borrow(r1).expect("can borrow r1"); + assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); + let h2 = bs.immut_borrow(r2).expect("can borrow r2"); + let h3 = bs.immut_borrow(r2).expect("can immut borrow r2 twice"); + + bs.unborrow(h2); + assert_eq!( + bs.has_outstanding_borrows(), + true, + "h1, h3 still outstanding" + ); + bs.unborrow(h1); + bs.unborrow(h3); + assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); } } diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs index fe2b172498..a625a48906 100644 --- a/crates/wiggle/src/lib.rs +++ b/crates/wiggle/src/lib.rs @@ -165,7 +165,8 @@ pub unsafe trait GuestMemory { /// `GuestMemory::has_outstanding_borrows` is true for the duration of the /// borrow, and that `GuestMemory::is_borrowed` of any overlapping region /// is false for the duration of the borrow. - fn borrow(&self, r: Region) -> Result; + fn mut_borrow(&self, r: Region) -> Result; + fn immut_borrow(&self, r: Region) -> Result; /// Unborrow a previously borrowed region. As long as `GuestSlice` and /// `GuestStr` are implemented correctly, a `BorrowHandle` should only be /// unborrowed once. @@ -189,8 +190,11 @@ unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T { fn is_borrowed(&self, r: Region) -> bool { T::is_borrowed(self, r) } - fn borrow(&self, r: Region) -> Result { - T::borrow(self, r) + fn mut_borrow(&self, r: Region) -> Result { + T::mut_borrow(self, r) + } + fn immut_borrow(&self, r: Region) -> Result { + T::immut_borrow(self, r) } fn unborrow(&self, h: BorrowHandle) { T::unborrow(self, h) @@ -207,8 +211,11 @@ unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T { fn is_borrowed(&self, r: Region) -> bool { T::is_borrowed(self, r) } - fn borrow(&self, r: Region) -> Result { - T::borrow(self, r) + fn mut_borrow(&self, r: Region) -> Result { + T::mut_borrow(self, r) + } + fn immut_borrow(&self, r: Region) -> Result { + T::immut_borrow(self, r) } fn unborrow(&self, h: BorrowHandle) { T::unborrow(self, h) @@ -225,8 +232,11 @@ unsafe impl GuestMemory for Box { fn is_borrowed(&self, r: Region) -> bool { T::is_borrowed(self, r) } - fn borrow(&self, r: Region) -> Result { - T::borrow(self, r) + fn mut_borrow(&self, r: Region) -> Result { + T::mut_borrow(self, r) + } + fn immut_borrow(&self, r: Region) -> Result { + T::immut_borrow(self, r) } fn unborrow(&self, h: BorrowHandle) { T::unborrow(self, h) @@ -243,8 +253,11 @@ unsafe impl GuestMemory for Rc { fn is_borrowed(&self, r: Region) -> bool { T::is_borrowed(self, r) } - fn borrow(&self, r: Region) -> Result { - T::borrow(self, r) + fn mut_borrow(&self, r: Region) -> Result { + T::mut_borrow(self, r) + } + fn immut_borrow(&self, r: Region) -> Result { + T::immut_borrow(self, r) } fn unborrow(&self, h: BorrowHandle) { T::unborrow(self, h) @@ -261,8 +274,11 @@ unsafe impl GuestMemory for Arc { fn is_borrowed(&self, r: Region) -> bool { T::is_borrowed(self, r) } - fn borrow(&self, r: Region) -> Result { - T::borrow(self, r) + fn mut_borrow(&self, r: Region) -> Result { + T::mut_borrow(self, r) + } + fn immut_borrow(&self, r: Region) -> Result { + T::immut_borrow(self, r) } fn unborrow(&self, h: BorrowHandle) { T::unborrow(self, h) @@ -489,7 +505,7 @@ impl<'a, T> GuestPtr<'a, [T]> { self.mem .validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T; - let borrow = self.mem.borrow(Region { + let borrow = self.mem.mut_borrow(Region { start: self.pointer.0, len, })?; @@ -617,7 +633,7 @@ impl<'a> GuestPtr<'a, str> { .mem .validate_size_align(self.pointer.0, 1, self.pointer.1)?; - let borrow = self.mem.borrow(Region { + let borrow = self.mem.mut_borrow(Region { start: self.pointer.0, len: self.pointer.1, })?; diff --git a/crates/wiggle/test-helpers/Cargo.toml b/crates/wiggle/test-helpers/Cargo.toml index ea49cf6af8..6c6c02ed42 100644 --- a/crates/wiggle/test-helpers/Cargo.toml +++ b/crates/wiggle/test-helpers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wiggle-test" -version = "0.19.0" +version = "0.21.0" authors = ["Pat Hickey ", "Jakub Konka ", "Alex Crichton "] license = "Apache-2.0 WITH LLVM-exception" edition = "2018" @@ -14,6 +14,7 @@ publish = false [dependencies] proptest = "0.10" wiggle = { path = "..", features = ["tracing_log"] } +wiggle-borrow = { path = "../borrow" } [dev-dependencies] thiserror = "1.0" diff --git a/crates/wiggle/test-helpers/src/lib.rs b/crates/wiggle/test-helpers/src/lib.rs index 6164e4f92f..32a260ba13 100644 --- a/crates/wiggle/test-helpers/src/lib.rs +++ b/crates/wiggle/test-helpers/src/lib.rs @@ -3,8 +3,7 @@ use std::cell::UnsafeCell; use std::marker; use wiggle::{BorrowHandle, GuestMemory, Region}; -mod borrow; -use borrow::BorrowChecker; +use wiggle_borrow::BorrowChecker; #[derive(Debug, Clone)] pub struct MemAreas(Vec); @@ -129,8 +128,11 @@ unsafe impl GuestMemory for HostMemory { fn is_borrowed(&self, r: Region) -> bool { self.bc.is_borrowed(r) } - fn borrow(&self, r: Region) -> Result { - self.bc.borrow(r) + fn mut_borrow(&self, r: Region) -> Result { + self.bc.mut_borrow(r) + } + fn immut_borrow(&self, r: Region) -> Result { + self.bc.immut_borrow(r) } fn unborrow(&self, h: BorrowHandle) { self.bc.unborrow(h) diff --git a/crates/wiggle/wasmtime/Cargo.toml b/crates/wiggle/wasmtime/Cargo.toml index 89a85970c8..87da0c32f2 100644 --- a/crates/wiggle/wasmtime/Cargo.toml +++ b/crates/wiggle/wasmtime/Cargo.toml @@ -15,6 +15,7 @@ wasmtime = { path = "../../wasmtime", version = "0.21.0", default-features = fal wasmtime-wiggle-macro = { path = "./macro", version = "0.21.0" } witx = { path = "../../wasi-common/WASI/tools/witx", version = "0.8.7", optional = true } wiggle = { path = "..", version = "0.21.0" } +wiggle-borrow = { path = "../borrow", version = "0.21.0" } [badges] maintenance = { status = "actively-developed" } diff --git a/crates/wiggle/wasmtime/src/borrow.rs b/crates/wiggle/wasmtime/src/borrow.rs deleted file mode 100644 index 5ed16e271b..0000000000 --- a/crates/wiggle/wasmtime/src/borrow.rs +++ /dev/null @@ -1,188 +0,0 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use wiggle::{BorrowHandle, GuestError, Region}; - -pub struct BorrowChecker { - bc: RefCell, -} - -impl BorrowChecker { - /// A `BorrowChecker` manages run-time validation of borrows from a `GuestMemory`. It keeps - /// track of regions of guest memory which are possible to alias with Rust references (via the - /// `GuestSlice` and `GuestStr` structs, which implement `std::ops::Deref` and - /// `std::ops::DerefMut`. It also enforces that `GuestPtr::read` and `GuestPtr::write` do not - /// access memory with an outstanding borrow. - pub fn new() -> Self { - BorrowChecker { - bc: RefCell::new(InnerBorrowChecker::new()), - } - } - /// Indicates whether any outstanding borrows are known to the `BorrowChecker`. This function - /// must be `false` in order for it to be safe to recursively call into a WebAssembly module, - /// or to manipulate the WebAssembly memory by any other means. - pub fn has_outstanding_borrows(&self) -> bool { - self.bc.borrow().has_outstanding_borrows() - } - - pub(crate) fn borrow(&self, r: Region) -> Result { - self.bc.borrow_mut().borrow(r) - } - pub(crate) fn unborrow(&self, h: BorrowHandle) { - self.bc.borrow_mut().unborrow(h) - } - pub(crate) fn is_borrowed(&self, r: Region) -> bool { - self.bc.borrow().is_borrowed(r) - } -} - -#[derive(Debug)] -/// This is a pretty naive way to account for borrows. This datastructure -/// could be made a lot more efficient with some effort. -struct InnerBorrowChecker { - /// Map from handle to region borrowed. A HashMap is probably not ideal - /// for this but it works. It would be more efficient if we could - /// check `is_borrowed` without an O(n) iteration, by organizing borrows - /// by an ordering of Region. - borrows: HashMap, - /// Handle to give out for the next borrow. This is the bare minimum of - /// bookkeeping of free handles, and in a pathological case we could run - /// out, hence [`GuestError::BorrowCheckerOutOfHandles`] - next_handle: BorrowHandle, -} - -impl InnerBorrowChecker { - fn new() -> Self { - InnerBorrowChecker { - borrows: HashMap::new(), - next_handle: BorrowHandle(0), - } - } - - fn has_outstanding_borrows(&self) -> bool { - !self.borrows.is_empty() - } - - fn is_borrowed(&self, r: Region) -> bool { - !self.borrows.values().all(|b| !b.overlaps(r)) - } - - fn new_handle(&mut self) -> Result { - // Reset handles to 0 if all handles have been returned. - if self.borrows.is_empty() { - self.next_handle = BorrowHandle(0); - } - let h = self.next_handle; - // Get the next handle. Since we don't recycle handles until all of - // them have been returned, there is a pathological case where a user - // may make a Very Large (usize::MAX) number of valid borrows and - // unborrows while always keeping at least one borrow outstanding, and - // we will run out of borrow handles. - self.next_handle = BorrowHandle( - h.0.checked_add(1) - .ok_or_else(|| GuestError::BorrowCheckerOutOfHandles)?, - ); - Ok(h) - } - - fn borrow(&mut self, r: Region) -> Result { - if self.is_borrowed(r) { - return Err(GuestError::PtrBorrowed(r)); - } - let h = self.new_handle()?; - self.borrows.insert(h, r); - Ok(h) - } - - fn unborrow(&mut self, h: BorrowHandle) { - let _ = self.borrows.remove(&h); - } -} - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn nonoverlapping() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - bs.borrow(r2).expect("can borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(10, 10); - let r2 = Region::new(0, 10); - assert!(!r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - bs.borrow(r2).expect("can borrow r2"); - } - - #[test] - fn overlapping() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(9, 10); - assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(2, 5); - assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(9, 10); - let r2 = Region::new(0, 10); - assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(2, 5); - let r2 = Region::new(0, 10); - assert!(r1.overlaps(r2)); - bs.borrow(r1).expect("can borrow r1"); - assert!(bs.borrow(r2).is_err(), "cant borrow r2"); - - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(2, 5); - let r2 = Region::new(10, 5); - let r3 = Region::new(15, 5); - let r4 = Region::new(0, 10); - assert!(r1.overlaps(r4)); - bs.borrow(r1).expect("can borrow r1"); - bs.borrow(r2).expect("can borrow r2"); - bs.borrow(r3).expect("can borrow r3"); - assert!(bs.borrow(r4).is_err(), "cant borrow r4"); - } - - #[test] - fn unborrowing() { - let mut bs = InnerBorrowChecker::new(); - let r1 = Region::new(0, 10); - let r2 = Region::new(10, 10); - assert!(!r1.overlaps(r2)); - assert_eq!(bs.has_outstanding_borrows(), false, "start with no borrows"); - let h1 = bs.borrow(r1).expect("can borrow r1"); - assert_eq!(bs.has_outstanding_borrows(), true, "h1 is outstanding"); - let h2 = bs.borrow(r2).expect("can borrow r2"); - - assert!(bs.borrow(r2).is_err(), "can't borrow r2 twice"); - bs.unborrow(h2); - assert_eq!( - bs.has_outstanding_borrows(), - true, - "h1 is still outstanding" - ); - bs.unborrow(h1); - assert_eq!(bs.has_outstanding_borrows(), false, "no remaining borrows"); - - let _h3 = bs - .borrow(r2) - .expect("can borrow r2 again now that its been unborrowed"); - } -} diff --git a/crates/wiggle/wasmtime/src/lib.rs b/crates/wiggle/wasmtime/src/lib.rs index e38ab3ac3a..07f6469c3a 100644 --- a/crates/wiggle/wasmtime/src/lib.rs +++ b/crates/wiggle/wasmtime/src/lib.rs @@ -1,9 +1,7 @@ pub use wasmtime_wiggle_macro::*; pub use wiggle::*; -mod borrow; - -use borrow::BorrowChecker; +use wiggle_borrow::BorrowChecker; /// Lightweight `wasmtime::Memory` wrapper so we can implement the /// `wiggle::GuestMemory` trait on it. @@ -40,8 +38,11 @@ unsafe impl GuestMemory for WasmtimeGuestMemory { fn is_borrowed(&self, r: Region) -> bool { self.bc.is_borrowed(r) } - fn borrow(&self, r: Region) -> Result { - self.bc.borrow(r) + fn immut_borrow(&self, r: Region) -> Result { + self.bc.immut_borrow(r) + } + fn mut_borrow(&self, r: Region) -> Result { + self.bc.mut_borrow(r) } fn unborrow(&self, h: BorrowHandle) { self.bc.unborrow(h)