diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2780d33..4dc9d50 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,19 +53,6 @@ jobs: cargo test -p ort --verbose --features fetch-models -- --test-threads 1 # Test examples that use in-tree graphs (do NOT run any of the examples that download ~700 MB graphs from pyke parcel...) cargo run --example custom-ops - test-wasm: - name: Test WebAssembly - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install nightly Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - name: Install wasm-pack - run: cargo install wasm-pack - - name: Run tests - working-directory: examples/webassembly - run: | - wasm-pack test --node # Disable cross-compile until cross updates aarch64-unknown-linux-gnu to Ubuntu 22.04 # ref https://github.com/cross-rs/cross/pull/973 #cross-compile: diff --git a/.gitignore b/.gitignore index 6ab7181..6d61845 100644 --- a/.gitignore +++ b/.gitignore @@ -187,7 +187,6 @@ WixTools/ **/*.onnx **/*.ort **/*.pbseq -!examples/webassembly/**/*.ort !tests/data/*.onnx !tests/data/*.ort diff --git a/Cargo.toml b/Cargo.toml index 7ac0af6..4562d0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,7 @@ members = [ 'examples/yolov8', 'examples/modnet', 'examples/sentence-transformers', - 'examples/training', - 'examples/webassembly' + 'examples/training' ] default-members = [ '.', @@ -49,7 +48,7 @@ codegen-units = 1 [package.metadata.docs.rs] features = [ "ndarray", "half", "training", "operator-libraries", "fetch-models", "load-dynamic", "copy-dylibs" ] -targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] +targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = [ "--cfg", "docsrs" ] [features] @@ -99,10 +98,6 @@ libc = { version = "0.2", optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", optional = true, features = [ "std", "libloaderapi" ] } -[target.'cfg(target_family = "wasm")'.dependencies] -js-sys = "0.3" -web-sys = "0.3" - [dev-dependencies] anyhow = "1.0" ureq = "2.1" @@ -112,7 +107,6 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [ " glassbench = "0.4" tokio = { version = "1.36", features = [ "test-util" ] } tokio-test = "0.4.3" -wasm-bindgen-test = "0.3" [[bench]] name = "squeezenet" diff --git a/docs/pages/setup/platforms.mdx b/docs/pages/setup/platforms.mdx index cb1e705..999b9a0 100644 --- a/docs/pages/setup/platforms.mdx +++ b/docs/pages/setup/platforms.mdx @@ -12,21 +12,19 @@ Here are the supported platforms and binary availability status, as of v2.0.0-rc * β­• - Supported. Precompiled binaries not available. * ❌ - Not supported. -| Platform | x86 | x86-64 | ARMv7 | ARM64 | WASM32 | -|:-------- |:------- |:------ |:------ |:------ |:------ | -| **Windows** | β­• | 🟒\* | β­• | πŸ”·\* | ❌ | -| **Linux** | β­• | πŸŸ’β€  | β­• | πŸ”·β€‘ | ❌ | -| **macOS** | ❌ | πŸ”·Β§ | ❌ | πŸ”· | ❌ | -| **iOS** | ❌ | ❌ | ❌ | β­• | ❌ | -| **Android** | ❌ | ❌ | β­• | β­• | ❌ | -| **Web** | ❌ | ❌ | ❌ | ❌ | πŸ”·ΒΆ | +| Platform | x86 | x86-64 | ARMv7 | ARM64 | +|:-------- |:------- |:------ |:------ |:------ | +| **Windows** | β­• | 🟒\* | β­• | πŸ”·\* | +| **Linux** | β­• | πŸŸ’β€  | β­• | πŸ”·β€‘ | +| **macOS** | ❌ | πŸ”·Β§ | ❌ | πŸ”· | +| **iOS** | ❌ | ❌ | ❌ | β­• | +| **Android** | ❌ | ❌ | β­• | β­• |

\* A recent version of Windows 10/11 & Visual Studio 2022 are required for pyke binaries.

† glibc β‰₯ 2.31 (Ubuntu β‰₯ 20.04) required for pyke binaries.

‑ glibc β‰₯ 2.35 (Ubuntu β‰₯ 22.04) required for pyke binaries.

Β§ macOS β‰₯ 10.15 required.

-

ΒΆ WASM supports a limited subset of ONNX Runtime features. For more info, see [the docs on WebAssembly support](/setup/webassembly).

