diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml index 24ca8005219..c303bddb705 100644 --- a/.github/workflows/benchmark.yaml +++ b/.github/workflows/benchmark.yaml @@ -24,7 +24,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 - name: Configure cargo data directory # After this point, all cargo registry and crate data is stored in # $GITHUB_WORKSPACE/.cargo_home. This allows us to cache only the files diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1028c950b2b..519138b2f5c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,6 +100,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: + toolchain: 1.64 target: ${{ matrix.target }} - uses: Swatinem/rust-cache@v1 if: matrix.use_sccache != true diff --git a/.github/workflows/cloudcompiler.yaml b/.github/workflows/cloudcompiler.yaml index 46744f53b8b..c7ef5b8ec78 100644 --- a/.github/workflows/cloudcompiler.yaml +++ b/.github/workflows/cloudcompiler.yaml @@ -21,7 +21,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.target }} - name: Install wasm32-wasi target shell: bash diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml index 31db70ee62d..4ffdfb468cf 100644 --- a/.github/workflows/documentation.yaml +++ b/.github/workflows/documentation.yaml @@ -16,7 +16,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 - name: Install LLVM shell: bash run: | diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9a203400626..4c11cf472ab 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -41,7 +41,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 components: rustfmt, clippy - name: Install libtinfo shell: bash @@ -93,7 +93,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 - name: Install NodeJS uses: actions/setup-node@v2 with: @@ -104,9 +104,11 @@ jobs: - name: make test-js run: | make test-js - - name: make test-js-core - run: | - make test-js-core + + # The no_std functionality doesn't work at the moment - no point in testing it. + # - name: make test-js-core + # run: | + # make test-js-core test_wasm_build: name: Test wasm build @@ -239,7 +241,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.metadata.target }} - name: Install Windows-GNU linker if: ${{ matrix.metadata.build == 'windows-gnu' }} @@ -457,7 +459,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.metadata.target }} - name: Install LLVM (macOS Apple Silicon) if: matrix.metadata.os == 'macos-11.0' && !matrix.metadata.llvm_url @@ -557,7 +559,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.63 + toolchain: 1.64 target: ${{ matrix.metadata.target }} - name: Cache uses: whywaita/actions-cache-s3@v2 diff --git a/Cargo.lock b/Cargo.lock index 80ef35a704e..742a1f4b0b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4200,6 +4200,7 @@ dependencies = [ "anyhow", "bytes", "cfg-if 1.0.0", + "derivative", "hashbrown 0.11.2", "indexmap", "js-sys", @@ -4667,6 +4668,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "corosensei", + "derivative", "enum-iterator", "indexmap", "lazy_static", @@ -4678,6 +4680,7 @@ dependencies = [ "scopeguard", "serde", "thiserror", + "tracing", "wasmer-types", "winapi", ] @@ -5044,9 +5047,9 @@ dependencies = [ [[package]] name = "webc" -version = "3.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef87e7b955d5d1feaa8697ae129f1a9ce8859e151574ad3baceae9413b48d2f0" +checksum = "1b44d4d5ad9ecc7392210891a8a9207c04f6984a594be82075f5e7abe9271fcc" dependencies = [ "anyhow", "base64", diff --git a/Makefile b/Makefile index fedb6073cbb..e2944c9f7a2 100644 --- a/Makefile +++ b/Makefile @@ -502,8 +502,10 @@ test-examples: test-stage-5-test-examples test-stage-6-test-examples-release test-js: test-js-api test-js-wasi -test-js-core: - cd lib/api && wasm-pack test --node -- --no-default-features --features js,core,wasm-types-polyfill,wat +# TODO: disabled because the no-std / core feature doesn't actually work at the moment. +# See https://github.com/wasmerio/wasmer/issues/3429 +# test-js-core: +# cd lib/api && wasm-pack test --node -- --no-default-features --features js,core,wasm-types-polyfill,wat test-js-api: cd lib/api && wasm-pack test --node -- --no-default-features --features js-default,wat diff --git a/deny.toml b/deny.toml index 839d4e4090b..897dfca8690 100644 --- a/deny.toml +++ b/deny.toml @@ -49,7 +49,13 @@ notice = "warn" # output a note when they are encountered. ignore = [ #"RUSTSEC-0000-0000", + ] +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +git-fetch-with-cli = true # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories # will still output a note when they are encountered. @@ -105,13 +111,21 @@ confidence-threshold = 0.8 exceptions = [ # Each entry is the crate and version constraint, and its specific allow # list - { allow = ["LicenseRef-LICENSE.txt"], name = "webc", version = "*" }, ] + + # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the # licensing information -#[[licenses.clarify]] +[[licenses.clarify]] +name = "webc" +version = "*" +expression = "BSL-1.0" +license-files = [ + { path = "LICENSE.txt", hash = 0xa2180a97 } +] + # The name of the crate the clarification applies to #name = "ring" # The optional version constraint for the crate diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 764b54afd94..7e1939737b9 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -26,6 +26,7 @@ indexmap = { version = "1.6" } cfg-if = "1.0" thiserror = "1.0" more-asserts = "0.2" +derivative = { version = "^2" } bytes = "1" # - Optional shared dependencies. wat = { version = "1.0", optional = true } diff --git a/lib/api/src/into_bytes.rs b/lib/api/src/into_bytes.rs new file mode 100644 index 00000000000..2016036819d --- /dev/null +++ b/lib/api/src/into_bytes.rs @@ -0,0 +1,50 @@ +use bytes::Bytes; +use std::borrow::Cow; + +/// Convert binary data into [`bytes::Bytes`]. +pub trait IntoBytes { + /// Convert binary data into [`bytes::Bytes`]. + fn into_bytes(self) -> Bytes; +} + +impl IntoBytes for Bytes { + fn into_bytes(self) -> Bytes { + self + } +} + +impl IntoBytes for Vec { + fn into_bytes(self) -> Bytes { + Bytes::from(self) + } +} + +impl IntoBytes for &Vec { + fn into_bytes(self) -> Bytes { + Bytes::from(self.clone()) + } +} + +impl IntoBytes for &[u8] { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} + +impl IntoBytes for &[u8; N] { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} + +impl IntoBytes for &str { + fn into_bytes(self) -> Bytes { + Bytes::from(self.as_bytes().to_vec()) + } +} + +impl IntoBytes for Cow<'_, [u8]> { + fn into_bytes(self) -> Bytes { + Bytes::from(self.to_vec()) + } +} diff --git a/lib/api/src/js/export.rs b/lib/api/src/js/export.rs index a8cb689ed02..180f1d76213 100644 --- a/lib/api/src/js/export.rs +++ b/lib/api/src/js/export.rs @@ -1,12 +1,17 @@ use crate::js::error::WasmError; use crate::js::store::{AsStoreMut, AsStoreRef, InternalStoreHandle}; use crate::js::wasm_bindgen_polyfill::Global; +use crate::MemoryView; use js_sys::Function; use js_sys::WebAssembly::{Memory, Table}; use serde::{Deserialize, Serialize}; use std::fmt; +#[cfg(feature = "tracing")] +use tracing::trace; use wasm_bindgen::{JsCast, JsValue}; -use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType, WASM_PAGE_SIZE}; +use wasmer_types::{ + ExternType, FunctionType, GlobalType, MemoryError, MemoryType, Pages, TableType, WASM_PAGE_SIZE, +}; /// Represents linear memory that is managed by the javascript runtime #[derive(Clone, Debug, PartialEq)] @@ -25,7 +30,8 @@ struct DummyBuffer { } impl VMMemory { - pub(crate) fn new(memory: Memory, ty: MemoryType) -> Self { + /// Creates a new memory directly from a WebAssembly javascript object + pub fn new(memory: Memory, ty: MemoryType) -> Self { Self { memory, ty } } @@ -45,6 +51,52 @@ impl VMMemory { pub(crate) fn try_clone(&self) -> Option { Some(self.clone()) } + + /// Copies this memory to a new memory + pub fn duplicate(&self) -> Result { + let new_memory = crate::Memory::new_internal(self.ty.clone())?; + + #[cfg(feature = "tracing")] + trace!("memory copy started"); + + let src = MemoryView::new_raw(&self.memory); + let amount = src.data_size() as usize; + let mut dst = MemoryView::new_raw(&new_memory); + let dst_size = dst.data_size() as usize; + + if amount > dst_size { + let delta = amount - dst_size; + let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; + + let our_js_memory: &crate::js::externals::memory::JSMemory = + JsCast::unchecked_from_js_ref(&new_memory); + our_js_memory.grow(pages as u32).map_err(|err| { + if err.is_instance_of::() { + let cur_pages = dst_size; + MemoryError::CouldNotGrow { + current: Pages(cur_pages as u32), + attempted_delta: Pages(pages as u32), + } + } else { + MemoryError::Generic(err.as_string().unwrap()) + } + })?; + + dst = MemoryView::new_raw(&new_memory); + } + + src.copy_to_memory(amount as u64, &dst).map_err(|err| { + wasmer_types::MemoryError::Generic(format!("failed to copy the memory - {}", err)) + })?; + + #[cfg(feature = "tracing")] + trace!("memory copy finished (size={})", dst.size().bytes().0); + + Ok(Self { + memory: new_memory, + ty: self.ty.clone(), + }) + } } #[derive(Clone, Debug, PartialEq)] diff --git a/lib/api/src/js/externals/function.rs b/lib/api/src/js/externals/function.rs index 770c462ae60..378e07e0885 100644 --- a/lib/api/src/js/externals/function.rs +++ b/lib/api/src/js/externals/function.rs @@ -61,6 +61,12 @@ pub struct Function { pub(crate) handle: StoreHandle, } +impl From> for Function { + fn from(handle: StoreHandle) -> Self { + Self { handle } + } +} + impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. /// @@ -393,6 +399,14 @@ impl Function { store: &mut impl AsStoreMut, params: &[Value], ) -> Result, RuntimeError> { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let params: Vec<_> = unsafe { + params + .iter() + .map(|a| a.as_raw_value(&store.as_store_ref())) + .collect() + }; let arr = js_sys::Array::new_with_length(params.len() as u32); // let raw_env = env.as_raw() as *mut u8; @@ -402,11 +416,35 @@ impl Function { let js_value = param.as_jsvalue(&store.as_store_ref()); arr.set(i as u32, js_value); } - let result = js_sys::Reflect::apply( - &self.handle.get(store.as_store_ref().objects()).function, - &wasm_bindgen::JsValue::NULL, - &arr, - )?; + + let result = { + let mut r; + // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + loop { + r = js_sys::Reflect::apply( + &self.handle.get(store.as_store_ref().objects()).function, + &wasm_bindgen::JsValue::NULL, + &arr, + ); + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { + continue; + } + Ok(wasmer_types::OnCalledAction::Finish) => { + break; + } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { + return Err(RuntimeError::user(trap)) + } + Err(trap) => return Err(RuntimeError::user(trap)), + } + } + break; + } + r? + }; let result_types = self.handle.get(store.as_store_ref().objects()).ty.results(); match result_types.len() { @@ -1134,16 +1172,18 @@ mod inner { T: Send + 'static, Func: Fn(FunctionEnvMut<'_, T>, $( $x , )*) -> RetsAsResult + 'static, { - // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; - let func: &Func = &*(&() as *const () as *const Func); let mut store = StoreMut::from_raw(store_ptr as *mut _); let mut store2 = StoreMut::from_raw(store_ptr as *mut _); - let result = panic::catch_unwind(AssertUnwindSafe(|| { - let handle: StoreHandle = StoreHandle::from_internal(store2.objects_mut().id(), InternalStoreHandle::from_index(handle_index).unwrap()); - let env: FunctionEnvMut = FunctionEnv::from_handle(handle).into_mut(&mut store2); - func(env, $( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() - })); + let result = { + // let env: &Env = unsafe { &*(ptr as *const u8 as *const Env) }; + let func: &Func = &*(&() as *const () as *const Func); + panic::catch_unwind(AssertUnwindSafe(|| { + let handle: StoreHandle = StoreHandle::from_internal(store2.objects_mut().id(), InternalStoreHandle::from_index(handle_index).unwrap()); + let env: FunctionEnvMut = FunctionEnv::from_handle(handle).into_mut(&mut store2); + func(env, $( FromToNativeWasmType::from_native(NativeWasmTypeInto::from_abi(&mut store, $x)) ),* ).into_result() + })) + }; match result { Ok(Ok(result)) => return result.into_c_struct(&mut store), diff --git a/lib/api/src/js/externals/memory.rs b/lib/api/src/js/externals/memory.rs index b57639b4897..58da006b201 100644 --- a/lib/api/src/js/externals/memory.rs +++ b/lib/api/src/js/externals/memory.rs @@ -11,7 +11,7 @@ use tracing::warn; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; -use wasmer_types::Pages; +use wasmer_types::{Pages, WASM_PAGE_SIZE}; use super::MemoryView; @@ -86,19 +86,26 @@ impl Memory { /// let m = Memory::new(&store, MemoryType::new(1, None, false)).unwrap(); /// ``` pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { + let vm_memory = VMMemory::new(Self::new_internal(ty.clone())?, ty); + Ok(Self::from_vm_export(store, vm_memory)) + } + + pub(crate) fn new_internal(ty: MemoryType) -> Result { let descriptor = js_sys::Object::new(); - js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); - if let Some(max) = ty.maximum { - js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.0.into()).unwrap(); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set(&descriptor, &"initial".into(), &ty.minimum.0.into()).unwrap(); + if let Some(max) = ty.maximum { + js_sys::Reflect::set(&descriptor, &"maximum".into(), &max.0.into()).unwrap(); + } + js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into()).unwrap(); } - js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into()).unwrap(); let js_memory = js_sys::WebAssembly::Memory::new(&descriptor) .map_err(|_e| MemoryError::Generic("Error while creating the memory".to_owned()))?; - let vm_memory = VMMemory::new(js_memory, ty); - let handle = StoreHandle::new(store.objects_mut(), vm_memory); - Ok(Self::from_vm_extern(store, handle.internal_handle())) + Ok(js_memory) } /// Creates a new host `Memory` from provided JavaScript memory. @@ -200,6 +207,41 @@ impl Memory { Ok(Pages(new_pages)) } + /// Copies the memory to a new store and returns a memory reference to it + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + // Create the new memory using the parameters of the existing memory + let view = self.view(store); + let ty = self.ty(store); + let amount = view.data_size() as usize; + + let new_memory = Self::new(new_store, ty)?; + let mut new_view = new_memory.view(&new_store); + let new_view_size = new_view.data_size() as usize; + if amount > new_view_size { + let delta = amount - new_view_size; + let pages = ((delta - 1) / WASM_PAGE_SIZE) + 1; + new_memory.grow(new_store, Pages(pages as u32))?; + new_view = new_memory.view(&new_store); + } + + // Copy the bytes + view.copy_to_memory(amount as u64, &new_view) + .map_err(|err| MemoryError::Generic(err.to_string()))?; + + // Return the new memory + Ok(new_memory) + } + + pub(crate) fn from_vm_export(store: &mut impl AsStoreMut, vm_memory: VMMemory) -> Self { + Self { + handle: StoreHandle::new(store.objects_mut(), vm_memory), + } + } + pub(crate) fn from_vm_extern( store: &mut impl AsStoreMut, internal: InternalStoreHandle, @@ -221,6 +263,12 @@ impl Memory { pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool { self.handle.store_id() == store.as_store_ref().objects().id() } + + /// Copies this memory to a new memory + pub fn duplicate(&mut self, store: &impl AsStoreRef) -> Result { + let mem = self.handle.get(store.as_store_ref().objects()); + mem.duplicate() + } } impl std::cmp::PartialEq for Memory { diff --git a/lib/api/src/js/externals/memory_view.rs b/lib/api/src/js/externals/memory_view.rs index a219572207e..d4eb10e44f7 100644 --- a/lib/api/src/js/externals/memory_view.rs +++ b/lib/api/src/js/externals/memory_view.rs @@ -27,11 +27,12 @@ pub struct MemoryView<'a> { impl<'a> MemoryView<'a> { pub(crate) fn new(memory: &Memory, store: &impl AsStoreRef) -> Self { - let buffer = memory - .handle - .get(store.as_store_ref().objects()) - .memory - .buffer(); + let memory = memory.handle.get(store.as_store_ref().objects()); + Self::new_raw(&memory.memory) + } + + pub(crate) fn new_raw(memory: &js_sys::WebAssembly::Memory) -> Self { + let buffer = memory.buffer(); let size = js_sys::Reflect::get(&buffer, &"byteLength".into()) .unwrap() @@ -250,4 +251,35 @@ impl<'a> MemoryView<'a> { view.set_index(offset, val); Ok(()) } + + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < self.data_size() { + let remaining = self.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < amount { + let remaining = amount - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + + new_memory.write(offset, &chunk[..sublen])?; + + offset += sublen as u64; + } + Ok(()) + } } diff --git a/lib/api/src/js/externals/mod.rs b/lib/api/src/js/externals/mod.rs index 60ed02f1879..259a8e13c9e 100644 --- a/lib/api/src/js/externals/mod.rs +++ b/lib/api/src/js/externals/mod.rs @@ -30,7 +30,7 @@ use js_sys::Function as JsFunction; use js_sys::WebAssembly::{Memory as JsMemory, Table as JsTable}; use std::fmt; use wasm_bindgen::{JsCast, JsValue}; -use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType}; +use wasmer_types::ExternType; /// The value of an export passed from one instance to another. pub enum VMExtern { diff --git a/lib/api/src/js/imports.rs b/lib/api/src/js/imports.rs index fb82e9885c1..de877fe0e57 100644 --- a/lib/api/src/js/imports.rs +++ b/lib/api/src/js/imports.rs @@ -1,8 +1,7 @@ //! The import module contains the implementation data structures and helper functions used to //! manipulate and access a wasm module's imports including memories, tables, globals, and //! functions. -use crate::js::error::{InstantiationError, LinkError, WasmError}; -use crate::js::export::Export; +use crate::js::error::{LinkError, WasmError}; use crate::js::exports::Exports; use crate::js::module::Module; use crate::js::store::{AsStoreMut, AsStoreRef}; @@ -172,11 +171,19 @@ impl Imports { for (ns, exports) in namespaces.into_iter() { let import_namespace = js_sys::Object::new(); for (name, ext) in exports { - js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(store)) - .expect("Error while setting into the js namespace object"); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set(&import_namespace, &name.into(), &ext.as_jsvalue(store)) + .expect("Error while setting into the js namespace object"); + } + } + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into()) + .expect("Error while setting into the js imports object"); } - js_sys::Reflect::set(&imports, &ns.into(), &import_namespace.into()) - .expect("Error while setting into the js imports object"); } imports } @@ -233,29 +240,43 @@ impl Imports { } impl AsJs for Imports { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] fn as_jsvalue(&self, store: &impl AsStoreRef) -> wasm_bindgen::JsValue { let imports_object = js_sys::Object::new(); for (namespace, name, extern_) in self.iter() { - let val = js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap(); + let val = unsafe { js_sys::Reflect::get(&imports_object, &namespace.into()).unwrap() }; if !val.is_undefined() { // If the namespace is already set - js_sys::Reflect::set( - &val, - &name.into(), - &extern_.as_jsvalue(&store.as_store_ref()), - ) - .unwrap(); + + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &val, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) + .unwrap(); + } } else { // If the namespace doesn't exist let import_namespace = js_sys::Object::new(); - js_sys::Reflect::set( - &import_namespace, - &name.into(), - &extern_.as_jsvalue(&store.as_store_ref()), - ) - .unwrap(); - js_sys::Reflect::set(&imports_object, &namespace.into(), &import_namespace.into()) + #[allow(unused_unsafe)] + unsafe { + js_sys::Reflect::set( + &import_namespace, + &name.into(), + &extern_.as_jsvalue(&store.as_store_ref()), + ) .unwrap(); + js_sys::Reflect::set( + &imports_object, + &namespace.into(), + &import_namespace.into(), + ) + .unwrap(); + } } } imports_object.into() diff --git a/lib/api/src/js/instance.rs b/lib/api/src/js/instance.rs index 5e1ddbf2e45..255240c5e82 100644 --- a/lib/api/src/js/instance.rs +++ b/lib/api/src/js/instance.rs @@ -1,5 +1,4 @@ use crate::js::error::InstantiationError; -use crate::js::export::Export; use crate::js::exports::Exports; use crate::js::externals::Extern; use crate::js::imports::Imports; @@ -63,11 +62,13 @@ impl Instance { module: &Module, imports: &Imports, ) -> Result { - let instance: WebAssembly::Instance = module + let (instance, externs) = module .instantiate(&mut store, imports) .map_err(|e| InstantiationError::Start(e))?; - let self_instance = Self::from_module_and_instance(store, module, instance)?; + let instance = instance.get(store.objects_mut()).clone(); + let mut self_instance = Self::from_module_and_instance(store, module, instance)?; + self_instance.ensure_memory_export(store, externs); //self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::>())?; Ok(self_instance) } @@ -109,20 +110,27 @@ impl Instance { instance: WebAssembly::Instance, ) -> Result { use crate::js::externals::VMExtern; + let instance_exports = instance.exports(); + let exports = module .exports() .map(|export_type| { let name = export_type.name(); let extern_type = export_type.ty().clone(); - let js_export = js_sys::Reflect::get(&instance_exports, &name.into()) - .map_err(|_e| InstantiationError::NotInExports(name.to_string()))?; + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let js_export = unsafe { + js_sys::Reflect::get(&instance_exports, &name.into()) + .map_err(|_e| InstantiationError::NotInExports(name.to_string()))? + }; let export: VMExtern = VMExtern::from_js_value(js_export, &mut store, extern_type)?.into(); let extern_ = Extern::from_vm_extern(&mut store, export); Ok((name.to_string(), extern_)) }) .collect::>()?; + let handle = StoreHandle::new(store.as_store_mut().objects_mut(), instance); Ok(Self { _handle: handle, @@ -131,6 +139,21 @@ impl Instance { }) } + /// This will check the memory is correctly setup + /// If the memory is imported then also export it for backwards compatibility reasons + /// (many will assume the memory is always exported) - later we can remove this + pub fn ensure_memory_export(&mut self, store: &mut impl AsStoreMut, externs: Vec) { + if self.exports.get_memory("memory").is_err() { + if let Some(memory) = externs + .iter() + .filter(|a| a.ty(store).memory().is_some()) + .next() + { + self.exports.insert("memory", memory.clone()); + } + } + } + /// Gets the [`Module`] associated with this instance. pub fn module(&self) -> &Module { &self.module diff --git a/lib/api/src/js/mem_access.rs b/lib/api/src/js/mem_access.rs index ac0a543436f..5a9142e48e4 100644 --- a/lib/api/src/js/mem_access.rs +++ b/lib/api/src/js/mem_access.rs @@ -328,6 +328,24 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { } Ok(vec) } + + /// Reads this `WasmSlice` into a `BytesMut` + #[inline] + pub fn read_to_bytes(self) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut ret = bytes::BytesMut::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + ret.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + ret.set_len(len); + } + Ok(ret) + } } impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 13cc7a2504b..baa41641fbf 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -77,9 +77,10 @@ pub mod vm { } pub use wasmer_types::is_wasm; +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ - Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, Pages, ValueType, WASM_MAX_PAGES, - WASM_MIN_PAGES, WASM_PAGE_SIZE, + Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, OnCalledAction, Pages, ValueType, + WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, }; #[cfg(feature = "wat")] diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 78096584b25..7b22ffaa0b1 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -3,19 +3,23 @@ use crate::js::error::WasmError; use crate::js::error::{CompileError, InstantiationError}; #[cfg(feature = "js-serializable-module")] use crate::js::error::{DeserializeError, SerializeError}; +use crate::js::externals::Extern; use crate::js::imports::Imports; use crate::js::store::AsStoreMut; use crate::js::types::{AsJs, ExportType, ImportType}; use crate::js::RuntimeError; use crate::AsStoreRef; +use crate::IntoBytes; +#[cfg(feature = "js-serializable-module")] use bytes::Bytes; use js_sys::{Reflect, Uint8Array, WebAssembly}; -use std::borrow::Cow; use std::fmt; use std::io; use std::path::Path; #[cfg(feature = "std")] use thiserror::Error; +#[cfg(feature = "tracing")] +use tracing::{debug, warn}; use wasm_bindgen::JsValue; use wasmer_types::{ ExportsIterator, ExternType, FunctionType, GlobalType, ImportsIterator, MemoryType, Mutability, @@ -51,46 +55,6 @@ pub struct ModuleTypeHints { pub exports: Vec, } -pub trait IntoBytes { - fn into_bytes(self) -> Bytes; -} - -impl IntoBytes for Bytes { - fn into_bytes(self) -> Bytes { - self - } -} - -impl IntoBytes for Vec { - fn into_bytes(self) -> Bytes { - Bytes::from(self) - } -} - -impl IntoBytes for &[u8] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &[u8; N] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &str { - fn into_bytes(self) -> Bytes { - Bytes::from(self.as_bytes().to_vec()) - } -} - -impl IntoBytes for Cow<'_, [u8]> { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - /// A WebAssembly Module contains stateless WebAssembly /// code that has already been compiled and can be instantiated /// multiple times. @@ -171,14 +135,18 @@ impl Module { /// ``` #[allow(unreachable_code)] pub fn new(_store: &impl AsStoreRef, bytes: impl AsRef<[u8]>) -> Result { + let bytes = bytes.as_ref(); #[cfg(feature = "wat")] - let bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { - CompileError::Wasm(WasmError::Generic(format!( - "Error when converting wat: {}", - e - ))) - })?; - Self::from_binary(_store, bytes.as_ref()) + if bytes.starts_with(b"\0asm") == false { + let parsed_bytes = wat::parse_bytes(bytes.as_ref()).map_err(|e| { + CompileError::Wasm(WasmError::Generic(format!( + "Error when converting wat: {}", + e + ))) + })?; + return Self::from_binary(_store, parsed_bytes.as_ref()); + } + Self::from_binary(_store, bytes) } /// Creates a new WebAssembly module from a file path. @@ -195,7 +163,6 @@ impl Module { /// the WebAssembly text format (if the "wat" feature is enabled for /// this crate). pub fn from_binary(_store: &impl AsStoreRef, binary: &[u8]) -> Result { - // // Self::validate(store, binary)?; unsafe { Self::from_binary_unchecked(_store, binary) } } @@ -207,16 +174,27 @@ impl Module { /// This is safe since the JS vm should be safe already. /// We maintain the `unsafe` to preserve the same API as Wasmer pub unsafe fn from_binary_unchecked( - _store: &impl AsStoreRef, + store: &impl AsStoreRef, binary: &[u8], ) -> Result { let js_bytes = Uint8Array::view(binary); let module = WebAssembly::Module::new(&js_bytes.into()).unwrap(); + Self::from_js_module(store, module, binary) + } + + /// Creates a new WebAssembly module skipping any kind of validation from a javascript module + /// + pub unsafe fn from_js_module( + _store: &impl AsStoreRef, + module: WebAssembly::Module, + binary: impl IntoBytes, + ) -> Result { + let binary = binary.into_bytes(); // The module is now validated, so we can safely parse it's types #[cfg(feature = "wasm-types-polyfill")] let (type_hints, name) = { - let info = crate::js::module_info_polyfill::translate_module(binary).unwrap(); + let info = crate::js::module_info_polyfill::translate_module(&binary[..]).unwrap(); ( Some(ModuleTypeHints { @@ -254,9 +232,13 @@ impl Module { /// validation of the Module. pub fn validate(_store: &impl AsStoreRef, binary: &[u8]) -> Result<(), CompileError> { let js_bytes = unsafe { Uint8Array::view(binary) }; - match WebAssembly::validate(&js_bytes.into()) { - Ok(true) => Ok(()), - _ => Err(CompileError::Validate("Invalid Wasm file".to_owned())), + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + match WebAssembly::validate(&js_bytes.into()) { + Ok(true) => Ok(()), + _ => Err(CompileError::Validate("Invalid Wasm file".to_owned())), + } } } @@ -264,7 +246,7 @@ impl Module { &self, store: &mut impl AsStoreMut, imports: &Imports, - ) -> Result { + ) -> Result<(crate::StoreHandle, Vec), RuntimeError> { // Ensure all imports come from the same store. if imports .into_iter() @@ -274,10 +256,81 @@ impl Module { InstantiationError::DifferentStores, ))); } + // TODO: refactor this if possible, after the WASIX merge. + // The imported/exported memory does not have the correct properties + // (incorrect size and shared flag) hence when using shared memory its + // failing - the only way to fix it is to resolve the import and use the + // correct memory properties. this regression issue was only found + // in WASIX on the browser as the other areas don't mind that they don't match up + // sharrattj/dash should be able to reproduce this. - let imports_js_obj = imports.as_jsvalue(store).into(); - Ok(WebAssembly::Instance::new(&self.module, &imports_js_obj) - .map_err(|e: JsValue| -> RuntimeError { e.into() })?) + let imports_object = js_sys::Object::new(); + let mut import_externs: Vec = vec![]; + for import_type in self.imports() { + let resolved_import = imports.get_export(import_type.module(), import_type.name()); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_variables)] + if let wasmer_types::ExternType::Memory(mem_ty) = import_type.ty() { + if resolved_import.is_some() { + #[cfg(feature = "tracing")] + debug!("imported shared memory {:?}", &mem_ty); + } else { + #[cfg(feature = "tracing")] + warn!( + "Error while importing {0:?}.{1:?}: memory. Expected {2:?}", + import_type.module(), + import_type.name(), + import_type.ty(), + ); + } + } + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + if let Some(import) = resolved_import { + let val = js_sys::Reflect::get(&imports_object, &import_type.module().into())?; + if !val.is_undefined() { + // If the namespace is already set + js_sys::Reflect::set( + &val, + &import_type.name().into(), + &import.as_jsvalue(&store.as_store_ref()), + )?; + } else { + // If the namespace doesn't exist + let import_namespace = js_sys::Object::new(); + js_sys::Reflect::set( + &import_namespace, + &import_type.name().into(), + &import.as_jsvalue(&store.as_store_ref()), + )?; + js_sys::Reflect::set( + &imports_object, + &import_type.module().into(), + &import_namespace.into(), + )?; + } + import_externs.push(import); + } else { + #[cfg(feature = "tracing")] + warn!( + "import not found {}:{}", + import_type.module(), + import_type.name() + ); + } + } + // in case the import is not found, the JS Wasm VM will handle + // the error for us, so we don't need to handle it + } + Ok(( + crate::StoreHandle::new( + store.as_store_mut().objects_mut(), + WebAssembly::Instance::new(&self.module, &imports_object) + .map_err(|e: JsValue| -> RuntimeError { e.into() })?, + ), + import_externs, + )) } /// Returns the name of the current module. @@ -416,39 +469,54 @@ impl Module { let imports = WebAssembly::Module::imports(&self.module); let iter = imports .iter() - .map(move |val| { - let module = Reflect::get(val.as_ref(), &"module".into()) - .unwrap() - .as_string() - .unwrap(); - let field = Reflect::get(val.as_ref(), &"name".into()) - .unwrap() - .as_string() - .unwrap(); - let kind = Reflect::get(val.as_ref(), &"kind".into()) - .unwrap() - .as_string() - .unwrap(); - let extern_type = match kind.as_str() { - "function" => { - let func_type = FunctionType::new(vec![], vec![]); - ExternType::Function(func_type) - } - "global" => { - let global_type = GlobalType::new(Type::I32, Mutability::Const); - ExternType::Global(global_type) - } - "memory" => { - let memory_type = MemoryType::new(Pages(1), None, false); - ExternType::Memory(memory_type) - } - "table" => { - let table_type = TableType::new(Type::FuncRef, 1, None); - ExternType::Table(table_type) - } - _ => unimplemented!(), - }; - ImportType::new(&module, &field, extern_type) + .enumerate() + .map(move |(i, val)| { + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + unsafe { + let module = Reflect::get(val.as_ref(), &"module".into()) + .unwrap() + .as_string() + .unwrap(); + let field = Reflect::get(val.as_ref(), &"name".into()) + .unwrap() + .as_string() + .unwrap(); + let kind = Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap(); + let type_hint = self + .type_hints + .as_ref() + .map(|hints| hints.imports.get(i).unwrap().clone()); + let extern_type = if let Some(hint) = type_hint { + hint + } else { + match kind.as_str() { + "function" => { + let func_type = FunctionType::new(vec![], vec![]); + ExternType::Function(func_type) + } + "global" => { + let global_type = GlobalType::new(Type::I32, Mutability::Const); + ExternType::Global(global_type) + } + "memory" => { + // The javascript API does not yet expose these properties so without + // the type_hints we don't know what memory to import. + let memory_type = MemoryType::new(Pages(1), None, false); + ExternType::Memory(memory_type) + } + "table" => { + let table_type = TableType::new(Type::FuncRef, 1, None); + ExternType::Table(table_type) + } + _ => unimplemented!(), + } + }; + ImportType::new(&module, &field, extern_type) + } }) .collect::>() .into_iter(); @@ -466,10 +534,14 @@ impl Module { return Err("The exports length must match the type hints lenght".to_owned()); } for (i, val) in exports.iter().enumerate() { - let kind = Reflect::get(val.as_ref(), &"kind".into()) - .unwrap() - .as_string() - .unwrap(); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let kind = unsafe { + Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap() + }; // It is safe to unwrap as we have already checked for the exports length let type_hint = type_hints.exports.get(i).unwrap(); let expected_kind = match type_hint { @@ -515,14 +587,22 @@ impl Module { .iter() .enumerate() .map(move |(i, val)| { - let field = Reflect::get(val.as_ref(), &"name".into()) - .unwrap() - .as_string() - .unwrap(); - let kind = Reflect::get(val.as_ref(), &"kind".into()) - .unwrap() - .as_string() - .unwrap(); + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let field = unsafe { + Reflect::get(val.as_ref(), &"name".into()) + .unwrap() + .as_string() + .unwrap() + }; + // Annotation is here to prevent spurious IDE warnings. + #[allow(unused_unsafe)] + let kind = unsafe { + Reflect::get(val.as_ref(), &"kind".into()) + .unwrap() + .as_string() + .unwrap() + }; let type_hint = self .type_hints .as_ref() diff --git a/lib/api/src/js/native.rs b/lib/api/src/js/native.rs index 69b350652a1..661034ac972 100644 --- a/lib/api/src/js/native.rs +++ b/lib/api/src/js/native.rs @@ -15,9 +15,11 @@ use crate::js::{FromToNativeWasmType, RuntimeError, WasmTypeList}; // use std::panic::{catch_unwind, AssertUnwindSafe}; use crate::js::export::VMFunction; use crate::js::types::param_from_js; +use crate::js::types::AsJs; use js_sys::Array; use std::iter::FromIterator; use wasm_bindgen::JsValue; +use wasmer_types::RawValue; /// A WebAssembly function that can be called natively /// (using the Native ABI). @@ -64,11 +66,35 @@ macro_rules! impl_native_traits { pub fn call(&self, mut store: &mut impl AsStoreMut, $( $x: $x, )* ) -> Result where $( $x: FromToNativeWasmType + crate::js::NativeWasmTypeInto, )* { - let params_list: Vec = vec![ $( JsValue::from_f64($x.into_raw(&mut store))),* ]; - let results = self.handle.get(store.as_store_ref().objects()).function.apply( - &JsValue::UNDEFINED, - &Array::from_iter(params_list.iter()) - )?; + #[allow(unused_unsafe)] + let params_list: Vec = unsafe { + vec![ $( RawValue { f64: $x.into_raw(store) } ),* ] + }; + let params_list: Vec = params_list + .into_iter() + .map(|a| a.as_jsvalue(&store.as_store_ref())) + .collect(); + let results = { + let mut r; + // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + loop { + r = self.handle.get(store.as_store_ref().objects()).function.apply( + &JsValue::UNDEFINED, + &Array::from_iter(params_list.iter()) + ); + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } + Ok(wasmer_types::OnCalledAction::Finish) => { break; } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) }, + Err(trap) => { return Err(RuntimeError::user(trap)) }, + } + } + break; + } + r? + }; let mut rets_list_array = Rets::empty_array(); let mut_rets = rets_list_array.as_mut() as *mut [f64] as *mut f64; match Rets::size() { @@ -92,7 +118,6 @@ macro_rules! impl_native_traits { } Ok(unsafe { Rets::from_array(store, rets_list_array) }) } - } #[allow(unused_parens)] diff --git a/lib/api/src/js/store.rs b/lib/api/src/js/store.rs index 66d6d58ec8c..830841d0642 100644 --- a/lib/api/src/js/store.rs +++ b/lib/api/src/js/store.rs @@ -1,10 +1,20 @@ use std::fmt; +use wasmer_types::OnCalledAction; + +/// Call handler for a store. +// TODO: better documentation! +// TODO: this type is duplicated in sys/store.rs. +// Maybe want to move it to wasmer_types... +pub type OnCalledHandler = Box< + dyn FnOnce(StoreMut<'_>) -> Result>, +>; /// We require the context to have a fixed memory address for its lifetime since /// various bits of the VM have raw pointers that point back to it. Hence we /// wrap the actual context in a box. pub(crate) struct StoreInner { pub(crate) objects: StoreObjects, + pub(crate) on_called: Option, } /// The store represents all global state that can be manipulated by @@ -27,6 +37,7 @@ impl Store { Self { inner: Box::new(StoreInner { objects: Default::default(), + on_called: None, }), } } @@ -37,6 +48,11 @@ impl Store { pub fn same(_a: &Self, _b: &Self) -> bool { true } + + /// Returns the ID of this store + pub fn id(&self) -> StoreId { + self.inner.objects.id() + } } impl PartialEq for Store { @@ -124,6 +140,17 @@ impl<'a> StoreMut<'a> { pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self { Self { inner: &mut *raw } } + + /// Sets the unwind callback which will be invoked when the call finishes + pub fn on_called(&mut self, callback: F) + where + F: FnOnce(StoreMut<'_>) -> Result> + + Send + + Sync + + 'static, + { + self.inner.on_called.replace(Box::new(callback)); + } } /// Helper trait for a value that is convertible to a [`StoreRef`]. @@ -183,6 +210,8 @@ impl AsStoreMut for &'_ mut T { pub use objects::*; mod objects { + use wasm_bindgen::JsValue; + use crate::js::{ export::{VMFunction, VMGlobal, VMMemory, VMTable}, function_env::VMFunctionEnvironment, @@ -286,6 +315,21 @@ mod objects { (&mut high[0], &mut low[a.index()]) } } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> core::slice::Iter { + self.globals.iter() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, val: u128) { + assert!(idx < self.globals.len()); + + let value = JsValue::from(val); + self.globals[idx].global.set_value(&value); + } } /// Handle to an object managed by a context. @@ -359,6 +403,11 @@ mod objects { self.id } + /// Overrides the store id with a new ID + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. /// /// # Safety @@ -446,6 +495,7 @@ mod objects { Instance(NonNull), } + #[allow(dead_code)] impl MaybeInstanceOwned { /// Returns underlying pointer to the VM data. #[allow(dead_code)] diff --git a/lib/api/src/js/trap.rs b/lib/api/src/js/trap.rs index e871be1c5d9..f38aa13e7be 100644 --- a/lib/api/src/js/trap.rs +++ b/lib/api/src/js/trap.rs @@ -2,7 +2,6 @@ use std::error::Error; use std::fmt; use std::sync::Arc; -use wasm_bindgen::convert::FromWasmAbi; use wasm_bindgen::prelude::*; use wasm_bindgen::JsValue; use wasm_bindgen_downcast::DowncastJS; @@ -31,6 +30,7 @@ impl CoreError for T {} impl dyn CoreError + 'static { /// Returns `true` if the inner type is the same as `T`. + #[allow(dead_code)] pub fn core_is_equal(&self) -> bool { let t = core::any::TypeId::of::(); let concrete = self.type_id(); @@ -40,6 +40,7 @@ impl dyn CoreError + 'static { impl dyn CoreError + Send + Sync + 'static { /// Returns `true` if the inner type is the same as `T`. + #[allow(dead_code)] pub fn core_is_equal(&self) -> bool { let t = core::any::TypeId::of::(); let concrete = self.type_id(); @@ -50,6 +51,7 @@ impl dyn CoreError + Send + Sync + 'static { impl dyn CoreError + Send { #[inline] /// Attempts to downcast the box to a concrete type. + #[allow(dead_code)] pub fn downcast_core( self: Box, ) -> Result, Box> { @@ -64,6 +66,7 @@ impl dyn CoreError + Send { impl dyn CoreError + Send + Sync { #[inline] /// Attempts to downcast the box to a concrete type. + #[allow(dead_code)] pub fn downcast_core(self: Box) -> Result, Box> { let err: Box = self; ::downcast_core(err).map_err(|s| unsafe { @@ -76,6 +79,7 @@ impl dyn CoreError + Send + Sync { impl dyn CoreError { #[inline] /// Attempts to downcast the box to a concrete type. + #[allow(dead_code)] pub fn downcast_core( self: Box, ) -> Result, Box> { diff --git a/lib/api/src/js/types.rs b/lib/api/src/js/types.rs index c7a4efd9f53..6b2da916594 100644 --- a/lib/api/src/js/types.rs +++ b/lib/api/src/js/types.rs @@ -54,3 +54,9 @@ impl AsJs for Value { } } } + +impl AsJs for wasmer_types::RawValue { + fn as_jsvalue(&self, _store: &impl AsStoreRef) -> JsValue { + unsafe { JsValue::from_f64(self.f64) } + } +} diff --git a/lib/api/src/js/value.rs b/lib/api/src/js/value.rs index 6eb2cb512aa..828cdc165f0 100644 --- a/lib/api/src/js/value.rs +++ b/lib/api/src/js/value.rs @@ -2,12 +2,13 @@ use std::convert::TryFrom; use std::fmt; use std::string::{String, ToString}; +use wasmer_types::RawValue; use wasmer_types::Type; //use crate::ExternRef; use crate::js::externals::function::Function; -use super::store::{AsStoreMut, AsStoreRef}; +use super::store::AsStoreRef; /// WebAssembly computations manipulate values of basic value types: /// * Integers (32 or 64 bit width) @@ -106,6 +107,13 @@ impl Value { } } + /// Converts the `Value` into a `RawValue`. + pub unsafe fn as_raw_value(&self, store: &impl AsStoreRef) -> RawValue { + RawValue { + f64: self.as_raw(store), + } + } + /// Converts a `f64` to a `Value`. /// /// # Safety diff --git a/lib/api/src/js/wasm_bindgen_polyfill.rs b/lib/api/src/js/wasm_bindgen_polyfill.rs index 1b5dad63a12..80f89a6a702 100644 --- a/lib/api/src/js/wasm_bindgen_polyfill.rs +++ b/lib/api/src/js/wasm_bindgen_polyfill.rs @@ -16,6 +16,7 @@ extern "C" { /// of the given type and value. /// /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) + #[allow(unused_doc_comments)] #[wasm_bindgen(constructor, js_namespace = WebAssembly, catch)] pub fn new(global_descriptor: &Object, value: &JsValue) -> Result; @@ -23,6 +24,7 @@ extern "C" { /// returns the value of the global. /// /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Global) + #[allow(unused_doc_comments)] #[wasm_bindgen(method, getter, structural, js_namespace = WebAssembly)] pub fn value(this: &Global) -> JsValue; diff --git a/lib/api/src/lib.rs b/lib/api/src/lib.rs index 663903104ba..dcb87c06186 100644 --- a/lib/api/src/lib.rs +++ b/lib/api/src/lib.rs @@ -437,3 +437,6 @@ mod js; #[cfg(feature = "js")] pub use js::*; + +mod into_bytes; +pub use into_bytes::IntoBytes; diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 8392e9b4203..3c3d383113a 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -1,22 +1,29 @@ +use wasmer_types::RawValue; +use wasmer_vm::{ + on_host_stack, raise_user_trap, resume_panic, InternalStoreHandle, StoreHandle, VMContext, + VMDynamicFunctionContext, VMExtern, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionKind, + VMTrampoline, +}; + use crate::sys::exports::{ExportError, Exportable}; use crate::sys::externals::Extern; -use crate::sys::store::{AsStoreMut, AsStoreRef, StoreInner, StoreMut}; -use crate::sys::FunctionType; -use crate::sys::RuntimeError; -use crate::sys::TypedFunction; +use crate::sys::store::{AsStoreMut, AsStoreRef}; +use crate::sys::{FunctionType, RuntimeError, TypedFunction}; +use crate::FunctionEnv; -use crate::{FunctionEnv, FunctionEnvMut, Value}; -use inner::StaticFunction; pub use inner::{FromToNativeWasmType, HostFunction, WasmTypeList, WithEnv, WithoutEnv}; -use std::cell::UnsafeCell; -use std::cmp::max; -use std::ffi::c_void; -use wasmer_types::RawValue; -use wasmer_vm::{ - on_host_stack, raise_user_trap, resume_panic, wasmer_call_trampoline, InternalStoreHandle, - MaybeInstanceOwned, StoreHandle, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, - VMExtern, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionContext, VMFunctionKind, - VMTrampoline, + +#[cfg(feature = "compiler")] +use { + crate::{ + sys::store::{StoreInner, StoreMut}, + FunctionEnvMut, Value, + }, + inner::StaticFunction, + std::{cell::UnsafeCell, cmp::max, ffi::c_void}, + wasmer_vm::{ + wasmer_call_trampoline, MaybeInstanceOwned, VMCallerCheckedAnyfunc, VMFunctionContext, + }, }; /// A WebAssembly `function` instance. @@ -41,6 +48,12 @@ pub struct Function { pub(crate) handle: StoreHandle, } +impl From> for Function { + fn from(handle: StoreHandle) -> Self { + Self { handle } + } +} + impl Function { /// Creates a new host `Function` (dynamic) with the provided signature. /// @@ -324,6 +337,22 @@ impl Function { } } + #[allow(missing_docs)] + #[allow(unused_variables)] + #[cfg(not(feature = "compiler"))] + pub fn new_typed_with_env( + store: &mut impl AsStoreMut, + env: &FunctionEnv, + func: F, + ) -> Self + where + F: HostFunction + 'static + Send + Sync, + Args: WasmTypeList, + Rets: WasmTypeList, + { + unimplemented!("this platform does not support functions without the 'compiler' feature") + } + /// Returns the [`FunctionType`] of the `Function`. /// /// # Example @@ -401,24 +430,62 @@ impl Function { *slot = arg.as_raw(store); } + // Invoke the call + self.call_wasm_raw(store, trampoline, values_vec, results)?; + Ok(()) + } + + #[cfg(feature = "compiler")] + fn call_wasm_raw( + &self, + store: &mut impl AsStoreMut, + trampoline: VMTrampoline, + mut params: Vec, + results: &mut [Value], + ) -> Result<(), RuntimeError> { // Call the trampoline. - let vm_function = self.handle.get(store.as_store_ref().objects()); - if let Err(error) = unsafe { - wasmer_call_trampoline( - store.as_store_ref().signal_handler(), - vm_function.anyfunc.as_ptr().as_ref().vmctx, - trampoline, - vm_function.anyfunc.as_ptr().as_ref().func_ptr, - values_vec.as_mut_ptr() as *mut u8, - ) - } { + let result = { + let mut r; + // TODO: This loop is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + loop { + let vm_function = self.handle.get(store.as_store_ref().objects()); + r = unsafe { + wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + vm_function.anyfunc.as_ptr().as_ref().vmctx, + trampoline, + vm_function.anyfunc.as_ptr().as_ref().func_ptr, + params.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { + continue; + } + Ok(wasmer_types::OnCalledAction::Finish) => { + break; + } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { + return Err(RuntimeError::user(trap)) + } + Err(trap) => return Err(RuntimeError::user(trap)), + } + } + break; + } + r + }; + if let Err(error) = result { return Err(RuntimeError::from_trap(error)); } // Load the return values out of `values_vec`. + let signature = self.ty(store); for (index, &value_type) in signature.results().iter().enumerate() { unsafe { - results[index] = Value::from_raw(store, value_type, values_vec[index]); + results[index] = Value::from_raw(store, value_type, params[index]); } } @@ -517,6 +584,27 @@ impl Function { Ok(results.into_boxed_slice()) } + #[doc(hidden)] + #[allow(missing_docs)] + #[cfg(feature = "compiler")] + pub fn call_raw( + &self, + store: &mut impl AsStoreMut, + params: Vec, + ) -> Result, RuntimeError> { + let trampoline = unsafe { + self.handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + .call_trampoline + }; + let mut results = vec![Value::null(); self.result_arity(store)]; + self.call_wasm_raw(store, trampoline, params, &mut results)?; + Ok(results.into_boxed_slice()) + } + pub(crate) fn vm_funcref(&self, store: &impl AsStoreRef) -> VMFuncRef { let vm_function = self.handle.get(store.as_store_ref().objects()); if vm_function.kind == VMFunctionKind::Dynamic { @@ -773,7 +861,9 @@ mod inner { use wasmer_vm::{raise_user_trap, resume_panic, VMFunctionBody}; use crate::sys::NativeWasmTypeInto; - use crate::{AsStoreMut, AsStoreRef, ExternRef, Function, FunctionEnv, StoreMut}; + use crate::{AsStoreMut, AsStoreRef, ExternRef, FunctionEnv, StoreMut}; + + use crate::Function; /// A trait to convert a Rust value to a `WasmNativeType` value, /// or to convert `WasmNativeType` value to a Rust value. diff --git a/lib/api/src/sys/externals/memory.rs b/lib/api/src/sys/externals/memory.rs index 0793513f5be..fddcb2995ab 100644 --- a/lib/api/src/sys/externals/memory.rs +++ b/lib/api/src/sys/externals/memory.rs @@ -132,6 +132,36 @@ impl Memory { self.handle.get_mut(store.objects_mut()).grow(delta.into()) } + /// Copies the memory to a new store and returns a memory reference to it + #[cfg(feature = "compiler")] + pub fn copy_to_store( + &self, + store: &impl AsStoreRef, + new_store: &mut impl AsStoreMut, + ) -> Result { + // Create the new memory using the parameters of the existing memory + let view = self.view(store); + let ty = self.ty(store); + let amount = view.data_size() as usize; + + let new_memory = Self::new(new_store, ty)?; + let mut new_view = new_memory.view(&new_store); + let new_view_size = new_view.data_size() as usize; + if amount > new_view_size { + let delta = amount - new_view_size; + let pages = ((delta - 1) / wasmer_types::WASM_PAGE_SIZE) + 1; + new_memory.grow(new_store, Pages(pages as u32))?; + new_view = new_memory.view(&new_store); + } + + // Copy the bytes + view.copy_to_memory(amount as u64, &new_view) + .map_err(|err| MemoryError::Generic(err.to_string()))?; + + // Return the new memory + Ok(new_memory) + } + pub(crate) fn from_vm_extern( store: &impl AsStoreRef, internal: InternalStoreHandle, diff --git a/lib/api/src/sys/externals/memory_view.rs b/lib/api/src/sys/externals/memory_view.rs index a872ea7c259..ee1cbaea17c 100644 --- a/lib/api/src/sys/externals/memory_view.rs +++ b/lib/api/src/sys/externals/memory_view.rs @@ -159,4 +159,35 @@ impl<'a> MemoryView<'a> { self.write(offset, &buf)?; Ok(()) } + + /// Copies the memory and returns it as a vector of bytes + pub fn copy_to_vec(&self) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < self.data_size() { + let remaining = self.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + + /// Copies the memory to another new memory object + pub fn copy_to_memory(&self, amount: u64, new_memory: &Self) -> Result<(), MemoryAccessError> { + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < amount { + let remaining = amount - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + self.read(offset, &mut chunk[..sublen])?; + + new_memory.write(offset, &chunk[..sublen])?; + + offset += sublen as u64; + } + Ok(()) + } } diff --git a/lib/api/src/sys/externals/table.rs b/lib/api/src/sys/externals/table.rs index 727c9063239..52137330846 100644 --- a/lib/api/src/sys/externals/table.rs +++ b/lib/api/src/sys/externals/table.rs @@ -1,9 +1,9 @@ use crate::sys::exports::{ExportError, Exportable}; use crate::sys::externals::Extern; use crate::sys::store::{AsStoreMut, AsStoreRef}; -use crate::sys::RuntimeError; use crate::sys::TableType; -use crate::{ExternRef, Function, Value}; +use crate::Value; +use crate::{sys::RuntimeError, ExternRef, Function}; use wasmer_vm::{InternalStoreHandle, StoreHandle, TableElement, VMExtern, VMTable}; /// A WebAssembly `table` instance. diff --git a/lib/api/src/sys/imports.rs b/lib/api/src/sys/imports.rs index 8e988dadeef..a08d1b6adad 100644 --- a/lib/api/src/sys/imports.rs +++ b/lib/api/src/sys/imports.rs @@ -6,6 +6,8 @@ use std::collections::HashMap; use std::fmt; use wasmer_compiler::LinkError; use wasmer_types::ImportError; +#[cfg(feature = "compiler")] +use wasmer_vm::VMSharedMemory; /// All of the import data used when instantiating. /// @@ -121,6 +123,37 @@ impl Imports { .insert((ns.to_string(), name.to_string()), val.into()); } + /// Imports (any) shared memory into the imports. + /// (if the module does not import memory then this function is ignored) + #[cfg(feature = "compiler")] + pub fn import_shared_memory( + &mut self, + module: &Module, + store: &mut impl crate::AsStoreMut, + ) -> Option { + // Determine if shared memory needs to be created and imported + let shared_memory = module + .imports() + .memories() + .next() + .map(|a| *a.ty()) + .map(|ty| { + let style = store.as_store_ref().tunables().memory_style(&ty); + VMSharedMemory::new(&ty, &style).unwrap() + }); + + if let Some(memory) = shared_memory { + self.define( + "env", + "memory", + crate::Memory::new_from_existing(store, memory.clone().into()), + ); + Some(memory) + } else { + None + } + } + /// Returns the contents of a namespace as an `Exports`. /// /// Returns `None` if the namespace doesn't exist. @@ -159,6 +192,32 @@ impl Imports { } Ok(ret) } + + /// Iterates through all the imports in this structure + pub fn iter(&self) -> ImportsIterator<'_> { + ImportsIterator::new(self) + } +} + +pub struct ImportsIterator<'a> { + iter: std::collections::hash_map::Iter<'a, (String, String), Extern>, +} + +impl<'a> ImportsIterator<'a> { + fn new(imports: &'a Imports) -> Self { + let iter = imports.map.iter(); + Self { iter } + } +} + +impl<'a> Iterator for ImportsIterator<'a> { + type Item = (&'a str, &'a str, &'a Extern); + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|(k, v)| (k.0.as_str(), k.1.as_str(), v)) + } } impl IntoIterator for &Imports { @@ -237,6 +296,7 @@ impl fmt::Debug for Imports { macro_rules! imports { ( $( $ns_name:expr => $ns:tt ),* $(,)? ) => { { + #[allow(unused_mut)] let mut import_object = $crate::Imports::new(); $({ diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index ab8e9d5c293..4e0d90d3f18 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -1,13 +1,14 @@ use crate::sys::exports::Exports; -use crate::sys::externals::Extern; -use crate::sys::imports::Imports; use crate::sys::module::Module; use crate::sys::{LinkError, RuntimeError}; use std::fmt; use thiserror::Error; use wasmer_vm::{InstanceHandle, StoreHandle}; +#[cfg(feature = "compiler")] use super::store::AsStoreMut; +#[cfg(feature = "compiler")] +use crate::sys::{externals::Extern, imports::Imports}; /// A WebAssembly Instance is a stateful, executable /// instance of a WebAssembly [`Module`]. @@ -115,11 +116,11 @@ impl Instance { module: &Module, imports: &Imports, ) -> Result { - let imports = imports + let externs = imports .imports_for_module(module) .map_err(InstantiationError::Link)?; - let mut handle = module.instantiate(store, &imports)?; - let exports = module + let mut handle = module.instantiate(store, &externs)?; + let mut exports = module .exports() .map(|export| { let name = export.name().to_string(); @@ -129,6 +130,14 @@ impl Instance { }) .collect::(); + // If the memory is imported then also export it for backwards compatibility reasons + // (many will assume the memory is always exported) - later we can remove this + if exports.get_memory("memory").is_err() { + if let Some(memory) = externs.iter().find(|a| a.ty(store).memory().is_some()) { + exports.insert("memory", memory.clone()); + } + } + let instance = Self { _handle: StoreHandle::new(store.objects_mut(), handle), module: module.clone(), @@ -154,9 +163,9 @@ impl Instance { module: &Module, externs: &[Extern], ) -> Result { - let imports = externs.to_vec(); - let mut handle = module.instantiate(store, &imports)?; - let exports = module + let externs = externs.to_vec(); + let mut handle = module.instantiate(store, &externs)?; + let mut exports = module .exports() .map(|export| { let name = export.name().to_string(); @@ -166,6 +175,14 @@ impl Instance { }) .collect::(); + // If the memory is imported then also export it for backwards compatibility reasons + // (many will assume the memory is always exported) - later we can remove this + if exports.get_memory("memory").is_err() { + if let Some(memory) = externs.iter().find(|a| a.ty(store).memory().is_some()) { + exports.insert("memory", memory.clone()); + } + } + let instance = Self { _handle: StoreHandle::new(store.objects_mut(), handle), module: module.clone(), diff --git a/lib/api/src/sys/mem_access.rs b/lib/api/src/sys/mem_access.rs index 28204577a22..8bceda732e9 100644 --- a/lib/api/src/sys/mem_access.rs +++ b/lib/api/src/sys/mem_access.rs @@ -330,6 +330,24 @@ impl<'a, T: ValueType> WasmSlice<'a, T> { } Ok(vec) } + + /// Reads this `WasmSlice` into a `BytesMut` + #[inline] + pub fn read_to_bytes(self) -> Result { + let len = self.len.try_into().expect("WasmSlice length overflow"); + let mut ret = bytes::BytesMut::with_capacity(len); + let bytes = unsafe { + slice::from_raw_parts_mut( + ret.as_mut_ptr() as *mut MaybeUninit, + len * mem::size_of::(), + ) + }; + self.buffer.read_uninit(self.offset, bytes)?; + unsafe { + ret.set_len(len); + } + Ok(ret) + } } impl<'a, T: ValueType> fmt::Debug for WasmSlice<'a, T> { diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 3fd2461fc82..3989ec10f36 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -40,9 +40,10 @@ pub use wasmer_compiler::{ pub use wasmer_compiler::{Features, FrameInfo, LinkError, RuntimeError, Tunables}; pub use wasmer_derive::ValueType; pub use wasmer_types::is_wasm; +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 pub use wasmer_types::{ CpuFeature, ExportType, ExternType, FunctionType, GlobalType, ImportType, MemoryType, - Mutability, TableType, Target, Type, + Mutability, OnCalledAction, TableType, Target, Type, }; pub use wasmer_types::{ diff --git a/lib/api/src/sys/module.rs b/lib/api/src/sys/module.rs index ea96c7fe681..9d49179265e 100644 --- a/lib/api/src/sys/module.rs +++ b/lib/api/src/sys/module.rs @@ -1,8 +1,4 @@ -use crate::sys::InstantiationError; -use crate::AsStoreMut; -use crate::AsStoreRef; use bytes::Bytes; -use std::borrow::Cow; use std::fmt; use std::io; use std::path::Path; @@ -17,6 +13,10 @@ use wasmer_types::{ CompileError, DeserializeError, ExportsIterator, ImportsIterator, ModuleInfo, SerializeError, }; use wasmer_types::{ExportType, ImportType}; + +#[cfg(feature = "compiler")] +use crate::{sys::InstantiationError, AsStoreMut, AsStoreRef, IntoBytes}; +#[cfg(feature = "compiler")] use wasmer_vm::InstanceHandle; /// IO Error on a Module Compilation @@ -58,46 +58,6 @@ pub struct Module { module_info: Arc, } -pub trait IntoBytes { - fn into_bytes(self) -> Bytes; -} - -impl IntoBytes for Bytes { - fn into_bytes(self) -> Bytes { - self - } -} - -impl IntoBytes for Vec { - fn into_bytes(self) -> Bytes { - Bytes::from(self) - } -} - -impl IntoBytes for &[u8] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &[u8; N] { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - -impl IntoBytes for &str { - fn into_bytes(self) -> Bytes { - Bytes::from(self.as_bytes().to_vec()) - } -} - -impl IntoBytes for Cow<'_, [u8]> { - fn into_bytes(self) -> Bytes { - Bytes::from(self.to_vec()) - } -} - impl Module { #[cfg(feature = "compiler")] /// Creates a new WebAssembly Module given the configuration diff --git a/lib/api/src/sys/native.rs b/lib/api/src/sys/native.rs index d5d3bc5aaa5..90fd7c14b36 100644 --- a/lib/api/src/sys/native.rs +++ b/lib/api/src/sys/native.rs @@ -7,6 +7,7 @@ //! let add_one = instance.exports.get_function("function_name")?; //! let add_one_native: TypedFunction = add_one.native().unwrap(); //! ``` +use std::cell::Cell; use std::marker::PhantomData; use crate::sys::{ @@ -14,6 +15,8 @@ use crate::sys::{ }; use wasmer_types::RawValue; +use super::store::OnCalledHandler; + /// A WebAssembly function that can be called natively /// (using the Native ABI). pub struct TypedFunction { @@ -45,6 +48,10 @@ impl Clone for TypedFunction } } +thread_local! { + static ON_CALLED: Cell> = Cell::new(None); +} + macro_rules! impl_native_traits { ( $( $x:ident ),* ) => { #[allow(unused_parens, non_snake_case)] @@ -87,15 +94,111 @@ macro_rules! impl_native_traits { } rets_list.as_mut() }; - unsafe { - wasmer_vm::wasmer_call_trampoline( - store.as_store_ref().signal_handler(), - anyfunc.vmctx, - anyfunc.call_trampoline, - anyfunc.func_ptr, - args_rets.as_mut_ptr() as *mut u8, - ) - }?; + + let mut r; + loop { + r = unsafe { + wasmer_vm::wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + anyfunc.vmctx, + anyfunc.call_trampoline, + anyfunc.func_ptr, + args_rets.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } + Ok(wasmer_types::OnCalledAction::Finish) => { break; } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) }, + Err(trap) => { return Err(RuntimeError::user(trap)) }, + } + } + break; + } + r?; + + let num_rets = rets_list.len(); + if !using_rets_array && num_rets > 0 { + let src_pointer = params_list.as_ptr(); + let rets_list = &mut rets_list_array.as_mut()[0] as *mut RawValue; + unsafe { + // TODO: we can probably remove this copy by doing some clever `transmute`s. + // we know it's not overlapping because `using_rets_array` is false + std::ptr::copy_nonoverlapping(src_pointer, + rets_list, + num_rets); + } + } + Ok(unsafe { Rets::from_array(store, rets_list_array) }) + // TODO: When the Host ABI and Wasm ABI are the same, we could do this instead: + // but we can't currently detect whether that's safe. + // + // let results = unsafe { + // wasmer_vm::catch_traps_with_result(self.vmctx, || { + // let f = std::mem::transmute::<_, unsafe extern "C" fn( *mut VMContext, $( $x, )*) -> Rets::CStruct>(self.address()); + // // We always pass the vmctx + // f( self.vmctx, $( $x, )* ) + // }).map_err(RuntimeError::from_trap)? + // }; + // Ok(Rets::from_c_struct(results)) + } + + #[doc(hidden)] + #[allow(missing_docs)] + #[allow(unused_mut)] + #[allow(clippy::too_many_arguments)] + pub fn call_raw(&self, store: &mut impl AsStoreMut, mut params_list: Vec ) -> Result { + let anyfunc = unsafe { + *self.func + .handle + .get(store.as_store_ref().objects()) + .anyfunc + .as_ptr() + .as_ref() + }; + // TODO: when `const fn` related features mature more, we can declare a single array + // of the correct size here. + let mut rets_list_array = Rets::empty_array(); + let rets_list: &mut [RawValue] = rets_list_array.as_mut(); + let using_rets_array; + let args_rets: &mut [RawValue] = if params_list.len() > rets_list.len() { + using_rets_array = false; + params_list.as_mut() + } else { + using_rets_array = true; + for (i, &arg) in params_list.iter().enumerate() { + rets_list[i] = arg; + } + rets_list.as_mut() + }; + + let mut r; + loop { + r = unsafe { + wasmer_vm::wasmer_call_trampoline( + store.as_store_ref().signal_handler(), + anyfunc.vmctx, + anyfunc.call_trampoline, + anyfunc.func_ptr, + args_rets.as_mut_ptr() as *mut u8, + ) + }; + let store_mut = store.as_store_mut(); + if let Some(callback) = store_mut.inner.on_called.take() { + // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + match callback(store_mut) { + Ok(wasmer_types::OnCalledAction::InvokeAgain) => { continue; } + Ok(wasmer_types::OnCalledAction::Finish) => { break; } + Ok(wasmer_types::OnCalledAction::Trap(trap)) => { return Err(RuntimeError::user(trap)) }, + Err(trap) => { return Err(RuntimeError::user(trap)) }, + } + } + break; + } + r?; + let num_rets = rets_list.len(); if !using_rets_array && num_rets > 0 { let src_pointer = params_list.as_ptr(); diff --git a/lib/api/src/sys/native_type.rs b/lib/api/src/sys/native_type.rs index 42f3f23939c..765b5f6b2c1 100644 --- a/lib/api/src/sys/native_type.rs +++ b/lib/api/src/sys/native_type.rs @@ -2,7 +2,9 @@ //! easily in Rust, thanks to its advanced typing system. use wasmer_types::{NativeWasmType, RawValue, Type}; -use wasmer_vm::{VMExternRef, VMFuncRef}; +use wasmer_vm::VMExternRef; +#[cfg(feature = "compiler")] +use wasmer_vm::VMFuncRef; use crate::{ExternRef, Function, TypedFunction, WasmTypeList}; diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index 612652014a2..032b6c60d23 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -1,20 +1,33 @@ use crate::sys::tunables::BaseTunables; +use derivative::Derivative; use std::fmt; -use std::sync::{Arc, RwLock}; #[cfg(feature = "compiler")] use wasmer_compiler::{AsEngineRef, Engine, EngineBuilder, EngineRef, Tunables}; -use wasmer_vm::{init_traps, TrapHandler, TrapHandlerFn}; +use wasmer_types::OnCalledAction; +use wasmer_vm::{init_traps, StoreId, TrapHandler, TrapHandlerFn}; use wasmer_vm::StoreObjects; +/// Call handler for a store. +// TODO: better documentation! +pub type OnCalledHandler = Box< + dyn FnOnce(StoreMut<'_>) -> Result>, +>; + /// We require the context to have a fixed memory address for its lifetime since /// various bits of the VM have raw pointers that point back to it. Hence we /// wrap the actual context in a box. +#[derive(Derivative)] +#[derivative(Debug)] pub(crate) struct StoreInner { pub(crate) objects: StoreObjects, + #[derivative(Debug = "ignore")] #[cfg(feature = "compiler")] pub(crate) engine: Engine, + #[derivative(Debug = "ignore")] pub(crate) trap_handler: Option>>, + #[derivative(Debug = "ignore")] + pub(crate) on_called: Option, } /// The store represents all global state that can be manipulated by @@ -30,7 +43,6 @@ pub struct Store { pub(crate) inner: Box, #[cfg(feature = "compiler")] engine: Engine, - trap_handler: Arc>>>>, } impl Store { @@ -48,9 +60,9 @@ impl Store { objects: Default::default(), engine: engine.cloned(), trap_handler: None, + on_called: None, }), engine: engine.cloned(), - trap_handler: Arc::new(RwLock::new(None)), } } @@ -100,6 +112,11 @@ impl Store { pub fn same(a: &Self, b: &Self) -> bool { a.engine.id() == b.engine.id() } + + /// Returns the ID of this store + pub fn id(&self) -> StoreId { + self.inner.objects.id() + } } #[cfg(feature = "compiler")] @@ -111,8 +128,8 @@ impl PartialEq for Store { unsafe impl TrapHandler for Store { fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool { - if let Some(handler) = self.trap_handler.read().unwrap().as_ref() { - call(handler) + if let Some(handler) = self.inner.trap_handler.as_ref() { + call(handler.as_ref()) } else { false } @@ -267,7 +284,7 @@ impl<'a> StoreRef<'a> { self.inner .trap_handler .as_ref() - .map(|handler| handler as *const _) + .map(|handler| handler.as_ref() as *const _) } } @@ -309,6 +326,18 @@ impl<'a> StoreMut<'a> { pub(crate) unsafe fn from_raw(raw: *mut StoreInner) -> Self { Self { inner: &mut *raw } } + + // TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 + /// Sets the unwind callback which will be invoked when the call finishes + pub fn on_called(&mut self, callback: F) + where + F: FnOnce(StoreMut<'_>) -> Result> + + Send + + Sync + + 'static, + { + self.inner.on_called.replace(Box::new(callback)); + } } /// Helper trait for a value that is convertible to a [`StoreRef`]. diff --git a/lib/api/src/sys/tunables.rs b/lib/api/src/sys/tunables.rs index 99803bac657..51d2f9ffda2 100644 --- a/lib/api/src/sys/tunables.rs +++ b/lib/api/src/sys/tunables.rs @@ -117,9 +117,21 @@ mod tests { .unwrap() } } + fn try_clone(&self) -> Option> { None } + + fn duplicate(&mut self) -> Result, MemoryError> { + let mem = self.mem.clone(); + Ok(Box::new(Self { + memory_definition: Some(UnsafeCell::new(VMMemoryDefinition { + base: mem.as_ptr() as _, + current_length: mem.len(), + })), + mem, + })) + } /* // this code allow custom memory to be ignoring init_memory use wasmer_vm::Trap; diff --git a/lib/api/src/sys/value.rs b/lib/api/src/sys/value.rs index f2b2a0b8a81..fe4fb5c4b5f 100644 --- a/lib/api/src/sys/value.rs +++ b/lib/api/src/sys/value.rs @@ -3,13 +3,13 @@ use std::fmt; use std::string::{String, ToString}; use wasmer_types::Type; -use wasmer_vm::VMExternRef; -use wasmer_vm::VMFuncRef; +#[cfg(feature = "compiler")] +use wasmer_vm::{VMExternRef, VMFuncRef}; use crate::ExternRef; use crate::Function; -use super::store::{AsStoreMut, AsStoreRef}; +use super::store::AsStoreRef; pub use wasmer_types::RawValue; @@ -111,7 +111,7 @@ impl Value { /// /// # Safety /// - pub unsafe fn from_raw(store: &mut impl AsStoreMut, ty: Type, raw: RawValue) -> Self { + pub unsafe fn from_raw(store: &mut impl crate::AsStoreMut, ty: Type, raw: RawValue) -> Self { match ty { Type::I32 => Self::I32(raw.i32), Type::I64 => Self::I64(raw.i64), diff --git a/lib/api/tests/reference_types.rs b/lib/api/tests/reference_types.rs index 8746936f06f..46b86da7727 100644 --- a/lib/api/tests/reference_types.rs +++ b/lib/api/tests/reference_types.rs @@ -47,17 +47,14 @@ pub mod reference_types { } let func_to_call = - Function::new_typed_with_env(&mut store, &env, |mut env: FunctionEnvMut| -> i32 { - env.data_mut().0.store(true, Ordering::SeqCst); + Function::new_typed_with_env(&mut store, &env, |env: FunctionEnvMut| -> i32 { + env.data().0.store(true, Ordering::SeqCst); 343 }); let call_set_value: &Function = instance.exports.get_function("call_set_value")?; let results: Box<[Value]> = call_set_value.call(&mut store, &[Value::FuncRef(Some(func_to_call))])?; - assert!(env - .as_mut(&mut store.as_store_mut()) - .0 - .load(Ordering::SeqCst)); + assert!(env.as_ref(&store.as_store_ref()).0.load(Ordering::SeqCst)); assert_eq!(&*results, &[Value::I32(343)]); Ok(()) diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index d3fa6617ca7..6f5190fa317 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -32,7 +32,7 @@ wasmer-middlewares = { version = "=3.1.0", path = "../middlewares", optional = t wasmer-wasi = { version = "=3.1.0", path = "../wasi", default-features = false, features = ["host-fs", "sys"], optional = true } wasmer-types = { version = "=3.1.0", path = "../types" } wasmer-vfs = { version = "=3.1.0", path = "../vfs", optional = true, default-features = false, features = ["static-fs"] } -webc = { version = "3.0.1", optional = true } +webc = { version = "4.0.0", optional = true } enumset = "1.0.2" cfg-if = "1.0" lazy_static = "1.4" diff --git a/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs b/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs index 8108065b9d0..71f936b556e 100644 --- a/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs +++ b/lib/c-api/examples/wasmer-capi-examples-runner/src/lib.rs @@ -9,9 +9,8 @@ pub struct RemoveTestsOnDrop {} impl Drop for RemoveTestsOnDrop { fn drop(&mut self) { - return; let manifest_dir = env!("CARGO_MANIFEST_DIR"); - for entry in std::fs::read_dir(&manifest_dir).unwrap() { + for entry in std::fs::read_dir(manifest_dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); @@ -21,13 +20,13 @@ impl Drop for RemoveTestsOnDrop { } } if let Some(parent) = std::path::Path::new(&manifest_dir).parent() { - for entry in std::fs::read_dir(&parent).unwrap() { + for entry in std::fs::read_dir(parent).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); if extension == Some("obj") || extension == Some("exe") || extension == Some("o") { println!("removing {}", path.display()); - let _ = std::fs::remove_file(&path); + let _ = std::fs::remove_file(path); } } } diff --git a/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs b/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs index df71ddc2561..f75456008bd 100644 --- a/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs +++ b/lib/c-api/tests/wasmer-c-api-test-runner/src/lib.rs @@ -66,7 +66,7 @@ pub struct RemoveTestsOnDrop {} impl Drop for RemoveTestsOnDrop { fn drop(&mut self) { let manifest_dir = env!("CARGO_MANIFEST_DIR"); - for entry in std::fs::read_dir(&manifest_dir).unwrap() { + for entry in std::fs::read_dir(manifest_dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); @@ -76,7 +76,7 @@ impl Drop for RemoveTestsOnDrop { } } if let Some(parent) = std::path::Path::new(&manifest_dir).parent() { - for entry in std::fs::read_dir(&parent).unwrap() { + for entry in std::fs::read_dir(parent).unwrap() { let entry = entry.unwrap(); let path = entry.path(); let extension = path.extension().and_then(|s| s.to_str()); diff --git a/lib/cache/src/filesystem.rs b/lib/cache/src/filesystem.rs index 8d326b64c8d..98259f94830 100644 --- a/lib/cache/src/filesystem.rs +++ b/lib/cache/src/filesystem.rs @@ -102,7 +102,13 @@ impl Cache for FileSystemCache { key.to_string() }; let path = self.path.join(filename); - Module::deserialize_from_file(engine, path) + let ret = Module::deserialize_from_file(engine, path.clone()); + if ret.is_err() { + // If an error occurs while deserializing then we can not trust it anymore + // so delete the cache file + let _ = std::fs::remove_file(path); + } + ret } fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> { diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index f8afdaf2cad..a1960afa947 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -70,7 +70,7 @@ toml = "0.5.9" url = "2.3.1" libc = { version = "^0.2", default-features = false } nuke-dir = { version = "0.1.0", optional = true } -webc = { version = "3.0.1", optional = true } +webc = { version = "4.0.0", optional = true } isatty = "0.1.9" dialoguer = "0.10.2" tldextract = "0.6.0" diff --git a/lib/compiler-cranelift/build.rs b/lib/compiler-cranelift/build.rs index 5e03bc2e028..0be0760c071 100644 --- a/lib/compiler-cranelift/build.rs +++ b/lib/compiler-cranelift/build.rs @@ -7,7 +7,7 @@ use std::process::Command; use std::str; fn main() { - let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() { + let git_rev = match Command::new("git").args(["rev-parse", "HEAD"]).output() { Ok(output) => str::from_utf8(&output.stdout).unwrap().trim().to_string(), Err(_) => env!("CARGO_PKG_VERSION").to_string(), }; diff --git a/lib/compiler-cranelift/src/compiler.rs b/lib/compiler-cranelift/src/compiler.rs index 807c07fab9b..9f2481bb0a2 100644 --- a/lib/compiler-cranelift/src/compiler.rs +++ b/lib/compiler-cranelift/src/compiler.rs @@ -52,6 +52,10 @@ impl CraneliftCompiler { } impl Compiler for CraneliftCompiler { + fn name(&self) -> &str { + "cranelift" + } + /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares diff --git a/lib/compiler-cranelift/src/translator/func_translator.rs b/lib/compiler-cranelift/src/translator/func_translator.rs index 0f31a97a4eb..d8a4db96a60 100644 --- a/lib/compiler-cranelift/src/translator/func_translator.rs +++ b/lib/compiler-cranelift/src/translator/func_translator.rs @@ -15,7 +15,6 @@ use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel}; use cranelift_codegen::timing; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use tracing::info; use wasmer_compiler::wasmparser; use wasmer_compiler::{ wasm_unsupported, wptype_to_type, FunctionBinaryReader, ModuleTranslationState, @@ -80,7 +79,7 @@ impl FuncTranslator { environ: &mut FE, ) -> WasmResult<()> { let _tt = timing::wasm_translate_function(); - info!( + tracing::trace!( "translate({} bytes, {}{})", reader.bytes_remaining(), func.name, diff --git a/lib/compiler-llvm/src/compiler.rs b/lib/compiler-llvm/src/compiler.rs index f9246bf0a5b..304dca18f4f 100644 --- a/lib/compiler-llvm/src/compiler.rs +++ b/lib/compiler-llvm/src/compiler.rs @@ -195,6 +195,10 @@ impl LLVMCompiler { } impl Compiler for LLVMCompiler { + fn name(&self) -> &str { + "llvm" + } + /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares diff --git a/lib/compiler-singlepass/src/compiler.rs b/lib/compiler-singlepass/src/compiler.rs index 7784a451ffc..5cee5d4ec5b 100644 --- a/lib/compiler-singlepass/src/compiler.rs +++ b/lib/compiler-singlepass/src/compiler.rs @@ -51,6 +51,10 @@ impl SinglepassCompiler { } impl Compiler for SinglepassCompiler { + fn name(&self) -> &str { + "singlepass" + } + /// Get the middlewares for this compiler fn get_middlewares(&self) -> &[Arc] { &self.config.middlewares diff --git a/lib/compiler/src/artifact_builders/artifact_builder.rs b/lib/compiler/src/artifact_builders/artifact_builder.rs index bbbc93e7f0a..e95061fb29b 100644 --- a/lib/compiler/src/artifact_builders/artifact_builder.rs +++ b/lib/compiler/src/artifact_builders/artifact_builder.rs @@ -8,20 +8,18 @@ use crate::EngineInner; use crate::Features; use crate::{ModuleEnvironment, ModuleMiddlewareChain}; use enumset::EnumSet; -use std::mem; use wasmer_types::entity::PrimaryMap; #[cfg(feature = "compiler")] use wasmer_types::CompileModuleInfo; -use wasmer_types::MetadataHeader; -use wasmer_types::SerializeError; use wasmer_types::{ CompileError, CpuFeature, CustomSection, Dwarf, FunctionIndex, LocalFunctionIndex, MemoryIndex, - MemoryStyle, ModuleInfo, OwnedDataInitializer, Relocation, SectionIndex, SignatureIndex, + MemoryStyle, ModuleInfo, OwnedDataInitializer, Pages, Relocation, SectionIndex, SignatureIndex, TableIndex, TableStyle, Target, }; use wasmer_types::{ CompiledFunctionFrameInfo, FunctionBody, SerializableCompilation, SerializableModule, }; +use wasmer_types::{MetadataHeader, SerializeError}; /// A compiled wasm module, ready to be instantiated. pub struct ArtifactBuild { @@ -121,10 +119,17 @@ impl ArtifactBuild { compile_info, data_initializers, cpu_features: cpu_features.as_u64(), + module_start: None, }; Ok(Self { serializable }) } + /// Specify the fixed virtual memory address for the compiled module + pub fn with_module_start(mut self, module_start: Option) -> Self { + self.serializable.module_start = module_start; + self + } + /// Compile a data buffer into a `ArtifactBuild`, which may then be instantiated. #[cfg(not(feature = "compiler"))] #[cfg(not(target_arch = "wasm32"))] @@ -145,6 +150,11 @@ impl ArtifactBuild { Self { serializable } } + /// Returns the memory start address for this compiled module + pub fn get_memory_start(&self) -> Option { + self.serializable.module_start + } + /// Get Functions Bodies ref pub fn get_function_bodies_ref(&self) -> &PrimaryMap { &self.serializable.compilation.function_bodies @@ -223,7 +233,7 @@ impl ArtifactCreate for ArtifactBuild { fn serialize(&self) -> Result, SerializeError> { let serialized_data = self.serializable.serialize()?; - assert!(mem::align_of::() <= MetadataHeader::ALIGN); + assert!(std::mem::align_of::() <= MetadataHeader::ALIGN); let mut metadata_binary = vec![]; metadata_binary.extend(Self::MAGIC_HEADER); diff --git a/lib/compiler/src/artifact_builders/trampoline.rs b/lib/compiler/src/artifact_builders/trampoline.rs index 5a7de39c118..6c634e148af 100644 --- a/lib/compiler/src/artifact_builders/trampoline.rs +++ b/lib/compiler/src/artifact_builders/trampoline.rs @@ -33,7 +33,7 @@ fn make_trampoline( ) { match target.triple().architecture { Architecture::Aarch64(_) => { - code.extend(&AARCH64_TRAMPOLINE); + code.extend(AARCH64_TRAMPOLINE); relocations.push(Relocation { kind: RelocationKind::Abs8, reloc_target: RelocationTarget::LibCall(libcall), @@ -42,7 +42,7 @@ fn make_trampoline( }); } Architecture::X86_64 => { - code.extend(&X86_64_TRAMPOLINE); + code.extend(X86_64_TRAMPOLINE); relocations.push(Relocation { kind: RelocationKind::Abs8, reloc_target: RelocationTarget::LibCall(libcall), diff --git a/lib/compiler/src/compiler.rs b/lib/compiler/src/compiler.rs index 61a4dcaef14..be25c218a70 100644 --- a/lib/compiler/src/compiler.rs +++ b/lib/compiler/src/compiler.rs @@ -79,6 +79,11 @@ where /// An implementation of a Compiler from parsed WebAssembly module to Compiled native code. pub trait Compiler: Send { + /// Returns a descriptive name for this compiler. + /// + /// Note that this is an API breaking change since 3.0 + fn name(&self) -> &str; + /// Validates a module. /// /// It returns the a succesful Result in case is valid, `CompileError` in case is not. diff --git a/lib/compiler/src/engine/artifact.rs b/lib/compiler/src/engine/artifact.rs index 7e0ec9538fc..b9ac9029227 100644 --- a/lib/compiler/src/engine/artifact.rs +++ b/lib/compiler/src/engine/artifact.rs @@ -28,11 +28,11 @@ use wasmer_types::MetadataHeader; use wasmer_types::SerializableCompilation; use wasmer_types::{ CompileError, CpuFeature, DataInitializer, DeserializeError, FunctionIndex, LocalFunctionIndex, - MemoryIndex, ModuleInfo, OwnedDataInitializer, SerializableModule, SerializeError, - SignatureIndex, TableIndex, + MemoryIndex, ModuleInfo, OwnedDataInitializer, SignatureIndex, TableIndex, }; #[cfg(feature = "static-artifact-create")] use wasmer_types::{CompileModuleInfo, Target}; +use wasmer_types::{SerializableModule, SerializeError}; use wasmer_vm::{FunctionBodyPtr, MemoryStyle, TableStyle, VMSharedSignatureIndex, VMTrampoline}; use wasmer_vm::{InstanceAllocator, InstanceHandle, StoreObjects, TrapHandlerFn, VMExtern}; @@ -77,7 +77,8 @@ impl Artifact { engine.target(), memory_styles, table_styles, - )?; + )? + .with_module_start(tunables.module_start()); Self::from_parts(&mut inner_engine, artifact) } @@ -711,6 +712,7 @@ impl Artifact { compile_info: metadata.compile_info, data_initializers: metadata.data_initializers, cpu_features: metadata.cpu_features, + module_start: None, }); let finished_function_lengths = finished_functions diff --git a/lib/compiler/src/engine/code_memory.rs b/lib/compiler/src/engine/code_memory.rs index 9c033347032..2cb3ef79995 100644 --- a/lib/compiler/src/engine/code_memory.rs +++ b/lib/compiler/src/engine/code_memory.rs @@ -39,7 +39,7 @@ impl CodeMemory { &mut self.unwind_registry } - /// Allocate a single contiguous block of memory for the functions and custom sections, and copy the data in place. + /// Allocate a single contiguous block of memory at a fixed virtual address for the functions and custom sections, and copy the data in place. #[allow(clippy::type_complexity)] pub fn allocate( &mut self, diff --git a/lib/compiler/src/engine/inner.rs b/lib/compiler/src/engine/inner.rs index 84e8774222f..0347b87b729 100644 --- a/lib/compiler/src/engine/inner.rs +++ b/lib/compiler/src/engine/inner.rs @@ -42,6 +42,7 @@ pub struct Engine { engine_id: EngineId, #[cfg(not(target_arch = "wasm32"))] tunables: Arc, + name: String, } impl Engine { @@ -54,9 +55,11 @@ impl Engine { ) -> Self { #[cfg(not(target_arch = "wasm32"))] let tunables = BaseTunables::for_target(&target); + let compiler = compiler_config.compiler(); + let name = format!("engine-{}", compiler.name()); Self { inner: Arc::new(Mutex::new(EngineInner { - compiler: Some(compiler_config.compiler()), + compiler: Some(compiler), features, #[cfg(not(target_arch = "wasm32"))] code_memory: vec![], @@ -67,9 +70,15 @@ impl Engine { engine_id: EngineId::default(), #[cfg(not(target_arch = "wasm32"))] tunables: Arc::new(tunables), + name, } } + /// Returns the name of this engine + pub fn name(&self) -> &str { + self.name.as_str() + } + /// Create a headless `Engine` /// /// A headless engine is an engine without any compiler attached. @@ -102,6 +111,7 @@ impl Engine { engine_id: EngineId::default(), #[cfg(not(target_arch = "wasm32"))] tunables: Arc::new(tunables), + name: "engine-headless".to_string(), } } @@ -223,6 +233,16 @@ impl AsEngineRef for Engine { } } +impl std::fmt::Debug for Engine { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Engine") + .field("target", &self.target) + .field("engine_id", &self.engine_id) + .field("name", &self.name) + .finish() + } +} + /// The inner contents of `Engine` pub struct EngineInner { #[cfg(feature = "compiler")] diff --git a/lib/compiler/src/engine/trap/error.rs b/lib/compiler/src/engine/trap/error.rs index e1c0dab5e62..59ef659d379 100644 --- a/lib/compiler/src/engine/trap/error.rs +++ b/lib/compiler/src/engine/trap/error.rs @@ -45,7 +45,7 @@ struct RuntimeErrorInner { /// The reconstructed Wasm trace (from the native trace and the `GlobalFrameInfo`). wasm_trace: Vec, /// The native backtrace - native_trace: Backtrace, + native_trace: Option, } fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) { @@ -191,7 +191,7 @@ impl RuntimeError { inner: Arc::new(RuntimeErrorInner { source, wasm_trace, - native_trace, + native_trace: Some(native_trace), }), } } diff --git a/lib/compiler/src/engine/tunables.rs b/lib/compiler/src/engine/tunables.rs index d67f15bee3a..9eab1ce85f1 100644 --- a/lib/compiler/src/engine/tunables.rs +++ b/lib/compiler/src/engine/tunables.rs @@ -13,6 +13,11 @@ use wasmer_vm::{VMMemoryDefinition, VMTableDefinition}; /// An engine delegates the creation of memories, tables, and globals /// to a foreign implementor of this trait. pub trait Tunables { + /// Fixed virtual memory address for the compiled module + fn module_start(&self) -> Option { + None + } + /// Construct a `MemoryStyle` for the provided `MemoryType` fn memory_style(&self, memory: &MemoryType) -> MemoryStyle; @@ -267,3 +272,87 @@ impl Tunables for BaseTunables { VMTable::from_definition(ty, style, vm_definition_location) } } + +impl Tunables for Box { + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + self.as_ref().memory_style(memory) + } + + fn table_style(&self, table: &TableType) -> TableStyle { + self.as_ref().table_style(table) + } + + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result { + self.as_ref().create_host_memory(ty, style) + } + + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_memory(ty, style, vm_definition_location) + } + + fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { + self.as_ref().create_host_table(ty, style) + } + + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_table(ty, style, vm_definition_location) + } +} + +impl Tunables for std::sync::Arc { + fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + self.as_ref().memory_style(memory) + } + + fn table_style(&self, table: &TableType) -> TableStyle { + self.as_ref().table_style(table) + } + + fn create_host_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + ) -> Result { + self.as_ref().create_host_memory(ty, style) + } + + unsafe fn create_vm_memory( + &self, + ty: &MemoryType, + style: &MemoryStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_memory(ty, style, vm_definition_location) + } + + fn create_host_table(&self, ty: &TableType, style: &TableStyle) -> Result { + self.as_ref().create_host_table(ty, style) + } + + unsafe fn create_vm_table( + &self, + ty: &TableType, + style: &TableStyle, + vm_definition_location: NonNull, + ) -> Result { + self.as_ref() + .create_vm_table(ty, style, vm_definition_location) + } +} diff --git a/lib/compiler/src/traits.rs b/lib/compiler/src/traits.rs index 8b0cb748794..170a436cf12 100644 --- a/lib/compiler/src/traits.rs +++ b/lib/compiler/src/traits.rs @@ -42,7 +42,7 @@ pub trait ArtifactCreate: Send + Sync + Upcastable { /// Serializes an artifact into a file path fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { let serialized = self.serialize()?; - fs::write(&path, serialized)?; + fs::write(path, serialized)?; Ok(()) } } diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index f60f0cc72dc..33571234e5a 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -23,9 +23,9 @@ use std::f64; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; use wasmer::{ - imports, namespace, AsStoreMut, ExportError, Exports, Function, FunctionEnv, FunctionEnvMut, - FunctionType, Global, Imports, Instance, Memory, MemoryType, Module, Pages, RuntimeError, - Table, TableType, TypedFunction, Value, WasmPtr, + imports, namespace, AsStoreMut, AsStoreRef, ExportError, Exports, Function, FunctionEnv, + FunctionEnvMut, FunctionType, Global, Imports, Instance, Memory, MemoryType, Module, Pages, + RuntimeError, Table, TableType, TypedFunction, Value, WasmPtr, }; use wasmer_types::Type as ValType; @@ -131,7 +131,7 @@ impl EmEnv { } } - pub fn set_memory(&mut self, memory: Memory) { + pub fn set_memory(&self, memory: Memory) { let mut w = self.memory.write().unwrap(); *w = Some(memory); } @@ -141,15 +141,12 @@ impl EmEnv { (*self.memory.read().unwrap()).as_ref().cloned().unwrap() } - pub fn set_functions(&mut self, funcs: EmscriptenFunctions) { - self.funcs = Arc::new(Mutex::new(funcs)); + pub fn set_functions(&self, funcs: EmscriptenFunctions) { + let mut w = self.funcs.lock().unwrap(); + *w = funcs; } - pub fn set_data( - &mut self, - data: &EmscriptenGlobalsData, - mapped_dirs: HashMap, - ) { + pub fn set_data(&self, data: &EmscriptenGlobalsData, mapped_dirs: HashMap) { let mut w = self.data.lock().unwrap(); *w = Some(EmscriptenData::new(data.clone(), mapped_dirs)); } @@ -887,7 +884,7 @@ pub fn run_emscripten_instance( if let Ok(func) = instance.exports.get_typed_function(&env, "setThrew") { emfuncs.set_threw = Some(func); } - env.data_mut().set_functions(emfuncs); + env.data().set_functions(emfuncs); set_up_emscripten(&mut env, instance)?; @@ -928,12 +925,12 @@ fn store_module_arguments(env: &mut FunctionEnvMut, args: Vec<&str>) -> ( } pub fn emscripten_set_up_memory( - store: &mut impl AsStoreMut, + store: &impl AsStoreRef, env: &FunctionEnv, memory: &Memory, globals: &EmscriptenGlobalsData, ) -> Result<(), String> { - env.as_mut(store).set_memory(memory.clone()); + env.as_ref(store).set_memory(memory.clone()); let memory = memory.view(store); let dynamictop_ptr = WasmPtr::::new(globals.dynamictop_ptr).deref(&memory); let dynamic_base = globals.dynamic_base; diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index a1fb62d8a07..a6608da9da6 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -25,7 +25,7 @@ tar = "0.4.38" flate2 = "1.0.24" semver = "1.0.14" lzma-rs = "0.2.0" -webc = { version ="3.0.1", features = ["mmap"] } +webc = { version ="4.0.0", features = ["mmap"] } hex = "0.4.3" tokio = "1.21.2" tempdir = "0.3.7" @@ -36,4 +36,4 @@ filetime = "0.2.19" tldextract = "0.6.0" console = "0.15.2" indicatif = "0.17.2" -lazy_static = "1.4.0" \ No newline at end of file +lazy_static = "1.4.0" diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 4eebc53e25e..1c84890dcc1 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -105,7 +105,8 @@ pub use value::{RawValue, ValueType}; pub use crate::libcalls::LibCall; pub use crate::memory::MemoryStyle; pub use crate::table::TableStyle; -pub use crate::trapcode::TrapCode; +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 +pub use crate::trapcode::{OnCalledAction, TrapCode}; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; pub use crate::utils::is_wasm; diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 2c986d90870..415d2f88cd2 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -59,6 +59,8 @@ pub unsafe trait MemorySize: Copy { + PartialOrd + Clone + Copy + + Sync + + Send + ValueType + Into + From @@ -76,7 +78,8 @@ pub unsafe trait MemorySize: Copy { + TryFrom + Add + Sum - + AddAssign; + + AddAssign + + 'static; /// Type used to pass this value as an argument or return value for a Wasm function. type Native: super::NativeWasmType; diff --git a/lib/types/src/serialize.rs b/lib/types/src/serialize.rs index 7df4fca9b5f..4b8d3be2bb4 100644 --- a/lib/types/src/serialize.rs +++ b/lib/types/src/serialize.rs @@ -1,4 +1,5 @@ use crate::entity::PrimaryMap; +use crate::Pages; use crate::{ compilation::target::CpuFeature, CompileModuleInfo, CompiledFunctionFrameInfo, CustomSection, DeserializeError, Dwarf, Features, FunctionBody, FunctionIndex, LocalFunctionIndex, @@ -61,6 +62,8 @@ pub struct SerializableModule { pub data_initializers: Box<[OwnedDataInitializer]>, /// CPU Feature flags for this compilation pub cpu_features: u64, + /// The start memory address of this serializable module + pub module_start: Option, } fn to_serialize_error(err: impl std::error::Error) -> SerializeError { @@ -160,7 +163,7 @@ impl SerializableModule { /// Serializes an artifact into a file path pub fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { let serialized = self.serialize()?; - fs::write(&path, serialized)?; + fs::write(path, serialized)?; Ok(()) } } diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index 7403f0f3e56..75c4e27d27b 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -120,6 +120,19 @@ impl FromStr for TrapCode { } } +// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451 +/// After the stack is unwound via asyncify what +/// should the call loop do next +#[derive(Debug)] +pub enum OnCalledAction { + /// Will call the function again + InvokeAgain, + /// Will return the result of the invocation + Finish, + /// Traps with an error + Trap(Box), +} + #[cfg(test)] mod tests { use super::*; diff --git a/lib/vfs/Cargo.toml b/lib/vfs/Cargo.toml index 2a02b3a64e2..cd44f06d4f0 100644 --- a/lib/vfs/Cargo.toml +++ b/lib/vfs/Cargo.toml @@ -13,7 +13,7 @@ tracing = { version = "0.1" } typetag = { version = "0.1", optional = true } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } slab = { version = "0.4", optional = true } -webc = { version = "3.0.1", optional = true } +webc = { version = "4.0.0", optional = true } anyhow = { version = "1.0.66", optional = true } [features] diff --git a/lib/vm/Cargo.toml b/lib/vm/Cargo.toml index 3a59d1db9c2..7a03b76cb2b 100644 --- a/lib/vm/Cargo.toml +++ b/lib/vm/Cargo.toml @@ -25,6 +25,9 @@ scopeguard = "1.1.0" lazy_static = "1.4.0" region = { version = "3.0" } corosensei = { version = "0.1.2" } +derivative = { version = "^2" } +# - Optional shared dependencies. +tracing = { version = "0.1", optional = true } [target.'cfg(target_vendor = "apple")'.dependencies] mach = "0.3.2" diff --git a/lib/vm/src/export.rs b/lib/vm/src/export.rs index 68427062f7c..d9df605e8ab 100644 --- a/lib/vm/src/export.rs +++ b/lib/vm/src/export.rs @@ -7,6 +7,7 @@ use crate::store::InternalStoreHandle; use crate::table::VMTable; use crate::vmcontext::VMFunctionKind; use crate::{MaybeInstanceOwned, VMCallerCheckedAnyfunc}; +use derivative::Derivative; use std::any::Any; use wasmer_types::FunctionType; @@ -26,9 +27,12 @@ pub enum VMExtern { } /// A function export value. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMFunction { /// Pointer to the `VMCallerCheckedAnyfunc` which contains data needed to /// call the function and check its signature. + #[derivative(Debug = "ignore")] pub anyfunc: MaybeInstanceOwned, /// The function type, used for compatibility checking. @@ -39,5 +43,6 @@ pub struct VMFunction { pub kind: VMFunctionKind, /// Associated data owned by a host function. + #[derivative(Debug = "ignore")] pub host_data: Box, } diff --git a/lib/vm/src/extern_ref.rs b/lib/vm/src/extern_ref.rs index f99a7e93f51..79f71b876b2 100644 --- a/lib/vm/src/extern_ref.rs +++ b/lib/vm/src/extern_ref.rs @@ -1,11 +1,14 @@ +use derivative::Derivative; use std::any::Any; - use wasmer_types::RawValue; use crate::store::InternalStoreHandle; /// Underlying object referenced by a `VMExternRef`. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMExternObj { + #[derivative(Debug = "ignore")] contents: Box, } diff --git a/lib/vm/src/function_env.rs b/lib/vm/src/function_env.rs index ccedf04385e..f2f8b728408 100644 --- a/lib/vm/src/function_env.rs +++ b/lib/vm/src/function_env.rs @@ -1,7 +1,11 @@ +use derivative::Derivative; use std::any::Any; /// Underlying FunctionEnvironment used by a `VMFunction`. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMFunctionEnvironment { + #[derivative(Debug = "ignore")] contents: Box, } diff --git a/lib/vm/src/global.rs b/lib/vm/src/global.rs index 682a66fb294..bddb55575e1 100644 --- a/lib/vm/src/global.rs +++ b/lib/vm/src/global.rs @@ -1,10 +1,14 @@ use crate::{store::MaybeInstanceOwned, vmcontext::VMGlobalDefinition}; +use derivative::Derivative; use std::{cell::UnsafeCell, ptr::NonNull}; use wasmer_types::GlobalType; /// A Global instance +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMGlobal { ty: GlobalType, + #[derivative(Debug = "ignore")] vm_global_definition: MaybeInstanceOwned, } @@ -30,4 +34,16 @@ impl VMGlobal { pub fn vmglobal(&self) -> NonNull { self.vm_global_definition.as_ptr() } + + /// Copies this global + pub fn copy_on_write(&self) -> Self { + unsafe { + Self { + ty: self.ty, + vm_global_definition: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new( + self.vm_global_definition.as_ptr().as_ref().clone(), + ))), + } + } + } } diff --git a/lib/vm/src/instance/allocator.rs b/lib/vm/src/instance/allocator.rs index ead0e9e27f4..ae3f360e871 100644 --- a/lib/vm/src/instance/allocator.rs +++ b/lib/vm/src/instance/allocator.rs @@ -1,5 +1,6 @@ use super::{Instance, InstanceHandle}; -use crate::vmcontext::{VMMemoryDefinition, VMTableDefinition}; +use crate::vmcontext::VMTableDefinition; +use crate::VMMemoryDefinition; use std::alloc::{self, Layout}; use std::convert::TryFrom; use std::mem; diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index c4370d474eb..d3d288a0c04 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -116,6 +116,27 @@ impl WasmMmap { Ok(prev_pages) } + + /// Copies the memory + /// (in this case it performs a copy-on-write to save memory) + pub fn duplicate(&mut self) -> Result { + let mem_length = self.size.bytes().0; + let mut alloc = self + .alloc + .duplicate(Some(mem_length)) + .map_err(MemoryError::Generic)?; + let base_ptr = alloc.as_mut_ptr(); + Ok(Self { + vm_memory_definition: MaybeInstanceOwned::Host(Box::new(UnsafeCell::new( + VMMemoryDefinition { + base: base_ptr, + current_length: mem_length, + }, + ))), + alloc, + size: self.size, + }) + } } /// A linear memory instance. @@ -157,18 +178,6 @@ pub struct VMOwnedMemory { unsafe impl Send for VMOwnedMemory {} unsafe impl Sync for VMOwnedMemory {} -/// A shared linear memory instance. -#[derive(Debug, Clone)] -pub struct VMSharedMemory { - // The underlying allocation. - mmap: Arc>, - // Configuration of this memory - config: VMMemoryConfig, -} - -unsafe impl Send for VMSharedMemory {} -unsafe impl Sync for VMSharedMemory {} - impl VMOwnedMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// @@ -270,9 +279,7 @@ impl VMOwnedMemory { }, }) } -} -impl VMOwnedMemory { /// Converts this owned memory into shared memory pub fn to_shared(self) -> VMSharedMemory { VMSharedMemory { @@ -280,6 +287,14 @@ impl VMOwnedMemory { config: self.config, } } + + /// Copies this memory to a new memory + pub fn duplicate(&mut self) -> Result { + Ok(Self { + mmap: self.mmap.duplicate()?, + config: self.config.clone(), + }) + } } impl LinearMemory for VMOwnedMemory { @@ -316,8 +331,26 @@ impl LinearMemory for VMOwnedMemory { fn try_clone(&self) -> Option> { None } + + /// Copies this memory to a new memory + fn duplicate(&mut self) -> Result, MemoryError> { + let forked = Self::duplicate(self)?; + Ok(Box::new(forked)) + } +} + +/// A shared linear memory instance. +#[derive(Debug, Clone)] +pub struct VMSharedMemory { + // The underlying allocation. + mmap: Arc>, + // Configuration of this memory + config: VMMemoryConfig, } +unsafe impl Send for VMSharedMemory {} +unsafe impl Sync for VMSharedMemory {} + impl VMSharedMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// @@ -341,6 +374,15 @@ impl VMSharedMemory { ) -> Result { Ok(VMOwnedMemory::from_definition(memory, style, vm_memory_location)?.to_shared()) } + + /// Copies this memory to a new memory + pub fn duplicate(&mut self) -> Result { + let mut guard = self.mmap.write().unwrap(); + Ok(Self { + mmap: Arc::new(RwLock::new(guard.duplicate()?)), + config: self.config.clone(), + }) + } } impl LinearMemory for VMSharedMemory { @@ -379,9 +421,15 @@ impl LinearMemory for VMSharedMemory { guard.vm_memory_definition.as_ptr() } - /// Owned memory can not be cloned (this will always return None) + /// Shared memory can always be cloned fn try_clone(&self) -> Option> { - None + Some(Box::new(self.clone())) + } + + /// Copies this memory to a new memory + fn duplicate(&mut self) -> Result, MemoryError> { + let forked = Self::duplicate(self)?; + Ok(Box::new(forked)) } } @@ -445,6 +493,11 @@ impl LinearMemory for VMMemory { unsafe fn initialize_with_data(&self, start: usize, data: &[u8]) -> Result<(), Trap> { self.0.initialize_with_data(start, data) } + + /// Copies this memory to a new memory + fn duplicate(&mut self) -> Result, MemoryError> { + self.0.duplicate() + } } impl VMMemory { @@ -503,6 +556,11 @@ impl VMMemory { { memory.into() } + + /// Copies this memory to a new memory + pub fn duplicate(&mut self) -> Result, MemoryError> { + LinearMemory::duplicate(self) + } } #[doc(hidden)] @@ -555,4 +613,7 @@ where initialize_memory_with_data(memory, start, data) } + + /// Copies this memory to a new memory + fn duplicate(&mut self) -> Result, MemoryError>; } diff --git a/lib/vm/src/mmap.rs b/lib/vm/src/mmap.rs index 6b3dcdd19cd..11fa061a204 100644 --- a/lib/vm/src/mmap.rs +++ b/lib/vm/src/mmap.rs @@ -24,7 +24,8 @@ pub struct Mmap { // `unsafe impl`. This type is sendable across threads and shareable since // the coordination all happens at the OS layer. ptr: usize, - len: usize, + total_size: usize, + accessible_size: usize, } impl Mmap { @@ -36,7 +37,8 @@ impl Mmap { let empty = Vec::::new(); Self { ptr: empty.as_ptr() as usize, - len: 0, + total_size: 0, + accessible_size: 0, } } @@ -84,7 +86,8 @@ impl Mmap { Self { ptr: ptr as usize, - len: mapping_size, + total_size: mapping_size, + accessible_size, } } else { // Reserve the mapping size. @@ -104,7 +107,8 @@ impl Mmap { let mut result = Self { ptr: ptr as usize, - len: mapping_size, + total_size: mapping_size, + accessible_size, }; if accessible_size != 0 { @@ -154,7 +158,8 @@ impl Mmap { Self { ptr: ptr as usize, - len: mapping_size, + total_size: mapping_size, + accessible_size, } } else { // Reserve the mapping size. @@ -166,7 +171,8 @@ impl Mmap { let mut result = Self { ptr: ptr as usize, - len: mapping_size, + total_size: mapping_size, + accessible_size, }; if accessible_size != 0 { @@ -186,8 +192,8 @@ impl Mmap { let page_size = region::page::size(); assert_eq!(start & (page_size - 1), 0); assert_eq!(len & (page_size - 1), 0); - assert_lt!(len, self.len); - assert_lt!(start, self.len - len); + assert_lt!(len, self.total_size); + assert_lt!(start, self.total_size - len); // Commit the accessible size. let ptr = self.ptr as *const u8; @@ -206,8 +212,8 @@ impl Mmap { let page_size = region::page::size(); assert_eq!(start & (page_size - 1), 0); assert_eq!(len & (page_size - 1), 0); - assert_lt!(len, self.len); - assert_lt!(start, self.len - len); + assert_lt!(len, self.len()); + assert_lt!(start, self.len() - len); // Commit the accessible size. let ptr = self.ptr as *const u8; @@ -229,12 +235,22 @@ impl Mmap { /// Return the allocated memory as a slice of u8. pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) } + unsafe { slice::from_raw_parts(self.ptr as *const u8, self.total_size) } + } + + /// Return the allocated memory as a slice of u8. + pub fn as_slice_accessible(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.ptr as *const u8, self.accessible_size) } } /// Return the allocated memory as a mutable slice of u8. pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.len) } + unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.total_size) } + } + + /// Return the allocated memory as a mutable slice of u8. + pub fn as_mut_slice_accessible(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.ptr as *mut u8, self.accessible_size) } } /// Return the allocated memory as a pointer to u8. @@ -249,27 +265,35 @@ impl Mmap { /// Return the length of the allocated memory. pub fn len(&self) -> usize { - self.len + self.total_size } /// Return whether any memory has been allocated. pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Duplicate in a new memory mapping. + pub fn duplicate(&mut self, _size_hint: Option) -> Result { + let mut new = Self::accessible_reserved(self.accessible_size, self.total_size)?; + new.as_mut_slice_accessible() + .copy_from_slice(self.as_slice_accessible()); + Ok(new) + } } impl Drop for Mmap { #[cfg(not(target_os = "windows"))] fn drop(&mut self) { - if self.len != 0 { - let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.len) }; + if self.total_size != 0 { + let r = unsafe { libc::munmap(self.ptr as *mut libc::c_void, self.total_size) }; assert_eq!(r, 0, "munmap failed: {}", io::Error::last_os_error()); } } #[cfg(target_os = "windows")] fn drop(&mut self) { - if self.len != 0 { + if self.len() != 0 { use winapi::ctypes::c_void; use winapi::um::memoryapi::VirtualFree; use winapi::um::winnt::MEM_RELEASE; diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index 69ed19856c7..4e0c4676525 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -1,3 +1,4 @@ +use core::slice::Iter; use std::{ cell::UnsafeCell, fmt, @@ -7,9 +8,9 @@ use std::{ sync::atomic::{AtomicU64, Ordering}, }; -use crate::VMExternObj; - -use crate::{InstanceHandle, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable}; +use crate::{ + InstanceHandle, VMExternObj, VMFunction, VMFunctionEnvironment, VMGlobal, VMMemory, VMTable, +}; /// Unique ID to identify a context. /// @@ -60,7 +61,7 @@ impl_context_object! { } /// Set of objects managed by a context. -#[derive(Default)] +#[derive(Debug, Default)] pub struct StoreObjects { id: StoreId, memories: Vec, @@ -101,6 +102,21 @@ impl StoreObjects { (&mut high[0], &mut low[a.index()]) } } + + /// Return an immutable iterator over all globals + pub fn iter_globals(&self) -> Iter { + self.globals.iter() + } + + /// Set a global, at index idx. Will panic if idx is out of range + /// Safety: the caller should check taht the raw value is compatible + /// with destination VMGlobal type + pub fn set_global_unchecked(&self, idx: usize, val: u128) { + assert!(idx < self.globals.len()); + unsafe { + self.globals[idx].vmglobal().as_mut().val.u128 = val; + } + } } /// Handle to an object managed by a context. @@ -176,6 +192,11 @@ impl StoreHandle { self.id } + /// Overrides the store id with a new ID + pub fn set_store_id(&mut self, id: StoreId) { + self.id = id; + } + /// Constructs a `StoreHandle` from a `StoreId` and an `InternalStoreHandle`. /// /// # Safety diff --git a/lib/vm/src/table.rs b/lib/vm/src/table.rs index b29d8c883c4..00c2555c2a3 100644 --- a/lib/vm/src/table.rs +++ b/lib/vm/src/table.rs @@ -10,6 +10,7 @@ use crate::vmcontext::VMTableDefinition; use crate::Trap; use crate::VMExternRef; use crate::VMFuncRef; +use derivative::Derivative; use std::cell::UnsafeCell; use std::convert::TryFrom; use std::fmt; @@ -69,13 +70,17 @@ impl Default for TableElement { } /// A table instance. +#[derive(Derivative)] +#[derivative(Debug)] pub struct VMTable { + #[derivative(Debug = "ignore")] vec: Vec, maximum: Option, /// The WebAssembly table description. table: TableType, /// Our chosen implementation style. style: TableStyle, + #[derivative(Debug = "ignore")] vm_table_definition: MaybeInstanceOwned, } @@ -306,6 +311,14 @@ impl VMTable { Ok(()) } + /// Copies the table into a new table + pub fn copy_on_write(&self) -> Result { + let mut ret = Self::new(&self.table, &self.style)?; + ret.copy(self, 0, 0, self.size()) + .map_err(|trap| format!("failed to copy the table - {:?}", trap))?; + Ok(ret) + } + /// Copy `len` elements from `table[src_index..]` to `table[dst_index..]`. /// /// # Errors diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 9d8c13bf414..b975d874c8b 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -28,7 +28,7 @@ bincode = { version = "1.3", optional = true } chrono = { version = "^0.4", default-features = false, features = [ "wasmbind", "std", "clock" ], optional = true } derivative = { version = "^2" } bytes = "1" -webc = { version = "3.0.1", optional = true, default-features = false, features = ["std", "mmap"] } +webc = { version = "4.0.0", optional = true, default-features = false, features = ["std", "mmap"] } serde_cbor = { version = "0.11.2", optional = true } anyhow = { version = "1.0.66", optional = true } wasmer-emscripten = { path = "../emscripten", version = "=3.1.0", optional = true } diff --git a/lib/wasi/src/runners/mod.rs b/lib/wasi/src/runners/mod.rs index 4cac42921f2..4965bbd6415 100644 --- a/lib/wasi/src/runners/mod.rs +++ b/lib/wasi/src/runners/mod.rs @@ -107,11 +107,11 @@ impl Bindings for WitBindings { container: &WapmContainer, value: &serde_cbor::Value, ) -> Result { - let value: webc::WitBindingsExtended = + let value: webc::BindingsExtended = serde_cbor::from_slice(&serde_cbor::to_vec(value).unwrap()) .map_err(|e| format!("could not parse WitBindings annotations: {e}"))?; - let mut wit_bindgen_filepath = value.wit.exports; + let mut wit_bindgen_filepath = value.exports().unwrap_or_default().to_string(); for v in container.get_volumes() { let schema = format!("{v}://"); diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 8179bd10fad..ebf755d920a 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -5548,7 +5548,7 @@ pub unsafe fn sock_send_file( // Write it down to the socket let bytes_written = wasi_try_ok!(__sock_actor_mut(&ctx, sock, Rights::SOCK_SEND, |socket| { - let buf = (&buf[..]).to_vec(); + let buf = (buf[..]).to_vec(); socket.send_bytes::(Bytes::from(buf)) })); total_written += bytes_written as u64; diff --git a/lib/wasi/src/utils.rs b/lib/wasi/src/utils.rs index b05c5c302db..ba0df86f276 100644 --- a/lib/wasi/src/utils.rs +++ b/lib/wasi/src/utils.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; #[cfg(not(feature = "js"))] use wasmer::vm::VMSharedMemory; -use wasmer::{AsStoreMut, Imports, Memory, Module}; +use wasmer::{AsStoreMut, Imports, Module}; use wasmer_wasi_types::wasi::Errno; #[allow(dead_code)] @@ -75,7 +75,7 @@ pub fn wasi_import_shared_memory( imports.define( "env", "memory", - Memory::new_from_existing(store, memory.into()), + wasmer::Memory::new_from_existing(store, memory.into()), ); } }; diff --git a/rust-toolchain b/rust-toolchain index 58e4eb6b299..8725364a8ec 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.63 +1.64 diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index d3a244b3e7d..4c7bb0c90d6 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -426,7 +426,7 @@ fn call_signature_mismatch(config: crate::Config) -> Result<()> { ) "#; - let module = Module::new(&store, &binary)?; + let module = Module::new(&store, binary)?; let err = Instance::new(&mut store, &module, &imports! {}).expect_err("expected error"); assert_eq!( format!("{}", err), diff --git a/tests/compilers/typed_functions.rs b/tests/compilers/typed_functions.rs index ca2630eda48..f55447a43a2 100644 --- a/tests/compilers/typed_functions.rs +++ b/tests/compilers/typed_functions.rs @@ -309,7 +309,7 @@ fn static_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { let mut store = config.store(); fn f(mut env: FunctionEnvMut, a: i32, b: i64, c: f32, d: f64) -> (f64, f32, i64, i32) { - let mut guard = env.data_mut().0.lock().unwrap(); + let mut guard = env.data().0.lock().unwrap(); assert_eq!(*guard, 100); *guard = 101; @@ -323,7 +323,7 @@ fn static_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { c: f32, d: f64, ) -> Result<(f64, f32, i64, i32), Infallible> { - let mut guard = env.data_mut().0.lock().unwrap(); + let mut guard = env.data().0.lock().unwrap(); assert_eq!(*guard, 100); *guard = 101; @@ -448,7 +448,7 @@ fn dynamic_host_function_with_env(config: crate::Config) -> anyhow::Result<()> { ], ), |mut env, values| { - let mut guard = env.data_mut().0.lock().unwrap(); + let mut guard = env.data().0.lock().unwrap(); assert_eq!(*guard, 100); *guard = 101;