diff --git a/Cargo.lock b/Cargo.lock index 341d0353af1..a0b16fb1eba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.28.0", + "gimli", ] [[package]] @@ -719,19 +719,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "corosensei" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" -dependencies = [ - "autocfg", - "cfg-if", - "libc", - "scopeguard", - "windows-sys 0.33.0", -] - [[package]] name = "cpp_demangle" version = "0.3.5" @@ -750,43 +737,13 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift-bforest" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" -dependencies = [ - "cranelift-entity 0.91.1", -] - [[package]] name = "cranelift-bforest" version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5e1df0da8488dd03b34afc134ba84b754d61862cc465932a9e5d07952f661e" dependencies = [ - "cranelift-entity 0.101.0", -] - -[[package]] -name = "cranelift-codegen" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" -dependencies = [ - "arrayvec", - "bumpalo", - "cranelift-bforest 0.91.1", - "cranelift-codegen-meta 0.91.1", - "cranelift-codegen-shared 0.91.1", - "cranelift-egraph", - "cranelift-entity 0.91.1", - "cranelift-isle 0.91.1", - "gimli 0.26.2", - "log", - "regalloc2 0.5.1", - "smallvec", - "target-lexicon", + "cranelift-entity", ] [[package]] @@ -796,44 +753,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a17ca4e699a0aaf49a0c88f6311a864f321048aa63f6b787cab20eb5f93f10" dependencies = [ "bumpalo", - "cranelift-bforest 0.101.0", - "cranelift-codegen-meta 0.101.0", - "cranelift-codegen-shared 0.101.0", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", "cranelift-control", - "cranelift-entity 0.101.0", - "cranelift-isle 0.101.0", - "gimli 0.28.0", + "cranelift-entity", + "cranelift-isle", + "gimli", "hashbrown 0.14.1", "log", - "regalloc2 0.9.3", + "regalloc2", "smallvec", "target-lexicon", ] -[[package]] -name = "cranelift-codegen-meta" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" -dependencies = [ - "cranelift-codegen-shared 0.91.1", -] - [[package]] name = "cranelift-codegen-meta" version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "022f2793cdade1d37a1f755ac42938a3f832f533eac6cafc8b26b209544c3c06" dependencies = [ - "cranelift-codegen-shared 0.101.0", + "cranelift-codegen-shared", ] -[[package]] -name = "cranelift-codegen-shared" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" - [[package]] name = "cranelift-codegen-shared" version = "0.101.0" @@ -849,26 +791,6 @@ dependencies = [ "arbitrary", ] -[[package]] -name = "cranelift-egraph" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" -dependencies = [ - "cranelift-entity 0.91.1", - "fxhash", - "hashbrown 0.12.3", - "indexmap 1.9.3", - "log", - "smallvec", -] - -[[package]] -name = "cranelift-entity" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" - [[package]] name = "cranelift-entity" version = "0.101.0" @@ -879,36 +801,18 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "cranelift-frontend" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" -dependencies = [ - "cranelift-codegen 0.91.1", - "log", - "smallvec", - "target-lexicon", -] - [[package]] name = "cranelift-frontend" version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44f497576ca3674581581601b6a55ccc1b43447217648c880e5bce70db3cf659" dependencies = [ - "cranelift-codegen 0.101.0", + "cranelift-codegen", "log", "smallvec", "target-lexicon", ] -[[package]] -name = "cranelift-isle" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" - [[package]] name = "cranelift-isle" version = "0.101.0" @@ -921,7 +825,7 @@ version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2870170ca44054b202c737626607b87be6e35655084bd94a6ff807a5812ba7df" dependencies = [ - "cranelift-codegen 0.101.0", + "cranelift-codegen", "libc", "target-lexicon", ] @@ -932,13 +836,13 @@ version = "0.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "103975f31b2c015ba53cd4bbb4fb9c509e91cf532752587d4e48748d0305334f" dependencies = [ - "cranelift-codegen 0.101.0", - "cranelift-entity 0.101.0", - "cranelift-frontend 0.101.0", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", "itertools 0.10.5", "log", "smallvec", - "wasmparser 0.115.0", + "wasmparser", "wasmtime-types", ] @@ -1019,20 +923,10 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", + "memoffset", "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -1219,17 +1113,6 @@ dependencies = [ "serde", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_more" version = "0.99.17" @@ -1254,6 +1137,16 @@ dependencies = [ "subtle", ] +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs" version = "5.0.1" @@ -1275,6 +1168,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "duct" version = "0.13.6" @@ -1347,26 +1251,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "enum-iterator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "enum-map" version = "2.6.3" @@ -1742,17 +1626,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator 0.2.0", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.28.0" @@ -2402,33 +2275,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -2480,12 +2326,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "more-asserts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" - [[package]] name = "multimap" version = "0.8.3" @@ -3435,18 +3275,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "regalloc2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" -dependencies = [ - "fxhash", - "log", - "slice-group-by", - "smallvec", -] - [[package]] name = "regalloc2" version = "0.9.3" @@ -3510,18 +3338,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" -[[package]] -name = "region" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach", - "winapi", -] - [[package]] name = "remove_dir_all" version = "0.5.3" @@ -3604,7 +3420,6 @@ dependencies = [ "bitvec", "bytecheck", "hashbrown 0.12.3", - "indexmap 1.9.3", "ptr_meta", "rend", "rkyv_derive", @@ -3831,12 +3646,6 @@ dependencies = [ "libc", ] -[[package]] -name = "self_cell" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" - [[package]] name = "semver" version = "1.0.20" @@ -3869,17 +3678,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde_derive" version = "1.0.189" @@ -4029,16 +3827,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shared-buffer" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" -dependencies = [ - "bytes", - "memmap2 0.6.2", -] - [[package]] name = "shared_child" version = "1.0.0" @@ -4411,6 +4199,7 @@ dependencies = [ "thiserror", "tokio", "tokio-util", + "toml 0.8.2", "tracing", "tracing-appender", "tracing-core", @@ -4421,10 +4210,7 @@ dependencies = [ "url", "urlencoding", "uuid", - "wasmer", - "wasmer-middlewares", - "wasmer-types", - "wasmer-vm", + "wasmtime", ] [[package]] @@ -5559,29 +5345,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-downcast" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dac026d43bcca6e7ce1c0956ba68f59edf6403e8e930a5d891be72c31a44340" -dependencies = [ - "js-sys", - "once_cell", - "wasm-bindgen", - "wasm-bindgen-downcast-macros", -] - -[[package]] -name = "wasm-bindgen-downcast-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "wasm-bindgen-futures" version = "0.4.37" @@ -5671,158 +5434,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "wasmer" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e626f958755a90a6552b9528f59b58a62ae288e6c17fcf40e99495bc33c60f0" -dependencies = [ - "bytes", - "cfg-if", - "derivative", - "indexmap 1.9.3", - "js-sys", - "more-asserts", - "rustc-demangle", - "serde", - "serde-wasm-bindgen", - "shared-buffer", - "target-lexicon", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-downcast", - "wasmer-compiler", - "wasmer-compiler-cranelift", - "wasmer-derive", - "wasmer-types", - "wasmer-vm", - "wat", - "winapi", -] - -[[package]] -name = "wasmer-compiler" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848e1922694cf97f4df680a0534c9d72c836378b5eb2313c1708fe1a75b40044" -dependencies = [ - "backtrace", - "bytes", - "cfg-if", - "enum-iterator", - "enumset", - "lazy_static", - "leb128", - "memmap2 0.5.10", - "more-asserts", - "region", - "rkyv", - "self_cell", - "shared-buffer", - "smallvec", - "thiserror", - "wasmer-types", - "wasmer-vm", - "wasmparser 0.95.0", - "winapi", -] - -[[package]] -name = "wasmer-compiler-cranelift" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d96bce6fad15a954edcfc2749b59e47ea7de524b6ef3df392035636491a40b4" -dependencies = [ - "cranelift-codegen 0.91.1", - "cranelift-entity 0.91.1", - "cranelift-frontend 0.91.1", - "gimli 0.26.2", - "more-asserts", - "rayon", - "smallvec", - "target-lexicon", - "tracing", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-derive" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f08f80d166a9279671b7af7a09409c28ede2e0b4e3acabbf0e3cb22c8038ba7" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "wasmer-middlewares" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb4b87c0ea9f8636c81a8ab8f52bad01c8623c9fcbb3db5f367d5f157fada30" -dependencies = [ - "wasmer", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-types" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae2c892882f0b416783fb4310e5697f5c30587f6f9555f9d4f2be85ab39d5d3d" -dependencies = [ - "bytecheck", - "enum-iterator", - "enumset", - "indexmap 1.9.3", - "more-asserts", - "rkyv", - "target-lexicon", - "thiserror", -] - -[[package]] -name = "wasmer-vm" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0a9a57b627fb39e5a491058d4365f099bc9b140031c000fded24a3306d9480" -dependencies = [ - "backtrace", - "cc", - "cfg-if", - "corosensei", - "crossbeam-queue", - "dashmap", - "derivative", - "enum-iterator", - "fnv", - "indexmap 1.9.3", - "lazy_static", - "libc", - "mach", - "memoffset 0.8.0", - "more-asserts", - "region", - "scopeguard", - "thiserror", - "wasmer-types", - "winapi", -] - -[[package]] -name = "wasmparser" -version = "0.95.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" -dependencies = [ - "indexmap 1.9.3", - "url", -] - [[package]] name = "wasmparser" version = "0.115.0" @@ -5856,7 +5467,8 @@ dependencies = [ "serde_json", "target-lexicon", "wasm-encoder", - "wasmparser 0.115.0", + "wasmparser", + "wasmtime-cache", "wasmtime-cranelift", "wasmtime-environ", "wasmtime-jit", @@ -5873,6 +5485,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "wasmtime-cache" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df12569f88409543cfeb41b3a9b620f3c40fa7d8edeb571acf8bcccdd6c21ab" +dependencies = [ + "anyhow", + "base64 0.21.4", + "bincode", + "directories-next", + "log", + "rustix", + "serde", + "serde_derive", + "sha2", + "toml 0.5.11", + "windows-sys 0.48.0", + "zstd", +] + [[package]] name = "wasmtime-cranelift" version = "14.0.0" @@ -5881,18 +5513,18 @@ checksum = "695e60462e0c4f897a06fdea52bf8667403ff4d7a6f1c312997b226f2bdd1ed6" dependencies = [ "anyhow", "cfg-if", - "cranelift-codegen 0.101.0", + "cranelift-codegen", "cranelift-control", - "cranelift-entity 0.101.0", - "cranelift-frontend 0.101.0", + "cranelift-entity", + "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.28.0", + "gimli", "log", "object", "target-lexicon", "thiserror", - "wasmparser 0.115.0", + "wasmparser", "wasmtime-cranelift-shared", "wasmtime-environ", "wasmtime-versioned-export-macros", @@ -5905,10 +5537,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18cef77aabba0155e713843f6ce698d325308c3ee76aca7a1f0b106d43b25a1e" dependencies = [ "anyhow", - "cranelift-codegen 0.101.0", + "cranelift-codegen", "cranelift-control", "cranelift-native", - "gimli 0.28.0", + "gimli", "object", "target-lexicon", "wasmtime-environ", @@ -5921,8 +5553,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7679dbbd0eb6ea23fc8c29dc8c46f77a951f6154bb25af969bda18b0386b4680" dependencies = [ "anyhow", - "cranelift-entity 0.101.0", - "gimli 0.28.0", + "cranelift-entity", + "gimli", "indexmap 2.0.2", "log", "object", @@ -5930,7 +5562,7 @@ dependencies = [ "serde_derive", "target-lexicon", "thiserror", - "wasmparser 0.115.0", + "wasmparser", "wasmtime-types", ] @@ -5945,7 +5577,7 @@ dependencies = [ "bincode", "cfg-if", "cpp_demangle", - "gimli 0.28.0", + "gimli", "log", "object", "rustc-demangle", @@ -5994,7 +5626,7 @@ dependencies = [ "log", "mach", "memfd", - "memoffset 0.9.0", + "memoffset", "paste", "rand 0.8.5", "rustix", @@ -6014,11 +5646,11 @@ version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dea1bc99dcd41d510b6f3535baefe56ec55741f0a9bd812b3323a1668b2eb50" dependencies = [ - "cranelift-entity 0.101.0", + "cranelift-entity", "serde", "serde_derive", "thiserror", - "wasmparser 0.115.0", + "wasmparser", ] [[package]] @@ -6038,27 +5670,6 @@ version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a6c6c4600bc33b7f0e2f63111406788c6595649a198638f75cd047e33ceaf5d" -[[package]] -name = "wast" -version = "66.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93cb43b0ac6dd156f2c375735ccfd72b012a7c0a6e6d09503499b8d3cb6e6072" -dependencies = [ - "leb128", - "memchr", - "unicode-width", - "wasm-encoder", -] - -[[package]] -name = "wat" -version = "1.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e367582095d2903caeeea9acbb140e1db9c7677001efa4347c3687fd34fe7072" -dependencies = [ - "wast", -] - [[package]] name = "web-sys" version = "0.3.64" @@ -6131,19 +5742,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows-sys" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" -dependencies = [ - "windows_aarch64_msvc 0.33.0", - "windows_i686_gnu 0.33.0", - "windows_i686_msvc 0.33.0", - "windows_x86_64_gnu 0.33.0", - "windows_x86_64_msvc 0.33.0", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -6204,12 +5802,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" -[[package]] -name = "windows_aarch64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6222,12 +5814,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" -[[package]] -name = "windows_i686_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6240,12 +5826,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" -[[package]] -name = "windows_i686_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6258,12 +5838,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" -[[package]] -name = "windows_x86_64_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6288,12 +5862,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" -[[package]] -name = "windows_x86_64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6357,3 +5925,32 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 25287681d78..15391889421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -182,12 +182,6 @@ uuid = { version = "1.2.1", features = ["v4"] } walkdir = "2.2.5" wasmbin = "0.6" -# wasmer prior to 4.1.1 had a signal handling bug on macOS. -wasmer = "4.1.1" -wasmer-middlewares = "4.1.1" -wasmer-types = "4.1.1" -wasmer-vm = "4.1.1" - wasmtime = { version = "14", default-features = false, features = ["cranelift"] } # We use the "ondemand" feature to allow connecting after the start, diff --git a/crates/cli/src/subcommands/publish.rs b/crates/cli/src/subcommands/publish.rs index 93f292b54b9..ddfffd475cd 100644 --- a/crates/cli/src/subcommands/publish.rs +++ b/crates/cli/src/subcommands/publish.rs @@ -19,8 +19,8 @@ pub fn cli() -> clap::Command { Arg::new("host_type") .long("host-type") .short('t') - .value_parser(["wasmer"]) - .default_value("wasmer") + .value_parser(["wasmtime"]) + .default_value("wasmtime") .help("The type of host that should be for hosting this module"), ) .arg( diff --git a/crates/client-api/src/routes/database.rs b/crates/client-api/src/routes/database.rs index 2f31a7ec492..38296986a7a 100644 --- a/crates/client-api/src/routes/database.rs +++ b/crates/client-api/src/routes/database.rs @@ -19,7 +19,7 @@ use spacetimedb::host::ReducerOutcome; use spacetimedb::host::UpdateDatabaseSuccess; use spacetimedb::identity::Identity; use spacetimedb::json::client_api::StmtResultJson; -use spacetimedb::messages::control_db::{Database, DatabaseInstance, HostType}; +use spacetimedb::messages::control_db::{Database, DatabaseInstance}; use spacetimedb::sql::execute::execute; use spacetimedb_lib::address::AddressForUrl; use spacetimedb_lib::identity::AuthCtx; @@ -386,9 +386,7 @@ pub async fn info( .ok_or((StatusCode::NOT_FOUND, "No such database."))?; log::trace!("Fetched database from the worker db for address: {address:?}"); - let host_type = match database.host_type { - HostType::Wasmer => "wasmer", - }; + let host_type: &str = database.host_type.as_ref(); let response_json = json!({ "address": database.address, "identity": database.identity, diff --git a/crates/client-api/src/routes/energy.rs b/crates/client-api/src/routes/energy.rs index c3d3517190c..e4ef4cd075f 100644 --- a/crates/client-api/src/routes/energy.rs +++ b/crates/client-api/src/routes/energy.rs @@ -46,11 +46,10 @@ pub async fn add_energy( let mut balance = ctx .get_energy_balance(&auth.identity) .map_err(log_and_500)? - .map(|quanta| quanta.0) - .unwrap_or(0); + .map_or(0, |quanta| quanta.get()); if let Some(satoshi) = amount { - ctx.add_energy(&auth.identity, EnergyQuanta(satoshi)) + ctx.add_energy(&auth.identity, EnergyQuanta::new(satoshi)) .await .map_err(log_and_500)?; balance += satoshi; @@ -68,8 +67,7 @@ fn get_budget_inner(ctx: impl ControlStateDelegate, identity: &Identity) -> axum let balance = ctx .get_energy_balance(identity) .map_err(log_and_500)? - .map(|quanta| quanta.0) - .unwrap_or(0); + .map_or(0, |quanta| quanta.get()); let response_json = json!({ // Note: balance must be returned as a string to avoid truncation. @@ -114,18 +112,17 @@ pub async fn set_energy_balance( let current_balance = ctx .get_energy_balance(&identity) .map_err(log_and_500)? - .map(|quanta| quanta.0) - .unwrap_or(0); + .map_or(0, |quanta| quanta.get()); let balance: i128 = if desired_balance > current_balance { let delta = desired_balance - current_balance; - ctx.add_energy(&identity, EnergyQuanta(delta)) + ctx.add_energy(&identity, EnergyQuanta::new(delta)) .await .map_err(log_and_500)?; delta } else { let delta = current_balance - desired_balance; - ctx.withdraw_energy(&identity, EnergyQuanta(delta)) + ctx.withdraw_energy(&identity, EnergyQuanta::new(delta)) .await .map_err(log_and_500)?; delta diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index af3843baf8b..d30a9610301 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -77,6 +77,7 @@ tempfile.workspace = true thiserror.workspace = true tokio-util.workspace = true tokio.workspace = true +toml.workspace = true tracing-appender.workspace = true tracing-core.workspace = true tracing-flame.workspace = true @@ -87,10 +88,7 @@ tracing.workspace = true url.workspace = true urlencoding.workspace = true uuid.workspace = true -wasmer-middlewares.workspace = true -wasmer-types.workspace = true -wasmer-vm.workspace = true -wasmer.workspace = true +wasmtime = { workspace = true, features = ["cache"] } # Rocksdb ostorage backend, linked only if "rocksdb" feature enabled. rocksdb = {workspace = true, optional = true} diff --git a/crates/core/src/control_db.rs b/crates/core/src/control_db.rs index 683aec9c26e..9f7f89a9b61 100644 --- a/crates/core/src/control_db.rs +++ b/crates/core/src/control_db.rs @@ -562,7 +562,7 @@ impl ControlDb { return Err(Error::DecodingError(bsatn::DecodeError::BufferLength)); }; let balance = i128::from_be_bytes(arr); - Ok(Some(EnergyQuanta(balance))) + Ok(Some(EnergyQuanta::new(balance))) } else { Ok(None) } @@ -573,7 +573,7 @@ impl ControlDb { /// `control_budget`, where a cached copy is stored along with business logic for managing it. pub fn set_energy_balance(&self, identity: Identity, energy_balance: EnergyQuanta) -> Result<()> { let tree = self.db.open_tree("energy_budget")?; - tree.insert(identity.as_bytes(), &energy_balance.0.to_be_bytes())?; + tree.insert(identity.as_bytes(), &energy_balance.get().to_be_bytes())?; Ok(()) } diff --git a/crates/core/src/host/host_controller.rs b/crates/core/src/host/host_controller.rs index 76d19f486e9..4f8b040366f 100644 --- a/crates/core/src/host/host_controller.rs +++ b/crates/core/src/host/host_controller.rs @@ -1,9 +1,8 @@ use crate::hash::hash_bytes; -use crate::host::wasmer; +use crate::host; use crate::messages::control_db::HostType; use crate::module_host_context::ModuleHostContext; use anyhow::Context; -// use parking_lot::{Condvar, Mutex}; use parking_lot::Mutex; use serde::Serialize; use std::collections::HashMap; @@ -90,28 +89,21 @@ impl fmt::Display for DescribedEntityType { /// for a user's balance to go negative. This is allowable /// for reasons of eventual consistency motivated by performance. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct EnergyQuanta(pub i128); +pub struct EnergyQuanta(i128); impl EnergyQuanta { pub const ZERO: Self = EnergyQuanta(0); pub const DEFAULT_BUDGET: Self = EnergyQuanta(1_000_000_000_000_000_000); - /// A conversion function to convert from the canonical unit to points used - /// by Wasmer to track energy usage. - pub fn as_points(&self) -> u64 { - if self.0 < 0 { - return 0; - } else if self.0 > u64::MAX as i128 { - return u64::MAX; - } - self.0 as u64 + #[inline] + pub fn new(v: i128) -> Self { + Self(v) } - /// A conversion function to convert from point used - /// by Wasmer to track energy usage, to our canonical unit. - pub fn from_points(points: u64) -> Self { - Self(points as i128) + #[inline] + pub fn get(&self) -> i128 { + self.0 } } @@ -273,13 +265,13 @@ impl HostController { let module_hash = hash_bytes(&mhc.program_bytes); let (threadpool, energy_monitor) = (self.threadpool.clone(), self.energy_monitor.clone()); let module_host = match mhc.host_type { - HostType::Wasmer => { + HostType::Wasmtime => { // make_actor with block_in_place since it's going to take some time to compute. let start = Instant::now(); let actor = tokio::task::block_in_place(|| { - wasmer::make_actor(mhc.dbic, module_hash, &mhc.program_bytes, mhc.scheduler, energy_monitor) + host::wasmtime::make_actor(mhc.dbic, module_hash, &mhc.program_bytes, mhc.scheduler, energy_monitor) })?; - log::trace!("wasmer::make_actor blocked for {:?}", start.elapsed()); + log::trace!("wasmtime::make_actor blocked for {:?}", start.elapsed()); ModuleHost::new(threadpool, actor) } }; diff --git a/crates/core/src/host/instance_env.rs b/crates/core/src/host/instance_env.rs index 99f5bd09573..2cba66e0e34 100644 --- a/crates/core/src/host/instance_env.rs +++ b/crates/core/src/host/instance_env.rs @@ -193,13 +193,13 @@ impl InstanceEnv { /// /// Errors with `TableNotFound` if the table does not exist. #[tracing::instrument(skip_all)] - pub fn get_table_id(&self, table_name: String) -> Result { + pub fn get_table_id(&self, table_name: &str) -> Result { let stdb = &*self.dbic.relational_db; let tx = &mut *self.get_tx()?; // Query the table id from the name. let table_id = stdb - .table_id_from_name(tx, &table_name)? + .table_id_from_name(tx, table_name)? .ok_or(NodesError::TableNotFound)?; Ok(table_id) @@ -235,7 +235,7 @@ impl InstanceEnv { IndexType::Hash => todo!("Hash indexes not yet supported"), }; - let cols = NonEmpty::from_slice(&col_ids) + let cols = NonEmpty::from_vec(col_ids) .expect("Attempt to create an index with zero columns") .map(Into::into); diff --git a/crates/core/src/host/mod.rs b/crates/core/src/host/mod.rs index 959977dc2b7..0fca8dc3cf9 100644 --- a/crates/core/src/host/mod.rs +++ b/crates/core/src/host/mod.rs @@ -12,7 +12,7 @@ use std::time::Duration; mod host_controller; pub(crate) mod module_host; pub mod scheduler; -mod wasmer; +mod wasmtime; // Visible for integration testing. pub mod instance_env; mod timestamp; @@ -168,7 +168,7 @@ impl EnergyMonitor for NullEnergyMonitor { } /// Tags for each call that a `WasmInstanceEnv` can make. -#[derive(Debug, Display, Enum)] +#[derive(Debug, Display, Enum, Clone, Copy)] pub enum AbiCall { CancelReducer, ConsoleLog, diff --git a/crates/core/src/host/wasm_common.rs b/crates/core/src/host/wasm_common.rs index 668020b66e5..e09da6aefea 100644 --- a/crates/core/src/host/wasm_common.rs +++ b/crates/core/src/host/wasm_common.rs @@ -4,6 +4,7 @@ pub mod module_host_actor; use std::time::Instant; +use super::AbiCall; use crate::error::{DBError, IndexError, NodesError}; pub const CALL_REDUCER_DUNDER: &str = "__call_reducer__"; @@ -67,7 +68,7 @@ macro_rules! type_eq { } }; } -type_eq!(wasmer::Type); +type_eq!(wasmtime::ValType); #[derive(Debug)] pub struct FuncSig> { @@ -81,22 +82,22 @@ impl StaticFuncSig { Self { params, results } } } -impl> PartialEq> for wasmer::ExternType { +impl> PartialEq> for wasmtime::ExternType { fn eq(&self, other: &FuncSig) -> bool { self.func().map_or(false, |f| { - f.params() == other.params.as_ref() && f.results() == other.results.as_ref() + f.params().eq(other.params.as_ref()) && f.results().eq(other.results.as_ref()) }) } } -impl FuncSigLike for wasmer::ExternType { +impl FuncSigLike for wasmtime::ExternType { fn to_func_sig(&self) -> Option { self.func().map(|f| FuncSig { - params: f.params().iter().map(|t| (*t).into()).collect(), - results: f.results().iter().map(|t| (*t).into()).collect(), + params: f.params().map(Into::into).collect(), + results: f.results().map(Into::into).collect(), }) } fn is_memory(&self) -> bool { - matches!(self, wasmer::ExternType::Memory(_)) + matches!(self, wasmtime::ExternType::Memory(_)) } } @@ -220,7 +221,7 @@ pub trait ResourceIndex { macro_rules! decl_index { ($name:ident => $resource:ty) => { - #[derive(Copy, Clone, wasmer::ValueType)] + #[derive(Copy, Clone)] #[repr(transparent)] pub(super) struct $name(pub u32); @@ -233,6 +234,14 @@ macro_rules! decl_index { self.0 } } + + impl $name { + #[allow(unused)] + #[doc(hidden)] + pub(super) fn to_le_bytes(self) -> [u8; 4] { + self.0.to_le_bytes() + } + } }; } @@ -355,7 +364,7 @@ pub fn err_to_errno(err: &NodesError) -> Option { #[derive(Debug, thiserror::Error)] #[error("runtime error calling {func}: {err}")] pub struct AbiRuntimeError { - pub func: &'static str, + pub func: AbiCall, #[source] pub err: NodesError, } diff --git a/crates/core/src/host/wasm_common/module_host_actor.rs b/crates/core/src/host/wasm_common/module_host_actor.rs index 53f14580fde..70a09799c9b 100644 --- a/crates/core/src/host/wasm_common/module_host_actor.rs +++ b/crates/core/src/host/wasm_common/module_host_actor.rs @@ -511,7 +511,7 @@ impl WasmModuleInstance { let reducer_span = tracing::trace_span!( "run_reducer", timings.total_duration = tracing::field::Empty, - energy.budget = budget.0, + energy.budget = budget.get(), energy.used = tracing::field::Empty, ) .entered(); diff --git a/crates/core/src/host/wasmer/mod.rs b/crates/core/src/host/wasmer/mod.rs deleted file mode 100644 index 7e3740cccc8..00000000000 --- a/crates/core/src/host/wasmer/mod.rs +++ /dev/null @@ -1,113 +0,0 @@ -use std::sync::Arc; - -use wasmer::wasmparser::Operator; -use wasmer::{AsStoreRef, CompilerConfig, EngineBuilder, Memory, MemoryAccessError, Module, RuntimeError, WasmPtr}; -use wasmer_middlewares::Metering; - -use crate::database_instance_context::DatabaseInstanceContext; -use crate::error::NodesError; -use crate::hash::Hash; - -mod opcode_cost; -mod wasm_instance_env; -mod wasmer_module; - -use wasmer_module::WasmerModule; - -use super::scheduler::Scheduler; -use super::wasm_common::{abi, module_host_actor::WasmModuleHostActor, ModuleCreationError}; -use super::{EnergyMonitor, EnergyQuanta}; - -pub fn make_actor( - dbic: Arc, - module_hash: Hash, - program_bytes: &[u8], - scheduler: Scheduler, - energy_monitor: Arc, -) -> Result { - let cost_function = - |operator: &Operator| -> u64 { opcode_cost::OperationType::operation_type_of(operator).energy_cost() }; - - // TODO(cloutiertyler): Why are we setting the initial points here? This - // seems like giving away free energy. Presumably this should always be set - // before calling reducer? - // I believe we can just set this to be zero and it's already being set by reducers - // but I don't want to break things, so I'm going to leave it. - let initial_points = EnergyQuanta::DEFAULT_BUDGET.as_points(); - let metering = Arc::new(Metering::new(initial_points, cost_function)); - - // let mut compiler_config = wasmer_compiler_llvm::LLVM::default(); - // compiler_config.opt_level(wasmer_compiler_llvm::LLVMOptLevel::Aggressive); - // compiler_config.push_middleware(metering); - let mut compiler_config = wasmer::Cranelift::default(); - compiler_config.opt_level(wasmer::CraneliftOptLevel::Speed); - compiler_config.push_middleware(metering); - - let engine: wasmer::Engine = EngineBuilder::new(compiler_config).into(); - - let module = Module::new(&engine, program_bytes).map_err(|e| ModuleCreationError::WasmCompileError(e.into()))?; - - let abi = abi::determine_spacetime_abi(module.imports().functions(), wasmer::ImportType::module)?; - - if let Some(abi) = abi { - abi::verify_supported(WasmerModule::IMPLEMENTED_ABI, abi)?; - } - - let module = WasmerModule::new(module, engine); - - WasmModuleHostActor::new(dbic, module_hash, module, scheduler, energy_monitor).map_err(Into::into) -} - -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -enum WasmError { - Db(#[from] NodesError), - Mem(#[from] MemoryAccessError), - Wasm(#[from] RuntimeError), -} - -/// Wraps access to WASM linear memory with some additional functionality. -#[derive(Clone)] -struct Mem { - /// The underlying WASM `memory` instance. - pub memory: Memory, -} - -impl Mem { - /// Constructs an instance of `Mem` from an exports map. - fn extract(exports: &wasmer::Exports) -> anyhow::Result { - let memory = exports.get_memory("memory")?.clone(); - Ok(Self { memory }) - } - - /// Creates and returns a view into the actual memory `store`. - /// This view allows for reads and writes. - - fn view<'a>(&self, store: &'a impl AsStoreRef) -> wasmer::MemoryView<'a> { - self.memory.view(store) - } - - /// Reads a slice of bytes starting from `ptr` - /// and lasting `len` bytes into a `Vec`. - /// - /// Returns an error if the slice length overflows a 64-bit address. - fn read_bytes(&self, store: &impl AsStoreRef, ptr: WasmPtr, len: u32) -> Result, MemoryAccessError> { - ptr.slice(&self.view(store), len)?.read_to_vec() - } - - /// Writes `data` into the slice starting from `ptr` - /// and lasting `len` bytes. - /// - /// Returns an error if - /// - the slice length overflows a 64-bit address - /// - `len != data.len()` - fn set_bytes( - &self, - store: &impl AsStoreRef, - ptr: WasmPtr, - len: u32, - data: &[u8], - ) -> Result<(), MemoryAccessError> { - ptr.slice(&self.view(store), len)?.write_slice(data) - } -} diff --git a/crates/core/src/host/wasmer/opcode_cost.rs b/crates/core/src/host/wasmer/opcode_cost.rs deleted file mode 100644 index 99468b76210..00000000000 --- a/crates/core/src/host/wasmer/opcode_cost.rs +++ /dev/null @@ -1,709 +0,0 @@ -/// Classification of WASM opcodes by operation type, along with our estimated cost of -/// these operations, in our own energy 'quanta' units. -use wasmer::wasmparser::Operator; - -/// Rough categorizations of types of operations, so that we can group things roughly by cost. -// TODO: These categorizations are likely going to need attention over time but reflected my -// broad understanding of the kinds of operations grouped in terms of their potential computation -// cost categorizations. -#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] -pub enum OperationType { - Nop, - - // Integer scalar ops - Bit, - AtomicBit, - AtomicXchg, - IntegerComparison, - AddSub, - AtomicAddSub, - Mul, - Div, - - // Float scalar ops - FloatComparison, - FloatAdd, - FloatMul, - FloatDiv, - FloatOperation, // abs/ceil/floor etc. - - // Variable / stack - Load, - Store, - AtomicLoad, - AtomicStore, - Const, - FloatConst, - Local, - Global, - - // Control flow and scope - Branch, - Scope, - Unreachable, - Call, - - // Type conversion - Conversion, - FloatConversion, - Reinterpretation, - - // Memory management - Memory, - - // I'm a snowflake! - AtomicSynchronization, - - // Vector SIMD operations. - Vector128Load, - Vector128Store, - Vector128Const, - Vector128Bit, - Vector128Integer, - Vector128Float, -} - -impl OperationType { - /// Classify a Wasmer Operator by its operation type. - #[tracing::instrument(skip_all)] - pub fn operation_type_of(instruction: &Operator) -> Self { - // TODO: keep an eye on this table. - // TODO: this table is going to be not-fun to maintain over time, esp as more vector ops - // are added, or wasmer versions change. There might be a more scalable approach. - use Operator as O; - match instruction { - O::Unreachable => OperationType::Unreachable, - O::Nop => OperationType::Nop, - O::Block { .. } => OperationType::Scope, - O::Loop { .. } => OperationType::Scope, - O::If { .. } => OperationType::Scope, - O::Else => OperationType::Scope, - O::End => OperationType::Scope, - O::Br { .. } => OperationType::Branch, - O::BrIf { .. } => OperationType::Branch, - O::BrTable { .. } => OperationType::Branch, - O::Return => OperationType::Branch, - - O::Drop => OperationType::Branch, - O::Select => OperationType::Branch, - O::Try { .. } => OperationType::Scope, - O::Catch { .. } => OperationType::Scope, - O::Throw { .. } => OperationType::Branch, - O::Rethrow { .. } => OperationType::Branch, - O::ReturnCall { .. } => OperationType::Branch, - O::ReturnCallIndirect { .. } => OperationType::Branch, - O::Delegate { .. } => OperationType::Branch, - O::CatchAll => OperationType::Scope, - O::TypedSelect { .. } => OperationType::Branch, - - O::Call { .. } => OperationType::Call, - O::CallIndirect { .. } => OperationType::Call, - - O::MemorySize { .. } => OperationType::Memory, - O::MemoryGrow { .. } => OperationType::Memory, - - // TODO: not clear on these, I'll treat them as Local for now - O::RefNull { .. } => OperationType::Local, - O::RefIsNull => OperationType::Local, - O::RefFunc { .. } => OperationType::Local, - - O::LocalGet { .. } => OperationType::Local, - O::LocalSet { .. } => OperationType::Local, - O::LocalTee { .. } => OperationType::Local, - - O::GlobalGet { .. } => OperationType::Global, - O::GlobalSet { .. } => OperationType::Global, - - O::I32Load { .. } => OperationType::Load, - O::I64Load { .. } => OperationType::Load, - O::F32Load { .. } => OperationType::Load, - O::F64Load { .. } => OperationType::Load, - O::I32Load8S { .. } => OperationType::Load, - O::I32Load8U { .. } => OperationType::Load, - O::I32Load16S { .. } => OperationType::Load, - O::I32Load16U { .. } => OperationType::Load, - O::I64Load8S { .. } => OperationType::Load, - O::I64Load8U { .. } => OperationType::Load, - O::I64Load16S { .. } => OperationType::Load, - O::I64Load16U { .. } => OperationType::Load, - O::I64Load32S { .. } => OperationType::Load, - O::I64Load32U { .. } => OperationType::Load, - O::I32AtomicLoad { .. } => OperationType::AtomicLoad, - O::I64AtomicLoad { .. } => OperationType::AtomicLoad, - O::I32AtomicLoad8U { .. } => OperationType::AtomicLoad, - O::I32AtomicLoad16U { .. } => OperationType::AtomicLoad, - O::I64AtomicLoad8U { .. } => OperationType::AtomicLoad, - O::I64AtomicLoad16U { .. } => OperationType::AtomicLoad, - O::I64AtomicLoad32U { .. } => OperationType::AtomicLoad, - - // Look at me, I'm a snowflake. - O::AtomicFence { .. } => OperationType::AtomicSynchronization, - - O::I32Store { .. } => OperationType::Store, - O::I64Store { .. } => OperationType::Store, - O::F32Store { .. } => OperationType::Store, - O::F64Store { .. } => OperationType::Store, - O::I32Store8 { .. } => OperationType::Store, - O::I32Store16 { .. } => OperationType::Store, - O::I64Store8 { .. } => OperationType::Store, - O::I64Store16 { .. } => OperationType::Store, - O::I64Store32 { .. } => OperationType::Store, - O::I32AtomicStore { .. } => OperationType::AtomicStore, - O::I64AtomicStore { .. } => OperationType::AtomicStore, - O::I32AtomicStore8 { .. } => OperationType::AtomicStore, - O::I32AtomicStore16 { .. } => OperationType::AtomicStore, - O::I64AtomicStore8 { .. } => OperationType::AtomicStore, - O::I64AtomicStore16 { .. } => OperationType::AtomicStore, - O::I64AtomicStore32 { .. } => OperationType::AtomicStore, - - O::I32Const { .. } => OperationType::Const, - O::I64Const { .. } => OperationType::Const, - - O::F32Const { .. } => OperationType::FloatConst, - O::F64Const { .. } => OperationType::FloatConst, - - O::I32Eqz => OperationType::IntegerComparison, - O::I32Eq => OperationType::IntegerComparison, - O::I32Ne => OperationType::IntegerComparison, - O::I32LtS => OperationType::IntegerComparison, - O::I32LtU => OperationType::IntegerComparison, - O::I32GtS => OperationType::IntegerComparison, - O::I32GtU => OperationType::IntegerComparison, - O::I32LeS => OperationType::IntegerComparison, - O::I32LeU => OperationType::IntegerComparison, - O::I32GeS => OperationType::IntegerComparison, - O::I32GeU => OperationType::IntegerComparison, - - O::I64Eqz => OperationType::IntegerComparison, - O::I64Eq => OperationType::IntegerComparison, - O::I64Ne => OperationType::IntegerComparison, - O::I64LtS => OperationType::IntegerComparison, - O::I64LtU => OperationType::IntegerComparison, - O::I64GtS => OperationType::IntegerComparison, - O::I64GtU => OperationType::IntegerComparison, - O::I64LeS => OperationType::IntegerComparison, - O::I64LeU => OperationType::IntegerComparison, - O::I64GeS => OperationType::IntegerComparison, - O::I64GeU => OperationType::IntegerComparison, - - O::F32Eq => OperationType::FloatComparison, - O::F32Ne => OperationType::FloatComparison, - O::F32Lt => OperationType::FloatComparison, - O::F32Gt => OperationType::FloatComparison, - O::F32Le => OperationType::FloatComparison, - O::F32Ge => OperationType::FloatComparison, - - O::F64Eq => OperationType::FloatComparison, - O::F64Ne => OperationType::FloatComparison, - O::F64Lt => OperationType::FloatComparison, - O::F64Gt => OperationType::FloatComparison, - O::F64Le => OperationType::FloatComparison, - O::F64Ge => OperationType::FloatComparison, - - O::I32Clz => OperationType::Bit, - O::I32Ctz => OperationType::Bit, - O::I32Popcnt => OperationType::Bit, - - O::I32Add => OperationType::AddSub, - O::I32Sub => OperationType::AddSub, - - O::I32AtomicRmwAdd { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmwAdd { .. } => OperationType::AtomicAddSub, - O::I32AtomicRmw8AddU { .. } => OperationType::AtomicAddSub, - O::I32AtomicRmw16AddU { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmw8AddU { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmw16AddU { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmw32AddU { .. } => OperationType::AtomicAddSub, - O::I32AtomicRmwSub { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmwSub { .. } => OperationType::AtomicAddSub, - O::I32AtomicRmw8SubU { .. } => OperationType::AtomicAddSub, - O::I32AtomicRmw16SubU { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmw8SubU { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmw16SubU { .. } => OperationType::AtomicAddSub, - O::I64AtomicRmw32SubU { .. } => OperationType::AtomicAddSub, - - O::I32AtomicRmwAnd { .. } => OperationType::AtomicBit, - O::I64AtomicRmwAnd { .. } => OperationType::AtomicBit, - O::I32AtomicRmw8AndU { .. } => OperationType::AtomicBit, - O::I32AtomicRmw16AndU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw8AndU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw16AndU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw32AndU { .. } => OperationType::AtomicBit, - O::I32AtomicRmwOr { .. } => OperationType::AtomicBit, - O::I64AtomicRmwOr { .. } => OperationType::AtomicBit, - O::I32AtomicRmw8OrU { .. } => OperationType::AtomicBit, - O::I32AtomicRmw16OrU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw8OrU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw16OrU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw32OrU { .. } => OperationType::AtomicBit, - O::I32AtomicRmwXor { .. } => OperationType::AtomicBit, - O::I64AtomicRmwXor { .. } => OperationType::AtomicBit, - O::I32AtomicRmw8XorU { .. } => OperationType::AtomicBit, - O::I32AtomicRmw16XorU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw8XorU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw16XorU { .. } => OperationType::AtomicBit, - O::I64AtomicRmw32XorU { .. } => OperationType::AtomicBit, - - O::I32AtomicRmwXchg { .. } => OperationType::AtomicXchg, - O::I64AtomicRmwXchg { .. } => OperationType::AtomicXchg, - O::I32AtomicRmw8XchgU { .. } => OperationType::AtomicXchg, - O::I32AtomicRmw16XchgU { .. } => OperationType::AtomicXchg, - O::I64AtomicRmw8XchgU { .. } => OperationType::AtomicXchg, - O::I64AtomicRmw16XchgU { .. } => OperationType::AtomicXchg, - O::I64AtomicRmw32XchgU { .. } => OperationType::AtomicXchg, - O::I32AtomicRmwCmpxchg { .. } => OperationType::AtomicXchg, - O::I64AtomicRmwCmpxchg { .. } => OperationType::AtomicXchg, - O::I32AtomicRmw8CmpxchgU { .. } => OperationType::AtomicXchg, - O::I32AtomicRmw16CmpxchgU { .. } => OperationType::AtomicXchg, - O::I64AtomicRmw8CmpxchgU { .. } => OperationType::AtomicXchg, - O::I64AtomicRmw16CmpxchgU { .. } => OperationType::AtomicXchg, - O::I64AtomicRmw32CmpxchgU { .. } => OperationType::AtomicXchg, - - O::I32Mul => OperationType::Mul, - O::I32DivS => OperationType::Div, - O::I32DivU => OperationType::Div, - O::I32RemS => OperationType::Div, - O::I32RemU => OperationType::Div, - O::I32And => OperationType::Bit, - O::I32Or => OperationType::Bit, - O::I32Xor => OperationType::Bit, - O::I32Shl => OperationType::Bit, - O::I32ShrS => OperationType::Bit, - O::I32ShrU => OperationType::Bit, - O::I32Rotl => OperationType::Bit, - O::I32Rotr => OperationType::Bit, - - O::I64Clz => OperationType::Bit, - O::I64Ctz => OperationType::Bit, - O::I64Popcnt => OperationType::Bit, - O::I64Add => OperationType::AddSub, - O::I64Sub => OperationType::AddSub, - O::I64Mul => OperationType::Mul, - O::I64DivS => OperationType::Div, - O::I64DivU => OperationType::Div, - O::I64RemS => OperationType::Div, - O::I64RemU => OperationType::Div, - O::I64And => OperationType::Bit, - O::I64Or => OperationType::Bit, - O::I64Xor => OperationType::Bit, - O::I64Shl => OperationType::Bit, - O::I64ShrS => OperationType::Bit, - O::I64ShrU => OperationType::Bit, - O::I64Rotl => OperationType::Bit, - O::I64Rotr => OperationType::Bit, - - O::F32Abs => OperationType::FloatOperation, - O::F32Neg => OperationType::FloatOperation, - O::F32Ceil => OperationType::FloatOperation, - O::F32Floor => OperationType::FloatOperation, - O::F32Trunc => OperationType::FloatOperation, - O::F32Nearest => OperationType::FloatOperation, - O::F32Sqrt => OperationType::FloatOperation, - O::F32Add => OperationType::FloatAdd, - O::F32Sub => OperationType::FloatAdd, - O::F32Mul => OperationType::FloatMul, - O::F32Div => OperationType::FloatDiv, - O::F32Min => OperationType::FloatComparison, - O::F32Max => OperationType::FloatComparison, - O::F32Copysign => OperationType::FloatComparison, - O::F64Abs => OperationType::FloatOperation, - O::F64Neg => OperationType::FloatComparison, - O::F64Ceil => OperationType::FloatOperation, - O::F64Floor => OperationType::FloatOperation, - O::F64Trunc => OperationType::FloatOperation, - O::F64Nearest => OperationType::FloatOperation, - O::F64Sqrt => OperationType::FloatOperation, - O::F64Add => OperationType::FloatAdd, - O::F64Sub => OperationType::FloatAdd, - O::F64Mul => OperationType::FloatMul, - O::F64Div => OperationType::FloatDiv, - O::F64Min => OperationType::FloatComparison, - O::F64Max => OperationType::FloatComparison, - O::F64Copysign => OperationType::FloatComparison, - - O::I32WrapI64 => OperationType::Conversion, - O::I64ExtendI32S => OperationType::Conversion, - O::I64ExtendI32U => OperationType::Conversion, - O::I32Extend8S => OperationType::Conversion, - O::I32Extend16S => OperationType::Conversion, - O::I64Extend8S => OperationType::Conversion, - O::I64Extend16S => OperationType::Conversion, - O::I64Extend32S => OperationType::Conversion, - - O::I32TruncF32S => OperationType::FloatConversion, - O::I32TruncF32U => OperationType::FloatConversion, - O::I32TruncF64S => OperationType::FloatConversion, - O::I32TruncF64U => OperationType::FloatConversion, - O::I64TruncF32S => OperationType::FloatConversion, - O::I64TruncF32U => OperationType::FloatConversion, - O::I64TruncF64S => OperationType::FloatConversion, - O::I64TruncF64U => OperationType::FloatConversion, - O::I32TruncSatF32S => OperationType::FloatConversion, - O::I32TruncSatF32U => OperationType::FloatConversion, - O::I32TruncSatF64S => OperationType::FloatConversion, - O::I32TruncSatF64U => OperationType::FloatConversion, - O::I64TruncSatF32S => OperationType::FloatConversion, - O::I64TruncSatF32U => OperationType::FloatConversion, - O::I64TruncSatF64S => OperationType::FloatConversion, - O::I64TruncSatF64U => OperationType::FloatConversion, - - O::F32ConvertI32S => OperationType::FloatConversion, - O::F32ConvertI32U => OperationType::FloatConversion, - O::F32ConvertI64S => OperationType::FloatConversion, - O::F32ConvertI64U => OperationType::FloatConversion, - O::F32DemoteF64 => OperationType::FloatConversion, - O::F64ConvertI32S => OperationType::FloatConversion, - O::F64ConvertI32U => OperationType::FloatConversion, - O::F64ConvertI64S => OperationType::FloatConversion, - O::F64ConvertI64U => OperationType::FloatConversion, - O::F64PromoteF32 => OperationType::FloatConversion, - - O::I32ReinterpretF32 => OperationType::Reinterpretation, - O::I64ReinterpretF64 => OperationType::Reinterpretation, - O::F32ReinterpretI32 => OperationType::Reinterpretation, - O::F64ReinterpretI64 => OperationType::Reinterpretation, - - O::MemoryInit { .. } => OperationType::Memory, - O::DataDrop { .. } => OperationType::Memory, - O::MemoryCopy { .. } => OperationType::Memory, - O::MemoryFill { .. } => OperationType::Memory, - O::TableInit { .. } => OperationType::Memory, - O::ElemDrop { .. } => OperationType::Memory, - O::TableCopy { .. } => OperationType::Memory, - O::TableFill { .. } => OperationType::Memory, - O::TableGet { .. } => OperationType::Memory, - O::TableSet { .. } => OperationType::Memory, - O::TableGrow { .. } => OperationType::Memory, - O::TableSize { .. } => OperationType::Memory, - - O::MemoryAtomicNotify { .. } => OperationType::Memory, - O::MemoryAtomicWait32 { .. } => OperationType::Memory, - O::MemoryAtomicWait64 { .. } => OperationType::Memory, - - O::V128Load { .. } => OperationType::Vector128Load, - O::V128Load8x8S { .. } => OperationType::Vector128Load, - O::V128Load8x8U { .. } => OperationType::Vector128Load, - O::V128Load16x4S { .. } => OperationType::Vector128Load, - O::V128Load16x4U { .. } => OperationType::Vector128Load, - O::V128Load32x2S { .. } => OperationType::Vector128Load, - O::V128Load32x2U { .. } => OperationType::Vector128Load, - O::V128Load8Splat { .. } => OperationType::Vector128Load, - O::V128Load16Splat { .. } => OperationType::Vector128Load, - O::V128Load32Splat { .. } => OperationType::Vector128Load, - O::V128Load64Splat { .. } => OperationType::Vector128Load, - O::V128Load32Zero { .. } => OperationType::Vector128Load, - O::V128Load64Zero { .. } => OperationType::Vector128Load, - O::V128Load8Lane { .. } => OperationType::Vector128Load, - O::V128Load16Lane { .. } => OperationType::Vector128Load, - O::V128Load32Lane { .. } => OperationType::Vector128Load, - O::V128Load64Lane { .. } => OperationType::Vector128Load, - - O::V128Store { .. } => OperationType::Vector128Store, - O::V128Store8Lane { .. } => OperationType::Vector128Store, - O::V128Store16Lane { .. } => OperationType::Vector128Store, - O::V128Store32Lane { .. } => OperationType::Vector128Store, - O::V128Store64Lane { .. } => OperationType::Vector128Store, - O::V128Const { .. } => OperationType::Vector128Const, - - O::V128Not => OperationType::Vector128Bit, - O::V128And => OperationType::Vector128Bit, - O::V128AndNot => OperationType::Vector128Bit, - O::V128Or => OperationType::Vector128Bit, - O::V128Xor => OperationType::Vector128Bit, - O::V128Bitselect => OperationType::Vector128Bit, - O::V128AnyTrue => OperationType::Vector128Bit, - - O::I8x16Shuffle { .. } => OperationType::Vector128Integer, - O::I8x16ExtractLaneS { .. } => OperationType::Vector128Integer, - O::I8x16ExtractLaneU { .. } => OperationType::Vector128Integer, - O::I8x16ReplaceLane { .. } => OperationType::Vector128Integer, - O::I16x8ExtractLaneS { .. } => OperationType::Vector128Integer, - O::I16x8ExtractLaneU { .. } => OperationType::Vector128Integer, - O::I16x8ReplaceLane { .. } => OperationType::Vector128Integer, - O::I32x4ExtractLane { .. } => OperationType::Vector128Integer, - O::I32x4ReplaceLane { .. } => OperationType::Vector128Integer, - O::I64x2ExtractLane { .. } => OperationType::Vector128Integer, - O::I64x2ReplaceLane { .. } => OperationType::Vector128Integer, - O::F32x4ExtractLane { .. } => OperationType::Vector128Integer, - O::F32x4ReplaceLane { .. } => OperationType::Vector128Integer, - O::F64x2ExtractLane { .. } => OperationType::Vector128Integer, - O::F64x2ReplaceLane { .. } => OperationType::Vector128Integer, - O::I8x16Swizzle => OperationType::Vector128Integer, - O::I8x16Splat => OperationType::Vector128Integer, - O::I16x8Splat => OperationType::Vector128Integer, - O::I32x4Splat => OperationType::Vector128Integer, - O::I64x2Splat => OperationType::Vector128Integer, - O::F32x4Splat => OperationType::Vector128Integer, - O::F64x2Splat => OperationType::Vector128Integer, - O::I8x16Eq => OperationType::Vector128Integer, - O::I8x16Ne => OperationType::Vector128Integer, - O::I8x16LtS => OperationType::Vector128Integer, - O::I8x16LtU => OperationType::Vector128Integer, - O::I8x16GtS => OperationType::Vector128Integer, - O::I8x16GtU => OperationType::Vector128Integer, - O::I8x16LeS => OperationType::Vector128Integer, - O::I8x16LeU => OperationType::Vector128Integer, - O::I8x16GeS => OperationType::Vector128Integer, - O::I8x16GeU => OperationType::Vector128Integer, - O::I16x8Eq => OperationType::Vector128Integer, - O::I16x8Ne => OperationType::Vector128Integer, - O::I16x8LtS => OperationType::Vector128Integer, - O::I16x8LtU => OperationType::Vector128Integer, - O::I16x8GtS => OperationType::Vector128Integer, - O::I16x8GtU => OperationType::Vector128Integer, - O::I16x8LeS => OperationType::Vector128Integer, - O::I16x8LeU => OperationType::Vector128Integer, - O::I16x8GeS => OperationType::Vector128Integer, - O::I16x8GeU => OperationType::Vector128Integer, - O::I32x4Eq => OperationType::Vector128Integer, - O::I32x4Ne => OperationType::Vector128Integer, - O::I32x4LtS => OperationType::Vector128Integer, - O::I32x4LtU => OperationType::Vector128Integer, - O::I32x4GtS => OperationType::Vector128Integer, - O::I32x4GtU => OperationType::Vector128Integer, - O::I32x4LeS => OperationType::Vector128Integer, - O::I32x4LeU => OperationType::Vector128Integer, - O::I32x4GeS => OperationType::Vector128Integer, - O::I32x4GeU => OperationType::Vector128Integer, - O::I64x2Eq => OperationType::Vector128Integer, - O::I64x2Ne => OperationType::Vector128Integer, - O::I64x2LtS => OperationType::Vector128Integer, - O::I64x2GtS => OperationType::Vector128Integer, - O::I64x2LeS => OperationType::Vector128Integer, - O::I64x2GeS => OperationType::Vector128Integer, - O::I8x16Abs => OperationType::Vector128Integer, - O::I8x16Neg => OperationType::Vector128Integer, - O::I8x16Popcnt => OperationType::Vector128Integer, - O::I8x16AllTrue => OperationType::Vector128Integer, - O::I8x16Bitmask => OperationType::Vector128Integer, - O::I8x16NarrowI16x8S => OperationType::Vector128Integer, - O::I8x16NarrowI16x8U => OperationType::Vector128Integer, - O::I8x16Shl => OperationType::Vector128Integer, - O::I8x16ShrS => OperationType::Vector128Integer, - O::I8x16ShrU => OperationType::Vector128Integer, - O::I8x16Add => OperationType::Vector128Integer, - O::I8x16AddSatS => OperationType::Vector128Integer, - O::I8x16AddSatU => OperationType::Vector128Integer, - O::I8x16Sub => OperationType::Vector128Integer, - O::I8x16SubSatS => OperationType::Vector128Integer, - O::I8x16SubSatU => OperationType::Vector128Integer, - O::I8x16MinS => OperationType::Vector128Integer, - O::I8x16MinU => OperationType::Vector128Integer, - O::I8x16MaxS => OperationType::Vector128Integer, - O::I8x16MaxU => OperationType::Vector128Integer, - O::I8x16AvgrU => OperationType::Vector128Integer, - O::I16x8ExtAddPairwiseI8x16S => OperationType::Vector128Integer, - O::I16x8ExtAddPairwiseI8x16U => OperationType::Vector128Integer, - O::I16x8Abs => OperationType::Vector128Integer, - O::I16x8Neg => OperationType::Vector128Integer, - O::I16x8Q15MulrSatS => OperationType::Vector128Integer, - O::I16x8AllTrue => OperationType::Vector128Integer, - O::I16x8Bitmask => OperationType::Vector128Integer, - O::I16x8NarrowI32x4S => OperationType::Vector128Integer, - O::I16x8NarrowI32x4U => OperationType::Vector128Integer, - O::I16x8ExtendLowI8x16S => OperationType::Vector128Integer, - O::I16x8ExtendHighI8x16S => OperationType::Vector128Integer, - O::I16x8ExtendLowI8x16U => OperationType::Vector128Integer, - O::I16x8ExtendHighI8x16U => OperationType::Vector128Integer, - O::I16x8Shl => OperationType::Vector128Integer, - O::I16x8ShrS => OperationType::Vector128Integer, - O::I16x8ShrU => OperationType::Vector128Integer, - O::I16x8Add => OperationType::Vector128Integer, - O::I16x8AddSatS => OperationType::Vector128Integer, - O::I16x8AddSatU => OperationType::Vector128Integer, - O::I16x8Sub => OperationType::Vector128Integer, - O::I16x8SubSatS => OperationType::Vector128Integer, - O::I16x8SubSatU => OperationType::Vector128Integer, - O::I16x8Mul => OperationType::Vector128Integer, - O::I16x8MinS => OperationType::Vector128Integer, - O::I16x8MinU => OperationType::Vector128Integer, - O::I16x8MaxS => OperationType::Vector128Integer, - O::I16x8MaxU => OperationType::Vector128Integer, - O::I16x8AvgrU => OperationType::Vector128Integer, - O::I16x8ExtMulLowI8x16S => OperationType::Vector128Integer, - O::I16x8ExtMulHighI8x16S => OperationType::Vector128Integer, - O::I16x8ExtMulLowI8x16U => OperationType::Vector128Integer, - O::I16x8ExtMulHighI8x16U => OperationType::Vector128Integer, - O::I32x4ExtAddPairwiseI16x8S => OperationType::Vector128Integer, - O::I32x4ExtAddPairwiseI16x8U => OperationType::Vector128Integer, - O::I32x4Abs => OperationType::Vector128Integer, - O::I32x4Neg => OperationType::Vector128Integer, - O::I32x4AllTrue => OperationType::Vector128Integer, - O::I32x4Bitmask => OperationType::Vector128Integer, - O::I32x4ExtendLowI16x8S => OperationType::Vector128Integer, - O::I32x4ExtendHighI16x8S => OperationType::Vector128Integer, - O::I32x4ExtendLowI16x8U => OperationType::Vector128Integer, - O::I32x4ExtendHighI16x8U => OperationType::Vector128Integer, - O::I32x4Shl => OperationType::Vector128Integer, - O::I32x4ShrS => OperationType::Vector128Integer, - O::I32x4ShrU => OperationType::Vector128Integer, - O::I32x4Add => OperationType::Vector128Integer, - O::I32x4Sub => OperationType::Vector128Integer, - O::I32x4Mul => OperationType::Vector128Integer, - O::I32x4MinS => OperationType::Vector128Integer, - O::I32x4MinU => OperationType::Vector128Integer, - O::I32x4MaxS => OperationType::Vector128Integer, - O::I32x4MaxU => OperationType::Vector128Integer, - O::I32x4DotI16x8S => OperationType::Vector128Integer, - O::I32x4ExtMulLowI16x8S => OperationType::Vector128Integer, - O::I32x4ExtMulHighI16x8S => OperationType::Vector128Integer, - O::I32x4ExtMulLowI16x8U => OperationType::Vector128Integer, - O::I32x4ExtMulHighI16x8U => OperationType::Vector128Integer, - O::I64x2Abs => OperationType::Vector128Integer, - O::I64x2Neg => OperationType::Vector128Integer, - O::I64x2AllTrue => OperationType::Vector128Integer, - O::I64x2Bitmask => OperationType::Vector128Integer, - O::I64x2ExtendLowI32x4S => OperationType::Vector128Integer, - O::I64x2ExtendHighI32x4S => OperationType::Vector128Integer, - O::I64x2ExtendLowI32x4U => OperationType::Vector128Integer, - O::I64x2ExtendHighI32x4U => OperationType::Vector128Integer, - O::I64x2Shl => OperationType::Vector128Integer, - O::I64x2ShrS => OperationType::Vector128Integer, - O::I64x2ShrU => OperationType::Vector128Integer, - O::I64x2Add => OperationType::Vector128Integer, - O::I64x2Sub => OperationType::Vector128Integer, - O::I64x2Mul => OperationType::Vector128Integer, - O::I64x2ExtMulLowI32x4S => OperationType::Vector128Integer, - O::I64x2ExtMulHighI32x4S => OperationType::Vector128Integer, - O::I64x2ExtMulLowI32x4U => OperationType::Vector128Integer, - O::I64x2ExtMulHighI32x4U => OperationType::Vector128Integer, - O::I8x16RelaxedLaneselect => OperationType::Vector128Integer, - O::I16x8RelaxedLaneselect => OperationType::Vector128Integer, - O::I32x4RelaxedLaneselect => OperationType::Vector128Integer, - O::I64x2RelaxedLaneselect => OperationType::Vector128Integer, - - O::F32x4Eq => OperationType::Vector128Float, - O::F32x4Ne => OperationType::Vector128Float, - O::F32x4Lt => OperationType::Vector128Float, - O::F32x4Gt => OperationType::Vector128Float, - O::F32x4Le => OperationType::Vector128Float, - O::F32x4Ge => OperationType::Vector128Float, - O::F64x2Eq => OperationType::Vector128Float, - O::F64x2Ne => OperationType::Vector128Float, - O::F64x2Lt => OperationType::Vector128Float, - O::F64x2Gt => OperationType::Vector128Float, - O::F64x2Le => OperationType::Vector128Float, - O::F64x2Ge => OperationType::Vector128Float, - O::F32x4Ceil => OperationType::Vector128Float, - O::F32x4Floor => OperationType::Vector128Float, - O::F32x4Trunc => OperationType::Vector128Float, - O::F32x4Nearest => OperationType::Vector128Float, - O::F32x4Abs => OperationType::Vector128Float, - O::F32x4Neg => OperationType::Vector128Float, - O::F32x4Sqrt => OperationType::Vector128Float, - O::F32x4Add => OperationType::Vector128Float, - O::F32x4Sub => OperationType::Vector128Float, - O::F32x4Mul => OperationType::Vector128Float, - O::F32x4Div => OperationType::Vector128Float, - O::F32x4Min => OperationType::Vector128Float, - O::F32x4Max => OperationType::Vector128Float, - O::F32x4PMin => OperationType::Vector128Float, - O::F32x4PMax => OperationType::Vector128Float, - O::F64x2Ceil => OperationType::Vector128Float, - O::F64x2Floor => OperationType::Vector128Float, - O::F64x2Trunc => OperationType::Vector128Float, - O::F64x2Nearest => OperationType::Vector128Float, - O::F64x2Abs => OperationType::Vector128Float, - O::F64x2Neg => OperationType::Vector128Float, - O::F64x2Sqrt => OperationType::Vector128Float, - O::F64x2Add => OperationType::Vector128Float, - O::F64x2Sub => OperationType::Vector128Float, - O::F64x2Mul => OperationType::Vector128Float, - O::F64x2Div => OperationType::Vector128Float, - O::F64x2Min => OperationType::Vector128Float, - O::F64x2Max => OperationType::Vector128Float, - O::F64x2PMin => OperationType::Vector128Float, - O::F64x2PMax => OperationType::Vector128Float, - O::I32x4TruncSatF32x4S => OperationType::Vector128Float, - O::I32x4TruncSatF32x4U => OperationType::Vector128Float, - O::F32x4ConvertI32x4S => OperationType::Vector128Float, - O::F32x4ConvertI32x4U => OperationType::Vector128Float, - O::I32x4TruncSatF64x2SZero => OperationType::Vector128Float, - O::I32x4TruncSatF64x2UZero => OperationType::Vector128Float, - O::F64x2ConvertLowI32x4S => OperationType::Vector128Float, - O::F64x2ConvertLowI32x4U => OperationType::Vector128Float, - O::F32x4DemoteF64x2Zero => OperationType::Vector128Float, - O::F64x2PromoteLowF32x4 => OperationType::Vector128Float, - O::I8x16RelaxedSwizzle => OperationType::Vector128Float, - O::I32x4RelaxedTruncSatF32x4S => OperationType::Vector128Float, - O::I32x4RelaxedTruncSatF32x4U => OperationType::Vector128Float, - O::I32x4RelaxedTruncSatF64x2SZero => OperationType::Vector128Float, - O::I32x4RelaxedTruncSatF64x2UZero => OperationType::Vector128Float, - O::F32x4RelaxedFma => OperationType::Vector128Float, - O::F32x4RelaxedFnma => OperationType::Vector128Float, - O::F64x2RelaxedFma => OperationType::Vector128Float, - O::F64x2RelaxedFnma => OperationType::Vector128Float, - O::F32x4RelaxedMin => OperationType::Vector128Float, - O::F32x4RelaxedMax => OperationType::Vector128Float, - O::F64x2RelaxedMin => OperationType::Vector128Float, - O::F64x2RelaxedMax => OperationType::Vector128Float, - - O::I16x8RelaxedQ15mulrS => OperationType::Vector128Integer, - O::I16x8DotI8x16I7x16S => OperationType::Vector128Integer, - O::I32x4DotI8x16I7x16AddS => OperationType::Vector128Integer, - O::F32x4RelaxedDotBf16x8AddF32x4 => OperationType::Vector128Float, - } - } - - /// Attempt to quantify the potential runtime/hardware cost of the operation. - // TODO: These will need attention over time. - #[tracing::instrument(skip_all)] - pub fn energy_cost(&self) -> u64 { - match self { - // Note I am assuming scalar operations (for integers and floats) have the same - // cost for 32-bits vs 64-bits, because I am assuming 64-bit hardware. - OperationType::Bit => 1000, - OperationType::AddSub => 1000, - OperationType::Mul => 5000, - - // Treat division as roughly twice as expensive as multiplication, mainly because - // division is more expensive on modern hardware because it can't be parallelized - // as efficiently. - OperationType::Div => 10000, - OperationType::Load => 3000, - OperationType::Store => 3000, - OperationType::Const => 100, - - OperationType::FloatConst => 250, - OperationType::FloatAdd => 1000, - OperationType::FloatMul => 5000, - OperationType::FloatDiv => 10000, - OperationType::FloatOperation => 3000, - OperationType::Local => 1500, // Register vs memory lookup, potentially. - OperationType::Global => 3000, - OperationType::IntegerComparison => 1000, - OperationType::FloatComparison => 2000, - OperationType::Conversion => 3000, - OperationType::FloatConversion => 3000, - OperationType::Reinterpretation => 000, - OperationType::Unreachable => 000, - OperationType::Nop => 100, - - OperationType::Scope => 500, - OperationType::Branch => 1000, - OperationType::Call => 4000, - - OperationType::Memory => 3000, - - OperationType::AtomicBit => 1000, - OperationType::AtomicXchg => 1000, - OperationType::AtomicAddSub => 1000, - OperationType::AtomicLoad => 1000, - OperationType::AtomicStore => 1000, - OperationType::AtomicSynchronization => 1000, - - // I am treating all vector operations as the same cost for now, but this will likely - // need tweaking over time. We may also need more granular classification of the - // operations but I'm broadly assuming that if there is SIMD hardware available, it - // will generally be fast. - // TODO: If we don't have SIMD hardware, we could maybe detect that and make these - // somewhat more expensive? - OperationType::Vector128Load => 10000, - OperationType::Vector128Store => 10000, - OperationType::Vector128Const => 10000, - OperationType::Vector128Bit => 10000, - OperationType::Vector128Integer => 10000, - OperationType::Vector128Float => 10000, - } - } -} diff --git a/crates/core/src/host/wasmer/wasmer_module.rs b/crates/core/src/host/wasmer/wasmer_module.rs deleted file mode 100644 index 5097e928023..00000000000 --- a/crates/core/src/host/wasmer/wasmer_module.rs +++ /dev/null @@ -1,314 +0,0 @@ -use super::wasm_instance_env::WasmInstanceEnv; -use super::Mem; -use crate::host::instance_env::InstanceEnv; -use crate::host::wasm_common::module_host_actor::{DescribeError, InitializationError, ReducerOp}; -use crate::host::wasm_common::*; -use crate::host::EnergyQuanta; -use bytes::Bytes; -use wasmer::{ - imports, AsStoreMut, Engine, ExternType, Function, FunctionEnv, Imports, Instance, Module, RuntimeError, Store, - TypedFunction, -}; -use wasmer_middlewares::metering as wasmer_metering; - -fn get_remaining_points(ctx: &mut impl AsStoreMut, instance: &Instance) -> u64 { - let remaining_points = wasmer_metering::get_remaining_points(ctx, instance); - match remaining_points { - wasmer_metering::MeteringPoints::Remaining(x) => x, - wasmer_metering::MeteringPoints::Exhausted => 0, - } -} - -fn log_traceback(func_type: &str, func: &str, e: &RuntimeError) { - let frames = e.trace(); - let frames_len = frames.len(); - - log::info!("{} \"{}\" runtime error: {}", func_type, func, e.message()); - for (i, frame) in frames.iter().enumerate().take(frames_len) { - log::info!( - " Frame #{}: {:?}::{}", - frames_len - i, - frame.module_name(), - rustc_demangle::demangle(frame.function_name().unwrap_or("")) - ); - } -} - -#[derive(Clone)] -pub struct WasmerModule { - module: Module, - engine: Engine, -} - -impl WasmerModule { - pub fn new(module: Module, engine: Engine) -> Self { - WasmerModule { module, engine } - } - - pub const IMPLEMENTED_ABI: abi::VersionTuple = abi::VersionTuple::new(7, 0); - - fn imports(&self, store: &mut Store, env: &FunctionEnv) -> Imports { - #[allow(clippy::assertions_on_constants)] - const _: () = assert!(WasmerModule::IMPLEMENTED_ABI.major == spacetimedb_lib::MODULE_ABI_MAJOR_VERSION); - imports! { - "spacetime_7.0" => { - "_schedule_reducer" => Function::new_typed_with_env(store, env, WasmInstanceEnv::schedule_reducer), - "_cancel_reducer" => Function::new_typed_with_env(store, env, WasmInstanceEnv::cancel_reducer), - "_delete_by_col_eq" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::delete_by_col_eq, - ), - "_delete_by_rel" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::delete_by_rel, - ), - "_insert" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::insert, - ), - "_get_table_id" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::get_table_id, - ), - "_create_index" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::create_index, - ), - "_iter_by_col_eq" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::iter_by_col_eq, - ), - "_iter_start" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::iter_start - ), - "_iter_start_filtered" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::iter_start_filtered - ), - "_iter_next" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::iter_next - ), - "_iter_drop" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::iter_drop - ), - "_console_log" => Function::new_typed_with_env( - store, - env, - WasmInstanceEnv::console_log - ), - "_buffer_len" => Function::new_typed_with_env(store, env, WasmInstanceEnv::buffer_len), - "_buffer_consume" => Function::new_typed_with_env(store, env, WasmInstanceEnv::buffer_consume), - "_buffer_alloc" => Function::new_typed_with_env(store, env, WasmInstanceEnv::buffer_alloc), - "_span_start" => Function::new_typed_with_env(store, env, WasmInstanceEnv::span_start), - "_span_end" => Function::new_typed_with_env(store, env, WasmInstanceEnv::span_end), - } - } - } -} - -impl module_host_actor::WasmModule for WasmerModule { - type Instance = WasmerInstance; - type InstancePre = Self; - - type ExternType = ExternType; - - fn get_export(&self, s: &str) -> Option { - self.module - .exports() - .find(|exp| exp.name() == s) - .map(|exp| exp.ty().clone()) - } - - fn for_each_export(&self, mut f: impl FnMut(&str, &Self::ExternType) -> Result<(), E>) -> Result<(), E> { - self.module.exports().try_for_each(|exp| f(exp.name(), exp.ty())) - } - - fn instantiate_pre(&self) -> Result { - Ok(self.clone()) - } -} - -impl module_host_actor::WasmInstancePre for WasmerModule { - type Instance = WasmerInstance; - - fn instantiate(&self, env: InstanceEnv, func_names: &FuncNames) -> Result { - let mut store = Store::new(self.engine.clone()); - let env = WasmInstanceEnv::new(env); - let env = FunctionEnv::new(&mut store, env); - let imports = self.imports(&mut store, &env); - let instance = Instance::new(&mut store, &self.module, &imports) - .map_err(|err| InitializationError::Instantiation(err.into()))?; - - let mem = Mem::extract(&instance.exports).unwrap(); - - env.as_mut(&mut store).instantiate(mem); - - // Note: this budget is just for initializers - let budget = EnergyQuanta::DEFAULT_BUDGET.as_points(); - wasmer_metering::set_remaining_points(&mut store, &instance, budget); - - for preinit in &func_names.preinits { - let func = instance.exports.get_typed_function::<(), ()>(&store, preinit).unwrap(); - func.call(&mut store).map_err(|err| InitializationError::RuntimeError { - err: err.into(), - func: preinit.clone(), - })?; - } - - let init = instance.exports.get_typed_function::<(), u32>(&store, SETUP_DUNDER); - if let Ok(init) = init { - match init.call(&mut store).map(BufferIdx) { - Ok(errbuf) if errbuf.is_invalid() => {} - Ok(errbuf) => { - let errbuf = env - .as_mut(&mut store) - .take_buffer(errbuf) - .unwrap_or_else(|| "unknown error".as_bytes().into()); - let errbuf = crate::util::string_from_utf8_lossy_owned(errbuf.into()).into(); - // TODO: catch this and return the error message to the http client - return Err(InitializationError::Setup(errbuf)); - } - Err(err) => { - return Err(InitializationError::RuntimeError { - err: err.into(), - func: SETUP_DUNDER.to_owned(), - }); - } - } - } - - let call_reducer = instance - .exports - .get_typed_function(&store, CALL_REDUCER_DUNDER) - .expect("no call_reducer"); - - Ok(WasmerInstance { - store, - env, - instance, - call_reducer, - }) - } -} - -pub struct WasmerInstance { - store: Store, - env: FunctionEnv, - instance: Instance, - call_reducer: TypedFunction<(u32, u32, u32, u64, u32), u32>, -} - -impl module_host_actor::WasmInstance for WasmerInstance { - fn extract_descriptions(&mut self) -> Result { - let describer_func_name = DESCRIBE_MODULE_DUNDER; - let describer = self.instance.exports.get_function(describer_func_name).unwrap(); - - let start = std::time::Instant::now(); - log::trace!("Start describer \"{}\"...", describer_func_name); - - let store = &mut self.store; - let describer = describer - .typed::<(), u32>(store) - .map_err(|_| DescribeError::Signature)?; - let result = describer.call(store).map(BufferIdx); - let duration = start.elapsed(); - log::trace!("Describer \"{}\" ran: {} us", describer_func_name, duration.as_micros(),); - let buf = result.map_err(|err| { - log_traceback("describer", describer_func_name, &err); - DescribeError::RuntimeError(err.into()) - })?; - let bytes = self - .env - .as_mut(store) - .take_buffer(buf) - .ok_or(DescribeError::BadBuffer)?; - - // Clear all of the instance state associated to this describer call. - self.env.as_mut(store).finish_reducer(); - - Ok(bytes) - } - - fn instance_env(&self) -> &InstanceEnv { - self.env.as_ref(&self.store).instance_env() - } - - type Trap = wasmer::RuntimeError; - - fn call_reducer( - &mut self, - op: ReducerOp<'_>, - budget: EnergyQuanta, - ) -> module_host_actor::ExecuteResult { - let store = &mut self.store; - let instance = &self.instance; - let budget = budget.as_points(); - wasmer_metering::set_remaining_points(store, instance, budget); - - let mut make_buf = |data| self.env.as_mut(store).insert_buffer(data); - - let identity_buf = make_buf(op.caller_identity.as_bytes().to_vec().into()); - let address_buf = make_buf(op.caller_address.as_slice().to_vec().into()); - let args_buf = make_buf(op.arg_bytes); - - self.env.as_mut(store).start_reducer(op.name); - - let result = self - .call_reducer - .call( - store, - op.id.0, - identity_buf.0, - address_buf.0, - op.timestamp.0, - args_buf.0, - ) - .and_then(|errbuf| { - let errbuf = BufferIdx(errbuf); - Ok(if errbuf.is_invalid() { - Ok(()) - } else { - let errmsg = self - .env - .as_mut(store) - .take_buffer(errbuf) - .ok_or_else(|| RuntimeError::new("invalid buffer handle"))?; - Err(crate::util::string_from_utf8_lossy_owned(errmsg.into()).into()) - }) - }); - - // Signal that this reducer call is finished. This gets us the timings - // associated to our reducer call, and clears all of the instance state - // associated to the call. - let timings = self.env.as_mut(store).finish_reducer(); - - let remaining = get_remaining_points(store, instance); - let energy = module_host_actor::EnergyStats { - used: EnergyQuanta::from_points(budget) - EnergyQuanta::from_points(remaining), - remaining: EnergyQuanta::from_points(remaining), - }; - - module_host_actor::ExecuteResult { - energy, - timings, - call_result: result, - } - } - - fn log_traceback(func_type: &str, func: &str, trap: &Self::Trap) { - log_traceback(func_type, func, trap) - } -} diff --git a/crates/core/src/host/wasmtime/mod.rs b/crates/core/src/host/wasmtime/mod.rs new file mode 100644 index 00000000000..7e1c262a602 --- /dev/null +++ b/crates/core/src/host/wasmtime/mod.rs @@ -0,0 +1,252 @@ +use std::borrow::Cow; +use std::sync::Arc; + +use anyhow::Context; +use once_cell::sync::Lazy; +use wasmtime::{Engine, Linker, Module, StoreContext, StoreContextMut}; + +use crate::database_instance_context::DatabaseInstanceContext; +use crate::error::NodesError; +use crate::hash::Hash; +use crate::stdb_path; + +mod wasm_instance_env; +mod wasmtime_module; + +use wasmtime_module::WasmtimeModule; + +use self::wasm_instance_env::WasmInstanceEnv; + +use super::scheduler::Scheduler; +use super::wasm_common::module_host_actor::InitializationError; +use super::wasm_common::{abi, module_host_actor::WasmModuleHostActor, ModuleCreationError}; +use super::{EnergyMonitor, EnergyQuanta}; + +static ENGINE: Lazy = Lazy::new(|| { + let mut config = wasmtime::Config::new(); + config + .cranelift_opt_level(wasmtime::OptLevel::Speed) + .consume_fuel(true) + .wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable); + + let cache_config = toml::toml! { + // see for options here + [cache] + enabled = true + directory = (toml::Value::try_from(stdb_path("worker_node/wasmtime_cache")).unwrap()) + }; + // ignore errors for this - if we're not able to set up caching, that's fine, it's just an optimization + let _ = set_cache_config(&mut config, cache_config); + + Engine::new(&config).unwrap() +}); + +fn set_cache_config(config: &mut wasmtime::Config, cache_config: toml::value::Table) -> anyhow::Result<()> { + use std::io::Write; + let tmpfile = tempfile::NamedTempFile::new()?; + write!(&tmpfile, "{cache_config}")?; + config.cache_config_load(tmpfile.path())?; + Ok(()) +} + +static LINKER: Lazy> = Lazy::new(|| { + let mut linker = Linker::new(&ENGINE); + WasmtimeModule::link_imports(&mut linker).unwrap(); + linker +}); + +pub fn make_actor( + dbic: Arc, + module_hash: Hash, + program_bytes: &[u8], + scheduler: Scheduler, + energy_monitor: Arc, +) -> Result { + let module = Module::new(&ENGINE, program_bytes).map_err(ModuleCreationError::WasmCompileError)?; + + let func_imports = module + .imports() + .filter(|imp| matches!(imp.ty(), wasmtime::ExternType::Func(_))); + let abi = abi::determine_spacetime_abi(func_imports, |imp| imp.module())?; + + if let Some(abi) = abi { + abi::verify_supported(WasmtimeModule::IMPLEMENTED_ABI, abi)?; + } + + let module = LINKER + .instantiate_pre(&module) + .map_err(InitializationError::Instantiation)?; + + let module = WasmtimeModule::new(module); + + WasmModuleHostActor::new(dbic, module_hash, module, scheduler, energy_monitor).map_err(Into::into) +} + +#[derive(Debug, derive_more::From)] +enum WasmError { + Db(NodesError), + Wasm(anyhow::Error), +} + +#[derive(Copy, Clone)] +struct WasmtimeFuel(u64); + +impl WasmtimeFuel { + /// 1000 energy quanta == 1 wasmtime fuel unit + const QUANTA_MULTIPLIER: i128 = 1_000; + + /// Convert from EnergyQuanta to wasmtime fuel. The second element of the tuple is the remainder + /// of quanta in the case that `eq` isn't in u64 range. It can be safely ignored if the supplied + /// budget isn't being bookkept. + fn from_energy_quanta(eq: EnergyQuanta) -> (Self, EnergyQuanta) { + if eq.get() < 0 { + return (Self(0), eq); + } + let fuel = eq.get() / Self::QUANTA_MULTIPLIER; + let div_remainder = eq.get() % Self::QUANTA_MULTIPLIER; + let u64_max = i128::from(u64::MAX); + let fuel_clamped = fuel.clamp(0, u64_max) as u64; + let clamp_remainder = if fuel > u64_max { + (fuel - u64_max) * Self::QUANTA_MULTIPLIER + } else { + 0 + }; + ( + WasmtimeFuel(fuel_clamped), + EnergyQuanta::new(div_remainder + clamp_remainder), + ) + } +} + +impl From for EnergyQuanta { + fn from(fuel: WasmtimeFuel) -> Self { + EnergyQuanta::new(i128::from(fuel.0) * WasmtimeFuel::QUANTA_MULTIPLIER) + } +} + +trait WasmPointee { + type Pointer; + fn write_to(self, mem: &mut MemView, ptr: Self::Pointer) -> Result<(), WasmError>; +} +macro_rules! impl_pointee { + ($($t:ty),*) => { + $(impl WasmPointee for $t { + type Pointer = u32; + fn write_to(self, mem: &mut MemView, ptr: Self::Pointer) -> Result<(), WasmError> { + let bytes = self.to_le_bytes(); + mem.deref_slice_mut(ptr, bytes.len() as u32)?.copy_from_slice(&bytes); + Ok(()) + } + })* + }; +} +impl_pointee!(u8, u16, u32, u64); +impl_pointee!(super::wasm_common::BufferIdx, super::wasm_common::BufferIterIdx); +type WasmPtr = ::Pointer; + +/// Wraps access to WASM linear memory with some additional functionality. +#[derive(Clone, Copy)] +struct Mem { + /// The underlying WASM `memory` instance. + pub memory: wasmtime::Memory, +} + +impl Mem { + /// Constructs an instance of `Mem` from an exports map. + fn extract(exports: &wasmtime::Instance, store: impl wasmtime::AsContextMut) -> anyhow::Result { + Ok(Self { + memory: exports.get_memory(store, "memory").context("no memory export")?, + }) + } + + /// Creates and returns a view into the actual memory `store`. + /// This view allows for reads and writes. + fn view_and_store_mut<'a, T>(&self, store: impl Into>) -> (&'a mut MemView, &'a mut T) { + let (mem, store_data) = self.memory.data_and_store_mut(store); + (MemView::from_slice_mut(mem), store_data) + } + + fn view<'a, T: 'a>(&self, store: impl Into>) -> &'a MemView { + MemView::from_slice(self.memory.data(store)) + } +} + +#[repr(transparent)] +struct MemView([u8]); + +impl MemView { + fn from_slice_mut(v: &mut [u8]) -> &mut Self { + // SAFETY: MemView is repr(transparent) over [u8] + unsafe { &mut *(v as *mut [u8] as *mut MemView) } + } + fn from_slice(v: &[u8]) -> &Self { + // SAFETY: MemView is repr(transparent) over [u8] + unsafe { &*(v as *const [u8] as *const MemView) } + } + + /// Get a byte slice of wasm memory given a pointer and a length. + fn deref_slice(&self, offset: WasmPtr, len: u32) -> Result<&[u8], MemError> { + if offset == 0 { + return Err(MemError::Null); + } + self.0 + .get(offset as usize..) + .and_then(|s| s.get(..len as usize)) + .ok_or(MemError::OutOfBounds) + } + + /// Get a utf8 slice of wasm memory given a pointer and a length. + fn deref_str(&self, offset: WasmPtr, len: u32) -> Result<&str, MemError> { + let b = self.deref_slice(offset, len)?; + std::str::from_utf8(b).map_err(MemError::Utf8) + } + + /// Lossily get a utf8 slice of wasm memory given a pointer and a length, converting any + /// non-utf8 bytes to `U+FFFD REPLACEMENT CHARACTER`. + fn deref_str_lossy(&self, offset: WasmPtr, len: u32) -> Result, MemError> { + self.deref_slice(offset, len).map(String::from_utf8_lossy) + } + + /// Get a mutable byte slice of wasm memory given a pointer and a length; + fn deref_slice_mut(&mut self, offset: WasmPtr, len: u32) -> Result<&mut [u8], MemError> { + if offset == 0 { + return Err(MemError::Null); + } + self.0 + .get_mut(offset as usize..) + .and_then(|s| s.get_mut(..len as usize)) + .ok_or(MemError::OutOfBounds) + } +} + +/// An error that can result from operations on [`MemView`]. +#[derive(thiserror::Error, Debug)] +enum MemError { + #[error("out of bounds pointer passed to a spacetime function")] + OutOfBounds, + #[error("null pointer passed to a spacetime function")] + Null, + #[error("invalid utf8 passed to a spacetime function")] + Utf8(#[from] std::str::Utf8Error), +} + +impl From for WasmError { + fn from(err: MemError) -> Self { + WasmError::Wasm(err.into()) + } +} + +/// Extension trait to gracefully handle null `WasmPtr`s, e.g. +/// `mem.deref_slice(ptr, len).check_nullptr()? == Option<&[u8]>`. +trait NullableMemOp { + fn check_nullptr(self) -> Result, WasmError>; +} +impl NullableMemOp for Result { + fn check_nullptr(self) -> Result, WasmError> { + match self { + Ok(x) => Ok(Some(x)), + Err(MemError::Null) => Ok(None), + Err(e) => Err(e.into()), + } + } +} diff --git a/crates/core/src/host/wasmer/wasm_instance_env.rs b/crates/core/src/host/wasmtime/wasm_instance_env.rs similarity index 59% rename from crates/core/src/host/wasmer/wasm_instance_env.rs rename to crates/core/src/host/wasmtime/wasm_instance_env.rs index c3c52eed195..082c23c39f1 100644 --- a/crates/core/src/host/wasmer/wasm_instance_env.rs +++ b/crates/core/src/host/wasmtime/wasm_instance_env.rs @@ -14,11 +14,12 @@ use crate::host::wasm_common::{ TimingSpan, TimingSpanIdx, TimingSpanSet, }; use crate::host::AbiCall; -use wasmer::{FunctionEnvMut, MemoryAccessError, RuntimeError, ValueType, WasmPtr}; +use anyhow::{anyhow, Context}; +use wasmtime::{AsContext, Caller, StoreContextMut}; use crate::host::instance_env::InstanceEnv; -use super::{Mem, WasmError}; +use super::{Mem, MemView, NullableMemOp, WasmError, WasmPointee, WasmPtr}; #[cfg(not(feature = "spacetimedb-wasm-instance-env-times"))] use instrumentation::noop as span; @@ -72,16 +73,7 @@ pub(super) struct WasmInstanceEnv { } type WasmResult = Result; -type RtResult = Result; - -fn mem_err(err: MemoryAccessError) -> RuntimeError { - match err { - MemoryAccessError::HeapOutOfBounds | MemoryAccessError::Overflow => { - RuntimeError::from(wasmer_vm::Trap::lib(wasmer_vm::TrapCode::HeapAccessOutOfBounds)) - } - _ => RuntimeError::user(err.into()), - } -} +type RtResult = anyhow::Result; /// Wraps an `InstanceEnv` with the magic necessary to push /// and pull bytes from webassembly memory. @@ -108,8 +100,13 @@ impl WasmInstanceEnv { } /// Returns a reference to the memory, assumed to be initialized. - pub fn mem(&self) -> Mem { - self.mem.clone().expect("Initialized memory") + pub fn get_mem(&self) -> Mem { + self.mem.expect("Initialized memory") + } + fn mem_env<'a>(ctx: impl Into>) -> (&'a mut MemView, &'a mut Self) { + let ctx = ctx.into(); + let mem = ctx.data().get_mem(); + mem.view_and_store_mut(ctx) } /// Return a reference to the `InstanceEnv`, @@ -160,6 +157,19 @@ impl WasmInstanceEnv { ExecutionContext::reducer(self.instance_env().dbic.address, self.reducer_name.as_str()) } + // TODO: make this part of cvt(), maybe? + /// Gather the appropriate metadata and log a wasm_abi_call_duration_ns with the given AbiCall & duration + fn start_abi_call_timer(&self, call: AbiCall) -> prometheus::HistogramTimer { + let ctx = self.reducer_context(); + let txn_type = ctx.txn_type(); + let db = ctx.database(); + + DB_METRICS + .wasm_abi_call_duration_sec + .with_label_values(&txn_type, &db, &self.reducer_name, &call) + .start_timer() + } + /// Call the function `f` with the name `func`. /// The function `f` is provided with the callers environment and the host's memory. /// @@ -169,16 +179,14 @@ impl WasmInstanceEnv { /// Some database errors are logged but are otherwise regarded as `Ok(_)`. /// See `err_to_errno` for a list. fn cvt( - mut caller: FunctionEnvMut<'_, Self>, - func: &'static str, - call: AbiCall, - f: impl FnOnce(FunctionEnvMut<'_, Self>, &Mem) -> WasmResult<()>, - ) -> RtResult { - let span_start = span::CallSpanStart::new(call); + mut caller: Caller<'_, Self>, + func: AbiCall, + f: impl FnOnce(&mut Caller<'_, Self>) -> WasmResult<()>, + ) -> RtResult { + let span_start = span::CallSpanStart::new(func); // Call `f` with the caller and a handle to the memory. - let mem = caller.data().mem(); - let result = f(caller.as_mut(), &mem); + let result = f(&mut caller); // Track the span of this call. let span = span_start.end(); @@ -194,11 +202,10 @@ impl WasmInstanceEnv { WasmError::Db(err) => match err_to_errno(&err) { Some(errno) => { log::debug!("abi call to {func} returned a normal error: {err:#}"); - return Ok(errno); + return Ok(errno.into()); } - None => RuntimeError::user(Box::new(AbiRuntimeError { func, err })), + None => anyhow::Error::from(AbiRuntimeError { func, err }), }, - WasmError::Mem(err) => mem_err(err), WasmError::Wasm(err) => err, }) } @@ -214,15 +221,17 @@ impl WasmInstanceEnv { /// as it helps with upholding the safety invariants of [`bindings_sys::call`]. /// /// Returns an error if writing `T` to `out` errors. - fn cvt_ret( - caller: FunctionEnvMut<'_, Self>, - func: &'static str, + fn cvt_ret( + caller: Caller<'_, Self>, call: AbiCall, out: WasmPtr, - f: impl FnOnce(FunctionEnvMut<'_, Self>, &Mem) -> WasmResult, - ) -> RtResult { - Self::cvt(caller, func, call, |mut caller, mem| { - f(caller.as_mut(), mem).and_then(|ret| out.write(&mem.view(&caller), ret).map_err(Into::into)) + f: impl FnOnce(&mut Caller<'_, Self>) -> WasmResult, + ) -> RtResult { + Self::cvt(caller, call, |caller| { + f(caller).and_then(|ret| { + let (mem, _) = Self::mem_env(caller); + ret.write_to(mem, out) + }) }) } @@ -231,27 +240,16 @@ impl WasmInstanceEnv { /// This is the version of `cvt` or `cvt_ret` for functions with no return value. /// One of `cvt`, `cvt_ret`, or `cvt_noret` should be used in the implementation of any /// host call, to provide consistent error handling and instrumentation. - fn cvt_noret(mut caller: FunctionEnvMut<'_, Self>, call: AbiCall, f: impl FnOnce(FunctionEnvMut<'_, Self>, &Mem)) { + fn cvt_noret(mut caller: Caller<'_, Self>, call: AbiCall, f: impl FnOnce(&mut Caller<'_, Self>)) { let span_start = span::CallSpanStart::new(call); // Call `f` with the caller and a handle to the memory. - let mem = caller.data().mem(); - f(caller.as_mut(), &mem); + f(&mut caller); let span = span_start.end(); span::record_span(&mut caller.data_mut().call_times, span); } - /// Reads a string from WASM memory starting at `ptr` and lasting `len` bytes. - /// - /// Returns an error if: - /// - `ptr + len` overflows a 64-bit address. - /// - the string was not valid UTF-8 - fn read_string(caller: &FunctionEnvMut<'_, Self>, mem: &Mem, ptr: WasmPtr, len: u32) -> RtResult { - let bytes = mem.read_bytes(&caller, ptr, len)?; - String::from_utf8(bytes).map_err(|_| RuntimeError::new("name must be utf8")) - } - /// Schedules a reducer to be called asynchronously at `time`. /// /// The reducer is named as the valid UTF-8 slice `(name, name_len)`, @@ -267,7 +265,7 @@ impl WasmInstanceEnv { /// - writing to `out` overflows a 64-bit integer #[tracing::instrument(skip_all)] pub fn schedule_reducer( - caller: FunctionEnvMut<'_, Self>, + caller: Caller<'_, Self>, name: WasmPtr, name_len: u32, args: WasmPtr, @@ -275,36 +273,30 @@ impl WasmInstanceEnv { time: u64, out: WasmPtr, ) -> RtResult<()> { - Self::cvt_ret( - caller, - "schedule_reducer", - AbiCall::ScheduleReducer, - out, - |caller, mem| { - // Read the index name as a string from `(name, name_len)`. - let name = Self::read_string(&caller, mem, name, name_len)?; - - // Read the reducer's arguments as a byte slice. - let args = mem.read_bytes(&caller, args, args_len)?; - - // Schedule it! - // TODO: Be more strict re. types by avoiding newtype unwrapping here? (impl ValueType?) - // Noa: This would be nice but I think the eventual goal/desire is to switch to wasmtime, - // which doesn't allow user types to impl ValueType. - // Probably the correct API choice, but makes things a bit less ergonomic sometimes. - let ScheduledReducerId(id) = caller - .data() - .instance_env + Self::cvt_ret(caller, AbiCall::ScheduleReducer, out, |caller| { + let (mem, env) = Self::mem_env(caller); + // Read the index name as a string from `(name, name_len)`. + let name = mem.deref_str(name, name_len)?.to_owned(); + + // Read the reducer's arguments as a byte slice. + let args = mem.deref_slice(args, args_len)?.to_vec(); + + // Schedule it! + // TODO: Be more strict re. types by avoiding newtype unwrapping here? (impl ValueType?) + // Noa: This would be nice but I think the eventual goal/desire is to switch to wasmtime, + // which doesn't allow user types to impl ValueType. + // Probably the correct API choice, but makes things a bit less ergonomic sometimes. + let ScheduledReducerId(id) = + env.instance_env .schedule(name, args, Timestamp(time)) .map_err(|e| match e { - ScheduleError::DelayTooLong(_) => RuntimeError::new("requested delay is too long"), + ScheduleError::DelayTooLong(_) => anyhow!("requested delay is too long"), ScheduleError::IdTransactionError(_) => { - RuntimeError::new("transaction to acquire ScheduleReducerId failed") + anyhow!("transaction to acquire ScheduleReducerId failed") } })?; - Ok(id) - }, - ) + Ok(id) + }) .map(|_| ()) } @@ -312,8 +304,8 @@ impl WasmInstanceEnv { /// /// This assumes that the reducer hasn't already been executed. #[tracing::instrument(skip_all)] - pub fn cancel_reducer(caller: FunctionEnvMut<'_, Self>, id: u64) { - Self::cvt_noret(caller, AbiCall::CancelReducer, |caller, _mem| { + pub fn cancel_reducer(caller: Caller<'_, Self>, id: u64) { + Self::cvt_noret(caller, AbiCall::CancelReducer, |caller| { caller.data().instance_env.cancel_reducer(ScheduledReducerId(id)) }) } @@ -334,8 +326,8 @@ impl WasmInstanceEnv { /// [`target`]: https://docs.rs/log/latest/log/struct.Record.html#method.target #[tracing::instrument(skip_all)] pub fn console_log( - caller: FunctionEnvMut<'_, Self>, - level: u8, + caller: Caller<'_, Self>, + level: u32, target: WasmPtr, target_len: u32, filename: WasmPtr, @@ -344,39 +336,32 @@ impl WasmInstanceEnv { message: WasmPtr, message_len: u32, ) { - Self::cvt_noret(caller, AbiCall::ConsoleLog, |caller, mem| { - // Reads a string lossily from the slice `(ptr, len)` in WASM memory. - let read_str = |ptr, len| { - mem.read_bytes(&caller, ptr, len) - .map(crate::util::string_from_utf8_lossy_owned) + let do_console_log = |caller: &mut Caller<'_, Self>| -> WasmResult<()> { + let env = caller.data(); + let mem = env.get_mem().view(&caller); + + // Read the `target`, `filename`, and `message` strings from WASM memory. + let target = mem.deref_str_lossy(target, target_len).check_nullptr()?; + let filename = mem.deref_str_lossy(filename, filename_len).check_nullptr()?; + let message = mem.deref_str_lossy(message, message_len)?; + + // The line number cannot be `u32::MAX` as this represents `Option::None`. + let line_number = (line_number != u32::MAX).then_some(line_number); + + let record = Record { + target: target.as_deref(), + filename: filename.as_deref(), + line_number, + message: &message, }; - // Reads as string optionally, unless `ptr.is_null()`. - let read_opt_str = |ptr: WasmPtr<_>, len| (!ptr.is_null()).then(|| read_str(ptr, len)).transpose(); - - let _ = (|| -> Result<_, MemoryAccessError> { - // Read the `target`, `filename`, and `message` strings from WASM memory. - let target = read_opt_str(target, target_len)?; - let filename = read_opt_str(filename, filename_len)?; - let message = read_str(message, message_len)?; - - // The line number cannot be `u32::MAX` as this represents `Option::None`. - let line_number = (line_number != u32::MAX).then_some(line_number); - - let record = Record { - target: target.as_deref(), - filename: filename.as_deref(), - line_number, - message: &message, - }; - - // Write the log record to the `DatabaseLogger` in the database instance context (dbic). - caller - .data() - .instance_env - .console_log(level.into(), &record, &WasmerBacktraceProvider); - Ok(()) - })(); + // Write the log record to the `DatabaseLogger` in the database instance context (dbic). + env.instance_env + .console_log((level as u8).into(), &record, &caller.as_context()); + Ok(()) + }; + Self::cvt_noret(caller, AbiCall::ConsoleLog, |caller| { + let _ = do_console_log(caller); }) } @@ -396,37 +381,23 @@ impl WasmInstanceEnv { /// - `(row, row_len)` doesn't decode from BSATN to a `ProductValue` /// according to the `ProductType` that the table's schema specifies. #[tracing::instrument(skip_all)] - pub fn insert(caller: FunctionEnvMut<'_, Self>, table_id: u32, row: WasmPtr, row_len: u32) -> RtResult { - let ctx = caller.data().reducer_context(); - let txn_type = &ctx.txn_type(); - let db = &ctx.database(); - let reducer = &ctx.reducer_name().unwrap_or_default().to_owned(); - let syscall = &AbiCall::Insert; - + pub fn insert(caller: Caller<'_, Self>, table_id: u32, row: WasmPtr, row_len: u32) -> RtResult { // TODO: Instead of writing this metric on every insert call, // we should aggregate and write at the end of the transaction. - let _guard = DB_METRICS - .wasm_abi_call_duration_sec - .with_label_values(txn_type, db, reducer, syscall) - .start_timer(); + let _guard = caller.data().start_abi_call_timer(AbiCall::Insert); + + Self::cvt(caller, AbiCall::Insert, |caller| { + let (mem, env) = Self::mem_env(caller); - Self::cvt(caller, "insert", AbiCall::Insert, |caller, mem| { // Read the row from WASM memory into a buffer. - let mut row_buffer = mem.read_bytes(&caller, row, row_len)?; + let row_buffer = mem.deref_slice_mut(row, row_len)?; // Insert the row into the DB. We get back the decoded version. // Then re-encode and write that back into WASM memory at `row`. // We're doing this because of autoinc. - let ctx = caller.data().reducer_context(); - let new_row = caller.data().instance_env.insert(&ctx, table_id.into(), &row_buffer)?; - row_buffer.clear(); - new_row.encode(&mut row_buffer); - assert_eq!( - row_buffer.len(), - row_len as usize, - "autoinc'd row is different encoded size from original row" - ); - mem.set_bytes(&caller, row, row_len, &row_buffer)?; + let ctx = env.reducer_context(); + let new_row = env.instance_env.insert(&ctx, table_id.into(), row_buffer)?; + new_row.encode(&mut { row_buffer }); Ok(()) }) } @@ -450,40 +421,24 @@ impl WasmInstanceEnv { /// - writing to `out` would overflow a 32-bit integer #[tracing::instrument(skip_all)] pub fn delete_by_col_eq( - caller: FunctionEnvMut<'_, Self>, + caller: Caller<'_, Self>, table_id: u32, col_id: u32, value: WasmPtr, value_len: u32, out: WasmPtr, - ) -> RtResult { - let ctx = caller.data().reducer_context(); - let txn_type = &ctx.txn_type(); - let db = &ctx.database(); - let reducer = &ctx.reducer_name().unwrap_or_default().to_owned(); - let syscall = &AbiCall::DeleteByColEq; - - let _guard = DB_METRICS - .wasm_abi_call_duration_sec - .with_label_values(txn_type, db, reducer, syscall) - .start_timer(); - - Self::cvt_ret( - caller, - "delete_by_col_eq", - AbiCall::DeleteByColEq, - out, - |caller, mem| { - let ctx = caller.data().reducer_context(); - let value = mem.read_bytes(&caller, value, value_len)?; - let count = - caller - .data() - .instance_env - .delete_by_col_eq(&ctx, table_id.into(), col_id.into(), &value)?; - Ok(count) - }, - ) + ) -> RtResult { + let _guard = caller.data().start_abi_call_timer(AbiCall::DeleteByColEq); + + Self::cvt_ret(caller, AbiCall::DeleteByColEq, out, |caller| { + let (mem, env) = Self::mem_env(caller); + let ctx = env.reducer_context(); + let value = mem.deref_slice(value, value_len)?; + let count = env + .instance_env + .delete_by_col_eq(&ctx, table_id.into(), col_id.into(), value)?; + Ok(count) + }) } /// Deletes those rows, in the table identified by `table_id`, @@ -504,15 +459,16 @@ impl WasmInstanceEnv { /// - writing to `out` would overflow a 32-bit integer #[tracing::instrument(skip_all)] pub fn delete_by_rel( - caller: FunctionEnvMut<'_, Self>, + caller: Caller<'_, Self>, table_id: u32, relation: WasmPtr, relation_len: u32, out: WasmPtr, - ) -> RtResult { - Self::cvt_ret(caller, "delete_by_rel", AbiCall::DeleteByRel, out, |caller, mem| { - let relation = mem.read_bytes(&caller, relation, relation_len)?; - Ok(caller.data().instance_env.delete_by_rel(table_id.into(), &relation)?) + ) -> RtResult { + Self::cvt_ret(caller, AbiCall::DeleteByRel, out, |caller| { + let (mem, env) = Self::mem_env(caller); + let relation = mem.deref_slice(relation, relation_len)?; + Ok(env.instance_env.delete_by_rel(table_id.into(), relation)?) }) } @@ -528,17 +484,18 @@ impl WasmInstanceEnv { /// - writing to `out` overflows a 32-bit integer #[tracing::instrument(skip_all)] pub fn get_table_id( - caller: FunctionEnvMut<'_, Self>, + caller: Caller<'_, Self>, name: WasmPtr, name_len: u32, out: WasmPtr, - ) -> RtResult { - Self::cvt_ret(caller, "get_table_id", AbiCall::GetTableId, out, |caller, mem| { + ) -> RtResult { + Self::cvt_ret::(caller, AbiCall::GetTableId, out, |caller| { + let (mem, env) = Self::mem_env(caller); // Read the table name from WASM memory. - let name = Self::read_string(&caller, mem, name, name_len)?; + let name = mem.deref_str(name, name_len)?; // Query the table id. - Ok(caller.data().instance_env.get_table_id(name)?.into()) + Ok(env.instance_env.get_table_id(name)?.into()) }) } @@ -561,26 +518,25 @@ impl WasmInstanceEnv { /// Panics if `index_type == 1` or `col_ids.len() != 1`. #[tracing::instrument(skip_all)] pub fn create_index( - caller: FunctionEnvMut<'_, Self>, + caller: Caller<'_, Self>, index_name: WasmPtr, index_name_len: u32, table_id: u32, - index_type: u8, + index_type: u32, col_ids: WasmPtr, col_len: u32, - ) -> RtResult { - Self::cvt(caller, "create_index", AbiCall::CreateIndex, |caller, mem| { + ) -> RtResult { + Self::cvt(caller, AbiCall::CreateIndex, |caller| { + let (mem, env) = Self::mem_env(caller); // Read the index name from WASM memory. - let index_name = Self::read_string(&caller, mem, index_name, index_name_len)?; + let index_name = mem.deref_str(index_name, index_name_len)?.to_owned(); // Read the column ids on which to create an index from WASM memory. // This may be one column or an index on several columns. - let cols = mem.read_bytes(&caller, col_ids, col_len)?; + let cols = mem.deref_slice(col_ids, col_len)?.to_vec(); - caller - .data() - .instance_env - .create_index(index_name, table_id.into(), index_type, cols)?; + env.instance_env + .create_index(index_name, table_id.into(), index_type as u8, cols)?; Ok(()) }) } @@ -604,46 +560,31 @@ impl WasmInstanceEnv { /// - `val + val_len` overflows a 64-bit integer #[tracing::instrument(skip_all)] pub fn iter_by_col_eq( - caller: FunctionEnvMut<'_, Self>, + caller: Caller<'_, Self>, table_id: u32, col_id: u32, val: WasmPtr, val_len: u32, out: WasmPtr, - ) -> RtResult { - let ctx = caller.data().reducer_context(); - let txn_type = &ctx.txn_type(); - let db = &ctx.database(); - let reducer = &ctx.reducer_name().unwrap_or_default().to_owned(); - let syscall = &AbiCall::IterByColEq; - - let _guard = DB_METRICS - .wasm_abi_call_duration_sec - .with_label_values(txn_type, db, reducer, syscall) - .start_timer(); - - Self::cvt_ret( - caller, - "iter_by_col_eq", - AbiCall::IterByColEq, - out, - |mut caller, mem| { - // Read the test value from WASM memory. - let value = mem.read_bytes(&caller, val, val_len)?; - - // Retrieve the execution context for the current reducer. - let ctx = caller.data().reducer_context(); - - // Find the relevant rows. - let data = caller - .data() - .instance_env - .iter_by_col_eq(&ctx, table_id.into(), col_id.into(), &value)?; - - // Insert the encoded + concatenated rows into a new buffer and return its id. - Ok(caller.data_mut().buffers.insert(data.into())) - }, - ) + ) -> RtResult { + let _guard = caller.data().start_abi_call_timer(AbiCall::IterByColEq); + + Self::cvt_ret(caller, AbiCall::IterByColEq, out, |caller| { + let (mem, env) = Self::mem_env(caller); + // Read the test value from WASM memory. + let value = mem.deref_slice(val, val_len)?; + + // Retrieve the execution context for the current reducer. + let ctx = env.reducer_context(); + + // Find the relevant rows. + let data = env + .instance_env + .iter_by_col_eq(&ctx, table_id.into(), col_id.into(), value)?; + + // Insert the encoded + concatenated rows into a new buffer and return its id. + Ok(env.buffers.insert(data.into())) + }) } /// Start iteration on each row, as bytes, of a table identified by `table_id`. @@ -654,28 +595,19 @@ impl WasmInstanceEnv { /// Returns an error if /// - a table with the provided `table_id` doesn't exist // #[tracing::instrument(skip_all)] - pub fn iter_start(caller: FunctionEnvMut<'_, Self>, table_id: u32, out: WasmPtr) -> RtResult { - let ctx = caller.data().reducer_context(); - let txn_type = &ctx.txn_type(); - let db = &ctx.database(); - let reducer = &ctx.reducer_name().unwrap_or_default().to_owned(); - let syscall = &AbiCall::IterStart; - - let _guard = DB_METRICS - .wasm_abi_call_duration_sec - .with_label_values(txn_type, db, reducer, syscall) - .start_timer(); + pub fn iter_start(caller: Caller<'_, Self>, table_id: u32, out: WasmPtr) -> RtResult { + let _guard = caller.data().start_abi_call_timer(AbiCall::IterStart); - Self::cvt_ret(caller, "iter_start", AbiCall::IterStart, out, |mut caller, _mem| { + Self::cvt_ret(caller, AbiCall::IterStart, out, |caller| { + let env = caller.data_mut(); // Retrieve the execution context for the current reducer. - let ctx = caller.data().reducer_context(); - + let ctx = env.reducer_context(); // Collect the iterator chunks. - let chunks = caller.data().instance_env.iter_chunks(&ctx, table_id.into())?; + let chunks = env.instance_env.iter_chunks(&ctx, table_id.into())?; // Register the iterator and get back the index to write to `out`. // Calls to the iterator are done through dynamic dispatch. - Ok(caller.data_mut().iters.insert(chunks.into_iter())) + Ok(env.iters.insert(chunks.into_iter())) }) } @@ -694,46 +626,29 @@ impl WasmInstanceEnv { /// - `filter + filter_len` overflows a 64-bit integer // #[tracing::instrument(skip_all)] pub fn iter_start_filtered( - caller: FunctionEnvMut<'_, Self>, + caller: Caller<'_, Self>, table_id: u32, filter: WasmPtr, filter_len: u32, out: WasmPtr, - ) -> RtResult { - let ctx = caller.data().reducer_context(); - let txn_type = &ctx.txn_type(); - let db = &ctx.database(); - let reducer = &ctx.reducer_name().unwrap_or_default().to_owned(); - let syscall = &AbiCall::IterStartFiltered; - - let _guard = DB_METRICS - .wasm_abi_call_duration_sec - .with_label_values(txn_type, db, reducer, syscall) - .start_timer(); - - Self::cvt_ret( - caller, - "iter_start_filtered", - AbiCall::IterStartFiltered, - out, - |mut caller, _mem| { - // Read the slice `(filter, filter_len)`. - let filter = caller.data().mem().read_bytes(&caller, filter, filter_len)?; - - // Retrieve the execution context for the current reducer. - let ctx = caller.data().reducer_context(); - - // Construct the iterator. - let chunks = caller - .data() - .instance_env - .iter_filtered_chunks(&ctx, table_id.into(), &filter)?; - - // Register the iterator and get back the index to write to `out`. - // Calls to the iterator are done through dynamic dispatch. - Ok(caller.data_mut().iters.insert(chunks.into_iter())) - }, - ) + ) -> RtResult { + let _guard = caller.data().start_abi_call_timer(AbiCall::IterStartFiltered); + + Self::cvt_ret(caller, AbiCall::IterStartFiltered, out, |caller| { + let (mem, env) = Self::mem_env(caller); + // Retrieve the execution context for the current reducer. + let ctx = env.reducer_context(); + + // Read the slice `(filter, filter_len)`. + let filter = mem.deref_slice(filter, filter_len)?; + + // Construct the iterator. + let chunks = env.instance_env.iter_filtered_chunks(&ctx, table_id.into(), filter)?; + + // Register the iterator and get back the index to write to `out`. + // Calls to the iterator are done through dynamic dispatch. + Ok(env.iters.insert(chunks.into_iter())) + }) } /// Advances the registered iterator with the index given by `iter_key`. @@ -748,20 +663,17 @@ impl WasmInstanceEnv { /// - writing to `out` would overflow a 32-bit integer /// - advancing the iterator resulted in an error // #[tracing::instrument(skip_all)] - pub fn iter_next(caller: FunctionEnvMut<'_, Self>, iter_key: u32, out: WasmPtr) -> RtResult { - Self::cvt_ret(caller, "iter_next", AbiCall::IterNext, out, |mut caller, _mem| { - let data_mut = caller.data_mut(); + pub fn iter_next(caller: Caller<'_, Self>, iter_key: u32, out: WasmPtr) -> RtResult { + Self::cvt_ret(caller, AbiCall::IterNext, out, |caller| { + let env = caller.data_mut(); // Retrieve the iterator by `iter_key`. - let iter = data_mut - .iters - .get_mut(BufferIterIdx(iter_key)) - .ok_or_else(|| RuntimeError::new("no such iterator"))?; + let iter = env.iters.get_mut(BufferIterIdx(iter_key)).context("no such iterator")?; // Advance the iterator. Ok(iter .next() - .map_or(BufferIdx::INVALID, |buf| data_mut.buffers.insert(buf.into()))) + .map_or(BufferIdx::INVALID, |buf| env.insert_buffer(buf.into()))) }) } @@ -770,14 +682,14 @@ impl WasmInstanceEnv { /// /// Returns an error if the iterator does not exist. // #[tracing::instrument(skip_all)] - pub fn iter_drop(caller: FunctionEnvMut<'_, Self>, iter_key: u32) -> RtResult { - Self::cvt(caller, "iter_drop", AbiCall::IterDrop, |mut caller, _mem| { + pub fn iter_drop(caller: Caller<'_, Self>, iter_key: u32) -> RtResult { + Self::cvt(caller, AbiCall::IterDrop, |caller| { caller .data_mut() .iters .take(BufferIterIdx(iter_key)) - .ok_or_else(|| RuntimeError::new("no such iterator").into()) - .map(drop) + .context("no such iterator")?; + Ok(()) }) } @@ -788,13 +700,13 @@ impl WasmInstanceEnv { /// /// Returns an error if the buffer does not exist. // #[tracing::instrument(skip_all)] - pub fn buffer_len(caller: FunctionEnvMut<'_, Self>, buffer: u32) -> RtResult { + pub fn buffer_len(caller: Caller<'_, Self>, buffer: u32) -> RtResult { caller .data() .buffers .get(BufferIdx(buffer)) .map(|b| b.len() as u32) - .ok_or_else(|| RuntimeError::new("no such buffer")) + .context("no such buffer") } /// Consumes the `buffer`, @@ -804,20 +716,12 @@ impl WasmInstanceEnv { /// - the buffer does not exist /// - `dst + dst_len` overflows a 64-bit integer // #[tracing::instrument(skip_all)] - pub fn buffer_consume( - mut caller: FunctionEnvMut<'_, Self>, - buffer: u32, - dst: WasmPtr, - dst_len: u32, - ) -> RtResult<()> { - let buf = caller - .data_mut() - .buffers - .take(BufferIdx(buffer)) - .ok_or_else(|| RuntimeError::new("no such buffer"))?; - dst.slice(&caller.data().mem().view(&caller), dst_len) - .and_then(|slice| slice.write_slice(&buf)) - .map_err(mem_err) + pub fn buffer_consume(mut caller: Caller<'_, Self>, buffer: u32, dst: WasmPtr, dst_len: u32) -> RtResult<()> { + let (mem, env) = Self::mem_env(&mut caller); + let buf = env.take_buffer(BufferIdx(buffer)).context("no such buffer")?; + anyhow::ensure!(dst_len as usize == buf.len(), "bad length passed to buffer_consume"); + mem.deref_slice_mut(dst, dst_len)?.copy_from_slice(&buf); + Ok(()) } /// Creates a buffer of size `data_len` in the host environment. @@ -830,30 +734,24 @@ impl WasmInstanceEnv { /// /// Returns an error if `data + data_len` overflows a 64-bit integer. // #[tracing::instrument(skip_all)] - pub fn buffer_alloc(mut caller: FunctionEnvMut<'_, Self>, data: WasmPtr, data_len: u32) -> RtResult { - let buf = caller - .data() - .mem() - .read_bytes(&caller, data, data_len) - .map_err(mem_err)?; - Ok(caller.data_mut().buffers.insert(buf.into()).0) + pub fn buffer_alloc(mut caller: Caller<'_, Self>, data: WasmPtr, data_len: u32) -> RtResult { + let (mem, env) = Self::mem_env(&mut caller); + let buf = mem.deref_slice(data, data_len)?; + Ok(env.buffers.insert(buf.to_vec().into()).0) } - pub fn span_start(mut caller: FunctionEnvMut<'_, Self>, name: WasmPtr, name_len: u32) -> RtResult { - let name = caller - .data() - .mem() - .read_bytes(&caller, name, name_len) - .map_err(mem_err)?; - Ok(caller.data_mut().timing_spans.insert(TimingSpan::new(name)).0) + pub fn span_start(mut caller: Caller<'_, Self>, name: WasmPtr, name_len: u32) -> RtResult { + let (mem, env) = Self::mem_env(&mut caller); + let name = mem.deref_slice(name, name_len)?.to_vec(); + Ok(env.timing_spans.insert(TimingSpan::new(name)).0) } - pub fn span_end(mut caller: FunctionEnvMut<'_, Self>, span_id: u32) -> RtResult<()> { + pub fn span_end(mut caller: Caller<'_, Self>, span_id: u32) -> RtResult<()> { let span = caller .data_mut() .timing_spans .take(TimingSpanIdx(span_id)) - .ok_or_else(|| RuntimeError::new("no such timing span"))?; + .context("no such timing span")?; let elapsed = span.start.elapsed(); @@ -866,32 +764,27 @@ impl WasmInstanceEnv { line_number: None, message: &message, }; - caller.data().instance_env.console_log( - crate::database_logger::LogLevel::Info, - &record, - &WasmerBacktraceProvider, - ); + caller + .data() + .instance_env + .console_log(crate::database_logger::LogLevel::Info, &record, &caller.as_context()); Ok(()) } } -struct WasmerBacktraceProvider; -impl BacktraceProvider for WasmerBacktraceProvider { +impl BacktraceProvider for wasmtime::StoreContext<'_, T> { fn capture(&self) -> Box { - Box::new(RuntimeError::new("")) + Box::new(wasmtime::WasmBacktrace::capture(self)) } } -impl ModuleBacktrace for RuntimeError { +impl ModuleBacktrace for wasmtime::WasmBacktrace { fn frames(&self) -> Vec> { - self.trace() + self.frames() .iter() - .map(|f| { - let module = f.module_name(); - BacktraceFrame { - module_name: (module != "").then_some(module), - func_name: f.function_name(), - } + .map(|f| BacktraceFrame { + module_name: None, + func_name: f.func_name(), }) .collect() } diff --git a/crates/core/src/host/wasmtime/wasmtime_module.rs b/crates/core/src/host/wasmtime/wasmtime_module.rs new file mode 100644 index 00000000000..e5c99895735 --- /dev/null +++ b/crates/core/src/host/wasmtime/wasmtime_module.rs @@ -0,0 +1,269 @@ +use super::wasm_instance_env::WasmInstanceEnv; +use super::{Mem, WasmtimeFuel}; +use crate::host::instance_env::InstanceEnv; +use crate::host::wasm_common::module_host_actor::{DescribeError, InitializationError, ReducerOp}; +use crate::host::wasm_common::*; +use crate::host::EnergyQuanta; +use crate::util::ResultInspectExt; +use anyhow::anyhow; +use bytes::Bytes; +use wasmtime::{AsContext, AsContextMut, ExternType, Instance, InstancePre, Linker, Store, TypedFunc, WasmBacktrace}; + +fn log_traceback(func_type: &str, func: &str, e: &wasmtime::Error) { + log::info!("{} \"{}\" runtime error: {}", func_type, func, e); + if let Some(bt) = e.downcast_ref::() { + let frames_len = bt.frames().len(); + for (i, frame) in bt.frames().iter().enumerate() { + log::info!( + " Frame #{}: {}", + frames_len - i, + rustc_demangle::demangle(frame.func_name().unwrap_or("")) + ); + } + } +} + +#[derive(Clone)] +pub struct WasmtimeModule { + module: InstancePre, +} + +impl WasmtimeModule { + pub(super) fn new(module: InstancePre) -> Self { + WasmtimeModule { module } + } + + pub const IMPLEMENTED_ABI: abi::VersionTuple = abi::VersionTuple::new(7, 0); + + pub(super) fn link_imports(linker: &mut Linker) -> anyhow::Result<()> { + #[allow(clippy::assertions_on_constants)] + const _: () = assert!(WasmtimeModule::IMPLEMENTED_ABI.major == spacetimedb_lib::MODULE_ABI_MAJOR_VERSION); + linker + .func_wrap("spacetime_7.0", "_schedule_reducer", WasmInstanceEnv::schedule_reducer)? + .func_wrap("spacetime_7.0", "_cancel_reducer", WasmInstanceEnv::cancel_reducer)? + .func_wrap("spacetime_7.0", "_delete_by_col_eq", WasmInstanceEnv::delete_by_col_eq)? + .func_wrap("spacetime_7.0", "delete_by_rel", WasmInstanceEnv::delete_by_rel)? + .func_wrap("spacetime_7.0", "_insert", WasmInstanceEnv::insert)? + .func_wrap("spacetime_7.0", "_get_table_id", WasmInstanceEnv::get_table_id)? + .func_wrap("spacetime_7.0", "_create_index", WasmInstanceEnv::create_index)? + .func_wrap("spacetime_7.0", "_iter_by_col_eq", WasmInstanceEnv::iter_by_col_eq)? + .func_wrap("spacetime_7.0", "_iter_start", WasmInstanceEnv::iter_start)? + .func_wrap( + "spacetime_7.0", + "_iter_start_filtered", + WasmInstanceEnv::iter_start_filtered, + )? + .func_wrap("spacetime_7.0", "_iter_next", WasmInstanceEnv::iter_next)? + .func_wrap("spacetime_7.0", "_iter_drop", WasmInstanceEnv::iter_drop)? + .func_wrap("spacetime_7.0", "_console_log", WasmInstanceEnv::console_log)? + .func_wrap("spacetime_7.0", "_buffer_len", WasmInstanceEnv::buffer_len)? + .func_wrap("spacetime_7.0", "_buffer_consume", WasmInstanceEnv::buffer_consume)? + .func_wrap("spacetime_7.0", "_buffer_alloc", WasmInstanceEnv::buffer_alloc)? + .func_wrap("spacetime_7.0", "_span_start", WasmInstanceEnv::span_start)? + .func_wrap("spacetime_7.0", "_span_end", WasmInstanceEnv::span_end)?; + Ok(()) + } +} + +impl module_host_actor::WasmModule for WasmtimeModule { + type Instance = WasmtimeInstance; + type InstancePre = Self; + + type ExternType = ExternType; + + fn get_export(&self, s: &str) -> Option { + self.module + .module() + .exports() + .find(|exp| exp.name() == s) + .map(|exp| exp.ty()) + } + + fn for_each_export(&self, mut f: impl FnMut(&str, &Self::ExternType) -> Result<(), E>) -> Result<(), E> { + self.module + .module() + .exports() + .try_for_each(|exp| f(exp.name(), &exp.ty())) + } + + fn instantiate_pre(&self) -> Result { + Ok(self.clone()) + } +} + +impl module_host_actor::WasmInstancePre for WasmtimeModule { + type Instance = WasmtimeInstance; + + fn instantiate(&self, env: InstanceEnv, func_names: &FuncNames) -> Result { + let env = WasmInstanceEnv::new(env); + let mut store = Store::new(self.module.module().engine(), env); + let instance = self + .module + .instantiate(&mut store) + .map_err(InitializationError::Instantiation)?; + + let mem = Mem::extract(&instance, &mut store).unwrap(); + store.data_mut().instantiate(mem); + + // Note: this budget is just for initializers + let (init_budget, _) = WasmtimeFuel::from_energy_quanta(EnergyQuanta::DEFAULT_BUDGET); + set_store_fuel(&mut store, init_budget); + + for preinit in &func_names.preinits { + let func = instance.get_typed_func::<(), ()>(&mut store, preinit).unwrap(); + func.call(&mut store, ()) + .map_err(|err| InitializationError::RuntimeError { + err, + func: preinit.clone(), + })?; + } + + let init = instance.get_typed_func::<(), u32>(&mut store, SETUP_DUNDER); + if let Ok(init) = init { + match init.call(&mut store, ()).map(BufferIdx) { + Ok(errbuf) if errbuf.is_invalid() => {} + Ok(errbuf) => { + let errbuf = store + .data_mut() + .take_buffer(errbuf) + .unwrap_or_else(|| "unknown error".as_bytes().into()); + let errbuf = crate::util::string_from_utf8_lossy_owned(errbuf.into()).into(); + // TODO: catch this and return the error message to the http client + return Err(InitializationError::Setup(errbuf)); + } + Err(err) => { + return Err(InitializationError::RuntimeError { + err, + func: SETUP_DUNDER.to_owned(), + }); + } + } + } + + let call_reducer = instance + .get_typed_func(&mut store, CALL_REDUCER_DUNDER) + .expect("no call_reducer"); + + Ok(WasmtimeInstance { + store, + instance, + call_reducer, + }) + } +} + +pub struct WasmtimeInstance { + store: Store, + instance: Instance, + call_reducer: TypedFunc<(u32, u32, u32, u64, u32), u32>, +} + +impl module_host_actor::WasmInstance for WasmtimeInstance { + fn extract_descriptions(&mut self) -> Result { + let describer_func_name = DESCRIBE_MODULE_DUNDER; + let describer = self.instance.get_func(&mut self.store, describer_func_name).unwrap(); + + let start = std::time::Instant::now(); + log::trace!("Start describer \"{}\"...", describer_func_name); + + let store = &mut self.store; + let describer = describer + .typed::<(), u32>(&mut *store) + .map_err(|_| DescribeError::Signature)?; + let result = describer.call(&mut *store, ()).map(BufferIdx); + let duration = start.elapsed(); + log::trace!("Describer \"{}\" ran: {} us", describer_func_name, duration.as_micros(),); + let buf = result + .inspect_err_(|err| log_traceback("describer", describer_func_name, err)) + .map_err(DescribeError::RuntimeError)?; + let bytes = store.data_mut().take_buffer(buf).ok_or(DescribeError::BadBuffer)?; + + // Clear all of the instance state associated to this describer call. + store.data_mut().finish_reducer(); + + Ok(bytes) + } + + fn instance_env(&self) -> &InstanceEnv { + self.store.data().instance_env() + } + + type Trap = anyhow::Error; + + fn call_reducer( + &mut self, + op: ReducerOp<'_>, + budget: EnergyQuanta, + ) -> module_host_actor::ExecuteResult { + let store = &mut self.store; + // note that this conversion is load-bearing - although we convert budget right back into + // EnergyQuanta at the end of this function, from_energy_quanta clamps it to a u64 range. + // otherwise, we'd return something like `used: i128::MAX - u64::MAX`, which is inaccurate. + let (budget, _excess_quanta) = WasmtimeFuel::from_energy_quanta(budget); + set_store_fuel(store, budget); + + let mut make_buf = |data| store.data_mut().insert_buffer(data); + + let identity_buf = make_buf(op.caller_identity.as_bytes().to_vec().into()); + let address_buf = make_buf(op.caller_address.as_slice().to_vec().into()); + let args_buf = make_buf(op.arg_bytes); + + store.data_mut().start_reducer(op.name); + + let call_result = self + .call_reducer + .call( + &mut *store, + (op.id.0, identity_buf.0, address_buf.0, op.timestamp.0, args_buf.0), + ) + .and_then(|errbuf| { + let errbuf = BufferIdx(errbuf); + Ok(if errbuf.is_invalid() { + Ok(()) + } else { + let errmsg = store + .data_mut() + .take_buffer(errbuf) + .ok_or_else(|| anyhow!("invalid buffer handle"))?; + Err(crate::util::string_from_utf8_lossy_owned(errmsg.into()).into()) + }) + }); + + // Signal that this reducer call is finished. This gets us the timings + // associated to our reducer call, and clears all of the instance state + // associated to the call. + let timings = store.data_mut().finish_reducer(); + + let remaining: EnergyQuanta = get_store_fuel(store).into(); + let energy = module_host_actor::EnergyStats { + used: EnergyQuanta::from(budget) - remaining, + remaining, + }; + + module_host_actor::ExecuteResult { + energy, + timings, + call_result, + } + } + + fn log_traceback(func_type: &str, func: &str, trap: &Self::Trap) { + log_traceback(func_type, func, trap) + } +} + +fn set_store_fuel(store: &mut impl AsContextMut, fuel: WasmtimeFuel) { + let fuel = fuel.0; + let mut store = store.as_context_mut(); + let rem = store.fuel_remaining().unwrap(); + let diff = rem.abs_diff(fuel); + if rem < fuel { + store.add_fuel(diff).unwrap(); + } else { + store.consume_fuel(diff).unwrap(); + } +} + +fn get_store_fuel(store: &impl AsContext) -> WasmtimeFuel { + WasmtimeFuel(store.as_context().fuel_remaining().unwrap()) +} diff --git a/crates/core/src/messages/control_db.rs b/crates/core/src/messages/control_db.rs index 5e4634c1301..28eb4596974 100644 --- a/crates/core/src/messages/control_db.rs +++ b/crates/core/src/messages/control_db.rs @@ -68,5 +68,5 @@ pub struct NodeStatus { #[strum(serialize_all = "lowercase")] #[repr(i32)] pub enum HostType { - Wasmer = 0, + Wasmtime = 0, } diff --git a/crates/standalone/src/energy_monitor.rs b/crates/standalone/src/energy_monitor.rs index 639a73ae749..e73955acc40 100644 --- a/crates/standalone/src/energy_monitor.rs +++ b/crates/standalone/src/energy_monitor.rs @@ -27,7 +27,7 @@ impl StandaloneEnergyMonitor { impl EnergyMonitor for StandaloneEnergyMonitor { fn reducer_budget(&self, _fingerprint: &EnergyMonitorFingerprint<'_>) -> EnergyQuanta { // Infinitely large reducer budget in Standalone - EnergyQuanta(i128::max_value()) + EnergyQuanta::new(i128::max_value()) } fn record( @@ -73,7 +73,7 @@ impl Inner { .control_db .get_energy_balance(&fingerprint.module_identity) .unwrap() - .unwrap_or(EnergyQuanta(0)); - EnergyQuanta(i128::max(balance.0, 0)) + .unwrap_or(EnergyQuanta::ZERO); + std::cmp::max(balance, EnergyQuanta::ZERO) } } diff --git a/crates/standalone/src/lib.rs b/crates/standalone/src/lib.rs index 47e93cbef8b..79b1f0a5ec6 100644 --- a/crates/standalone/src/lib.rs +++ b/crates/standalone/src/lib.rs @@ -304,7 +304,7 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { id: 0, address: spec.address, identity: *identity, - host_type: HostType::Wasmer, + host_type: HostType::Wasmtime, num_replicas: spec.num_replicas, program_bytes_address, publisher_address, @@ -385,17 +385,17 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { async fn add_energy(&self, identity: &Identity, amount: EnergyQuanta) -> spacetimedb::control_db::Result<()> { let mut balance = ::get_energy_balance(self, identity)? - .map(|quanta| quanta.0) - .unwrap_or(0); - balance = balance.saturating_add(amount.0); + .map_or(0, |quanta| quanta.get()); + balance = balance.saturating_add(amount.get()); - self.control_db.set_energy_balance(*identity, EnergyQuanta(balance)) + self.control_db + .set_energy_balance(*identity, EnergyQuanta::new(balance)) } async fn withdraw_energy(&self, identity: &Identity, amount: EnergyQuanta) -> spacetimedb::control_db::Result<()> { let energy_balance = self.control_db.get_energy_balance(identity)?; - let energy_balance = energy_balance.unwrap_or(EnergyQuanta(0)); - log::trace!("Withdrawing {} energy from {}", amount.0, identity); - log::trace!("Old balance: {}", energy_balance.0); + let energy_balance = energy_balance.unwrap_or(EnergyQuanta::new(0)); + log::trace!("Withdrawing {} energy from {}", amount.get(), identity); + log::trace!("Old balance: {}", energy_balance.get()); let new_balance = energy_balance - amount; self.control_db.set_energy_balance(*identity, new_balance.as_quanta()) }