If your platform is marked as 🟒 or πŸ”·, you're in luck! Almost no setup will be required to get `ort` up and running. diff --git a/docs/pages/setup/webassembly.mdx b/docs/pages/setup/webassembly.mdx deleted file mode 100644 index 3e5e5cc..0000000 --- a/docs/pages/setup/webassembly.mdx +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: WebAssembly -description: Deploy ONNX models to the web ---- - -WebAssembly support in `ort` is currently experimental. If you experience any issues using `ort` in WebAssembly, please [open an issue](https://github.com/pykeio/ort/issues/new). - -By nature, some features of ONNX Runtime are not available in the web. These include: -- **Support for `.onnx` models.** You instead need to [convert `.onnx` models to the `.ort` format](https://onnxruntime.ai/docs/performance/model-optimizations/ort-format-models.html). -- **Runtime graph optimizations**, aka `SessionBuilder::with_optimization_level`. You can statically optimize the graph using the `.ort` conversion tool, though. -- **Loading models with `commit_from_file`/`commit_from_url`.** You can create models from a slice of bytes in memory with `SessionBuilder::commit_from_memory` or `SessionBuilder::commit_from_memory_directly`. - -Additionally, you'll need to call `ort::wasm::initialize()` at the earliest possible point in your code, before you use any `ort` APIs: -```rust filename="main.rs" copy -use ort::Session; - -static MODEL_BYTES: &[u8] = include_bytes!("../model.ort"); - -fn main() -> anyhow::Result<()> { - #[cfg(target_arch = "wasm32")] - ort::wasm::initialize(); - - let session = Session::builder()?.commit_from_memory_directly(MODEL_BYTES)?; - ... -} -``` diff --git a/examples/webassembly/Cargo.toml b/examples/webassembly/Cargo.toml deleted file mode 100644 index 51c6977..0000000 --- a/examples/webassembly/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -publish = false -name = "example-webassembly" -version = "0.0.0" -edition = "2021" - -[lib] -name = "ortwasm" -crate-type = ["cdylib"] - -[dependencies] -ort = { path = "../../" } -ndarray = "0.16" -wasm-bindgen = "0.2.92" -web-sys = "0.3" -tracing = "0.1" -tracing-subscriber = "0.3" -tracing-subscriber-wasm = "0.1" -image = { version = "0.25", default-features = false, features = [ "jpeg" ]} - -[dev-dependencies] -wasm-bindgen-test = "0.3" -console_error_panic_hook = "0.1" - -[features] -load-dynamic = [ "ort/load-dynamic" ] diff --git a/examples/webassembly/src/lib.rs b/examples/webassembly/src/lib.rs deleted file mode 100644 index dbc0d73..0000000 --- a/examples/webassembly/src/lib.rs +++ /dev/null @@ -1,75 +0,0 @@ -use image::{ImageBuffer, Luma, Pixel}; -use ort::{ArrayExtensions, Session}; -use wasm_bindgen::prelude::*; - -static IMAGE_BYTES: &[u8] = include_bytes!("../../../tests/data/mnist_5.jpg"); -static MODEL_BYTES: &[u8] = include_bytes!("mnist.ort"); - -pub fn upsample_inner() -> ort::Result<()> { - let session = Session::builder()? - .commit_from_memory_directly(MODEL_BYTES) - .expect("Could not read model from memory"); - - // NOTE: An earlier nightly version of Rust 1.78 includes a patch required for ONNX Runtime to link properly, but a - // later version enables debug assertions in `dlmalloc`, which surfaces an allocation bug in the `image` crate: - // https://github.com/rustwasm/wasm-pack/issues/1389 Because of this, using `image::load_from_memory` crashes. - // For demonstration purposes, we're replacing the image loading code shown below with zeros(). In a real application, - // you can get the image from another source, like an HTML canvas. - // - // let image_buffer: ImageBuffer, Vec> = image::load_from_memory(IMAGE_BYTES).unwrap().to_luma8(); - // let array = ndarray::Array::from_shape_fn((1, 1, 28, 28), |(_, c, j, i)| { - // let pixel = image_buffer.get_pixel(i as u32, j as u32); - // let channels = pixel.channels(); - // (channels[c] as f32) / 255.0 - // }); - let array = ndarray::Array4::::zeros((1, 1, 28, 28)); - - let outputs = session.run(ort::inputs![array]?)?; - - let mut probabilities: Vec<(usize, f32)> = outputs[0] - .try_extract_tensor()? - .softmax(ndarray::Axis(1)) - .iter() - .copied() - .enumerate() - .collect::>(); - - probabilities.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); - - assert_eq!(probabilities[0].0, 5, "Expecting class '5' (got {})", probabilities[0].0); - - Ok(()) -} - -macro_rules! console_log { - ($($t:tt)*) => (web_sys::console::log_1(&format_args!($($t)*).to_string().into())) -} - -#[wasm_bindgen] -pub fn upsample() { - if let Err(e) = upsample_inner() { - console_log!("Error occurred while performing inference: {e:?}"); - } -} - -#[cfg(test)] -#[wasm_bindgen_test::wasm_bindgen_test] -fn run_test() { - use tracing::Level; - use tracing_subscriber::fmt; - use tracing_subscriber_wasm::MakeConsoleWriter; - - #[cfg(target_arch = "wasm32")] - ort::wasm::initialize(); - - fmt() - .with_ansi(false) - .with_max_level(Level::DEBUG) - .with_writer(MakeConsoleWriter::default().map_trace_level_to(Level::DEBUG)) - .without_time() - .init(); - - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - - upsample(); -} diff --git a/examples/webassembly/src/mnist.ort b/examples/webassembly/src/mnist.ort deleted file mode 100644 index 1845899..0000000 Binary files a/examples/webassembly/src/mnist.ort and /dev/null differ diff --git a/ort-sys/build.rs b/ort-sys/build.rs index a7bbcf0..b2b56e5 100644 --- a/ort-sys/build.rs +++ b/ort-sys/build.rs @@ -215,15 +215,6 @@ fn prepare_libort_dir() -> (PathBuf, bool) { println!("cargo:rustc-link-lib=static=noexcep_operators"); } - if target_arch == "wasm32" { - for lib in &["webassembly", "providers_js"] { - let lib_path = lib_dir.join(platform_format_lib(&format!("onnxruntime_{lib}"))); - if lib_path.exists() { - println!("cargo:rustc-link-lib=static=onnxruntime_{lib}"); - } - } - } - let protobuf_build = transform_dep(external_lib_dir.join("protobuf-build"), &profile); add_search_dir(&protobuf_build); for lib in ["protobuf-lited", "protobuf-lite", "protobuf"] { @@ -245,17 +236,15 @@ fn prepare_libort_dir() -> (PathBuf, bool) { println!("cargo:rustc-link-lib=static=nsync_cpp"); } - if target_arch != "wasm32" { - add_search_dir(transform_dep(external_lib_dir.join("pytorch_cpuinfo-build"), &profile)); - let clog_path = transform_dep(external_lib_dir.join("pytorch_cpuinfo-build").join("deps").join("clog"), &profile); - if clog_path.exists() { - add_search_dir(clog_path); - } else { - add_search_dir(transform_dep(external_lib_dir.join("pytorch_clog-build"), &profile)); - } - println!("cargo:rustc-link-lib=static=cpuinfo"); - println!("cargo:rustc-link-lib=static=clog"); + add_search_dir(transform_dep(external_lib_dir.join("pytorch_cpuinfo-build"), &profile)); + let clog_path = transform_dep(external_lib_dir.join("pytorch_cpuinfo-build").join("deps").join("clog"), &profile); + if clog_path.exists() { + add_search_dir(clog_path); + } else { + add_search_dir(transform_dep(external_lib_dir.join("pytorch_clog-build"), &profile)); } + println!("cargo:rustc-link-lib=static=cpuinfo"); + println!("cargo:rustc-link-lib=static=clog"); add_search_dir(transform_dep(external_lib_dir.join("re2-build"), &profile)); println!("cargo:rustc-link-lib=static=re2"); diff --git a/ort-sys/src/lib.rs b/ort-sys/src/lib.rs index f7cb853..f027d5a 100644 --- a/ort-sys/src/lib.rs +++ b/ort-sys/src/lib.rs @@ -19,7 +19,7 @@ pub type ortchar = c_ushort; #[cfg(not(target_os = "windows"))] pub type ortchar = c_char; -#[cfg(any(target_arch = "x86_64", target_arch = "x86", target_arch = "wasm32"))] +#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] pub type size_t = usize; #[cfg(all(target_arch = "aarch64", target_os = "windows"))] pub type size_t = c_ulonglong; diff --git a/src/execution_providers/xnnpack.rs b/src/execution_providers/xnnpack.rs index bd3763e..7210b24 100644 --- a/src/execution_providers/xnnpack.rs +++ b/src/execution_providers/xnnpack.rs @@ -39,8 +39,7 @@ impl ExecutionProvider for XNNPACKExecutionProvider { cfg!(any( target_arch = "aarch64", all(target_arch = "arm", any(target_os = "linux", target_os = "android")), - target_arch = "x86_64", - target_arch = "wasm32" + target_arch = "x86_64" )) } diff --git a/src/lib.rs b/src/lib.rs index 5e9d255..8b610fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,9 +27,6 @@ pub(crate) mod tensor; pub(crate) mod training; pub(crate) mod util; pub(crate) mod value; -#[cfg_attr(docsrs, doc(cfg(target_arch = "wasm32")))] -#[cfg(target_arch = "wasm32")] -pub mod wasm; #[cfg(feature = "load-dynamic")] use std::sync::Arc; diff --git a/src/session/builder.rs b/src/session/builder.rs index 74e5e12..f650db5 100644 --- a/src/session/builder.rs +++ b/src/session/builder.rs @@ -2,11 +2,10 @@ use std::ffi::CString; #[cfg(feature = "fetch-models")] use std::fmt::Write; -#[cfg(not(target_arch = "wasm32"))] -use std::path::Path; use std::{ any::Any, marker::PhantomData, + path::Path, ptr::{self, NonNull}, rc::Rc, sync::{atomic::Ordering, Arc} @@ -162,8 +161,6 @@ impl SessionBuilder { /// newly optimized model to the given path (for 'offline' graph optimization). /// /// Note that the file will only be created after the model is committed. - #[cfg(not(target_arch = "wasm32"))] - #[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))] pub fn with_optimized_model_path>(self, path: S) -> Result { #[cfg(windows)] let path = path.as_ref().encode_utf16().chain([0]).collect::>(); @@ -175,8 +172,6 @@ impl SessionBuilder { /// Enables profiling. Profile information will be writen to `profiling_file` after profiling completes. /// See [`Session::end_profiling`]. - #[cfg(not(target_arch = "wasm32"))] - #[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))] pub fn with_profiling>(self, profiling_file: S) -> Result { #[cfg(windows)] let profiling_file = profiling_file.as_ref().encode_utf16().chain([0]).collect::>(); @@ -205,8 +200,8 @@ impl SessionBuilder { } /// Registers a custom operator library at the given library path. - #[cfg(all(feature = "operator-libraries", not(target_arch = "wasm32")))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "operator-libraries", not(target_arch = "wasm32")))))] + #[cfg(feature = "operator-libraries")] + #[cfg_attr(docsrs, doc(cfg(feature = "operator-libraries")))] pub fn with_operator_library(mut self, lib_path: impl AsRef) -> Result { let path_cstr = CString::new(lib_path.as_ref())?; @@ -232,8 +227,6 @@ impl SessionBuilder { } /// Enables [`onnxruntime-extensions`](https://github.com/microsoft/onnxruntime-extensions) custom operators. - #[cfg(not(target_arch = "wasm32"))] - #[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))] pub fn with_extensions(self) -> Result { let status = ortsys![unsafe EnableOrtCustomOps(self.session_options_ptr.as_ptr())]; status_to_result(status).map_err(Error::CreateSessionOptions)?; @@ -248,8 +241,8 @@ impl SessionBuilder { } /// Downloads a pre-trained ONNX model from the given URL and builds the session. - #[cfg(all(feature = "fetch-models", not(target_arch = "wasm32")))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "fetch-models", not(target_arch = "wasm32")))))] + #[cfg(feature = "fetch-models")] + #[cfg_attr(docsrs, doc(cfg(feature = "fetch-models")))] pub fn commit_from_url(self, model_url: impl AsRef) -> Result { let mut download_dir = ort_sys::internal::dirs::cache_dir() .expect("could not determine cache directory") @@ -299,8 +292,6 @@ impl SessionBuilder { } /// Loads an ONNX model from a file and builds the session. - #[cfg(not(target_arch = "wasm32"))] - #[cfg_attr(docsrs, doc(cfg(not(target_arch = "wasm32"))))] pub fn commit_from_file

(mut self, model_filepath_ref: P) -> Result where P: AsRef diff --git a/src/wasm.rs b/src/wasm.rs deleted file mode 100644 index 235a219..0000000 --- a/src/wasm.rs +++ /dev/null @@ -1,236 +0,0 @@ -//! Utilities for using `ort` in WebAssembly. -//! -//! You **must** call `ort::wasm::initialize()` before using any `ort` APIs in WASM: -//! ``` -//! # use ort::Session; -//! # static MODEL_BYTES: &[u8] = include_bytes!("../tests/data/upsample.ort"); -//! # fn main() -> ort::Result<()> { -//! #[cfg(target_arch = "wasm32")] -//! ort::wasm::initialize(); -//! -//! let session = Session::builder()?.commit_from_memory_directly(MODEL_BYTES)?; -//! # Ok(()) -//! # } -//! ``` - -use std::{ - alloc::{self, Layout}, - arch::wasm32, - ptr, slice, str -}; - -mod fmt_shims { - // localized time string formatting functions - // TODO: remove any remaining codepaths to these - - #[no_mangle] - pub unsafe extern "C" fn strftime_l(_s: *mut u8, _l: usize, _m: *const u8, _t: *const (), _lt: *const ()) -> usize { - unimplemented!() - } - #[no_mangle] - pub unsafe extern "C" fn _tzset_js(_timezone: *mut u32, _daylight: *const i32, _name: *const u8, _dst_name: *mut u8) { - unimplemented!() - } - #[no_mangle] - pub unsafe extern "C" fn _mktime_js(_tm: *mut ()) -> ! { - unimplemented!() - } - #[no_mangle] - pub unsafe extern "C" fn _localtime_js(_time_t: i64, _tm: *mut ()) -> ! { - unimplemented!() - } - #[no_mangle] - pub unsafe extern "C" fn _gmtime_js(_time_t: i64, _tm: *mut ()) -> ! { - unimplemented!() - } -} - -pub(crate) mod libc_shims { - use super::*; - - // Rust, unlike C, requires us to know the exact layout of an allocation in order to deallocate it, so we need to - // store this data at the beginning of the allocation for us to be able to pick up on deallocation: - // - // β”Œ---- actual allocated pointer - // β–Ό - // +-------------+-------+------+----------------+ - // | ...padding | align | size | data... | - // | -align..-8 | -8 | -4 | 0..size | - // +-------------+- -----+------+----------------+ - // β–² - // pointer returned to C ---β”˜ - // - // This does unfortunately mean we waste a little extra memory (note that most allocators *also* store the layout - // information in a similar manner, but we can't access it). - - const _: () = assert!(std::mem::size_of::() == 4, "32-bit pointer width (wasm32) required"); - - unsafe fn alloc_inner(size: usize, align: usize) -> *mut u8 { - // need enough space to store the size & alignment bytes - let align = align.max(8); - - let layout = Layout::from_size_align_unchecked(size + align, align); - let ptr = if ZERO { alloc::alloc_zeroed(layout) } else { alloc::alloc(layout) }; - ptr::copy_nonoverlapping(size.to_le_bytes().as_ptr(), ptr.add(align - 4), 4); - ptr::copy_nonoverlapping(align.to_le_bytes().as_ptr(), ptr.add(align - 8), 4); - ptr.add(align) - } - - unsafe fn free_inner(ptr: *mut u8) { - // something likes to free(NULL) a lot, which is valid in C (because of course it is...) - if ptr.is_null() { - return; - } - - let size = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(4), 4).try_into().unwrap_unchecked()); - let align = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(8), 4).try_into().unwrap_unchecked()); - let layout = Layout::from_size_align_unchecked(size + align, align); - alloc::dealloc(ptr.sub(align), layout); - } - - const DEFAULT_ALIGNMENT: usize = 32; - - #[no_mangle] - pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { - alloc_inner::(size, DEFAULT_ALIGNMENT) - } - #[no_mangle] - pub unsafe extern "C" fn __libc_malloc(size: usize) -> *mut u8 { - alloc_inner::(size, DEFAULT_ALIGNMENT) - } - #[no_mangle] - pub unsafe extern "C" fn __libc_calloc(n: usize, size: usize) -> *mut u8 { - alloc_inner::(size * n, DEFAULT_ALIGNMENT) - } - #[no_mangle] - pub unsafe extern "C" fn free(ptr: *mut u8) { - free_inner(ptr) - } - #[no_mangle] - pub unsafe extern "C" fn __libc_free(ptr: *mut u8) { - free_inner(ptr) - } - - #[no_mangle] - pub unsafe extern "C" fn posix_memalign(ptr: *mut *mut u8, align: usize, size: usize) -> i32 { - *ptr = alloc_inner::(size, align); - 0 - } - - #[no_mangle] - pub unsafe extern "C" fn realloc(ptr: *mut u8, newsize: usize) -> *mut u8 { - let size = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(4), 4).try_into().unwrap_unchecked()); - let align = usize::from_le_bytes(slice::from_raw_parts_mut(ptr.sub(8), 4).try_into().unwrap_unchecked()); - let layout = Layout::from_size_align_unchecked(size + align, align); - let ptr = alloc::realloc(ptr.sub(align), layout, newsize); - ptr::copy_nonoverlapping(size.to_le_bytes().as_ptr(), ptr.add(align - 4), 4); - ptr.add(align) - } - - #[no_mangle] - pub unsafe extern "C" fn abort() -> ! { - std::process::abort() - } -} - -#[cfg(not(target_os = "wasi"))] -mod wasi_shims { - #[allow(non_camel_case_types)] - type __wasi_errno_t = u16; - - const __WASI_ENOTSUP: __wasi_errno_t = 58; - - // mock filesystem for non-WASI platforms - most of the codepaths to any FS operations should've been removed, but we - // return ENOTSUP just to be safe - - #[no_mangle] - pub unsafe extern "C" fn __wasi_environ_sizes_get(argc: *mut usize, argv_buf_size: *mut usize) -> __wasi_errno_t { - *argc = 0; - *argv_buf_size = 0; - __WASI_ENOTSUP - } - - #[no_mangle] - pub unsafe extern "C" fn __wasi_environ_get(_environ: *mut *mut u8, _buf: *mut u8) -> __wasi_errno_t { - __WASI_ENOTSUP - } - - #[no_mangle] - pub unsafe extern "C" fn __wasi_fd_seek(_fd: u32, _offset: i64, _whence: u8, _new_offset: *mut u64) -> __wasi_errno_t { - __WASI_ENOTSUP - } - #[no_mangle] - pub unsafe extern "C" fn __wasi_fd_write(_fd: u32, _iovs: *const (), _iovs_len: usize, _nwritten: *mut usize) -> __wasi_errno_t { - __WASI_ENOTSUP - } - #[no_mangle] - pub unsafe extern "C" fn __wasi_fd_read(_fd: u32, _iovs: *const (), _iovs_len: usize, _nread: *mut usize) -> __wasi_errno_t { - __WASI_ENOTSUP - } - #[no_mangle] - pub unsafe extern "C" fn __wasi_fd_close(_fd: u32) -> __wasi_errno_t { - __WASI_ENOTSUP - } -} - -mod emscripten_shims { - use super::*; - - #[no_mangle] - pub unsafe extern "C" fn emscripten_memcpy_js(dst: *mut (), src: *const (), n: usize) { - std::ptr::copy_nonoverlapping(src, dst, n) - } - - #[no_mangle] - pub unsafe extern "C" fn emscripten_get_now() -> f64 { - js_sys::Date::now() - } - - #[no_mangle] - pub unsafe extern "C" fn emscripten_get_heap_max() -> usize { - wasm32::memory_size(0) << 16 - } - - #[no_mangle] - pub unsafe extern "C" fn emscripten_date_now() -> f64 { - js_sys::Date::now() - } - - #[no_mangle] - pub unsafe extern "C" fn _emscripten_get_now_is_monotonic() -> i32 { - 0 - } - - #[no_mangle] - pub unsafe extern "C" fn emscripten_builtin_malloc(size: usize) -> *mut u8 { - alloc::alloc_zeroed(Layout::from_size_align_unchecked(size, 32)) - } - - #[no_mangle] - #[tracing::instrument] - pub unsafe extern "C" fn emscripten_errn(str: *const u8, len: usize) { - let c = str::from_utf8_unchecked(slice::from_raw_parts(str, len)); - tracing::error!("Emscripten error: {c}"); - } - - // despite disabling exceptions literally everywhere when compiling, we still have to stub this... - #[no_mangle] - pub unsafe extern "C" fn __cxa_throw(_ptr: *const (), _type: *const (), _destructor: *const ()) -> ! { - std::process::abort(); - } -} - -#[no_mangle] -#[export_name = "_initialize"] -pub fn initialize() { - // The presence of an `_initialize` function prevents the linker from calling `__wasm_call_ctors` at the top of every - // function - including the functions `wasm-bindgen` interprets to generate JS glue code. `__wasm_call_ctors` calls - // complex functions that wbg's interpreter isn't equipped to handle, which was preventing wbg from outputting - // anything. - // I'm not entirely sure what `__wasm_call_ctors` is initializing, but it seems to have something to do with C++ - // vtables, and it's crucial for proper operation. - extern "C" { - fn __wasm_call_ctors(); - } - unsafe { __wasm_call_ctors() }; -}