diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c0499cacd..dd53ba8d7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -493,6 +493,11 @@ jobs: build-cmd: "make build-capi && make build-capi-headless && make package-capi && make tar-capi", name: "Build and test C-API", }, + { + key: capi-v8, + build-cmd: "make test-capi-v8", + name: "Build and test C-API with v8", + }, { key: wasmer, build-cmd: "make build-wasmer && make package-wasmer && make tar-wasmer", @@ -511,6 +516,10 @@ jobs: target: x86_64-unknown-linux-gnu, exe: "", llvm_url: "https://github.com/wasmerio/llvm-custom-builds/releases/download/18.x/llvm-linux-amd64.tar.xz", + # Unfortunately, building a shared lib of the c_api with the v8 + # backend does not work, due to unsupported relocations to hidden + # symbols in wee8. + supports_v8: false }, { build: macos-x64, @@ -518,6 +527,7 @@ jobs: target: x86_64-apple-darwin, exe: "", llvm_url: "https://github.com/wasmerio/llvm-custom-builds/releases/download/18.x/llvm-darwin-amd64.tar.xz", + supports_v8: true }, { build: macos-arm, @@ -525,6 +535,7 @@ jobs: target: aarch64-apple-darwin, exe: "", llvm_url: "https://github.com/wasmerio/llvm-custom-builds/releases/download/18.x/llvm-darwin-aarch64.tar.xz", + supports_v8: true }, { build: windows-x64, @@ -533,11 +544,13 @@ jobs: exe: ".exe", # For now, disable LLVM in `windows-x64.` # llvm_url: 'https://github.com/wasmerio/llvm-custom-builds/releases/download/18.x/llvm-windows-amd64.tar.xz' + supports_v8: false }, { build: windows-gnu, target: x86_64-pc-windows-gnu, os: ubuntu-22.04, + supports_v8: false }, { build: linux-musl, @@ -545,6 +558,7 @@ jobs: os: ubuntu-22.04, exe: "", container: "alpine:latest", + supports_v8: false }, ] container: ${{ matrix.metadata.container }} @@ -684,6 +698,10 @@ jobs: TARGET: ${{ matrix.metadata.target }} TARGET_DIR: target/${{ matrix.metadata.target }}/release CARGO_TARGET: ${{ matrix.metadata.target }} + - name: Test C-API (v8) + shell: bash + run: ${{ matrix.build-what.build-cmd }} + if: ${{ matrix.build-what.key == 'capi-v8' && matrix.metadata.supports_v8 == true }} - name: Build Wasmer shell: bash if: ${{ matrix.build-what.key == 'wasmer' && matrix.metadata.build != 'windows-gnu' }} diff --git a/Makefile b/Makefile index b7c54660b..46d4d78ca 100644 --- a/Makefile +++ b/Makefile @@ -559,7 +559,7 @@ build-docs-capi: build-capi: RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ - --no-default-features --features wat,compiler,wasi,middlewares,webc_runner $(capi_compiler_features) --locked + --no-default-features --features wat,sys-default,compiler,wasi,middlewares,webc_runner $(capi_compiler_features) --locked build-capi-singlepass: RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ @@ -585,9 +585,21 @@ build-capi-llvm-universal: RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features wat,compiler,llvm,wasi,middlewares,webc_runner --locked +build-capi-v8: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,v8-default,wasi --locked + +build-capi-wamr: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,wamr-default,wasi --locked + +build-capi-wasmi: + RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features wat,wasmi-default,wasi --locked + build-capi-jsc: RUSTFLAGS="${RUSTFLAGS}" $(CARGO_BINARY) build $(CARGO_TARGET_FLAG) --manifest-path lib/c-api/Cargo.toml --release \ - --no-default-features --features wat,jsc,wasi --locked + --no-default-features --features wat,jsc-default,wasi --locked # Headless (we include the minimal to be able to run) @@ -703,6 +715,10 @@ test-capi-ci: $(foreach compiler_engine,$(capi_compilers_engines),test-capi-crat # compilers first test-capi: build-capi package-capi test-capi-ci +test-capi-v8: build-capi-v8 package-capi test-capi-integration-v8 +test-capi-wasmi: build-capi-wasmi package-capi test-capi-integration-wasmi +test-capi-wamr: build-capi-wamr package-capi test-capi-integration-wamr + test-capi-jsc: build-capi-jsc package-capi test-capi-integration-jsc test-capi-crate-%: diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 01527336c..5d5e6a362 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -161,6 +161,7 @@ js = ["wasm-bindgen", "js-sys"] js-default = ["js", "std", "wasm-types-polyfill"] wasm-types-polyfill = ["wasmparser"] +wat = ["dep:wat", "wasmparser"] jsc = ["rusty_jsc", "wasm-types-polyfill", "wasmparser"] jsc-default = ["jsc"] diff --git a/lib/api/build.rs b/lib/api/build.rs index 841ccc445..f0e28731e 100644 --- a/lib/api/build.rs +++ b/lib/api/build.rs @@ -1,8 +1,9 @@ #[cfg(feature = "wamr")] fn build_wamr() { use bindgen::callbacks::ParseCallbacks; - const WAMR_ZIP: &str = "https://github.com/bytecodealliance/wasm-micro-runtime/archive/0e4dffc47922bb6fcdcaed7de2a6edfe8c48a7cd.zip"; - const ZIP_NAME: &str = "wasm-micro-runtime-0e4dffc47922bb6fcdcaed7de2a6edfe8c48a7cd"; + const WAMR_ZIP: &str = + "https://github.com/bytecodealliance/wasm-micro-runtime/archive/refs/tags/WAMR-2.2.0.zip"; + const ZIP_NAME: &str = "wasm-micro-runtime-WAMR-2.2.0"; use cmake::Config; use std::{env, path::PathBuf}; @@ -36,24 +37,27 @@ fn build_wamr() { // Cleanup tmp data from prior builds let wamr_dir = PathBuf::from(&crate_root).join("third_party/wamr"); - let zip_dir = PathBuf::from(&crate_root) - .join("third_party") - .join(ZIP_NAME); - let _ = std::fs::remove_dir_all(&wamr_dir); - let _ = std::fs::remove_dir_all(&zip_dir); + if !wamr_dir.exists() { + let zip_dir = PathBuf::from(&crate_root).join("third_party"); + let _ = std::fs::remove_dir_all(&wamr_dir); + let _ = std::fs::remove_dir_all(&zip_dir); - // Fetch & extract wasm-micro-runtime source - let zip = ureq::get(WAMR_ZIP).call().expect("failed to download wamr"); - let mut zip_data = Vec::new(); - zip.into_reader() - .read_to_end(&mut zip_data) - .expect("failed to download wamr"); - zip::read::ZipArchive::new(std::io::Cursor::new(zip_data)) - .expect("failed to open wamr zip file") - .extract(&zip_dir) - .expect("failed to extract wamr zip file"); - let _ = std::fs::remove_dir_all(&wamr_dir); - std::fs::rename(zip_dir.join(ZIP_NAME), &wamr_dir).expect("failed to rename wamr dir"); + // Fetch & extract wasm-micro-runtime source + let zip = ureq::get(WAMR_ZIP).call().expect("failed to download wamr"); + let mut zip_data = Vec::new(); + zip.into_reader() + .read_to_end(&mut zip_data) + .expect("failed to download wamr"); + zip::read::ZipArchive::new(std::io::Cursor::new(zip_data)) + .expect("failed to open wamr zip file") + .extract(&zip_dir) + .expect("failed to extract wamr zip file"); + let _ = std::fs::remove_dir_all(&wamr_dir); + std::fs::rename(zip_dir.join(ZIP_NAME), &wamr_dir) + .expect(&format!("failed to rename wamr dir: {zip_dir:?}")); + } else { + println!("cargo::rerun-if-changed={}", wamr_dir.display()); + } let wamr_platform_dir = wamr_dir.join("product-mini/platforms").join(target_os); let mut dst = Config::new(wamr_platform_dir.as_path()); @@ -83,7 +87,7 @@ fn build_wamr() { .define("WAMR_BUILD_LIBC_WASI", "0") .define("WAMR_BUILD_LIBC_BUILTIN", "0") .define("WAMR_BUILD_SHARED_MEMORY", "1") - .define("WAMR_BUILD_MULTI_MODULE", "0") + .define("WAMR_BUILD_MULTI_MODULE", "1") .define("WAMR_DISABLE_HW_BOUND_CHECK", "1") .define("WAMR_BUILD_TARGET", target_arch); @@ -216,7 +220,7 @@ fn build_wamr() { "cargo:rustc-link-search=native={}", dst.join("build").display() ); - println!("cargo:rustc-link-lib=wamr"); + println!("cargo:rustc-link-lib=static=wamr"); } #[cfg(feature = "v8")] @@ -297,8 +301,17 @@ fn build_v8() { } let header_path = v8_header_path.join("wasm.h"); + let mut args = vec![]; + if cfg!(target_os = "macos") { + args.push("-I/usr/local/include"); + args.push("-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1"); + args.push("-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include"); + args.push("-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"); + args.push("-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"); + } let bindings = bindgen::Builder::default() .header(header_path.display().to_string()) + .clang_args(args) .derive_default(true) .derive_debug(true) .parse_callbacks(Box::new(Wee8Renamer {})) diff --git a/lib/api/src/backend/js/mod.rs b/lib/api/src/backend/js/mod.rs index 4ff2a3aed..0e4f2a732 100644 --- a/lib/api/src/backend/js/mod.rs +++ b/lib/api/src/backend/js/mod.rs @@ -1,4 +1,4 @@ -//! Data types, functions and traits for the `sys` runtime. +//! Data types, functions and traits for the `js` backend. pub(crate) mod entities; pub(crate) mod error; diff --git a/lib/api/src/backend/jsc/mod.rs b/lib/api/src/backend/jsc/mod.rs index 4ff2a3aed..e2e559332 100644 --- a/lib/api/src/backend/jsc/mod.rs +++ b/lib/api/src/backend/jsc/mod.rs @@ -1,4 +1,4 @@ -//! Data types, functions and traits for the `sys` runtime. +//! Data types, functions and traits for the `jsc` backend. pub(crate) mod entities; pub(crate) mod error; diff --git a/lib/api/src/backend/mod.rs b/lib/api/src/backend/mod.rs index 2df5c2235..18730741e 100644 --- a/lib/api/src/backend/mod.rs +++ b/lib/api/src/backend/mod.rs @@ -59,3 +59,93 @@ pub enum BackendKind { /// The `jsc` runtime. Jsc, } + +impl Default for BackendKind { + fn default() -> Self { + #[cfg(feature = "sys-default")] + { + #[cfg(feature = "cranelift")] + { + return Self::Cranelift; + } + #[cfg(feature = "singlepass")] + { + return Self::Singlepass; + } + #[cfg(feature = "llvm")] + { + return Self::LLVM; + } + return Self::Headless; + } + + #[cfg(feature = "wamr-default")] + { + return Self::Wamr; + } + + #[cfg(feature = "wasmi-default")] + { + return Self::Wasmi; + } + + #[cfg(feature = "v8-default")] + { + return Self::V8; + } + + #[cfg(feature = "js-default")] + { + return Self::Js; + } + + #[cfg(feature = "jsc-default")] + { + return Self::Jsc; + } + + #[cfg(feature = "sys")] + { + #[cfg(feature = "cranelift")] + { + return Self::Cranelift; + } + #[cfg(feature = "singlepass")] + { + return Self::Singlepass; + } + #[cfg(feature = "llvm")] + { + return Self::LLVM; + } + return Self::Headless; + } + + #[cfg(feature = "wamr")] + { + return Self::Wamr; + } + + #[cfg(feature = "wasmi")] + { + return Self::Wasmi; + } + + #[cfg(feature = "v8")] + { + return Self::V8; + } + + #[cfg(feature = "js")] + { + return Self::Js; + } + + #[cfg(feature = "jsc")] + { + return Self::Jsc; + } + + panic!("No runtime enabled!") + } +} diff --git a/lib/api/src/backend/sys/entities/engine.rs b/lib/api/src/backend/sys/entities/engine.rs index 6cfe97143..40d723f7d 100644 --- a/lib/api/src/backend/sys/entities/engine.rs +++ b/lib/api/src/backend/sys/entities/engine.rs @@ -28,7 +28,7 @@ pub fn get_default_compiler_config() -> Option Engine { +pub fn default_engine() -> Engine { #[allow(unreachable_code, unused_mut)] fn get_engine() -> Engine { cfg_if::cfg_if! { diff --git a/lib/api/src/backend/v8/entities/memory/mod.rs b/lib/api/src/backend/v8/entities/memory/mod.rs index 70513d2cc..89624c0d8 100644 --- a/lib/api/src/backend/v8/entities/memory/mod.rs +++ b/lib/api/src/backend/v8/entities/memory/mod.rs @@ -33,12 +33,28 @@ impl Memory { let mut store_mut = store.as_store_mut(); let v8_store = store_mut.inner.store.as_v8(); + let max_requested = ty.maximum.unwrap_or(Pages::max_value()); + + let min = ty.minimum.0; + let max = max_requested.0; + + if max < min { + return Err(MemoryError::InvalidMemory { + reason: format!("the maximum ({max} pages) is less than the minimum ({min} pages)",), + }); + } + + let max_allowed = Pages::max_value(); + if max_requested > max_allowed { + return Err(MemoryError::MaximumMemoryTooLarge { + max_requested, + max_allowed, + }); + } + let limits = Box::into_raw(Box::new(wasm_limits_t { - min: ty.minimum.0, - max: match ty.maximum { - Some(v) => v.0, - None => wasm_limits_max_default, - }, + min, + max, shared: ty.shared, })); diff --git a/lib/api/src/backend/v8/entities/module.rs b/lib/api/src/backend/v8/entities/module.rs index 891497af4..6a965dad4 100644 --- a/lib/api/src/backend/v8/entities/module.rs +++ b/lib/api/src/backend/v8/entities/module.rs @@ -222,7 +222,7 @@ impl Module { let off = usize::from_ne_bytes(off.try_into().unwrap()); let name_bytes = &binary[8..(8 + off)]; let name = String::from_utf8_lossy(name_bytes).to_string(); - let mod_bytes = &binary[off..]; + let mod_bytes = &binary[(8 + off)..]; let module = ModuleHandle::deserialize(engine, mod_bytes)?; Ok(Self { @@ -275,7 +275,12 @@ impl Module { wasm_module_imports(module as *const _, &mut imports as *mut _); - let imports = std::slice::from_raw_parts(imports.data, imports.size).to_vec(); + let imports = + if imports.data.is_null() || !imports.data.is_aligned() || imports.size == 0 { + vec![] + } else { + std::slice::from_raw_parts(imports.data, imports.size).to_vec() + }; let mut wasmer_imports = vec![]; for i in imports.into_iter() { @@ -287,9 +292,17 @@ impl Module { let name = std::slice::from_raw_parts((*name).data as *const u8, (*name).size); let name_str = String::from_utf8_lossy(name).to_string(); let module = wasm_importtype_module(i as *const _); - let module = - std::slice::from_raw_parts((*module).data as *const u8, (*module).size); - let module_str = String::from_utf8_lossy(module).to_string(); + let module_str = if module.is_null() + || (*module).data.is_null() + || !(*module).data.is_aligned() + || (*module).size == 0 + { + String::new() + } else { + let str = + std::slice::from_raw_parts((*module).data as *const u8, (*module).size); + String::from_utf8_lossy(str).to_string() + }; let ty = IntoWasmerExternType::into_wextt(wasm_importtype_type(i as *const _)); if ty.is_err() { diff --git a/lib/api/src/backend/v8/mod.rs b/lib/api/src/backend/v8/mod.rs index a0f9e9f6b..f7be9f943 100644 --- a/lib/api/src/backend/v8/mod.rs +++ b/lib/api/src/backend/v8/mod.rs @@ -1,4 +1,4 @@ -//! Data types, functions and traits for the `sys` runtime. +//! Data types, functions and traits for the `v8` backend. pub(crate) mod bindings; pub(crate) mod entities; diff --git a/lib/api/src/backend/wamr/entities/instance.rs b/lib/api/src/backend/wamr/entities/instance.rs index c690553b6..30a61b39d 100644 --- a/lib/api/src/backend/wamr/entities/instance.rs +++ b/lib/api/src/backend/wamr/entities/instance.rs @@ -1,6 +1,7 @@ //! Data types, functions and traits for `wamr`'s `Instance` implementation. use std::sync::Arc; +use crate::entities::external::VMExternToExtern; use crate::{ backend::wamr::bindings::*, vm::VMExtern, wamr::error::Trap, AsStoreMut, AsStoreRef, Exports, Extern, Imports, InstantiationError, Module, @@ -39,17 +40,7 @@ impl InstanceHandle { std::mem::forget(externs); - let stack_size = 2 * 1024 * 1024; - let heap_size = 2 * 1024 * 1024; - - wasm_instance_new_with_args( - store, - module, - &mut imports, - &mut trap, - stack_size, - heap_size, - ) + wasm_instance_new(store, module, &mut imports, &mut trap) }; if instance.is_null() { @@ -61,29 +52,66 @@ impl InstanceHandle { } fn get_exports(&self, mut store: &mut impl AsStoreMut, module: &Module) -> Exports { - let mut exports = unsafe { + let mut c_api_externs = unsafe { let mut vec = Default::default(); wasm_instance_exports(self.0, &mut vec); vec }; - let wasm_exports: &[*mut wasm_extern_t] = - unsafe { std::slice::from_raw_parts(exports.data, exports.size) }; + let c_api_externs: Vec<*mut wasm_extern_t> = if c_api_externs.data.is_null() + || !c_api_externs.data.is_aligned() + || c_api_externs.size == 0 + { + vec![] + } else { + unsafe { std::slice::from_raw_parts(c_api_externs.data, c_api_externs.size) }.to_vec() + }; + let c_api_externs = c_api_externs.to_vec(); + let mut exports = unsafe { + let mut vec = Default::default(); + wasm_module_exports(module.as_wamr().handle.inner, &mut vec); + vec + }; - let exports_ty = module.exports().collect::>(); - let exports = exports_ty - .iter() - .zip(wasm_exports.into_iter()) - .map(|(export_type, wasm_export)| { - let name = export_type.name(); - let mut store = store.as_store_mut(); - let extern_type = export_type.ty(); - // Annotation is here to prevent spurious IDE warnings. + let c_api_exports: Vec<*mut wasm_exporttype_t> = + if exports.data.is_null() || !exports.data.is_aligned() || exports.size == 0 { + vec![] + } else { + unsafe { std::slice::from_raw_parts(exports.data, exports.size) }.to_vec() + }; - let extern_ = Extern::from_vm_extern(&mut store, VMExtern::Wamr(*wasm_export)); - (name.to_string(), extern_) + // We need to use the order of the exports from the polyfill info to get the right + // one, i.e. the from the declaration. + let module_exports = module.exports().collect::>(); + + let c_api_exports: Exports = c_api_exports + .into_iter() + .zip(c_api_externs.into_iter()) + .map(|(export, ext)| unsafe { + let name = wasm_exporttype_name(export); + let name = std::slice::from_raw_parts((*name).data as *const u8, (*name).size); + let mut name = name.to_vec(); + // Remove the '\0' at the end of the NULL-terminated string. + name.pop(); + let name = String::from_utf8(name.to_vec()).unwrap_or_default(); + let ext = ext.to_extern(&mut store); + (name, ext) }) - .collect::(); + .collect(); + + let mut exports = Exports::new(); + + for e in module_exports { + let ext: Extern = c_api_exports + .get::(e.name()) + .expect(&format!( + "c_api_exports: {c_api_exports:?} (name: {})", + e.name() + )) + .clone(); + exports.insert(e.name().to_string(), ext); + } + exports } } diff --git a/lib/api/src/backend/wamr/entities/memory/mod.rs b/lib/api/src/backend/wamr/entities/memory/mod.rs index 4257199c5..5419a2259 100644 --- a/lib/api/src/backend/wamr/entities/memory/mod.rs +++ b/lib/api/src/backend/wamr/entities/memory/mod.rs @@ -9,14 +9,7 @@ use wasmer_types::{MemoryType, Pages, WASM_PAGE_SIZE}; use crate::{ shared::SharedMemory, vm::{VMExtern, VMExternMemory}, - wamr::{ - bindings::{ - wasm_limits_max_default, wasm_limits_t, wasm_memory_as_extern, wasm_memory_copy, - wasm_memory_new, wasm_memory_type, wasm_memorytype_limits, wasm_memorytype_new, - wasm_memorytype_t, - }, - vm::VMMemory, - }, + wamr::{bindings::*, vm::VMMemory}, AsStoreMut, AsStoreRef, BackendMemory, MemoryAccessError, }; @@ -34,14 +27,28 @@ unsafe impl Sync for Memory {} impl Memory { pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { - let limits = Box::into_raw(Box::new(wasm_limits_t { - min: ty.minimum.0, - max: match ty.maximum { - Some(v) => v.0, - None => wasm_limits_max_default, - }, - })); + let max_requested = ty.maximum.unwrap_or(Pages::max_value()); + let min = ty.minimum.0; + let max = max_requested.0; + + if max < min { + return Err(MemoryError::InvalidMemory { + reason: format!("the maximum ({max} pages) is less than the minimum ({min} pages)",), + }); + } + + let max_allowed = Pages::max_value(); + if max_requested > max_allowed { + return Err(MemoryError::MaximumMemoryTooLarge { + max_requested, + max_allowed, + }); + } + + let limits = Box::into_raw(Box::new(wasm_limits_t { min, max })); + + eprintln!("limits: {limits:?}"); let memorytype = unsafe { wasm_memorytype_new(limits) }; let mut store = store.as_store_mut(); diff --git a/lib/api/src/backend/wamr/entities/module.rs b/lib/api/src/backend/wamr/entities/module.rs index b7dba79f4..0b12f36c5 100644 --- a/lib/api/src/backend/wamr/entities/module.rs +++ b/lib/api/src/backend/wamr/entities/module.rs @@ -1,12 +1,7 @@ //! Data types, functions and traits for `wamr`'s `Module` implementation. use std::{path::Path, sync::Arc}; -use crate::{ - backend::wamr::bindings::{ - wasm_byte_vec_t, wasm_module_delete, wasm_module_new, wasm_module_t, - }, - AsEngineRef, BackendModule, IntoBytes, -}; +use crate::{backend::wamr::bindings::*, AsEngineRef, BackendModule, IntoBytes}; use bytes::Bytes; use wasmer_types::{ @@ -97,8 +92,25 @@ impl Module { } pub fn validate(engine: &impl AsEngineRef, binary: &[u8]) -> Result<(), CompileError> { - let engine = engine.as_engine_ref(); - unimplemented!(); + let engine = engine.as_engine_ref().engine().clone(); + let store = super::store::Store::new(engine); + let bytes = wasm_byte_vec_t { + size: binary.len(), + data: binary.as_ptr() as _, + num_elems: binary.len(), + size_of_elem: 1, + lock: std::ptr::null_mut(), + }; + let store = store.inner; + unsafe { + if !wasm_module_validate(store, &bytes as *const _) { + return Err(CompileError::Validate(String::from( + "WAMR could not validate the given module", + ))); + } + } + + Ok(()) } pub fn name(&self) -> Option<&str> { diff --git a/lib/api/src/backend/wamr/mod.rs b/lib/api/src/backend/wamr/mod.rs index 1347849ee..9c1cec4ad 100644 --- a/lib/api/src/backend/wamr/mod.rs +++ b/lib/api/src/backend/wamr/mod.rs @@ -1,4 +1,4 @@ -//! Data types, functions and traits for `wamr`. +//! Data types, functions and traits for the `wamr` backend. pub(crate) mod bindings; pub(crate) mod entities; diff --git a/lib/api/src/backend/wasmi/entities/instance.rs b/lib/api/src/backend/wasmi/entities/instance.rs index 94c19acb3..5b3bcc14e 100644 --- a/lib/api/src/backend/wasmi/entities/instance.rs +++ b/lib/api/src/backend/wasmi/entities/instance.rs @@ -2,8 +2,9 @@ use std::sync::Arc; use crate::{ - backend::wasmi::bindings::*, vm::VMExtern, wasmi::error::Trap, AsStoreMut, AsStoreRef, Exports, - Extern, Imports, InstantiationError, Module, + backend::wasmi::bindings::*, entities::external::VMExternToExtern, vm::VMExtern, + wasmi::error::Trap, AsStoreMut, AsStoreRef, Exports, Extern, Imports, InstantiationError, + Module, }; #[derive(PartialEq, Eq)] @@ -51,29 +52,48 @@ impl InstanceHandle { } fn get_exports(&self, mut store: &mut impl AsStoreMut, module: &Module) -> Exports { - let mut exports = unsafe { + let mut c_api_externs = unsafe { let mut vec = Default::default(); wasm_instance_exports(self.0, &mut vec); vec }; - let wasm_exports: &[*mut wasm_extern_t] = + let c_api_externs: &[*mut wasm_extern_t] = + unsafe { std::slice::from_raw_parts(c_api_externs.data, c_api_externs.size) }; + let c_api_externs = c_api_externs.to_vec(); + let mut exports = unsafe { + let mut vec = Default::default(); + wasm_module_exports(module.as_wasmi().handle.inner, &mut vec); + vec + }; + + let c_api_exports: &[*mut wasm_exporttype_t] = unsafe { std::slice::from_raw_parts(exports.data, exports.size) }; + let c_api_exports = c_api_exports.to_vec(); - let exports_ty = module.exports().collect::>(); - let exports = exports_ty - .iter() - .zip(wasm_exports.into_iter()) - .map(|(export_type, wasm_export)| { - let name = export_type.name(); - let mut store = store.as_store_mut(); - let extern_type = export_type.ty(); - // Annotation is here to prevent spurious IDE warnings. + // We need to use the order of the exports from the polyfill info to get the right + // one, i.e. the from the declaration. + let module_exports = module.exports().collect::>(); - let extern_ = Extern::from_vm_extern(&mut store, VMExtern::Wasmi(*wasm_export)); - (name.to_string(), extern_) + let c_api_exports: Exports = c_api_exports + .into_iter() + .zip(c_api_externs.into_iter()) + .map(|(export, ext)| unsafe { + let name = wasm_exporttype_name(export); + let name = std::slice::from_raw_parts((*name).data as *const u8, (*name).size); + let name = String::from_utf8(name.to_vec()).unwrap_or_default(); + let ext = ext.to_extern(&mut store); + (name, ext) }) - .collect::(); + .collect(); + + let mut exports = Exports::new(); + + for e in module_exports { + let ext: Extern = c_api_exports.get::(e.name()).unwrap().clone(); + exports.insert(e.name().to_string(), ext); + } + exports } } diff --git a/lib/api/src/backend/wasmi/entities/memory/mod.rs b/lib/api/src/backend/wasmi/entities/memory/mod.rs index 7cf2571b8..b7c241a93 100644 --- a/lib/api/src/backend/wasmi/entities/memory/mod.rs +++ b/lib/api/src/backend/wasmi/entities/memory/mod.rs @@ -27,13 +27,26 @@ unsafe impl Sync for Memory {} impl Memory { pub fn new(store: &mut impl AsStoreMut, ty: MemoryType) -> Result { - let limits = Box::into_raw(Box::new(wasm_limits_t { - min: ty.minimum.0, - max: match ty.maximum { - Some(v) => v.0, - None => wasm_limits_max_default, - }, - })); + let max_requested = ty.maximum.unwrap_or(Pages::max_value()); + + let min = ty.minimum.0; + let max = max_requested.0; + + if max < min { + return Err(MemoryError::InvalidMemory { + reason: format!("the maximum ({max} pages) is less than the minimum ({min} pages)",), + }); + } + + let max_allowed = Pages::max_value(); + if max_requested > max_allowed { + return Err(MemoryError::MaximumMemoryTooLarge { + max_requested, + max_allowed, + }); + } + + let limits = Box::into_raw(Box::new(wasm_limits_t { min, max })); let memorytype = unsafe { wasm_memorytype_new(limits) }; diff --git a/lib/api/src/backend/wasmi/mod.rs b/lib/api/src/backend/wasmi/mod.rs index 3e37e77ce..0e2080b5e 100644 --- a/lib/api/src/backend/wasmi/mod.rs +++ b/lib/api/src/backend/wasmi/mod.rs @@ -1,4 +1,4 @@ -//! Data types, functions and traits for `wasmi`. +//! Data types, functions and traits for the `wasmi` backend. pub(crate) mod bindings; pub(crate) mod entities; diff --git a/lib/c-api/Cargo.toml b/lib/c-api/Cargo.toml index 84f76e2eb..571e780c9 100644 --- a/lib/c-api/Cargo.toml +++ b/lib/c-api/Cargo.toml @@ -66,43 +66,76 @@ wasmer-inline-c = "0.1.1" inline-c = "0.1.7" [features] -default = ["wat", "cranelift", "compiler", "wasi", "middlewares"] -sys = [] -jsc = ["wasmer-api/jsc", "wasmer-api/std"] +default = ["wat", "sys-default", "cranelift", "compiler", "wasi", "middlewares"] wat = ["wasmer-api/wat"] wasi = ["wasmer-wasix"] middlewares = ["compiler", "wasmer-middlewares"] + +# Enable the `sys` backend. +sys = [] + +# Enable the `singlepass` compiler. +singlepass = [ + "dep:wasmer-compiler-singlepass", + "wasmer-api/singlepass", + "compiler", +] +# Enable the `cranelift` compiler. +cranelift = [ + "dep:wasmer-compiler-cranelift", + "wasmer-api/cranelift", + "compiler", +] +# Enable the `llvm` compiler. +llvm = ["dep:wasmer-compiler-llvm", "wasmer-api/llvm", "compiler"] + +# Enable the use of compiler. Implies enabling the `sys` feature. compiler = [ + "sys", "wasmer-compiler", "wasmer-api/compiler", "wasmer-compiler/translator", "wasmer-compiler/compiler", ] + +# Enable the use of the headless compiler. Implies enabling the `sys` feature. compiler-headless = [ + "sys", "wasmer-artifact-load", "static-artifact-load", "wasmer-api/compiler", "wasmer-compiler/translator", "wasmer-compiler/compiler", ] -singlepass = ["wasmer-compiler-singlepass", "compiler"] -cranelift = ["wasmer-compiler-cranelift", "compiler"] -llvm = ["wasmer-compiler-llvm", "compiler"] -############ -# WARNING! # -############ -# These features are here but, for now (wasmer 6.0.0-alpha1) they do nothing. -# In the next alpha release they will be wired up to use the matching backend. -# These features are being added now to make CI easier, since we will -# nonetheless have to add them later. +# Enable the `jsc` backend. +jsc = ["wasmer-api/jsc", "wasmer-api/std"] -v8 = [] -wasmi = [] -wamr = [] +# Enable the `v8` backend. +v8 = ["wasmer-api/v8"] -############ -############ +# Enable the `wasmi` backend. +wasmi = ["wasmer-api/wasmi"] + +# Enable the `wamr` backend. +wamr = ["wasmer-api/wamr"] + +# Enable the `sys` backend and use it as default. Notice that this does not +# enable a default compiler, and users should therefore add one (or more) of +# the `singlepass`, `cranelift` and `llvm` features. +sys-default = ["sys", "wasmer-api/sys-default"] + +# Enable the `jsc` backend and use it as default. +jsc-default = ["jsc", "wasmer-api/jsc-default"] + +# Enable the `v8` backend and use it as default. +v8-default = ["v8", "wasmer-api/v8-default"] + +# Enable the `wamr` backend and use it as default. +wamr-default = ["wamr", "wasmer-api/wamr-default"] + +# Enable the `wasmi` backend and use it as default. +wasmi-default = ["wasmi", "wasmer-api/wasmi-default"] wasmer-artifact-load = ["wasmer-compiler/wasmer-artifact-load"] wasmer-artifact-create = ["wasmer-compiler/wasmer-artifact-create"] diff --git a/lib/c-api/src/lib.rs b/lib/c-api/src/lib.rs index 82a3fa12c..1b1179236 100644 --- a/lib/c-api/src/lib.rs +++ b/lib/c-api/src/lib.rs @@ -25,7 +25,10 @@ )] // Because this crate exposes a lot of C APIs which are unsafe by definition, // we allow unsafe without explicit safety documentation for each of them. +// +// For the same reason, we also turn off the warning for camel_case types. #![allow(clippy::missing_safety_doc)] +#![allow(non_camel_case_types)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] pub mod error; diff --git a/lib/c-api/src/wasm_c_api/engine.rs b/lib/c-api/src/wasm_c_api/engine.rs deleted file mode 100644 index e1186ea43..000000000 --- a/lib/c-api/src/wasm_c_api/engine.rs +++ /dev/null @@ -1,493 +0,0 @@ -pub use super::unstable::engine::wasm_config_set_features; -use super::unstable::features::wasmer_features_t; -use crate::error::update_last_error; -use cfg_if::cfg_if; - -#[cfg(feature = "compiler")] -pub use super::unstable::engine::{wasm_config_set_target, wasmer_is_compiler_available}; -#[cfg(any(feature = "compiler", feature = "compiler-headless"))] -use super::unstable::target_lexicon::wasmer_target_t; - -#[cfg(feature = "middlewares")] -pub use super::unstable::middlewares::wasm_config_push_middleware; -#[cfg(feature = "middlewares")] -use super::unstable::middlewares::wasmer_middleware_t; - -#[cfg(any(feature = "compiler", feature = "compiler-headless"))] -use wasmer_compiler::{Engine, EngineBuilder}; - -#[cfg(not(any(feature = "compiler", feature = "compiler-headless")))] -use wasmer_api::Engine; - -/// Kind of compilers that can be used by the engines. -/// -/// This is a Wasmer-specific type with Wasmer-specific functions for -/// manipulating it. -#[cfg(feature = "compiler")] -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub enum wasmer_compiler_t { - /// Variant to represent the Cranelift compiler. See the - /// [`wasmer_compiler_cranelift`] Rust crate. - CRANELIFT = 0, - - /// Variant to represent the LLVM compiler. See the - /// [`wasmer_compiler_llvm`] Rust crate. - LLVM = 1, - - /// Variant to represent the Singlepass compiler. See the - /// [`wasmer_compiler_singlepass`] Rust crate. - SINGLEPASS = 2, -} - -#[cfg(feature = "compiler")] -impl Default for wasmer_compiler_t { - fn default() -> Self { - cfg_if! { - if #[cfg(feature = "cranelift")] { - Self::CRANELIFT - } else if #[cfg(feature = "llvm")] { - Self::LLVM - } else if #[cfg(feature = "singlepass")] { - Self::SINGLEPASS - } else { - compile_error!("Please enable one of the compiler backends") - } - } - } -} - -/// Kind of engines that can be used by the store. -/// -/// This is a Wasmer-specific type with Wasmer-specific functions for -/// manipulating it. -#[derive(Debug, Copy, Clone)] -#[repr(C)] -#[allow(non_camel_case_types)] -pub enum wasmer_engine_t { - /// Variant to represent the Universal engine. See the - /// [`wasmer_engine_universal`] Rust crate. - UNIVERSAL = 0, -} - -impl Default for wasmer_engine_t { - fn default() -> Self { - Self::UNIVERSAL - } -} - -/// A configuration holds the compiler and the engine used by the store. -/// -/// cbindgen:ignore -#[derive(Debug, Default)] -#[repr(C)] -pub struct wasm_config_t { - engine: wasmer_engine_t, - #[cfg(feature = "compiler")] - compiler: wasmer_compiler_t, - #[cfg(feature = "middlewares")] - pub(super) middlewares: Vec, - pub(super) nan_canonicalization: bool, - pub(super) features: Option>, - #[cfg(any(feature = "compiler", feature = "compiler-headless"))] - pub(super) target: Option>, -} - -/// Create a new default Wasmer configuration. -/// -/// # Example -/// -/// ```rust -/// # use wasmer_inline_c::assert_c; -/// # fn main() { -/// # (assert_c! { -/// # #include "tests/wasmer.h" -/// # -/// int main() { -/// // Create the configuration. -/// wasm_config_t* config = wasm_config_new(); -/// -/// // Create the engine. -/// wasm_engine_t* engine = wasm_engine_new_with_config(config); -/// -/// // Check we have an engine! -/// assert(engine); -/// -/// // Free everything. -/// wasm_engine_delete(engine); -/// -/// return 0; -/// } -/// # }) -/// # .success(); -/// # } -/// ``` -/// -/// cbindgen:ignore -#[no_mangle] -pub extern "C" fn wasm_config_new() -> Box { - Box::::default() -} - -/// Delete a Wasmer config object. -/// -/// This function does not need to be called if `wasm_engine_new_with_config` or -/// another function that takes ownership of the `wasm_config_t` is called. -/// -/// # Example -/// -/// ```rust -/// # use wasmer_inline_c::assert_c; -/// # fn main() { -/// # (assert_c! { -/// # #include "tests/wasmer.h" -/// # -/// int main() { -/// // Create the configuration. -/// wasm_config_t* config = wasm_config_new(); -/// -/// // Delete the configuration -/// wasm_config_delete(config); -/// -/// return 0; -/// } -/// # }) -/// # .success(); -/// # } -/// ``` -/// cbindgen:ignore -#[no_mangle] -pub extern "C" fn wasm_config_delete(_config: Option>) {} - -/// Updates the configuration to specify a particular compiler to use. -/// -/// This is a Wasmer-specific function. -/// -/// # Example -/// -/// ```rust -/// # use wasmer_inline_c::assert_c; -/// # fn main() { -/// # (assert_c! { -/// # #include "tests/wasmer.h" -/// # -/// int main() { -/// // Create the configuration. -/// wasm_config_t* config = wasm_config_new(); -/// -/// // Use the Cranelift compiler, if available. -/// if (wasmer_is_compiler_available(CRANELIFT)) { -/// wasm_config_set_compiler(config, CRANELIFT); -/// } -/// // Or maybe LLVM? -/// else if (wasmer_is_compiler_available(LLVM)) { -/// wasm_config_set_compiler(config, LLVM); -/// } -/// // Or maybe Singlepass? -/// else if (wasmer_is_compiler_available(SINGLEPASS)) { -/// wasm_config_set_compiler(config, SINGLEPASS); -/// } -/// // OK, let's run with no particular compiler. -/// -/// // Create the engine. -/// wasm_engine_t* engine = wasm_engine_new_with_config(config); -/// -/// // Check we have an engine! -/// assert(engine); -/// -/// // Free everything. -/// wasm_engine_delete(engine); -/// -/// return 0; -/// } -/// # }) -/// # .success(); -/// # } -/// ``` -#[cfg(feature = "compiler")] -#[no_mangle] -pub extern "C" fn wasm_config_set_compiler( - config: &mut wasm_config_t, - compiler: wasmer_compiler_t, -) { - config.compiler = compiler; -} - -/// Updates the configuration to specify a particular engine to use. -/// -/// This is a Wasmer-specific function. -/// -/// # Example -/// -/// ```rust -/// # use wasmer_inline_c::assert_c; -/// # fn main() { -/// # (assert_c! { -/// # #include "tests/wasmer.h" -/// # -/// int main() { -/// // Create the configuration. -/// wasm_config_t* config = wasm_config_new(); -/// -/// // Create the engine. -/// wasm_engine_t* engine = wasm_engine_new_with_config(config); -/// -/// // Check we have an engine! -/// assert(engine); -/// -/// // Free everything. -/// wasm_engine_delete(engine); -/// -/// return 0; -/// } -/// # }) -/// # .success(); -/// # } -/// ``` -#[no_mangle] -pub extern "C" fn wasm_config_set_engine(config: &mut wasm_config_t, engine: wasmer_engine_t) { - config.engine = engine; -} - -/// An engine is used by the store to drive the compilation and the -/// execution of a WebAssembly module. -/// -/// cbindgen:ignore -#[repr(C)] -pub struct wasm_engine_t { - pub(crate) inner: Engine, -} - -#[cfg(feature = "compiler")] -use wasmer_api::sys::CompilerConfig; - -#[cfg(feature = "compiler")] -fn get_default_compiler_config() -> Box { - cfg_if! { - if #[cfg(feature = "cranelift")] { - Box::::default() - } else if #[cfg(feature = "llvm")] { - Box::::default() - } else if #[cfg(feature = "singlepass")] { - Box::::default() - } else { - compile_error!("Please enable one of the compiler backends") - } - } -} - -cfg_if! { - if #[cfg(feature = "compiler")] { - /// Creates a new Universal engine with the default compiler. - /// - /// # Example - /// - /// See [`wasm_engine_delete`]. - /// - /// cbindgen:ignore - #[no_mangle] - pub extern "C" fn wasm_engine_new() -> Box { - let compiler_config: Box = get_default_compiler_config(); - let engine: Engine = EngineBuilder::new(compiler_config).engine(); - Box::new(wasm_engine_t { inner: engine }) - } - } else if #[cfg(feature = "compiler-headless")] { - /// Creates a new headless Universal engine. - /// - /// # Example - /// - /// See [`wasm_engine_delete`]. - /// - /// cbindgen:ignore - #[no_mangle] - pub extern "C" fn wasm_engine_new() -> Box { - let engine: Engine = EngineBuilder::headless().engine(); - Box::new(wasm_engine_t { inner: engine }) - } - } else if #[cfg(feature = "jsc")] { - /// Creates the JavascriptCore Engine. - /// - /// # Example - /// - /// See [`wasm_engine_delete`]. - /// - /// cbindgen:ignore - #[no_mangle] - pub extern "C" fn wasm_engine_new() -> Box { - let engine: Engine = Engine::default(); - Box::new(wasm_engine_t { inner: engine }) - } - } else { - /// Creates a new unknown engine, i.e. it will panic with an error message. - /// - /// # Example - /// - /// See [`wasm_engine_delete`]. - /// - /// cbindgen:ignore - #[no_mangle] - pub extern "C" fn wasm_engine_new() -> Box { - unimplemented!("No engine attached; You might want to recompile `wasmer_c_api` with for example `--feature compiler`"); - } - } -} - -/// Deletes an engine. -/// -/// # Example -/// -/// ```rust -/// # use wasmer_inline_c::assert_c; -/// # fn main() { -/// # (assert_c! { -/// # #include "tests/wasmer.h" -/// # -/// int main() { -/// // Create a default engine. -/// wasm_engine_t* engine = wasm_engine_new(); -/// -/// // Check we have an engine! -/// assert(engine); -/// -/// // Free everything. -/// wasm_engine_delete(engine); -/// -/// return 0; -/// } -/// # }) -/// # .success(); -/// # } -/// ``` -/// -/// cbindgen:ignore -#[no_mangle] -pub unsafe extern "C" fn wasm_engine_delete(_engine: Option>) {} - -/// Creates an engine with a particular configuration. -/// -/// # Example -/// -/// See [`wasm_config_new`]. -/// -/// cbindgen:ignore -#[no_mangle] -pub extern "C" fn wasm_engine_new_with_config( - config: Option>, -) -> Option> { - #[allow(dead_code)] - fn return_with_error(msg: &str) -> Option> { - update_last_error(msg); - None - } - - #[allow(unused)] - let config = config?; - //#[cfg(not(any(feature = "compiler", feature = "compiler-headless")))] - //return return_with_error("Wasmer has not been compiled with the `compiler` feature."); - - cfg_if! { - if #[cfg(feature = "compiler")] { - #[allow(unused_mut)] - let mut compiler_config: Box = match config.compiler { - wasmer_compiler_t::CRANELIFT => { - cfg_if! { - if #[cfg(feature = "cranelift")] { - Box::::default() - } else { - return return_with_error("Wasmer has not been compiled with the `cranelift` feature."); - } - } - }, - wasmer_compiler_t::LLVM => { - cfg_if! { - if #[cfg(feature = "llvm")] { - Box::::default() - } else { - return return_with_error("Wasmer has not been compiled with the `llvm` feature."); - } - } - }, - wasmer_compiler_t::SINGLEPASS => { - cfg_if! { - if #[cfg(feature = "singlepass")] { - Box::::default() - } else { - return return_with_error("Wasmer has not been compiled with the `singlepass` feature."); - } - } - }, - }; - - #[cfg(feature = "middlewares")] - for middleware in config.middlewares { - compiler_config.push_middleware(middleware.inner); - } - - if config.nan_canonicalization { - compiler_config.canonicalize_nans(true); - } - - let inner: Engine = - { - let mut builder = EngineBuilder::new(compiler_config); - - if let Some(target) = config.target { - builder = builder.set_target(Some(target.inner)); - } - - if let Some(features) = config.features { - builder = builder.set_features(Some(features.inner)); - } - - builder.engine() - }; - Some(Box::new(wasm_engine_t { inner })) - } else { - #[cfg(feature = "compiler-headless")] - let inner: Engine = - { - let mut builder = EngineBuilder::headless(); - - if let Some(target) = config.target { - builder = builder.set_target(Some(target.inner)); - } - - if let Some(features) = config.features { - builder = builder.set_features(Some(features.inner)); - } - - builder.engine() - }; - #[cfg(not(any(feature = "compiler-headless", feature="compiler")))] - let inner: Engine = Engine::default(); - - Some(Box::new(wasm_engine_t { inner })) - } - } -} - -#[cfg(test)] -mod tests { - #[cfg(not(target_os = "windows"))] - use inline_c::assert_c; - #[cfg(target_os = "windows")] - use wasmer_inline_c::assert_c; - - #[cfg_attr(coverage, ignore)] - #[test] - fn test_engine_new() { - (assert_c! { - #include "tests/wasmer.h" - - int main() { - wasm_engine_t* engine = wasm_engine_new(); - assert(engine); - - wasm_engine_delete(engine); - - return 0; - } - }) - .success(); - } -} diff --git a/lib/c-api/src/wasm_c_api/engine/config/jsc.rs b/lib/c-api/src/wasm_c_api/engine/config/jsc.rs new file mode 100644 index 000000000..608207b40 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/engine/config/jsc.rs @@ -0,0 +1,38 @@ +use crate::{ + error::update_last_error, + wasm_c_api::engine::{wasm_config_t, wasm_engine_t, wasmer_backend_t}, +}; + +use super::wasmer_backend_config_kind_t; + +/// Configuration specific for the `jsc` engine. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for +/// manipulating it. +/// +/// cbindgen:ignore +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct wasmer_jsc_engine_config_t; + +/// Create a new [`wasm_engine_t`] backed by a `jsc` engine. +pub(crate) fn wasm_jsc_engine_new_with_config(config: wasm_config_t) -> Option> { + if !matches!(config.backend, wasmer_backend_t::JSC) || !config.backend_config.inner.is_jsc() { + update_last_error("Cannot create a new `jsc` engine with a non-jsc-specific config!"); + return None; + } + + Some(Box::new(wasm_engine_t { + inner: wasmer_api::jsc::Engine::default().into(), + })) +} + +impl wasmer_backend_config_kind_t { + /// Returns `true` if the wasmer_engine_config_t is [`Jsc`]. + /// + /// [`Jsc`]: wasmer_engine_config_t::Jsc + #[must_use] + pub(super) fn is_jsc(&self) -> bool { + matches!(self, Self::Jsc(..)) + } +} diff --git a/lib/c-api/src/wasm_c_api/engine/config/mod.rs b/lib/c-api/src/wasm_c_api/engine/config/mod.rs new file mode 100644 index 000000000..33fc3dc49 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/engine/config/mod.rs @@ -0,0 +1,145 @@ +#[cfg(feature = "sys")] +pub(crate) mod sys; +#[cfg(feature = "sys")] +pub use sys::*; + +use crate::wasm_c_api::unstable::target_lexicon::wasmer_target_t; + +use super::{wasm_config_t, wasmer_backend_t}; + +#[cfg(feature = "jsc")] +pub(crate) mod jsc; + +#[cfg(feature = "v8")] +pub(crate) mod v8; + +#[cfg(feature = "wasmi")] +pub(crate) mod wasmi; + +#[cfg(feature = "wamr")] +pub(crate) mod wamr; + +#[repr(C)] +#[derive(Debug)] +pub(crate) enum wasmer_backend_config_kind_t { + #[cfg(feature = "sys")] + Sys(sys::wasmer_sys_engine_config_t), + + #[cfg(feature = "jsc")] + Jsc(jsc::wasmer_jsc_engine_config_t), + + #[cfg(feature = "v8")] + V8(v8::wasmer_v8_engine_config_t), + + #[cfg(feature = "wasmi")] + Wasmi(wasmi::wasmer_wasmi_engine_config_t), + + #[cfg(feature = "wamr")] + Wamr(wamr::wasmer_wamr_engine_config_t), +} + +impl Default for wasmer_backend_config_kind_t { + fn default() -> Self { + match wasmer_backend_t::default() { + #[cfg(feature = "llvm")] + super::wasmer_backend_t::LLVM => Self::Sys(sys::wasmer_sys_engine_config_t::default()), + #[cfg(feature = "cranelift")] + super::wasmer_backend_t::CRANELIFT => { + Self::Sys(sys::wasmer_sys_engine_config_t::default()) + } + #[cfg(feature = "singlepass")] + super::wasmer_backend_t::SINGLEPASS => { + Self::Sys(sys::wasmer_sys_engine_config_t::default()) + } + #[cfg(feature = "sys")] + super::wasmer_backend_t::HEADLESS => { + Self::Sys(sys::wasmer_sys_engine_config_t::default()) + } + #[cfg(feature = "v8")] + super::wasmer_backend_t::V8 => Self::V8(v8::wasmer_v8_engine_config_t::default()), + #[cfg(feature = "wasmi")] + super::wasmer_backend_t::WASMI => { + Self::Wasmi(wasmi::wasmer_wasmi_engine_config_t::default()) + } + #[cfg(feature = "wamr")] + super::wasmer_backend_t::WAMR => { + Self::Wamr(wamr::wasmer_wamr_engine_config_t::default()) + } + #[cfg(feature = "jsc")] + super::wasmer_backend_t::JSC => Self::Jsc(jsc::wasmer_jsc_engine_config_t::default()), + + _ => unreachable!(), + } + } +} + +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct wasmer_backend_config_t { + pub inner: wasmer_backend_config_kind_t, + pub target: Option>, + #[cfg(feature = "middlewares")] + pub middlewares: Vec, +} + +/// Unstable non-standard Wasmer-specific API to update the +/// configuration to specify a particular target for the engine. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Set the target. +/// { +/// wasmer_triple_t* triple = wasmer_triple_new_from_host(); +/// wasmer_cpu_features_t* cpu_features = wasmer_cpu_features_new(); +/// wasmer_target_t* target = wasmer_target_new(triple, cpu_features); +/// +/// wasm_config_sys_set_target(config, target); +/// } +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_set_target(config: &mut wasm_config_t, target: Box) { + config.backend_config.target = Some(target); +} + +/// Updates the configuration to add a module middleware. +/// +/// This function takes ownership of `middleware`. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// See the documentation of the [`metering`] module. +#[no_mangle] +#[cfg(feature = "middlewares")] +pub extern "C" fn wasm_config_push_middleware( + config: &mut wasm_config_t, + middleware: Box, +) { + config.backend_config.middlewares.push(*middleware) +} diff --git a/lib/c-api/src/wasm_c_api/engine/config/sys.rs b/lib/c-api/src/wasm_c_api/engine/config/sys.rs new file mode 100644 index 000000000..61d40f7a2 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/engine/config/sys.rs @@ -0,0 +1,205 @@ +// Necessary when a single backend is enabled. +#![allow(irrefutable_let_patterns)] + +use crate::{ + error::update_last_error, + wasm_c_api::engine::{ + wasm_config_t, wasm_engine_t, wasmer_backend_config_kind_t, wasmer_backend_t, + }, +}; + +#[cfg(feature = "middlewares")] +pub use crate::wasm_c_api::unstable::middlewares::wasmer_middleware_t; + +use cfg_if::cfg_if; +use wasmer_api::Engine; +use wasmer_compiler::EngineBuilder; + +#[cfg(feature = "compiler")] +use wasmer_api::sys::CompilerConfig; + +/// Configuration specific for `sys` (Cranelift, LLVM, Singlepass) engines. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for manipulating it. +/// +/// cbindgen:ignore +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct wasmer_sys_engine_config_t { + pub nan_canonicalization: bool, +} + +/// Updates the configuration to enable NaN canonicalization. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Enable NaN canonicalization. +/// wasm_config_sys_canonicalize_nans(config, true); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_sys_canonicalize_nans(config: &mut wasm_config_t, enable: bool) { + if let wasmer_backend_config_kind_t::Sys(ref mut c) = config.backend_config.inner { + c.nan_canonicalization = enable; + } else { + let sys_config = wasmer_sys_engine_config_t { + nan_canonicalization: enable, + }; + config.backend_config.inner = wasmer_backend_config_kind_t::Sys(sys_config); + } +} + +/// Create a new [`wasm_engine_t`] backed by a `sys` engine. +pub fn wasm_sys_engine_new_with_config(config: wasm_config_t) -> Option> { + if !matches!(config.backend, wasmer_backend_t::LLVM) + && !matches!(config.backend, wasmer_backend_t::SINGLEPASS) + && !matches!(config.backend, wasmer_backend_t::CRANELIFT) + && !matches!(config.backend, wasmer_backend_t::HEADLESS) + && !config.backend_config.inner.is_sys() + { + update_last_error(format!( + "Cannot create a new `sys` engine with a non-sys-specific config! {config:?}" + )); + return None; + } + + let backend = config.backend; + #[allow(unused)] + let sys_config = match config.backend_config.inner.try_into_sys() { + Err(_) => { + update_last_error(format!( + "Could not create new `sys` engine with the selected {backend:?} backend." + )); + return None; + } + Ok(s) => s, + }; + + cfg_if! { + if #[cfg(feature = "compiler")] { + #[allow(unused_mut)] + let mut compiler_config: Box = match &backend { + wasmer_backend_t::CRANELIFT => { + cfg_if! { + if #[cfg(feature = "cranelift")] { + Box::::default() + } else { + update_last_error("Wasmer has not been compiled with the `cranelift` feature."); + return None; + } + } + }, + wasmer_backend_t::LLVM => { + cfg_if! { + if #[cfg(feature = "llvm")] { + Box::::default() + } else { + update_last_error("Wasmer has not been compiled with the `llvm` feature."); + return None; + } + } + }, + wasmer_backend_t::SINGLEPASS => { + cfg_if! { + if #[cfg(feature = "singlepass")] { + Box::::default() + } else { + update_last_error("Wasmer has not been compiled with the `singlepass` feature."); + return None; + } + } + }, + _ => panic!("not a `sys` backend!") + }; + + #[cfg(feature = "middlewares")] + for middleware in config.backend_config.middlewares { + compiler_config.push_middleware(middleware.inner.clone()); + } + + if sys_config.nan_canonicalization { + compiler_config.canonicalize_nans(true); + } + + let inner: Engine = + { + let mut builder = EngineBuilder::new(compiler_config); + + if let Some(target) = config.backend_config.target { + builder = builder.set_target(Some(target.inner)); + } + + if let Some(features) = config.features { + builder = builder.set_features(Some(features.inner)); + } + + builder.engine().into() + }; + Some(Box::new(wasm_engine_t { inner })) + } else if #[cfg(feature = "compiler-headless")] { + let inner: Engine = + { + let mut builder = EngineBuilder::headless(); + + if let Some(target) = config.backend_config.target { + builder = builder.set_target(Some(target.inner)); + } + + if let Some(features) = config.features { + builder = builder.set_features(Some(features.inner)); + } + + builder.engine().into() + }; + + Some(Box::new(wasm_engine_t { inner })) + } else { + update_last_error("No backend enabled for the `sys` engine: enable one of `compiler` or `compiler-headless`!"); + return None; + } + } +} + +impl wasmer_backend_config_kind_t { + /// Returns `true` if the wasmer_engine_config_t is [`Sys`]. + /// + /// [`Sys`]: wasmer_engine_config_t::Sys + #[must_use] + pub(super) fn is_sys(&self) -> bool { + matches!(self, Self::Sys(..)) + } + + pub(super) fn try_into_sys(self) -> Result { + if let Self::Sys(v) = self { + Ok(v) + } else { + Err(self) + } + } +} diff --git a/lib/c-api/src/wasm_c_api/engine/config/v8.rs b/lib/c-api/src/wasm_c_api/engine/config/v8.rs new file mode 100644 index 000000000..8be52c994 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/engine/config/v8.rs @@ -0,0 +1,38 @@ +use crate::{ + error::update_last_error, + wasm_c_api::engine::{wasm_config_t, wasm_engine_t, wasmer_backend_t}, +}; + +use super::wasmer_backend_config_kind_t; + +/// Configuration specific for the `v8` engine. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for +/// manipulating it. +/// +/// cbindgen:ignore +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct wasmer_v8_engine_config_t; + +/// Create a new [`wasm_engine_t`] backed by a `v8` engine. +pub(crate) fn wasm_v8_engine_new_with_config(config: wasm_config_t) -> Option> { + if !matches!(config.backend, wasmer_backend_t::V8) || !config.backend_config.inner.is_v8() { + update_last_error("Cannot create a new `v8` engine with a non-v8-specific config!"); + return None; + } + + Some(Box::new(wasm_engine_t { + inner: wasmer_api::v8::V8::default().into(), + })) +} + +impl wasmer_backend_config_kind_t { + /// Returns `true` if the wasmer_engine_config_t is [`V8`]. + /// + /// [`V8`]: wasmer_engine_config_t::V8 + #[must_use] + pub(super) fn is_v8(&self) -> bool { + matches!(self, Self::V8(..)) + } +} diff --git a/lib/c-api/src/wasm_c_api/engine/config/wamr.rs b/lib/c-api/src/wasm_c_api/engine/config/wamr.rs new file mode 100644 index 000000000..d4c14d868 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/engine/config/wamr.rs @@ -0,0 +1,40 @@ +use crate::{ + error::update_last_error, + wasm_c_api::engine::{wasm_config_t, wasm_engine_t, wasmer_backend_t}, +}; + +use super::wasmer_backend_config_kind_t; + +/// Configuration specific for the `wamr` engine. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for +/// manipulating it. +/// +/// cbindgen:ignore +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct wasmer_wamr_engine_config_t; + +/// Create a new [`wasm_engine_t`] backed by a `wamr` engine. +pub(crate) fn wasm_wamr_engine_new_with_config( + config: wasm_config_t, +) -> Option> { + if !matches!(config.backend, wasmer_backend_t::WAMR) || !config.backend_config.inner.is_wamr() { + update_last_error("Cannot create a new `wamr` engine with a non-wamr-specific config!"); + return None; + } + + Some(Box::new(wasm_engine_t { + inner: wasmer_api::wamr::Wamr::default().into(), + })) +} + +impl wasmer_backend_config_kind_t { + /// Returns `true` if the wasmer_engine_config_t is [`WAMR`]. + /// + /// [`WAMR`]: wasmer_engine_config_t::WAMR + #[must_use] + pub(super) fn is_wamr(&self) -> bool { + matches!(self, Self::Wamr(..)) + } +} diff --git a/lib/c-api/src/wasm_c_api/engine/config/wasmi.rs b/lib/c-api/src/wasm_c_api/engine/config/wasmi.rs new file mode 100644 index 000000000..c0b8da824 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/engine/config/wasmi.rs @@ -0,0 +1,41 @@ +use crate::{ + error::update_last_error, + wasm_c_api::engine::{wasm_config_t, wasm_engine_t, wasmer_backend_t}, +}; + +use super::wasmer_backend_config_kind_t; + +/// Configuration specific for the `wasmi` engine. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for +/// manipulating it. +/// +/// cbindgen:ignore +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct wasmer_wasmi_engine_config_t; + +/// Create a new [`wasm_engine_t`] backed by a `wasmi` engine. +pub(crate) fn wasm_wasmi_engine_new_with_config( + config: wasm_config_t, +) -> Option> { + if !matches!(config.backend, wasmer_backend_t::WASMI) || !config.backend_config.inner.is_wasmi() + { + update_last_error("Cannot create a new `wasmi` engine with a non-wasmi-specific config!"); + return None; + } + + Some(Box::new(wasm_engine_t { + inner: wasmer_api::wasmi::Wasmi::default().into(), + })) +} + +impl wasmer_backend_config_kind_t { + /// Returns `true` if the wasmer_engine_config_t is [`WASMI`]. + /// + /// [`WASMI`]: wasmer_engine_config_t::WASMI + #[must_use] + pub(super) fn is_wasmi(&self) -> bool { + matches!(self, Self::Wasmi(..)) + } +} diff --git a/lib/c-api/src/wasm_c_api/engine/mod.rs b/lib/c-api/src/wasm_c_api/engine/mod.rs new file mode 100644 index 000000000..d61638100 --- /dev/null +++ b/lib/c-api/src/wasm_c_api/engine/mod.rs @@ -0,0 +1,306 @@ +pub use super::unstable::engine::wasm_config_set_features; +use super::unstable::features::wasmer_features_t; + +use wasmer_api::{BackendKind, Engine}; + +mod config; +#[allow(unused_imports)] +pub use config::*; + +/// Kind of engines that can be used by the store. +/// +/// This is a Wasmer-specific type with Wasmer-specific functions for +/// manipulating it. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +#[allow(non_camel_case_types)] +pub enum wasmer_backend_t { + /// The cranelift backend. + CRANELIFT = 0, + + /// The LLVM backend. + LLVM, + + /// The singlepass backend. + SINGLEPASS, + + /// The headless backend. + HEADLESS, + + /// The V8 backend. + V8, + + /// The WASMI backend. + WASMI, + + /// The WAMR backend. + WAMR, + + /// The JSC backend. + JSC, +} + +impl From for wasmer_backend_t { + fn from(value: BackendKind) -> Self { + match value { + #[cfg(feature = "cranelift")] + BackendKind::Cranelift => Self::CRANELIFT, + #[cfg(feature = "llvm")] + BackendKind::LLVM => Self::LLVM, + #[cfg(feature = "singlepass")] + BackendKind::Singlepass => Self::SINGLEPASS, + #[cfg(feature = "sys")] + BackendKind::Headless => Self::HEADLESS, + #[cfg(feature = "wamr")] + BackendKind::Wamr => Self::WAMR, + #[cfg(feature = "wasmi")] + BackendKind::Wasmi => Self::WASMI, + #[cfg(feature = "v8")] + BackendKind::V8 => Self::V8, + #[cfg(feature = "jsc")] + BackendKind::Jsc => Self::JSC, + v => panic!("Unsupported backend kind {v:?}"), + } + } +} + +impl Default for wasmer_backend_t { + fn default() -> Self { + // Let the `wasmer_api` crate decide which is the default engine, given the enabled + // features. + wasmer_api::BackendKind::default().into() + } +} + +/// An engine is used by the store to drive the compilation and the +/// execution of a WebAssembly module. +/// +/// cbindgen:ignore +#[repr(C)] +// We can let the API decide which engine is default with the given set of +// features. +// +// See the impl of `Default` for `BackendEngine` in the `wasmer` (API) crate. +#[derive(Default)] +pub struct wasm_engine_t { + pub(crate) inner: Engine, +} + +#[no_mangle] +#[allow(unreachable_code)] +pub extern "C" fn wasm_engine_new() -> Box { + Box::new(wasm_engine_t::default()) +} + +/// Deletes an engine. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create a default engine. +/// wasm_engine_t* engine = wasm_engine_new(); +/// int error_length = wasmer_last_error_length(); +/// if (error_length > 0) { +/// char *error_message = malloc(error_length); +/// wasmer_last_error_message(error_message, error_length); +/// +/// printf("Attempted to set an immutable global: `%s`\n", error_message); +/// free(error_message); +/// } +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// cbindgen:ignore +#[no_mangle] +pub unsafe extern "C" fn wasm_engine_delete(_engine: Option>) {} + +/// Creates an engine with a particular configuration. +/// +/// # Example +/// +/// See [`wasm_config_new`]. +/// +/// cbindgen:ignore +#[no_mangle] +pub extern "C" fn wasm_engine_new_with_config( + config: Option>, +) -> Option> { + #[allow(unused)] + let config = *(config?); + + match config.backend { + #[cfg(feature = "llvm")] + wasmer_backend_t::LLVM => config::sys::wasm_sys_engine_new_with_config(config), + #[cfg(feature = "cranelift")] + wasmer_backend_t::CRANELIFT => config::sys::wasm_sys_engine_new_with_config(config), + #[cfg(feature = "singlepass")] + wasmer_backend_t::SINGLEPASS => config::sys::wasm_sys_engine_new_with_config(config), + #[cfg(feature = "sys")] + wasmer_backend_t::HEADLESS => config::sys::wasm_sys_engine_new_with_config(config), + #[cfg(feature = "v8")] + wasmer_backend_t::V8 => config::v8::wasm_v8_engine_new_with_config(config), + #[cfg(feature = "wasmi")] + wasmer_backend_t::WASMI => config::wasmi::wasm_wasmi_engine_new_with_config(config), + #[cfg(feature = "wamr")] + wasmer_backend_t::WAMR => config::wamr::wasm_wamr_engine_new_with_config(config), + #[cfg(feature = "jsc")] + wasmer_backend_t::JSC => config::jsc::wasm_jsc_engine_new_with_config(config), + _ => unreachable!(), + } +} + +/// A configuration holds the compiler and the engine used by the store. +/// +/// cbindgen:ignore +#[derive(Debug, Default)] +#[repr(C)] +pub struct wasm_config_t { + pub(super) backend: wasmer_backend_t, + pub(super) backend_config: wasmer_backend_config_t, + pub(super) features: Option>, +} + +/// Create a new default Wasmer configuration. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// +/// cbindgen:ignore +#[no_mangle] +pub extern "C" fn wasm_config_new() -> Box { + Box::::default() +} + +/// Delete a Wasmer config object. +/// +/// This function does not need to be called if `wasm_engine_new_with_config` or +/// another function that takes ownership of the `wasm_config_t` is called. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Delete the configuration +/// wasm_config_delete(config); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +/// cbindgen:ignore +#[no_mangle] +pub extern "C" fn wasm_config_delete(_config: Option>) {} + +/// Updates the configuration to specify a particular engine to use. +/// +/// This is a Wasmer-specific function. +/// +/// # Example +/// +/// ```rust +/// # use wasmer_inline_c::assert_c; +/// # fn main() { +/// # (assert_c! { +/// # #include "tests/wasmer.h" +/// # +/// int main() { +/// // Create the configuration. +/// wasm_config_t* config = wasm_config_new(); +/// +/// // Create the engine. +/// wasm_engine_t* engine = wasm_engine_new_with_config(config); +/// +/// // Check we have an engine! +/// assert(engine); +/// +/// // Free everything. +/// wasm_engine_delete(engine); +/// +/// return 0; +/// } +/// # }) +/// # .success(); +/// # } +/// ``` +#[no_mangle] +pub extern "C" fn wasm_config_set_backend(config: &mut wasm_config_t, engine: wasmer_backend_t) { + config.backend = engine; +} + +#[cfg(test)] +mod tests { + #[cfg(not(target_os = "windows"))] + use inline_c::assert_c; + #[cfg(target_os = "windows")] + use wasmer_inline_c::assert_c; + + #[cfg_attr(coverage, ignore)] + #[test] + fn test_engine_new() { + (assert_c! { + #include "tests/wasmer.h" + + int main() { + wasm_engine_t* engine = wasm_engine_new(); + assert(engine); + + wasm_engine_delete(engine); + + return 0; + } + }) + .success(); + } +} diff --git a/lib/c-api/src/wasm_c_api/unstable/engine.rs b/lib/c-api/src/wasm_c_api/unstable/engine.rs index d4e4ab511..6366e3978 100644 --- a/lib/c-api/src/wasm_c_api/unstable/engine.rs +++ b/lib/c-api/src/wasm_c_api/unstable/engine.rs @@ -2,58 +2,10 @@ //! `wasm_engine_t` and siblings. use super::{ - super::engine::{wasm_config_t, wasmer_engine_t}, + super::engine::{wasm_config_t, wasmer_backend_t}, features::wasmer_features_t, }; -#[cfg(feature = "compiler")] -use super::{super::engine::wasmer_compiler_t, target_lexicon::wasmer_target_t}; - -/// Unstable non-standard Wasmer-specific API to update the -/// configuration to specify a particular target for the engine. -/// -/// # Example -/// -/// ```rust -/// # use wasmer_inline_c::assert_c; -/// # fn main() { -/// # (assert_c! { -/// # #include "tests/wasmer.h" -/// # -/// int main() { -/// // Create the configuration. -/// wasm_config_t* config = wasm_config_new(); -/// -/// // Set the target. -/// { -/// wasmer_triple_t* triple = wasmer_triple_new_from_host(); -/// wasmer_cpu_features_t* cpu_features = wasmer_cpu_features_new(); -/// wasmer_target_t* target = wasmer_target_new(triple, cpu_features); -/// -/// wasm_config_set_target(config, target); -/// } -/// -/// // Create the engine. -/// wasm_engine_t* engine = wasm_engine_new_with_config(config); -/// -/// // Check we have an engine! -/// assert(engine); -/// -/// // Free everything. -/// wasm_engine_delete(engine); -/// -/// return 0; -/// } -/// # }) -/// # .success(); -/// # } -/// ``` -#[no_mangle] -#[cfg(feature = "compiler")] -pub extern "C" fn wasm_config_set_target(config: &mut wasm_config_t, target: Box) { - config.target = Some(target); -} - /// Unstable non-standard Wasmer-specific API to update the /// configuration to specify particular features for the engine. /// @@ -100,58 +52,6 @@ pub extern "C" fn wasm_config_set_features( config.features = Some(features); } -/// Updates the configuration to enable NaN canonicalization. -/// -/// This is a Wasmer-specific function. -/// -/// # Example -/// -/// ```rust -/// # use wasmer_inline_c::assert_c; -/// # fn main() { -/// # (assert_c! { -/// # #include "tests/wasmer.h" -/// # -/// int main() { -/// // Create the configuration. -/// wasm_config_t* config = wasm_config_new(); -/// -/// // Enable NaN canonicalization. -/// wasm_config_canonicalize_nans(config, true); -/// -/// // Create the engine. -/// wasm_engine_t* engine = wasm_engine_new_with_config(config); -/// -/// // Check we have an engine! -/// assert(engine); -/// -/// // Free everything. -/// wasm_engine_delete(engine); -/// -/// return 0; -/// } -/// # }) -/// # .success(); -/// # } -/// ``` -#[no_mangle] -pub extern "C" fn wasm_config_canonicalize_nans(config: &mut wasm_config_t, enable: bool) { - config.nan_canonicalization = enable; -} - -/// Check whether the given compiler is available, i.e. part of this -/// compiled library. -#[no_mangle] -#[cfg(feature = "compiler")] -pub extern "C" fn wasmer_is_compiler_available(compiler: wasmer_compiler_t) -> bool { - match compiler { - wasmer_compiler_t::CRANELIFT if cfg!(feature = "cranelift") => true, - wasmer_compiler_t::LLVM if cfg!(feature = "llvm") => true, - wasmer_compiler_t::SINGLEPASS if cfg!(feature = "singlepass") => true, - _ => false, - } -} - /// Check whether there is no compiler available in this compiled /// library. #[no_mangle] @@ -159,11 +59,20 @@ pub extern "C" fn wasmer_is_headless() -> bool { !cfg!(feature = "compiler") } -/// Check whether the given engine is available, i.e. part of this +/// Check whether the given backend is available, i.e. part of this /// compiled library. #[no_mangle] -pub extern "C" fn wasmer_is_engine_available(engine: wasmer_engine_t) -> bool { - matches!(engine, wasmer_engine_t::UNIVERSAL if cfg!(feature = "compiler")) +pub extern "C" fn wasmer_is_backend_available(backend: wasmer_backend_t) -> bool { + match backend { + wasmer_backend_t::LLVM => cfg!(feature = "llvm"), + wasmer_backend_t::CRANELIFT => cfg!(feature = "cranelift"), + wasmer_backend_t::SINGLEPASS => cfg!(feature = "singlepass"), + wasmer_backend_t::HEADLESS => cfg!(feature = "sys"), + wasmer_backend_t::V8 => cfg!(feature = "v8"), + wasmer_backend_t::WASMI => cfg!(feature = "wasmi"), + wasmer_backend_t::WAMR => cfg!(feature = "wamr"), + wasmer_backend_t::JSC => cfg!(feature = "jsc"), + } } #[cfg(test)] @@ -197,7 +106,7 @@ mod tests { } #[test] - fn test_wasmer_is_compiler_available() { + fn test_wasmer_is_backend_available() { set_var( "CRANELIFT", if cfg!(feature = "cranelift") { @@ -221,9 +130,9 @@ mod tests { #include int main() { - assert(wasmer_is_compiler_available(CRANELIFT) == (getenv("CRANELIFT")[0] == '1')); - assert(wasmer_is_compiler_available(LLVM) == (getenv("LLVM")[0] == '1')); - assert(wasmer_is_compiler_available(SINGLEPASS) == (getenv("SINGLEPASS")[0] == '1')); + assert(wasmer_is_backend_available(CRANELIFT) == (getenv("CRANELIFT")[0] == '1')); + assert(wasmer_is_backend_available(LLVM) == (getenv("LLVM")[0] == '1')); + assert(wasmer_is_backend_available(SINGLEPASS) == (getenv("SINGLEPASS")[0] == '1')); return 0; } @@ -234,26 +143,4 @@ mod tests { remove_var("LLVM"); remove_var("SINGLEPASS"); } - - #[test] - fn test_wasmer_is_engine_available() { - set_var( - "UNIVERSAL", - if cfg!(feature = "compiler") { "1" } else { "0" }, - ); - - (assert_c! { - #include "tests/wasmer.h" - #include - - int main() { - assert(wasmer_is_engine_available(UNIVERSAL) == (getenv("UNIVERSAL")[0] == '1')); - - return 0; - } - }) - .success(); - - remove_var("UNIVERSAL"); - } } diff --git a/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs b/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs index 43b660aa3..f75b9a27d 100644 --- a/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs +++ b/lib/c-api/src/wasm_c_api/unstable/middlewares/mod.rs @@ -3,7 +3,6 @@ pub mod metering; -use super::super::engine::wasm_config_t; use std::sync::Arc; use wasmer_api::sys::ModuleMiddleware; @@ -21,20 +20,3 @@ compile_error!("The `middlewares` feature requires the `compiler` feature to be pub struct wasmer_middleware_t { pub(in crate::wasm_c_api) inner: Arc, } - -/// Updates the configuration to add a module middleware. -/// -/// This function takes ownership of `middleware`. -/// -/// This is a Wasmer-specific function. -/// -/// # Example -/// -/// See the documentation of the [`metering`] module. -#[no_mangle] -pub extern "C" fn wasm_config_push_middleware( - config: &mut wasm_config_t, - middleware: Box, -) { - config.middlewares.push(*middleware); -} diff --git a/lib/c-api/src/wasm_c_api/unstable/mod.rs b/lib/c-api/src/wasm_c_api/unstable/mod.rs index 7369aa181..be245f1f4 100644 --- a/lib/c-api/src/wasm_c_api/unstable/mod.rs +++ b/lib/c-api/src/wasm_c_api/unstable/mod.rs @@ -5,7 +5,6 @@ pub mod middlewares; pub mod module; #[cfg(feature = "compiler")] pub mod parser; -#[cfg(any(feature = "compiler", feature = "compiler-headless"))] pub mod target_lexicon; #[cfg(feature = "wasi")] pub mod wasi; diff --git a/lib/c-api/src/wasm_c_api/unstable/module.rs b/lib/c-api/src/wasm_c_api/unstable/module.rs index 7fdbf8811..c5b3ddc6d 100644 --- a/lib/c-api/src/wasm_c_api/unstable/module.rs +++ b/lib/c-api/src/wasm_c_api/unstable/module.rs @@ -172,7 +172,7 @@ pub unsafe extern "C" fn wasmer_module_new( engine: Option<&mut wasm_engine_t>, bytes: Option<&wasm_byte_vec_t>, ) -> Option> { - let engine: wasmer_api::Engine = engine?.inner.clone().into(); + let engine: wasmer_api::Engine = engine?.inner.clone(); let bytes = bytes?; let module = c_try!(Module::from_binary(&engine, bytes.as_slice())); diff --git a/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs b/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs index 32ef2c825..183e27499 100644 --- a/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs +++ b/lib/c-api/src/wasm_c_api/unstable/target_lexicon.rs @@ -57,7 +57,7 @@ use super::super::types::wasm_name_t; use enumset::EnumSet; use std::slice; use std::str::{self, FromStr}; -use wasmer_api::sys::{CpuFeature, Target, Triple}; +use wasmer_types::target::{CpuFeature, Target, Triple}; /// Unstable non-standard Wasmer-specific API to represent a triple + /// CPU features pair. diff --git a/lib/c-api/tests/wasm.h b/lib/c-api/tests/wasm.h index 1582dd0ec..cdc463873 100644 --- a/lib/c-api/tests/wasm.h +++ b/lib/c-api/tests/wasm.h @@ -16,30 +16,36 @@ wasm_engine_t *wasm_engine_new() { wasm_config_t *config = wasm_config_new(); - char *wasmer_test_compiler = getenv("WASMER_CAPI_CONFIG"); + char *wasmer_test_backend = getenv("WASMER_CAPI_CONFIG"); char *wasmer_test_engine; - strtok_r(wasmer_test_compiler, "-", &wasmer_test_engine); - printf("Using compiler: %s, engine: %s\n", wasmer_test_compiler, - wasmer_test_engine); - if (strcmp(wasmer_test_compiler, "cranelift") == 0) { - assert(wasmer_is_compiler_available(CRANELIFT)); - wasm_config_set_compiler(config, CRANELIFT); - } else if (strcmp(wasmer_test_compiler, "llvm") == 0) { - assert(wasmer_is_compiler_available(LLVM)); - wasm_config_set_compiler(config, LLVM); - } else if (strcmp(wasmer_test_compiler, "singlepass") == 0) { - assert(wasmer_is_compiler_available(SINGLEPASS)); - wasm_config_set_compiler(config, SINGLEPASS); - } else if (wasmer_test_compiler) { - printf("Compiler %s not recognized\n", wasmer_test_compiler); - abort(); - } - if (strcmp(wasmer_test_engine, "universal") == 0) { - assert(wasmer_is_engine_available(UNIVERSAL)); - wasm_config_set_engine(config, UNIVERSAL); - } else if (wasmer_test_engine) { - printf("Engine %s not recognized\n", wasmer_test_engine); + printf("Using backend: %s\n", wasmer_test_backend); + + strtok_r(wasmer_test_backend, "-", &wasmer_test_engine); + + if (strcmp(wasmer_test_backend, "cranelift") == 0) { + assert(wasmer_is_backend_available(CRANELIFT)); + wasm_config_set_backend(config, CRANELIFT); + } else if (strcmp(wasmer_test_backend, "llvm") == 0) { + assert(wasmer_is_backend_available(LLVM)); + wasm_config_set_backend(config, LLVM); + } else if (strcmp(wasmer_test_backend, "singlepass") == 0) { + assert(wasmer_is_backend_available(SINGLEPASS)); + wasm_config_set_backend(config, SINGLEPASS); + } else if (strcmp(wasmer_test_backend, "headless") == 0) { + assert(wasmer_is_backend_available(HEADLESS)); + wasm_config_set_backend(config, HEADLESS); + } else if (strcmp(wasmer_test_backend, "v8") == 0) { + assert(wasmer_is_backend_available(V8)); + wasm_config_set_backend(config, V8); + } else if (strcmp(wasmer_test_backend, "wamr") == 0) { + assert(wasmer_is_backend_available(WAMR)); + wasm_config_set_backend(config, WAMR); + } else if (strcmp(wasmer_test_backend, "wasmi") == 0) { + assert(wasmer_is_backend_available(WASMI)); + wasm_config_set_backend(config, WASMI); + } else if (wasmer_test_backend) { + printf("Compiler %s not recognized\n", wasmer_test_backend); abort(); }