Skip to content

Commit

Permalink
Merge pull request #99 from holochain/2023-09-20-wasmer3
Browse files Browse the repository at this point in the history
2023 09 20 wasmer4
  • Loading branch information
thedavidmeister authored Oct 19, 2023
2 parents 1e2b4e0 + d00d882 commit 625d450
Show file tree
Hide file tree
Showing 19 changed files with 1,730 additions and 1,452 deletions.
3 changes: 1 addition & 2 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ default = []
error_as_host = []

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
wasmer = "=2.3.0"
wasmer-engine = "=2.3.0"
wasmer = "=4.2.2"
4 changes: 2 additions & 2 deletions crates/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ pub fn split_u64(u: u64) -> Result<(u32, u32), WasmError> {
/// Performs the inverse of `merge_usize`.
pub fn split_usize(u: DoubleUSize) -> Result<(usize, usize), WasmError> {
#[cfg(target_pointer_width = "64")]
return split_u128(u as u128).map(|(a, b)| (a as usize, b as usize));
return split_u128(u).map(|(a, b)| (a as usize, b as usize));
#[cfg(target_pointer_width = "32")]
return split_u64(u as u64).map(|(a, b)| (a as usize, b as usize));
return split_u64(u).map(|(a, b)| (a as usize, b as usize));
}

#[cfg(test)]
Expand Down
7 changes: 4 additions & 3 deletions crates/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ authors = [ "thedavidmeister", "[email protected]" ]
edition = "2021"

[dependencies]
wasmer = "=2.3.0"
wasmer = "=4.2.2"
wasmer-middlewares = "=4.2.2"
holochain_wasmer_common = { version = "=0.0.85", path = "../common" }
holochain_serialized_bytes = "=0.0.53"
serde = "1"
Expand All @@ -16,6 +17,7 @@ parking_lot = "0.12"
once_cell = "1"
rand = "0.8"
bimap = "0.6"
bytes = "1"

[lib]
name = "holochain_wasmer_host"
Expand All @@ -25,5 +27,4 @@ path = "src/host.rs"
[features]
default = ["error_as_host"]
debug_memory = []
error_as_host = ["holochain_wasmer_common/error_as_host"]
dylib = ["wasmer/dylib"]
error_as_host = ["holochain_wasmer_common/error_as_host"]
75 changes: 38 additions & 37 deletions crates/host/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,51 @@ use std::num::TryFromIntError;

use crate::guest::read_bytes;
use crate::prelude::*;
use wasmer::Function;
use wasmer::LazyInit;
use wasmer::Memory;
use wasmer::WasmerEnv;
use wasmer::StoreMut;
use wasmer::TypedFunction;

#[derive(Clone, Default, WasmerEnv)]
#[derive(Clone, Default)]
pub struct Env {
#[wasmer(export)]
memory: LazyInit<Memory>,
#[wasmer(export(name = "__hc__allocate_1"))]
allocate: LazyInit<Function>,
#[wasmer(export(name = "__hc__deallocate_1"))]
deallocate: LazyInit<Function>,
pub memory: Option<Memory>,
pub allocate: Option<TypedFunction<i32, i32>>,
pub deallocate: Option<TypedFunction<(i32, i32), ()>>,
}

impl Env {
/// Given some input I that can be serialized, request an allocation from the
/// guest and copy the serialized bytes to the allocated pointer. The guest
/// MUST subsequently take ownership of these bytes or it will leak memory.
pub fn move_data_to_guest<I>(&self, input: I) -> Result<GuestPtrLen, wasmer::RuntimeError>
pub fn move_data_to_guest<I>(
&self,
store_mut: &mut StoreMut,
input: I,
) -> Result<GuestPtrLen, wasmer::RuntimeError>
where
I: serde::Serialize + std::fmt::Debug,
{
let data = holochain_serialized_bytes::encode(&input).map_err(|e| wasm_error!(e))?;
let guest_ptr: GuestPtr = match self
.allocate_ref()
let guest_ptr: GuestPtr = self
.allocate
.as_ref()
.ok_or(wasm_error!(WasmErrorInner::Memory))?
.call(&[Value::I32(
.call(
store_mut,
data.len()
.try_into()
.map_err(|_| wasm_error!(WasmErrorInner::PointerMap))?,
)])
)
.map_err(|e| wasm_error!(e.to_string()))?
.get(0)
{
Some(Value::I32(guest_ptr)) => (*guest_ptr)
.try_into()
.map_err(|e: TryFromIntError| wasm_error!(e))?,
_ => return Err(wasm_error!(WasmErrorInner::PointerMap).into()),
};
.try_into()
.map_err(|e: TryFromIntError| wasm_error!(e))?;
let len: Len = match data.len().try_into() {
Ok(len) => len,
Err(e) => return Err(wasm_error!(e).into()),
};
crate::guest::write_bytes(
self.memory_ref()
store_mut,
self.memory
.as_ref()
.ok_or(wasm_error!(WasmErrorInner::Memory))?,
guest_ptr,
&data,
Expand All @@ -61,32 +60,34 @@ impl Env {
/// deserialization is successful.
pub fn consume_bytes_from_guest<O>(
&self,
store_mut: &mut StoreMut,
guest_ptr: GuestPtr,
len: Len,
) -> Result<O, wasmer::RuntimeError>
where
O: serde::de::DeserializeOwned + std::fmt::Debug,
{
let bytes = read_bytes(
self.memory_ref()
.ok_or(wasm_error!(WasmErrorInner::Memory))?,
&self
.memory
.as_ref()
.ok_or(wasm_error!(WasmErrorInner::Memory))?
.view(&store_mut),
guest_ptr,
len,
)
.map_err(|_| wasm_error!(WasmErrorInner::Memory))?;
self.deallocate_ref()
self.deallocate
.as_ref()
.ok_or(wasm_error!(WasmErrorInner::Memory))?
.call(&[
Value::I32(
guest_ptr
.try_into()
.map_err(|_| wasm_error!(WasmErrorInner::PointerMap))?,
),
Value::I32(
len.try_into()
.map_err(|_| wasm_error!(WasmErrorInner::PointerMap))?,
),
])
.call(
store_mut,
guest_ptr
.try_into()
.map_err(|_| wasm_error!(WasmErrorInner::PointerMap))?,
len.try_into()
.map_err(|_| wasm_error!(WasmErrorInner::PointerMap))?,
)
.map_err(|e| wasm_error!(e.to_string()))?;
match holochain_serialized_bytes::decode(&bytes) {
Ok(v) => Ok(v),
Expand Down
75 changes: 39 additions & 36 deletions crates/host/src/guest.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::prelude::*;
use core::num::TryFromIntError;
use holochain_serialized_bytes::prelude::*;
use parking_lot::Mutex;
use std::sync::Arc;
use wasmer::Instance;
use wasmer::Memory;
use wasmer::MemoryView;
use wasmer::StoreMut;
use wasmer::Value;
use wasmer::WasmSlice;

/// Write a slice of bytes to the guest in a safe-ish way.
///
Expand Down Expand Up @@ -51,6 +53,7 @@ use wasmer::Value;
///
/// @see read_bytes()
pub fn write_bytes(
store_mut: &mut StoreMut,
memory: &Memory,
guest_ptr: GuestPtr,
slice: &[u8],
Expand All @@ -62,15 +65,7 @@ pub fn write_bytes(
#[cfg(feature = "debug_memory")]
tracing::debug!("writing bytes from host to guest at: {} {}", guest_ptr, len);

let ptr: WasmPtr<u8, Array> = WasmPtr::new(guest_ptr);
// Write the length prefix immediately before the slice at the guest pointer position.
for (byte, cell) in slice.iter().zip(
ptr.deref(memory, 0, len)
.ok_or(wasm_error!(WasmErrorInner::Memory))?
.iter(),
) {
cell.set(*byte)
}
WasmSlice::new(&memory.view(store_mut), guest_ptr.into(), len.into())?.write_slice(slice)?;

Ok(())
}
Expand All @@ -95,32 +90,29 @@ pub fn write_bytes(
/// A better approach is to use an immutable deref from a `WasmPtr`, which checks against memory
/// bounds for the guest, and map over the whole thing to a `Vec<u8>`.
pub fn read_bytes(
memory: &Memory,
memory_view: &MemoryView,
guest_ptr: GuestPtr,
len: Len,
) -> Result<Vec<u8>, wasmer::RuntimeError> {
) -> Result<Vec<u8>, wasmer::MemoryAccessError> {
#[cfg(feature = "debug_memory")]
tracing::debug!("reading bytes from guest to host at: {} {}", guest_ptr, len);

let ptr: WasmPtr<u8, Array> = WasmPtr::new(guest_ptr);
Ok(ptr
.deref(memory, 0, len)
.ok_or(wasm_error!(WasmErrorInner::Memory))?
.iter()
.map(|cell| cell.get())
.collect::<Vec<u8>>())
WasmSlice::new(memory_view, guest_ptr.into(), len.into())?.read_to_vec()
}

/// Deserialize any DeserializeOwned type out of the guest from a guest pointer.
pub fn from_guest_ptr<O>(
store_mut: &mut StoreMut,
memory: &Memory,
guest_ptr: GuestPtr,
len: Len,
) -> Result<O, wasmer::RuntimeError>
where
O: serde::de::DeserializeOwned + std::fmt::Debug,
{
let bytes = read_bytes(memory, guest_ptr, len)?;
let bytes =
WasmSlice::new(&memory.view(store_mut), guest_ptr.into(), len.into())?.read_to_vec()?;

match holochain_serialized_bytes::decode(&bytes) {
Ok(v) => Ok(v),
Err(e) => {
Expand All @@ -133,16 +125,19 @@ where
/// Host calling guest for the function named `call` with the given `payload` in a vector of bytes
/// result is either a vector of bytes from the guest found at the location of the returned guest
/// allocation pointer or a `RuntimeError` built from a `WasmError`.
/// The reason that this takes a separate store and instance is that the host does not neccessarily
/// have access to an InstanceWithStore, such as the case when the guest is called from within a
/// host function call.
pub fn call<I, O>(
instance: Arc<Mutex<Instance>>,
store_mut: &mut StoreMut,
instance: Arc<Instance>,
f: &str,
input: I,
) -> Result<O, wasmer::RuntimeError>
where
I: serde::Serialize + std::fmt::Debug,
O: serde::de::DeserializeOwned + std::fmt::Debug,
{
let instance = instance.lock();
// The guest will use the same crate for decoding if it uses the wasm common crate.
let payload: Vec<u8> =
holochain_serialized_bytes::encode(&input).map_err(|e| wasm_error!(e))?;
Expand All @@ -153,11 +148,12 @@ where
.try_into()
.map_err(|e: TryFromIntError| wasm_error!(WasmErrorInner::CallError(e.to_string())))?;
let guest_input_length_value: Value = Value::I32(guest_input_length);

let (guest_input_ptr, guest_input_ptr_value) = match instance
.exports
.get_function("__hc__allocate_1")
.map_err(|e| wasm_error!(WasmErrorInner::CallError(e.to_string())))?
.call(&[guest_input_length_value.clone()])
.call(store_mut, &[guest_input_length_value.clone()])
.map_err(|e| wasm_error!(WasmErrorInner::CallError(e.to_string())))?
.get(0)
{
Expand All @@ -179,6 +175,7 @@ where

// Write the input payload into the guest at the offset specified by the allocation.
write_bytes(
store_mut,
instance
.exports
.get_memory("memory")
Expand All @@ -193,8 +190,10 @@ where
.exports
.get_function(f)
.map_err(|e| wasm_error!(WasmErrorInner::CallError(e.to_string())))?
.call(&[guest_input_ptr_value, guest_input_length_value])
{
.call(
store_mut,
&[guest_input_ptr_value, guest_input_length_value],
) {
Ok(v) => match v.get(0) {
Some(Value::I64(i)) => {
let u: GuestPtrLen = (*i)
Expand Down Expand Up @@ -230,6 +229,7 @@ where
// The host MUST discard any wasm instance that errors at this point to avoid memory leaks.
// The WasmError in the result type here is for deserializing out of the guest.
let return_value: Result<O, WasmError> = from_guest_ptr(
store_mut,
instance
.exports
.get_memory("memory")
Expand All @@ -243,17 +243,20 @@ where
.exports
.get_function("__hc__deallocate_1")
.map_err(|e| wasm_error!(WasmErrorInner::CallError(e.to_string())))?
.call(&[
Value::I32(
guest_return_ptr
.try_into()
.map_err(|e: TryFromIntError| wasm_error!(e))?,
),
Value::I32(
len.try_into()
.map_err(|e: TryFromIntError| wasm_error!(e))?,
),
])
.call(
store_mut,
&[
Value::I32(
guest_return_ptr
.try_into()
.map_err(|e: TryFromIntError| wasm_error!(e))?,
),
Value::I32(
len.try_into()
.map_err(|e: TryFromIntError| wasm_error!(e))?,
),
],
)
.map_err(|e| wasm_error!(WasmErrorInner::CallError(format!("{:?}", e))))?;

return_value.map_err(|e| e.into())
Expand Down
Loading

0 comments on commit 625d450

Please sign in to comment.