summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorHenry Gressmann <mail@henrygressmann.de>2024-01-26 18:03:59 +0100
committerHenry Gressmann <mail@henrygressmann.de>2024-01-26 18:03:59 +0100
commit8adce672578683dcc7ab62e5492e0f5dbc07a0b4 (patch)
treeeda57b01f206faf5bcfb7ee70c24fd7be8546e66 /crates
parent6fd283b06c4912f5d3408b2072cbc3c4e195a236 (diff)
docs: string example
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
Diffstat (limited to 'crates')
-rw-r--r--crates/tinywasm/src/instance.rs4
-rw-r--r--crates/tinywasm/src/reference.rs128
-rw-r--r--crates/tinywasm/src/runtime/interpreter/macros.rs2
-rw-r--r--crates/tinywasm/src/store.rs61
4 files changed, 181 insertions, 14 deletions
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index 5346ac3..35b54d6 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -199,14 +199,14 @@ impl ModuleInstance {
pub fn memory(&self, store: &Store, addr: MemAddr) -> Result<MemoryRef> {
let addr = self.resolve_mem_addr(addr);
let mem = store.get_mem(addr as usize)?;
- Ok(MemoryRef { _instance: mem.clone() })
+ Ok(MemoryRef { instance: mem.clone() })
}
/// Get the start function of the module
///
/// Returns None if the module has no start function
/// If no start function is specified, also checks for a _start function in the exports
- /// (which is not part of the spec, but used by llvm)
+ /// (which is not part of the spec, but used by some compilers)
///
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#start-function>
pub fn start_func(&self, store: &Store) -> Result<Option<FuncHandle>> {
diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs
index bc02338..fdaae18 100644
--- a/crates/tinywasm/src/reference.rs
+++ b/crates/tinywasm/src/reference.rs
@@ -1,17 +1,135 @@
-use core::cell::RefCell;
+use core::{
+ cell::{Ref, RefCell},
+ ffi::CStr,
+};
+
+use crate::{GlobalInstance, MemoryInstance, Result};
+use alloc::{
+ ffi::CString,
+ rc::Rc,
+ string::{String, ToString},
+ vec::Vec,
+};
+use tinywasm_types::WasmValue;
-use crate::{GlobalInstance, MemoryInstance};
-use alloc::rc::Rc;
// This module essentially contains the public APIs to interact with the data stored in the store
/// A reference to a memory instance
#[derive(Debug, Clone)]
pub struct MemoryRef {
- pub(crate) _instance: Rc<RefCell<MemoryInstance>>,
+ pub(crate) instance: Rc<RefCell<MemoryInstance>>,
+}
+
+/// A borrowed reference to a memory instance
+#[derive(Debug)]
+pub struct BorrowedMemory<'a> {
+ pub(crate) instance: Ref<'a, MemoryInstance>,
+}
+
+impl<'a> BorrowedMemory<'a> {
+ /// Load a slice of memory
+ pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
+ self.instance.load(offset, 0, len)
+ }
+
+ /// Load a C-style string from memory
+ pub fn load_cstr(&self, offset: usize, len: usize) -> Result<&CStr> {
+ let bytes = self.load(offset, len)?;
+ CStr::from_bytes_with_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string()))
+ }
+
+ /// Load a C-style string from memory, stopping at the first nul byte
+ pub fn load_cstr_until_nul(&self, offset: usize, max_len: usize) -> Result<&CStr> {
+ let bytes = self.load(offset, max_len)?;
+ CStr::from_bytes_until_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string()))
+ }
+}
+
+impl MemoryRef {
+ /// Borrow the memory instance
+ ///
+ /// This is useful for when you want to load only a reference to a slice of memory
+ /// without copying the data. The borrow should be dropped before any other memory
+ /// operations are performed.
+ pub fn borrow(&self) -> BorrowedMemory<'_> {
+ BorrowedMemory { instance: self.instance.borrow() }
+ }
+
+ /// Load a slice of memory
+ pub fn load_vec(&self, offset: usize, len: usize) -> Result<Vec<u8>> {
+ self.instance.borrow().load(offset, 0, len).map(|x| x.to_vec())
+ }
+
+ /// Grow the memory by the given number of pages
+ pub fn grow(&self, delta_pages: i32) -> Option<i32> {
+ self.instance.borrow_mut().grow(delta_pages)
+ }
+
+ /// Get the current size of the memory in pages
+ pub fn page_count(&self) -> usize {
+ self.instance.borrow().page_count()
+ }
+
+ /// Copy a slice of memory to another place in memory
+ pub fn copy_within(&self, src: usize, dst: usize, len: usize) -> Result<()> {
+ self.instance.borrow_mut().copy_within(src, dst, len)
+ }
+
+ /// Fill a slice of memory with a value
+ pub fn fill(&self, offset: usize, len: usize, val: u8) -> Result<()> {
+ self.instance.borrow_mut().fill(offset, len, val)
+ }
+
+ /// Load a UTF-8 string from memory
+ pub fn load_string(&self, offset: usize, len: usize) -> Result<String> {
+ let bytes = self.load_vec(offset, len)?;
+ Ok(String::from_utf8(bytes).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string()))?)
+ }
+
+ /// Load a C-style string from memory
+ pub fn load_cstring(&self, offset: usize, len: usize) -> Result<CString> {
+ Ok(CString::from(self.borrow().load_cstr(offset, len)?))
+ }
+
+ /// Load a C-style string from memory, stopping at the first nul byte
+ pub fn load_cstring_until_nul(&self, offset: usize, max_len: usize) -> Result<CString> {
+ Ok(CString::from(self.borrow().load_cstr_until_nul(offset, max_len)?))
+ }
+
+ /// Load a JavaScript-style utf-16 string from memory
+ pub fn load_js_string(&self, offset: usize, len: usize) -> Result<String> {
+ let memref = self.borrow();
+ let bytes = memref.load(offset, len)?;
+ let mut string = String::new();
+ for i in 0..(len / 2) {
+ let c = u16::from_le_bytes([bytes[i * 2], bytes[i * 2 + 1]]);
+ string.push(
+ char::from_u32(c as u32).ok_or_else(|| crate::Error::Other("Invalid UTF-16 string".to_string()))?,
+ );
+ }
+ Ok(string)
+ }
+
+ /// Store a slice of memory
+ pub fn store(&self, offset: usize, len: usize, data: &[u8]) -> Result<()> {
+ self.instance.borrow_mut().store(offset, 0, data, len)
+ }
}
/// A reference to a global instance
#[derive(Debug, Clone)]
pub struct GlobalRef {
- pub(crate) _instance: Rc<RefCell<GlobalInstance>>,
+ pub(crate) instance: Rc<RefCell<GlobalInstance>>,
+}
+
+impl GlobalRef {
+ /// Get the value of the global
+ pub fn get(&self) -> WasmValue {
+ self.instance.borrow().get()
+ }
+
+ /// Set the value of the global
+ pub fn set(&self, val: WasmValue) -> Result<()> {
+ self.instance.borrow_mut().set(val)
+ }
}
diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs
index 909acb3..cc65812 100644
--- a/crates/tinywasm/src/runtime/interpreter/macros.rs
+++ b/crates/tinywasm/src/runtime/interpreter/macros.rs
@@ -63,7 +63,7 @@ macro_rules! mem_store {
let val = val as $store_type;
let val = val.to_le_bytes();
- mem.borrow_mut().store(($arg.offset + addr) as usize, $arg.align as usize, &val)?;
+ mem.borrow_mut().store(($arg.offset + addr) as usize, $arg.align as usize, &val, val.len())?;
}};
}
diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs
index d281b5c..1c0d260 100644
--- a/crates/tinywasm/src/store.rs
+++ b/crates/tinywasm/src/store.rs
@@ -306,7 +306,9 @@ impl Store {
})?;
// See comment for active element sections in the function above why we need to do this here
- if let Err(Error::Trap(trap)) = mem.borrow_mut().store(offset as usize, 0, &data.data) {
+ if let Err(Error::Trap(trap)) =
+ mem.borrow_mut().store(offset as usize, 0, &data.data, data.data.len())
+ {
return Ok((data_addrs.into_boxed_slice(), Some(trap)));
}
@@ -607,8 +609,8 @@ impl MemoryInstance {
}
}
- pub(crate) fn store(&mut self, addr: usize, _align: usize, data: &[u8]) -> Result<()> {
- let end = addr.checked_add(data.len()).ok_or_else(|| {
+ pub(crate) fn store(&mut self, addr: usize, _align: usize, data: &[u8], len: usize) -> Result<()> {
+ let end = addr.checked_add(len).ok_or_else(|| {
Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len: data.len(), max: self.data.len() })
})?;
@@ -646,9 +648,37 @@ impl MemoryInstance {
self.page_count
}
- pub(crate) fn grow(&mut self, delta: i32) -> Option<i32> {
+ pub(crate) fn fill(&mut self, addr: usize, len: usize, val: u8) -> Result<()> {
+ let end = addr
+ .checked_add(len)
+ .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() }))?;
+ if end > self.data.len() {
+ return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() }));
+ }
+ self.data[addr..end].fill(val);
+ Ok(())
+ }
+
+ pub(crate) fn copy_within(&mut self, dst: usize, src: usize, len: usize) -> Result<()> {
+ let end = src
+ .checked_add(len)
+ .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: src, len, max: self.data.len() }))?;
+ if end > self.data.len() || end < src {
+ return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: src, len, max: self.data.len() }));
+ }
+ let end = dst
+ .checked_add(len)
+ .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len, max: self.data.len() }))?;
+ if end > self.data.len() || end < dst {
+ return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len, max: self.data.len() }));
+ }
+ self.data[dst..end].copy_within(src..end, len);
+ Ok(())
+ }
+
+ pub(crate) fn grow(&mut self, pages_delta: i32) -> Option<i32> {
let current_pages = self.page_count();
- let new_pages = current_pages as i64 + delta as i64;
+ let new_pages = current_pages as i64 + pages_delta as i64;
if new_pages < 0 || new_pages > MAX_PAGES as i64 {
return None;
@@ -669,7 +699,7 @@ impl MemoryInstance {
self.page_count = new_pages as usize;
log::debug!("memory was {} pages", current_pages);
- log::debug!("memory grown by {} pages", delta);
+ log::debug!("memory grown by {} pages", pages_delta);
log::debug!("memory grown to {} pages", self.page_count);
Some(current_pages.try_into().expect("memory size out of bounds, this should have been caught earlier"))
@@ -690,6 +720,25 @@ impl GlobalInstance {
pub(crate) fn new(ty: GlobalType, value: RawWasmValue, owner: ModuleInstanceAddr) -> Self {
Self { ty, value, _owner: owner }
}
+
+ pub(crate) fn get(&self) -> WasmValue {
+ self.value.attach_type(self.ty.ty)
+ }
+
+ pub(crate) fn set(&mut self, val: WasmValue) -> Result<()> {
+ if val.val_type() != self.ty.ty {
+ return Err(Error::Other(format!(
+ "global type mismatch: expected {:?}, got {:?}",
+ self.ty.ty,
+ val.val_type()
+ )));
+ }
+ if !self.ty.mutable {
+ return Err(Error::Other("global is immutable".to_string()));
+ }
+ self.value = val.into();
+ Ok(())
+ }
}
/// A WebAssembly Element Instance