diff --git a/build.rs b/build.rs index f79c691..f328e4d 100644 --- a/build.rs +++ b/build.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} diff --git a/src/env.rs b/src/env.rs index 804a5ab..438e7fc 100644 --- a/src/env.rs +++ b/src/env.rs @@ -1,9 +1,9 @@ -use wasmer::{HostEnvInitError, Instance, Memory, LazyInit, WasmerEnv, Function}; +use wasmer::{Function, HostEnvInitError, Instance, LazyInit, Memory, WasmerEnv}; #[derive(Clone, Default)] pub struct Env { pub memory: LazyInit, - pub new: Option + pub new: Option, } impl Env { @@ -25,9 +25,7 @@ impl WasmerEnv for Env { if let Ok(func) = instance.exports.get_function("__new") { self.new = Some(func.clone()) } - self.memory.initialize( - mem, - ); + self.memory.initialize(mem); Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 303e239..533f67d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,13 @@ mod env; -mod tools; mod string_ptr; +mod tools; -pub use string_ptr::StringPtr; pub use env::Env; +pub use string_ptr::StringPtr; pub use tools::abort; use std::fmt; -use wasmer::{Memory}; +use wasmer::Memory; pub trait Read { fn read(self, memory: &Memory) -> anyhow::Result; @@ -16,8 +16,8 @@ pub trait Read { pub trait Write { fn alloc(value: &str, memory: &Env) -> anyhow::Result>; - fn write(value: &str, memory: &Env) -> anyhow::Result>; - fn free() -> anyhow::Result<()>; + fn write(&self, value: &str, env: &Env) -> anyhow::Result<()>; + fn free(memory: &Env) -> anyhow::Result<()>; } #[derive(Debug)] diff --git a/src/string_ptr.rs b/src/string_ptr.rs index 6a57d05..9263f9e 100644 --- a/src/string_ptr.rs +++ b/src/string_ptr.rs @@ -1,6 +1,6 @@ +use super::{Env, Memory, Read, Write}; use std::convert::TryFrom; -use wasmer::{WasmPtr, Array, Value}; -use super::{Read, Memory, Env, Write}; +use wasmer::{Array, Value, WasmPtr}; pub type StringPtr = WasmPtr; @@ -24,27 +24,56 @@ impl Read for StringPtr { impl Write for StringPtr { fn alloc(value: &str, env: &Env) -> anyhow::Result> { - let new = env.new.as_ref().expect("Assembly Script Runtime ot exported"); - let size = i32::try_from(value.len()).expect("Cannot convert value size t i32"); + let new = env + .new + .as_ref() + .expect("Assembly Script Runtime ot exported"); + let size = i32::try_from(value.len())?; - let ptr = new.call(&[Value::I32(size << 1), Value::I32(1)]).expect("Failed to call __new").get(0).unwrap().i32().unwrap(); - let utf16 = value.encode_utf16(); - let view = env.memory.get_ref().expect("Failed to load memory").view::(); + let offset = u32::try_from( + new.call(&[Value::I32(size << 1), Value::I32(1)]) + .expect("Failed to call __new") + .get(0) + .unwrap() + .i32() + .unwrap(), + )?; + write_str(offset, value, env)?; + Ok(Box::new(StringPtr::new(offset))) + } - let from = usize::try_from(ptr)? / 2; - for (bytes, cell) in utf16.into_iter().zip(view[from..from + (size as usize)].iter()) { - cell.set(bytes); + fn write(&self, value: &str, env: &Env) -> anyhow::Result<()> { + let prev_size = size( + self.offset(), + env.memory.get_ref().expect("Failed to load memory"), + )?; + let new_size = u32::try_from(value.len())? << 1; + if prev_size == new_size { + write_str(self.offset(), value, env)? + } else { + todo!("Remove this and reallocate of bigger or smaller space") } - Ok(Box::new(StringPtr::new(ptr as u32))) + Ok(()) } - fn write(value: &str, memory: &Env) -> anyhow::Result> { - todo!() + fn free(_env: &Env) -> anyhow::Result<()> { + todo!("Release the memory from this string") } +} - fn free() -> anyhow::Result<()> { - todo!() +fn write_str(offset: u32, value: &str, env: &Env) -> anyhow::Result<()> { + let utf16 = value.encode_utf16(); + let view = env + .memory + .get_ref() + .expect("Failed to load memory") + .view::(); + // We count in 32 so we have to devide by 2 + let from = usize::try_from(offset)? / 2; + for (bytes, cell) in utf16.into_iter().zip(view[from..from + value.len()].iter()) { + cell.set(bytes); } + Ok(()) } fn size(offset: u32, memory: &Memory) -> anyhow::Result { @@ -58,4 +87,4 @@ fn size(offset: u32, memory: &Memory) -> anyhow::Result { } else { anyhow::bail!("Wrong offset: can't read size") } -} \ No newline at end of file +} diff --git a/src/tools.rs b/src/tools.rs index 27b9c9f..af6161d 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,15 +1,9 @@ -use super::{Env, StringPtr, Read}; +use super::{Env, Read, StringPtr}; // if get_string throws an exception abort for some reason is being called -pub fn abort( - env: &Env, - message: StringPtr, - filename: StringPtr, - line: i32, - col: i32 -) { +pub fn abort(env: &Env, message: StringPtr, filename: StringPtr, line: i32, col: i32) { let memory = env.memory.get_ref().expect("initialized memory"); let message = message.read(memory).unwrap(); let filename = filename.read(memory).unwrap(); eprintln!("Error: {} at {}:{} col: {}", message, filename, line, col); -} \ No newline at end of file +} diff --git a/tests/strings.rs b/tests/strings.rs index 3dec089..4d1ad26 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -1,15 +1,42 @@ use std::error::Error; -use wasmer::{Store, Module, Instance, imports, Function}; -use wasmer_as::{Read, Write, StringPtr, Env, abort}; +use wasmer::{imports, Function, Instance, Module, Store}; +use wasmer_as::{abort, Env, Read, StringPtr, Write}; #[test] fn read_strings() -> Result<(), Box> { + let wasm_bytes = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/test_wat.wat")); + let store = Store::default(); + let module = Module::new(&store, wasm_bytes)?; + let import_object = imports! { + "env" => { + "abort" => Function::new_native_with_env(&store, Env::default(), abort), + }, + }; + + let instance = Instance::new(&module, &import_object)?; + let memory = instance.exports.get_memory("memory").expect("get memory"); + + let get_string = instance + .exports + .get_native_function::<(), StringPtr>("getString")?; + + let str_ptr = get_string.call()?; + let string = str_ptr.read(memory)?; + + assert_eq!(string, "$¢ह한𝌆"); + + Ok(()) +} + +#[test] +fn read_alloc_strings() -> Result<(), Box> { let wasm_bytes = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), - "/tests/test_wat.wat" + "/tests/runtime_exported.wat" )); let store = Store::default(); let module = Module::new(&store, wasm_bytes)?; + let import_object = imports! { "env" => { "abort" => Function::new_native_with_env(&store, Env::default(), abort), @@ -19,6 +46,14 @@ fn read_strings() -> Result<(), Box> { let instance = Instance::new(&module, &import_object)?; let memory = instance.exports.get_memory("memory").expect("get memory"); + let env = Env::new( + memory.clone(), + match instance.exports.get_function("__new") { + Ok(func) => Some(func.clone()), + _ => None, + }, + ); + let get_string = instance .exports .get_native_function::<(), StringPtr>("getString")?; @@ -26,7 +61,11 @@ fn read_strings() -> Result<(), Box> { let str_ptr = get_string.call()?; let string = str_ptr.read(memory)?; - assert_eq!(string, "$¢ह한𝌆"); + assert_eq!(string, "hello test"); + + let str_ptr_2 = StringPtr::alloc("hello return", &env)?; + let string = str_ptr_2.read(memory)?; + assert_eq!(string, "hello return"); Ok(()) } @@ -49,10 +88,13 @@ fn read_write_strings() -> Result<(), Box> { let instance = Instance::new(&module, &import_object)?; let memory = instance.exports.get_memory("memory").expect("get memory"); - let env = Env::new(memory.clone(), match instance.exports.get_function("__new") { - Ok(func) => Some(func.clone()), - _ => None - }); + let env = Env::new( + memory.clone(), + match instance.exports.get_function("__new") { + Ok(func) => Some(func.clone()), + _ => None, + }, + ); let get_string = instance .exports @@ -63,9 +105,11 @@ fn read_write_strings() -> Result<(), Box> { assert_eq!(string, "hello test"); - let str_ptr_2 = StringPtr::alloc("hello return", &env)?; + str_ptr.write("hallo tast", &env)?; + + let str_ptr_2 = get_string.call()?; let string = str_ptr_2.read(memory)?; - assert_eq!(string, "hello return"); + assert_eq!(string, "hallo tast"); Ok(()) -} \ No newline at end of file +}