├── native └── orb_wasmtime │ ├── .gitignore │ ├── src │ ├── atom.rs │ └── lib.rs │ ├── Cross.toml │ ├── Cargo.toml │ ├── README.md │ ├── .cargo │ └── config.toml │ └── Cargo.lock ├── test ├── test_helper.exs ├── orb_wasmtime_test.exs └── orb_wasmtime │ ├── instance │ ├── int_test.exs │ ├── import_caller_test.exs │ ├── import_test.exs │ ├── instance_test.exs │ └── url_encoded_test.exs │ ├── wat2wasm_test.exs │ └── wasm_test.exs ├── .formatter.exs ├── lib ├── orb_wasmtime.ex └── orb_wasmtime │ ├── rust.ex │ ├── instance.ex │ └── wasm.ex ├── Makefile ├── .gitignore ├── samples └── memory.exs ├── LICENSE ├── README.md ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── mix.exs └── mix.lock /native/orb_wasmtime/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /test/orb_wasmtime_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtimeTest do 2 | use ExUnit.Case 3 | doctest OrbWasmtime 4 | 5 | test "greets the world" do 6 | assert OrbWasmtime.hello() == :world 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/orb_wasmtime.ex: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime do 2 | @moduledoc """ 3 | Execute WebAssembly via Wasmtime. 4 | """ 5 | 6 | @doc """ 7 | Hello world. 8 | 9 | ## Examples 10 | 11 | iex> OrbWasmtime.hello() 12 | :world 13 | 14 | """ 15 | def hello do 16 | :world 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /native/orb_wasmtime/src/atom.rs: -------------------------------------------------------------------------------- 1 | rustler::atoms! { 2 | ok, 3 | error, 4 | 5 | i32, 6 | i64, 7 | f32, 8 | f64, 9 | 10 | run_instance_start, 11 | reply_to_func_call_out, 12 | // v128, 13 | // extern_ref, 14 | // func_ref, 15 | 16 | // func, 17 | // global, 18 | // table, 19 | // memory, 20 | // call_exfn, 21 | // gen_reply 22 | } 23 | -------------------------------------------------------------------------------- /native/orb_wasmtime/Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = [ 3 | "RUSTLER_NIF_VERSION" 4 | ] 5 | 6 | [target.riscv64gc-unknown-linux-gnu] 7 | pre-build = ["apt update && apt install -y cmake"] 8 | 9 | [target.aarch64-unknown-linux-gnu] 10 | pre-build = ["apt update && apt install build-essential && apt install libstdc++6 && apt install libclang-dev"] -------------------------------------------------------------------------------- /native/orb_wasmtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "orb_wasmtime" 3 | version = "0.1.17" 4 | authors = [] 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "orb_wasmtime" 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | rustler = "0.29.0" 14 | anyhow = "1.0" 15 | crossbeam-channel = "0.5" 16 | wasmtime = "19.0.0" 17 | wabt = "0.10.0" 18 | -------------------------------------------------------------------------------- /native/orb_wasmtime/README.md: -------------------------------------------------------------------------------- 1 | # NIF for Elixir.OrbWasmtime 2 | 3 | ## To build the NIF module: 4 | 5 | - Your NIF will now build along with your project. 6 | 7 | ## To load the NIF: 8 | 9 | ```elixir 10 | defmodule OrbWasmtime do 11 | use Rustler, otp_app: :orb_wasmtime, crate: "orb_wasmtime" 12 | 13 | # When your NIF is loaded, it will override this function. 14 | def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded) 15 | end 16 | ``` 17 | 18 | ## Examples 19 | 20 | [This](https://github.com/rusterlium/NifIo) is a complete example of a NIF written in Rust. 21 | -------------------------------------------------------------------------------- /native/orb_wasmtime/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-apple-darwin] 2 | rustflags = [ 3 | "-C", "link-arg=-undefined", 4 | "-C", "link-arg=dynamic_lookup", 5 | ] 6 | 7 | [target.aarch64-apple-darwin] 8 | rustflags = [ 9 | "-C", "link-arg=-undefined", 10 | "-C", "link-arg=dynamic_lookup" 11 | ] 12 | 13 | [target.x86_64-unknown-linux-musl] 14 | rustflags = [ 15 | "-C", "target-feature=-crt-static", 16 | "-C", "link-arg=-lstdc++", 17 | "-L", "/usr/lib/gcc/x86_64-linux-gnu/10" 18 | ] 19 | 20 | [target.aarch64-unknown-linux-musl] 21 | rustflags = [ 22 | "-C", "target-feature=-crt-static", 23 | "-l", "static=stdc++", 24 | "-L", "/usr/lib/gcc/aarch64-linux-gnu/10" 25 | ] 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: force_build 2 | force_build: 3 | ORB_WASMTIME_BUILD=1 mix compile 4 | 5 | .PHONY: cargo_check 6 | cargo_check: 7 | cd native/orb_wasmtime && cargo check 8 | 9 | .PHONY: cargo_clean 10 | cargo_clean: 11 | cd native/orb_wasmtime && cargo clean 12 | 13 | .PHONY: test 14 | test: 15 | mix format --check-formatted 16 | ORB_WASMTIME_BUILD=1 mix test 17 | 18 | .PHONY: rustler_precompiled_download 19 | rustler_precompiled_download: checksum-Elixir.OrbWasmtime.Rust.exs cargo_clean 20 | mix hex.build 21 | 22 | checksum-Elixir.OrbWasmtime.Rust.exs: mix.exs 23 | mix rustler_precompiled.download OrbWasmtime.Rust --all --print --ignore-unavailable 24 | $(MAKE) rustler_precompiled_download 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | orb_wasmtime-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | 28 | .DS_Store 29 | 30 | # Rustler 31 | priv/native/ 32 | /checksum-Elixir.OrbWasmtime.Rust.exs 33 | -------------------------------------------------------------------------------- /samples/memory.exs: -------------------------------------------------------------------------------- 1 | wasm_module_memory = fn min_size -> 2 | """ 3 | (module $example 4 | (memory #{min_size}) 5 | (func (export "answer") (result i32) 6 | (i32.const 42) 7 | ) 8 | (func (export "answer_loop") (result i32) 9 | (local $i i32) 10 | (loop $increment 11 | (local.get $i) 12 | (i32.add (i32.const 1)) 13 | (local.tee $i) 14 | (i32.eq 42) 15 | (br_if $increment) 16 | ) 17 | (local.get $i) 18 | ) 19 | ) 20 | """ 21 | end 22 | 23 | wasm_1 = wasm_module_memory.(1) 24 | wasm_100 = wasm_module_memory.(100) 25 | wasm_10_000 = wasm_module_memory.(10_000) 26 | 27 | alias OrbWasmtime.Wasm 28 | 29 | Benchee.run( 30 | %{ 31 | "(memory 1)" => fn -> Wasm.call(wasm_1, :answer) end, 32 | "(memory 1) loop" => fn -> Wasm.call(wasm_1, :answer_loop) end, 33 | "(memory 1) x 2" => fn -> Wasm.call(wasm_1, :answer); Wasm.call(wasm_1, :answer) end, 34 | "(memory 100)" => fn -> Wasm.call(wasm_100, :answer) end, 35 | "(memory 100) x 2" => fn -> Wasm.call(wasm_100, :answer); Wasm.call(wasm_100, :answer) end, 36 | "(memory 10,000)" => fn -> Wasm.call(wasm_10_000, :answer) end, 37 | "(memory 10,000) x 2" => fn -> Wasm.call(wasm_10_000, :answer); Wasm.call(wasm_10_000, :answer) end, 38 | }, 39 | memory_time: 2 40 | ) 41 | -------------------------------------------------------------------------------- /test/orb_wasmtime/instance/int_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Instance.IntTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias OrbWasmtime.Instance 5 | alias OrbWasmtime.Wasm 6 | 7 | test "passing 32-bit integers" do 8 | inst = Instance.run(wat()) 9 | identity_i32 = Instance.capture(inst, :u32, :identity_i32, 1) 10 | 11 | assert identity_i32.(0x0000_0000) === 0x0000_0000 12 | assert identity_i32.(0x0000_0001) === 0x0000_0001 13 | assert identity_i32.(0x00AB_CDEF) === 0x00AB_CDEF 14 | assert identity_i32.(0x7FFF_FFFF) === 0x7FFF_FFFF 15 | assert identity_i32.(0x8FFF_FFFF) === 0x8FFF_FFFF 16 | assert identity_i32.(0x9FFF_FFFF) === 0x9FFF_FFFF 17 | assert identity_i32.(0xEFFF_FFFF) === 0xEFFF_FFFF 18 | assert identity_i32.(0xFFFF_FFFF) === 0xFFFF_FFFF 19 | end 20 | 21 | defp wat() do 22 | """ 23 | (module 24 | (func $identity_i32 (export "identity_i32") (param $a i32) (result i32) 25 | (local.get $a) 26 | ) 27 | ) 28 | """ 29 | end 30 | 31 | @mathI64 ~S""" 32 | (module $MathI64 33 | (func $math (export "math") (param $a i64) (param $b i64) (result i64) 34 | (local $denominator i64) 35 | (i64.sub (i64.add (i64.const 4) (local.get $a)) (local.get $b)) 36 | (local.set $denominator) 37 | (i64.div_s (i64.mul (local.get $a) (local.get $b)) (local.get $denominator)) 38 | ) 39 | ) 40 | """ 41 | 42 | test "call with i64" do 43 | assert @mathI64 |> Wasm.call(:math, {:i64, 11}, {:i64, 7}) === 9 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Patrick George Wyndham Smith 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OrbWasmtime 2 | 3 | Run WebAssembly modules in Elixir via Rust & Wasmtime. 4 | 5 | **Note: this project is in alpha and will change. For another WebAssembly runtime for Elixir [check out Wasmex](https://github.com/tessi/wasmex).** 6 | 7 | ## Installation 8 | 9 | Add `orb_wasmtime` to your list of dependencies in `mix.exs`: 10 | 11 | ```elixir 12 | def deps do 13 | [ 14 | {:orb_wasmtime, "~> 0.1.2"} 15 | ] 16 | end 17 | ``` 18 | 19 | ## About 20 | 21 | OrbWasmtime lets you run WebAssembly modules. You can call functions, list exports, pass imports, get/set globals, and read/write memory. 22 | 23 | ```elixir 24 | defmodule Example do 25 | alias OrbWasmtime.Instance 26 | 27 | def run() do 28 | inst = Instance.run(example_wat()) 29 | add = Instance.capture(inst, :add, 2) 30 | add.(2, 3) # 5 31 | add.(4, 5) # 9 32 | end 33 | 34 | defp example_wat() do 35 | """ 36 | (module $Add 37 | (func (export "add") (param $a i32) (param $b i32) (result i32) 38 | (i32.add (local.get $a) (local.get $b)) 39 | ) 40 | ) 41 | """ 42 | end 43 | end 44 | ``` 45 | 46 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) 47 | and published on [HexDocs](https://hexdocs.pm). Once published, the docs can 48 | be found at . 49 | 50 | ## Releasing 51 | 52 | ```bash 53 | git tag v0.x.x 54 | git push --tags 55 | # Wait for GitHub Action to succeed 56 | mix rustler_precompiled.download OrbWasmtime.Rust --all --print --ignore-unavailable 57 | rm -rf native/orb_wasmtime/target/ 58 | mix hex.publish 59 | ``` 60 | -------------------------------------------------------------------------------- /test/orb_wasmtime/instance/import_caller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Instance.ImportCallerTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias OrbWasmtime.Instance 5 | 6 | test "can read and write strings" do 7 | inst = 8 | Instance.run(wat(), [ 9 | {:test, :write_abc, 10 | fn caller, memory_offset -> 11 | len = Instance.Caller.write_string_nul_terminated(caller, memory_offset, "abc") 12 | assert len === 4 13 | len 14 | end}, 15 | {:test, :strlen, 16 | fn caller, memory_offset -> 17 | s = Instance.Caller.read_string_nul_terminated(caller, memory_offset) 18 | assert s === "hello" 19 | byte_size(s) 20 | end} 21 | ]) 22 | 23 | f = Instance.capture(inst, :test, 0) 24 | 25 | assert f.() === nil 26 | end 27 | 28 | defp wat() do 29 | """ 30 | (module 31 | (import "test" "write_abc" (func $write_abc (param i32) (result i32))) 32 | (import "test" "strlen" (func $strlen (param i32) (result i32))) 33 | (memory (export "memory") 2) 34 | (data (i32.const 0x100) "hello") 35 | (func $assert! (param $condition i32) 36 | (if (local.get $condition) 37 | (then nop) 38 | (else unreachable) 39 | ) 40 | ) 41 | (func (export "test") 42 | (call $strlen (i32.const 0x100)) 43 | (call $assert! (i32.eq (i32.const 5))) 44 | 45 | (call $write_abc (i32.const 0x200)) 46 | drop 47 | (call $assert! (i32.eq (i32.load8_u (i32.const 0x200)) (i32.const #{?a}))) 48 | ) 49 | ) 50 | """ 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | pull_request: 6 | workflow_dispatch: 7 | 8 | env: 9 | ELIXIR_VERSION: 1.16 10 | OTP_VERSION: 26 11 | MIX_ENV: test 12 | ORB_WASMTIME_BUILD: "true" 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-latest 17 | name: test 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: actions/cache@v4 21 | with: 22 | path: | 23 | deps 24 | _build 25 | key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} 26 | restore-keys: | 27 | ${{ runner.os }}-mix- 28 | - uses: actions/cache@v3 29 | with: 30 | path: | 31 | ~/.cargo/bin/ 32 | ~/.cargo/registry/index/ 33 | ~/.cargo/registry/cache/ 34 | ~/.cargo/git/db/ 35 | native/orb_wasmtime/target/ 36 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 37 | 38 | - name: Install Rust toolchain 39 | uses: dtolnay/rust-toolchain@stable 40 | 41 | - uses: erlef/setup-beam@v1 42 | with: 43 | otp-version: "${{ env.OTP_VERSION }}" 44 | elixir-version: "${{ env.ELIXIR_VERSION }}" 45 | - run: mix deps.get 46 | - run: mix compile #--warnings-as-errors 47 | - run: mix test #--warnings-as-errors 48 | format: 49 | runs-on: ubuntu-latest 50 | name: mix format 51 | steps: 52 | - uses: actions/checkout@v4 53 | - uses: erlef/setup-beam@v1 54 | with: 55 | otp-version: "${{ env.OTP_VERSION }}" 56 | elixir-version: "${{ env.ELIXIR_VERSION }}" 57 | - run: mix format --check-formatted 58 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.MixProject do 2 | use Mix.Project 3 | 4 | @version "0.1.17" 5 | @source_url "https://github.com/RoyalIcing/orb_wasmtime" 6 | @force_build? System.get_env("ORB_WASMTIME_BUILD") in ["1", "true"] 7 | 8 | def project do 9 | [ 10 | app: :orb_wasmtime, 11 | name: "Orb Wasmtime", 12 | description: "Run WebAssembly in Elixir via Wasmtime", 13 | version: @version, 14 | elixir: "~> 1.15", 15 | start_permanent: Mix.env() == :prod, 16 | deps: deps(), 17 | package: package(), 18 | docs: docs(), 19 | preferred_cli_env: [ 20 | docs: :docs, 21 | "hex.publish": :docs 22 | ] 23 | ] 24 | end 25 | 26 | # Run "mix help compile.app" to learn about applications. 27 | def application do 28 | [ 29 | extra_applications: [:logger] 30 | ] 31 | end 32 | 33 | # Run "mix help deps" to learn about dependencies. 34 | defp deps do 35 | [ 36 | {:ex_doc, "~> 0.31.2", only: :docs, runtime: false}, 37 | {:rustler, "~> 0.34.0", optional: not @force_build?}, 38 | {:rustler_precompiled, "~> 0.7.2"}, 39 | {:benchee, "~> 1.0", only: :dev} 40 | # {:dep_from_hexpm, "~> 0.3.0"}, 41 | # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} 42 | ] 43 | end 44 | 45 | defp docs do 46 | [ 47 | main: "OrbWasmtime", 48 | source_ref: "v#{@version}", 49 | source_url: @source_url, 50 | extras: [ 51 | "LICENSE" 52 | # "notebooks/pretrained.livemd" 53 | ] 54 | ] 55 | end 56 | 57 | defp package do 58 | [ 59 | files: [ 60 | "lib", 61 | "native", 62 | "checksum-*.exs", 63 | "mix.exs", 64 | # , 65 | "README.md" 66 | # "LICENSE" 67 | ], 68 | licenses: ["BSD-3-Clause"], 69 | links: %{"GitHub" => @source_url}, 70 | maintainers: ["Patrick Smith"] 71 | ] 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /test/orb_wasmtime/instance/import_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Instance.ImportTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias OrbWasmtime.Instance 5 | 6 | test "passing 32-bit integers" do 7 | pid = self() 8 | 9 | inst = 10 | Instance.run(wat(), [ 11 | {:log, :int32, 12 | fn value -> 13 | Process.send(pid, {:logi32, value}, []) 14 | 42 15 | end}, 16 | {:log, :float32, 17 | fn value -> 18 | Process.send(pid, {:logf32, value}, []) 19 | 12.5 20 | end}, 21 | {:math, :powf32, 22 | fn a, b -> 23 | Float.pow(a, b) 24 | end}, 25 | {:log, :ping, 26 | fn -> 27 | nil 28 | end}, 29 | {:generate, :magic_numbers, 30 | fn -> 31 | {42, 99.0} 32 | end} 33 | ]) 34 | 35 | f = Instance.capture(inst, :test, 0) 36 | 37 | f.() 38 | assert_receive({:logi32, 1}) 39 | assert_receive({:logi32, 42}) 40 | assert_receive({:logf32, 1.5}) 41 | assert_receive({:logf32, 12.5}) 42 | assert_receive({:logf32, 8.0}) 43 | assert_receive({:logf32, 99.0}) 44 | assert_receive({:logi32, 42}) 45 | end 46 | 47 | defp wat() do 48 | """ 49 | (module 50 | (import "log" "int32" (func $logi32 (param i32) (result i32))) 51 | (import "log" "float32" (func $logf32 (param f32) (result f32))) 52 | (import "math" "powf32" (func $powf32 (param f32 f32) (result f32))) 53 | (import "log" "ping" (func $ping)) 54 | (import "generate" "magic_numbers" (func $magic_numbers (result i32 f32))) 55 | (func (export "test") 56 | (local $i i32) 57 | (local $f f32) 58 | 59 | (call $logi32 (i32.const 1)) 60 | (local.set $i) 61 | (call $logi32 (local.get $i)) 62 | drop 63 | 64 | (call $logf32 (f32.const 1.5)) 65 | (local.set $f) 66 | (call $logf32 (local.get $f)) 67 | drop 68 | 69 | (call $powf32 (f32.const 2.0) (f32.const 3.0)) 70 | (call $logf32) 71 | drop 72 | 73 | (call $ping) 74 | 75 | (call $magic_numbers) 76 | (call $logf32) 77 | drop 78 | (call $logi32) 79 | drop 80 | ) 81 | ) 82 | """ 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /test/orb_wasmtime/instance/instance_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Instance.InstanceTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias OrbWasmtime.Instance 5 | alias OrbWasmtime.Wasm 6 | 7 | test "run_instance with wat via wat2wasm" do 8 | wat_source = """ 9 | (module $single_func 10 | (func (export "answer") (result i32) 11 | i32.const 42 12 | ) 13 | ) 14 | """ 15 | 16 | wasm_source = wat_source |> Wasm.to_wasm() 17 | assert wasm_source =~ "\0asm" 18 | 19 | instance = Instance.run(wasm_source) 20 | assert Instance.call(instance, "answer") == 42 21 | end 22 | 23 | test "run_instance with wasm" do 24 | wasm_source = 25 | <<"\0asm", 0x01000000::32>> <> 26 | <<0x01, 0x07, 0x01, (<<0x60, 0x02, 0x7E, 0x7E, 0x01, 0x7E>>)>> <> 27 | <<0x03, 0x02, 0x01, 0x00>> <> 28 | <<0x07, 0x08, 0x01, 0x04, "math", 0x00, 0x00>> <> 29 | <<0x0A, 0x18, 0x01, 0x16, <<0x01, 0x01, 0x7E>>, 30 | <<"", <<0x42, 0x04>>, <<0x20, 0x00>>, 0x7C, <<0x20, 0x01>>, 0x7D>>, <<0x21, 0x02>>, 31 | <<"", <<0x20, 0x00>>, <<0x20, 0x01>>, 0x7E, <<0x20, 0x02>>, 0x7F>>, 0x0B>> 32 | 33 | instance = Instance.run(wasm_source) 34 | assert Instance.call(instance, "math", {:i64, 11}, {:i64, 7}) == 9 35 | end 36 | 37 | test "run_instance with wasm 2" do 38 | # 0x72, 0x65, 0x61, 0x64, 0x5F, 0x75, 0x38 39 | 40 | wasm_source = 41 | <<"\0asm", 0x01000000::32>> <> 42 | <<0x1, 0x6, 0x1, 0x60, 0x1, 0x7F, 0x1, 0x7F, 0x3, 0x3, 0x2, 0x0, 0x0, 0x5, 0x3, 0x1, 0x0, 43 | 0x1, 0x7, 0x1F, 0x3, 0x6, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x2, 0x0, 0x7, "read_u8", 44 | 0x0, 0x0, 0x8, "read_i32", 0x0, 0x1, 0xA, 0x11, 0x2, 0x7, 0x0, 0x20, 0x0, 0x2D, 0x0, 45 | 0x0, 0xB, 0x7, 0x0, 0x20, 0x0, 0x28, 0x2, 0x0, 0xB, 0xB, 0x40, 0x6, 0x0, 0x41, 0x80, 46 | 0x2, 0xB, 0x3, 0x61, 0x62, 0x63, 0x0, 0x41, 0x80, 0x4, 0xB, 0x3, 0x64, 0x65, 0x66, 0x0, 47 | 0x41, 0x80, 0x6, 0xB, 0x4, 0x1, 0x2, 0x3, 0x4, 0x0, 0x41, 0x80, 0x8, 0xB, 0x1, 0x7B, 48 | 0x0, 0x41, 0x80, 0xA, 0xB, 0x4, 0x4, 0x10, 0x3, 0xC, 0x0, 0x41, 0x80, 0xC, 0xB, 0xC, 49 | 0x4, 0x0, 0x0, 0x0, 0x40, 0xE2, 0x1, 0x0, 0x3, 0x0, 0x0, 0x0>> 50 | 51 | instance = Instance.run(wasm_source) 52 | assert 1 = Instance.call(instance, "read_u8", 0x300) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/orb_wasmtime/rust.ex: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Rust do 2 | # if false and Mix.env() == :dev do 3 | # use Rustler, otp_app: :components_guide, crate: :componentsguide_rustler_math 4 | 5 | # Inspired by https://github.com/tessi/wasmex 6 | 7 | mix_config = Mix.Project.config() 8 | version = mix_config[:version] 9 | github_url = mix_config[:package][:links]["GitHub"] 10 | # Since Rustler 0.27.0, we need to change manually the mode for each env. 11 | # We want "debug" in dev and test because it's faster to compile. 12 | # mode = if Mix.env() in [:dev, :test], do: :debug, else: :release 13 | 14 | use RustlerPrecompiled, 15 | otp_app: :orb_wasmtime, 16 | base_url: "#{github_url}/releases/download/v#{version}", 17 | version: version, 18 | # riscv64gc-unknown-linux-gnu 19 | targets: ~w( 20 | aarch64-apple-darwin 21 | aarch64-unknown-linux-gnu 22 | aarch64-unknown-linux-musl 23 | x86_64-apple-darwin 24 | x86_64-pc-windows-gnu 25 | x86_64-pc-windows-msvc 26 | x86_64-unknown-linux-gnu 27 | x86_64-unknown-linux-musl 28 | ), 29 | # mode: mode, 30 | force_build: System.get_env("ORB_WASMTIME_BUILD") in ["1", "true"] 31 | 32 | defp error, do: :erlang.nif_error(:nif_not_loaded) 33 | 34 | def add(_, _), do: error() 35 | def reverse_string(_), do: error() 36 | 37 | def strlen(_), do: error() 38 | 39 | def wasm_list_exports(_), do: error() 40 | def wasm_list_imports(_), do: error() 41 | 42 | def wasm_call(_, _, _), do: error() 43 | def wasm_call_i32_string(_, _, _), do: error() 44 | 45 | def wasm_steps(_, _), do: error() 46 | 47 | def wasm_run_instance(_, _, _, _), do: error() 48 | def wasm_instance_get_global_i32(_, _), do: error() 49 | def wasm_instance_set_global_i32(_, _, _), do: error() 50 | def wasm_instance_call_func(_, _, _), do: error() 51 | def wasm_instance_call_func_i32_string(_, _, _), do: error() 52 | def wasm_instance_cast_func_i32(_, _, _), do: error() 53 | def wasm_instance_write_i32(_, _, _), do: error() 54 | def wasm_instance_write_i64(_, _, _), do: error() 55 | def wasm_instance_write_memory(_, _, _), do: error() 56 | def wasm_instance_write_string_nul_terminated(_, _, _), do: error() 57 | def wasm_instance_read_memory(_, _, _), do: error() 58 | def wasm_instance_read_string_nul_terminated(_, _), do: error() 59 | def wasm_call_out_reply(_, _), do: error() 60 | def wasm_caller_read_string_nul_terminated(_, _), do: error() 61 | def wasm_caller_write_string_nul_terminated(_, _, _), do: error() 62 | 63 | def wat2wasm(_), do: error() 64 | def validate_module_definition(_), do: error() 65 | end 66 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build precompiled NIFs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | # Just run on main branch if "native" path changed. Tags will always run. 9 | - "native/**" 10 | # If deps change 11 | - "mix.exs" 12 | - "lib/orb_wasmtime/rust.ex" 13 | # Also run in case there is any changes to this file. 14 | - ".github/workflows/release.yml" 15 | tags: 16 | - "*" 17 | 18 | jobs: 19 | build_release: 20 | name: NIF ${{ matrix.nif }} - ${{ matrix.job.target }} (${{ matrix.job.os }}) 21 | runs-on: ${{ matrix.job.os }} 22 | permissions: 23 | contents: write 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | nif: ["2.15", "2.16"] 28 | job: 29 | - { target: aarch64-apple-darwin, os: macos-13 } 30 | - { 31 | target: aarch64-unknown-linux-gnu, 32 | os: ubuntu-20.04, 33 | use-cross: true, 34 | } 35 | #- { target: aarch64-unknown-linux-musl, os: ubuntu-20.04, use-cross: true } 36 | # - { target: arm-unknown-linux-gnueabihf, os: ubuntu-20.04, use-cross: true } 37 | #- { 38 | # target: riscv64gc-unknown-linux-gnu, 39 | # os: ubuntu-20.04, 40 | # use-cross: true, 41 | # cargo-args: "--no-default-features", 42 | # } 43 | - { target: x86_64-apple-darwin, os: macos-13 } 44 | - { target: x86_64-pc-windows-gnu, os: windows-2019 } 45 | - { target: x86_64-pc-windows-msvc, os: windows-2019 } 46 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-20.04 } 47 | # - { target: x86_64-unknown-linux-musl, os: ubuntu-20.04, use-cross: true } 48 | 49 | steps: 50 | - name: Checkout source code 51 | uses: actions/checkout@v4 52 | 53 | - name: Extract crate information 54 | shell: bash 55 | run: | 56 | # Get the project version from mix.exs 57 | echo "PROJECT_VERSION=$(sed -n 's/^ @version "\(.*\)"/\1/p' mix.exs | head -n1)" >> $GITHUB_ENV 58 | 59 | - name: Install Rust toolchain 60 | uses: dtolnay/rust-toolchain@stable 61 | with: 62 | target: ${{ matrix.job.target }} 63 | 64 | - name: Build the project 65 | id: build-crate 66 | uses: philss/rustler-precompiled-action@v1.0.1 67 | with: 68 | nif-version: ${{ matrix.nif }} 69 | project-dir: "native/orb_wasmtime" 70 | project-name: orb_wasmtime 71 | project-version: ${{ env.PROJECT_VERSION }} 72 | target: ${{ matrix.job.target }} 73 | use-cross: ${{ matrix.job.use-cross }} 74 | 75 | - name: Artifact upload 76 | uses: actions/upload-artifact@v3 77 | with: 78 | name: ${{ steps.build-crate.outputs.file-name }} 79 | path: ${{ steps.build-crate.outputs.file-path }} 80 | 81 | - name: Publish archives and packages 82 | uses: softprops/action-gh-release@v1 83 | with: 84 | files: | 85 | ${{ steps.build-crate.outputs.file-path }} 86 | if: startsWith(github.ref, 'refs/tags/') 87 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"}, 3 | "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, 4 | "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, 5 | "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, 6 | "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"}, 7 | "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, 8 | "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, 9 | "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, 10 | "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, 11 | "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, 12 | "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, 13 | "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, 14 | "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, 15 | "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, 16 | "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, 17 | "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, 18 | "req": {:hex, :req, "0.5.4", "e375e4812adf83ffcf787871d7a124d873e983e3b77466e6608b973582f7f837", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "a17998ffe2ef54f79bfdd782ef9f4cbf987d93851e89444cbc466a6a25eee494"}, 19 | "rustler": {:hex, :rustler, "0.34.0", "e9a73ee419fc296a10e49b415a2eb87a88c9217aa0275ec9f383d37eed290c1c", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "1d0c7449482b459513003230c0e2422b0252245776fe6fd6e41cb2b11bd8e628"}, 20 | "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.2", "097f657e401f02e7bc1cab808cfc6abdc1f7b9dc5e5adee46bf2fd8fdcce9ecf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "7663faaeadc9e93e605164dcf9e69168e35f2f8b7f2b9eb4e400d1a8e0fe2999"}, 21 | "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, 22 | "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, 23 | "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, 24 | } 25 | -------------------------------------------------------------------------------- /lib/orb_wasmtime/instance.ex: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Instance do 2 | @moduledoc """ 3 | Instantiate a WebAssembly module and interact with it: call functions, get/set globals, and read/write memory. 4 | """ 5 | 6 | alias OrbWasmtime.Wasm 7 | alias OrbWasmtime.Rust 8 | 9 | alias __MODULE__ 10 | 11 | require Logger 12 | 13 | defstruct [:elixir_mod, :exports, :handle] 14 | 15 | def run(input, imports \\ []) do 16 | exports = Wasm.grouped_exports(input) 17 | handle = Wasm.run_instance(input, imports) 18 | %__MODULE__{elixir_mod: input, exports: exports, handle: handle} 19 | rescue 20 | x in [RuntimeError, Protocol.UndefinedError] -> 21 | wat = 22 | case input do 23 | s when is_binary(s) -> s 24 | mod when is_atom(mod) -> mod.to_wat() 25 | end 26 | 27 | case Wasm.to_wasm(wat) do 28 | {:error, reason} -> 29 | Logger.error(reason) 30 | 31 | _ -> 32 | Logger.error(wat) 33 | end 34 | 35 | # Logger.error(wat) 36 | reraise x, __STACKTRACE__ 37 | end 38 | 39 | defdelegate get_global(instance, global_name), to: Wasm, as: :instance_get_global 40 | defdelegate set_global(instance, global_name, new_value), to: Wasm, as: :instance_set_global 41 | 42 | defdelegate read_memory(instance, start, length), to: Wasm, as: :instance_read_memory 43 | defdelegate write_i32(instance, memory_offset, value), to: Wasm, as: :instance_write_i32 44 | defdelegate write_i64(instance, memory_offset, value), to: Wasm, as: :instance_write_i64 45 | 46 | defdelegate write_memory(instance, memory_offset, bytes), 47 | to: Wasm, 48 | as: :instance_write_memory 49 | 50 | defdelegate read_string_nul_terminated(instance, memory_offset), 51 | to: Wasm, 52 | as: :instance_read_string_nul_terminated 53 | 54 | defdelegate write_string_nul_terminated(instance, memory_offset, string), 55 | to: Wasm, 56 | as: :instance_write_string_nul_terminated 57 | 58 | defdelegate call(instance, f), to: Wasm, as: :instance_call 59 | defdelegate call(instance, f, a), to: Wasm, as: :instance_call 60 | defdelegate call(instance, f, a, b), to: Wasm, as: :instance_call 61 | defdelegate call(instance, f, a, b, c), to: Wasm, as: :instance_call 62 | 63 | defdelegate call_reading_string(instance, f), to: Wasm, as: :instance_call_returning_string 64 | defdelegate call_reading_string(instance, f, a), to: Wasm, as: :instance_call_returning_string 65 | 66 | # TODO: call Rust directly 67 | # def call_reading_string(%__MODULE__{handle: handle}, f), 68 | # do: Wasm.instance_call_returning_string(handle, f) 69 | # 70 | # def call_reading_string(%__MODULE__{handle: handle}, f, a), 71 | # do: Wasm.instance_call_returning_string(handle, f, a) 72 | 73 | defdelegate call_reading_string(instance, f, a, b), 74 | to: Wasm, 75 | as: :instance_call_returning_string 76 | 77 | defdelegate call_reading_string(instance, f, a, b, c), 78 | to: Wasm, 79 | as: :instance_call_returning_string 80 | 81 | defdelegate call_stream_string_chunks(instance, f), 82 | to: Wasm, 83 | as: :instance_call_stream_string_chunks 84 | 85 | defdelegate call_joining_string_chunks(instance, f), 86 | to: Wasm, 87 | as: :instance_call_joining_string_chunks 88 | 89 | defmodule CaptureFuncError do 90 | defexception [:func_name, :instance] 91 | 92 | @impl true 93 | def message(%{func_name: func_name, instance: %{exports: %{func: funcs}}}) do 94 | func_names = Map.keys(funcs) 95 | "func #{func_name} not found in exports #{inspect(func_names)}" 96 | end 97 | end 98 | 99 | defp capture_then(inst, f, arity, transform_result) when is_function(transform_result) do 100 | f = to_string(f) 101 | 102 | case inst do 103 | %__MODULE__{exports: %{func: %{^f => _}}} -> 104 | nil 105 | 106 | %__MODULE__{} -> 107 | raise CaptureFuncError, func_name: f, instance: inst 108 | 109 | _ -> 110 | nil 111 | end 112 | 113 | wrap = &alloc_if_needed(inst, &1) 114 | 115 | # call = Function.capture(__MODULE__, :call, arity + 2) 116 | case arity do 117 | 0 -> 118 | fn -> call(inst, f) |> then(transform_result) end 119 | 120 | 1 -> 121 | fn a -> call(inst, f, wrap.(a)) |> then(transform_result) end 122 | 123 | 2 -> 124 | fn a, b -> call(inst, f, wrap.(a), wrap.(b)) |> then(transform_result) end 125 | 126 | 3 -> 127 | fn a, b, c -> 128 | call(inst, f, wrap.(a), wrap.(b), wrap.(c)) |> then(transform_result) 129 | end 130 | end 131 | end 132 | 133 | def capture(inst, f, arity) do 134 | capture_then(inst, f, arity, &Function.identity/1) 135 | end 136 | 137 | def capture(inst, :u32, f, arity) do 138 | capture_then(inst, f, arity, fn value when is_integer(value) -> 139 | if value < 0 do 140 | # TODO: we could also let Rust handle this 141 | Bitwise.bxor(0xFFFF_FFFF, value) * -1 - 1 142 | else 143 | value 144 | end 145 | end) 146 | end 147 | 148 | def capture(inst, String, f, arity) do 149 | wrap = &alloc_if_needed(inst, &1) 150 | 151 | case arity do 152 | 0 -> 153 | fn -> call_reading_string(inst, f) end 154 | 155 | 1 -> 156 | fn a -> call_reading_string(inst, f, wrap.(a)) end 157 | 158 | 2 -> 159 | fn a, b -> call_reading_string(inst, f, wrap.(a), wrap.(b)) end 160 | 161 | 3 -> 162 | fn a, b, c -> 163 | call_reading_string(inst, f, wrap.(a), wrap.(b), wrap.(c)) 164 | end 165 | end 166 | end 167 | 168 | defdelegate cast(instance, f), to: Wasm, as: :instance_cast 169 | defdelegate cast(instance, f, a), to: Wasm, as: :instance_cast 170 | defdelegate cast(instance, f, a, b), to: Wasm, as: :instance_cast 171 | defdelegate cast(instance, f, a, b, c), to: Wasm, as: :instance_cast 172 | 173 | def alloc_string(instance, string) do 174 | # offset = call(instance, :alloc, byte_size(string) + 1) 175 | # count = Rust.strlen(string) 176 | count = byte_size(string) 177 | 178 | # FIXME: With complex strings this count isn’t large enough so we have to add 2 for some reason. 179 | offset = call(instance, :alloc, count + 2) 180 | write_string_nul_terminated(instance, offset, string) 181 | # write_memory(instance, offset, String.to_charlist(string)) 182 | offset 183 | end 184 | 185 | def alloc_list(_instance, []), do: 0x0 186 | 187 | def alloc_list(instance, [single]) when is_binary(single), 188 | do: cons(instance, alloc_string(instance, single), 0x0) 189 | 190 | def alloc_list(instance, [single]) when is_list(single), 191 | do: cons(instance, alloc_list(instance, single), 0x0) 192 | 193 | def alloc_list(instance, [head | tail]) when is_binary(head), 194 | do: cons(instance, alloc_string(instance, head), alloc_list(instance, tail)) 195 | 196 | def alloc_list(instance, [head | tail]) when is_list(head), 197 | do: cons(instance, alloc_list(instance, head), alloc_list(instance, tail)) 198 | 199 | defp cons(instance, head, tail) do 200 | offset_list = call(instance, :alloc, 8) 201 | write_i32(instance, offset_list, head) 202 | write_i32(instance, offset_list + 4, tail) 203 | offset_list 204 | end 205 | 206 | defp alloc_if_needed(inst, value) when is_binary(value), do: alloc_string(inst, value) 207 | defp alloc_if_needed(_inst, value), do: value 208 | 209 | def log_memory(instance, start, length) do 210 | bytes = read_memory(instance, start, length) 211 | hex = Base.encode16(bytes) 212 | hex_pretty = hex |> String.to_charlist() |> Stream.chunk_every(8) |> Enum.join(" ") 213 | IO.inspect(hex_pretty, limit: :infinite, label: "Wasm instance memory") 214 | # IO.inspect(bytes, base: :hex, label: "Wasm instance memory") 215 | end 216 | 217 | def exports(instance) do 218 | Wasm.list_exports(instance.elixir_mod) 219 | end 220 | 221 | defimpl String.Chars do 222 | def to_string(instance) do 223 | exports = Wasm.list_exports(instance.elixir_mod) 224 | 225 | to_string = List.keyfind(exports, "to_string", 1) 226 | next_body_chunk = List.keyfind(exports, "next_body_chunk", 1) 227 | 228 | cond do 229 | match?({:func, _}, to_string) -> 230 | Instance.call_reading_string(instance, :to_string) 231 | 232 | match?({:func, _}, next_body_chunk) -> 233 | Instance.call_stream_string_chunks(instance, :next_body_chunk) |> Enum.join() 234 | 235 | true -> 236 | nil 237 | end 238 | end 239 | end 240 | 241 | defmodule Caller do 242 | defdelegate read_string_nul_terminated(caller, memory_offset), 243 | to: Rust, 244 | as: :wasm_caller_read_string_nul_terminated 245 | 246 | defdelegate write_string_nul_terminated(caller, memory_offset, string), 247 | to: Rust, 248 | as: :wasm_caller_write_string_nul_terminated 249 | end 250 | end 251 | -------------------------------------------------------------------------------- /test/orb_wasmtime/wat2wasm_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Wat2WasmTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias OrbWasmtime.Wasm 5 | 6 | @tag :skip 7 | test "works" do 8 | wasm = Wasm.to_wasm(wat()) 9 | assert is_binary(wasm) 10 | end 11 | 12 | @wat ~S""" 13 | (module $LabSwatch 14 | (import "math" "powf32" (func $powf32 (param f32 f32) (result f32))) 15 | (import "format" "f32" (func $format_f32 (param f32 i32) (result i32))) 16 | (memory (export "memory") 2) 17 | (global $bump_offset (mut i32) (i32.const 65536)) 18 | (global $bump_mark (mut i32) (i32.const 0)) 19 | (global $bump_write_level (mut i32) (i32.const 0)) 20 | (data (i32.const 255) "\n") 23 | (data (i32.const 340) "\n") 24 | (data (i32.const 360) "\n") 30 | (func $bump_alloc (param $size i32) (result i32) 31 | (global.get $bump_offset) 32 | (i32.add (global.get $bump_offset) (local.get $size)) 33 | (global.set $bump_offset) 34 | ) 35 | (func $memcpy (param $dest i32) (param $src i32) (param $byte_count i32) 36 | (local $i i32) 37 | (loop $EachByte 38 | (i32.eq (local.get $i) (local.get $byte_count)) 39 | (if 40 | (then 41 | return 42 | ) 43 | ) 44 | (i32.store8 (i32.add (local.get $dest) (local.get $i)) (i32.load8_u (i32.add (local.get $src) (local.get $i)))) 45 | (i32.add (local.get $i) (i32.const 1)) 46 | (local.set $i) 47 | br $EachByte 48 | ) 49 | ) 50 | (func $memset (param $dest i32) (param $u8 i32) (param $byte_count i32) 51 | (local $i i32) 52 | (loop $EachByte 53 | (i32.eq (local.get $i) (local.get $byte_count)) 54 | (if 55 | (then 56 | return 57 | ) 58 | ) 59 | (i32.store8 (i32.add (local.get $dest) (local.get $i)) (local.get $u8)) 60 | (i32.add (local.get $i) (i32.const 1)) 61 | (local.set $i) 62 | br $EachByte 63 | ) 64 | ) 65 | (func $streq (param $address_a i32) (param $address_b i32) (result i32) 66 | (local $i i32) 67 | (local $byte_a i32) 68 | (local $byte_b i32) 69 | (loop $EachByte (result i32) 70 | (i32.load8_u (i32.add (local.get $address_a) (local.get $i))) 71 | (local.set $byte_a) 72 | (i32.load8_u (i32.add (local.get $address_b) (local.get $i))) 73 | (local.set $byte_b) 74 | (i32.eqz (local.get $byte_a)) 75 | (if 76 | (then 77 | (return (i32.eqz (local.get $byte_b))) 78 | ) 79 | ) 80 | (i32.eq (local.get $byte_a) (local.get $byte_b)) 81 | (if 82 | (then 83 | (i32.add (local.get $i) (i32.const 1)) 84 | (local.set $i) 85 | br $EachByte 86 | ) 87 | ) 88 | (return (i32.const 0)) 89 | ) 90 | ) 91 | (func $strlen (param $string_ptr i32) (result i32) 92 | (local $count i32) 93 | (loop $EachChar 94 | (i32.load8_u (i32.add (local.get $string_ptr) (local.get $count))) 95 | (if 96 | (then 97 | (i32.add (local.get $count) (i32.const 1)) 98 | (local.set $count) 99 | br $EachChar 100 | ) 101 | ) 102 | ) 103 | (local.get $count) 104 | ) 105 | (func $u32toa_count (param $value i32) (result i32) 106 | (local $digit_count i32) 107 | (local $digit i32) 108 | (loop $Digits 109 | (i32.add (local.get $digit_count) (i32.const 1)) 110 | (local.set $digit_count) 111 | (i32.rem_u (local.get $value) (i32.const 10)) 112 | (local.set $digit) 113 | (i32.div_u (local.get $value) (i32.const 10)) 114 | (local.set $value) 115 | (i32.gt_u (local.get $value) (i32.const 0)) 116 | br_if $Digits 117 | ) 118 | (local.get $digit_count) 119 | ) 120 | (func $u32toa (param $value i32) (param $end_offset i32) (result i32) 121 | (local $working_offset i32) 122 | (local $digit i32) 123 | (local.get $end_offset) 124 | (local.set $working_offset) 125 | (loop $Digits 126 | (i32.sub (local.get $working_offset) (i32.const 1)) 127 | (local.set $working_offset) 128 | (i32.rem_u (local.get $value) (i32.const 10)) 129 | (local.set $digit) 130 | (i32.div_u (local.get $value) (i32.const 10)) 131 | (local.set $value) 132 | (i32.store8 (local.get $working_offset) (i32.add (i32.const 48) (local.get $digit))) 133 | (i32.gt_u (local.get $value) (i32.const 0)) 134 | br_if $Digits 135 | ) 136 | (local.get $working_offset) 137 | ) 138 | (func $write_u32 (param $value i32) (param $str_ptr i32) (result i32) 139 | (local $working_offset i32) 140 | (local $last_offset i32) 141 | (local $digit i32) 142 | (i32.add (local.get $str_ptr) (call $u32toa_count (local.get $value))) 143 | (local.set $last_offset) 144 | (local.get $last_offset) 145 | (local.set $working_offset) 146 | (loop $Digits 147 | (i32.sub (local.get $working_offset) (i32.const 1)) 148 | (local.set $working_offset) 149 | (i32.rem_u (local.get $value) (i32.const 10)) 150 | (local.set $digit) 151 | (i32.div_u (local.get $value) (i32.const 10)) 152 | (local.set $value) 153 | (i32.store8 (local.get $working_offset) (i32.add (i32.const 48) (local.get $digit))) 154 | (i32.gt_u (local.get $value) (i32.const 0)) 155 | br_if $Digits 156 | ) 157 | (local.get $last_offset) 158 | ) 159 | (func $bump_write_start 160 | (i32.eqz (global.get $bump_write_level)) 161 | (if 162 | (then 163 | (global.get $bump_offset) 164 | (global.set $bump_mark) 165 | ) 166 | ) 167 | (i32.add (global.get $bump_write_level) (i32.const 1)) 168 | (global.set $bump_write_level) 169 | ) 170 | (func $bump_write_done (result i32) 171 | (global.get $bump_write_level) 172 | (if 173 | (then 174 | nop 175 | ) 176 | (else 177 | unreachable 178 | ) 179 | ) 180 | (i32.sub (global.get $bump_write_level) (i32.const 1)) 181 | (global.set $bump_write_level) 182 | (i32.eqz (global.get $bump_write_level)) 183 | (if 184 | (then 185 | (i32.store8 (global.get $bump_offset) (i32.const 0)) 186 | (i32.add (global.get $bump_offset) (i32.const 1)) 187 | (global.set $bump_offset) 188 | ) 189 | ) 190 | (global.get $bump_mark) 191 | ) 192 | (func $bump_write_str (param $str_ptr i32) 193 | (local $len i32) 194 | (i32.eq (local.get $str_ptr) (global.get $bump_mark)) 195 | (if 196 | (then 197 | return 198 | ) 199 | ) 200 | (call $strlen (local.get $str_ptr)) 201 | (local.set $len) 202 | (call $memcpy (global.get $bump_offset) (local.get $str_ptr) (local.get $len)) 203 | (i32.add (global.get $bump_offset) (local.get $len)) 204 | (global.set $bump_offset) 205 | ) 206 | (func $pow (param $base f32) (param $exponent f32) (result f32) 207 | (local $out f32) 208 | (local $index f32) 209 | (f32.const 1.0) 210 | (local.set $out) 211 | (f32.const 1.0) 212 | (local.set $index) 213 | (block $Outer 214 | (loop $Inner 215 | (f32.mul (local.get $out) (local.get $base)) 216 | (local.set $out) 217 | (f32.add (local.get $index) (f32.const 1.0)) 218 | (local.set $index) 219 | (f32.gt (local.get $index) (local.get $exponent)) 220 | br_if $Outer 221 | br $Inner 222 | ) 223 | ) 224 | (local.get $out) 225 | ) 226 | (func $lab_to_xyz_component (param $v f32) (result f32) 227 | (local $cubed f32) 228 | (call $pow (local.get $v) (f32.const 3.0)) 229 | (local.set $cubed) 230 | (f32.gt (local.get $cubed) (f32.const 0.008856451679035631)) 231 | (if (result f32) 232 | (then 233 | (local.get $cubed) 234 | ) 235 | (else 236 | (f32.div (f32.sub (f32.mul (f32.const 116.0) (local.get $v)) (f32.const 16.0)) (f32.const 903.2962962962963)) 237 | ) 238 | ) 239 | ) 240 | (func $lab_to_xyz (param $l f32) (param $a f32) (param $b f32) (result f32 f32 f32) 241 | (local $fy f32) 242 | (local $fx f32) 243 | (local $fz f32) 244 | (f32.div (f32.add (local.get $l) (f32.const 16.0)) (f32.const 116.0)) 245 | (local.set $fy) 246 | (f32.add (f32.div (local.get $a) (f32.const 500.0)) (local.get $fy)) 247 | (local.set $fx) 248 | (f32.sub (local.get $fy) (f32.div (local.get $b) (f32.const 200.0))) 249 | (local.set $fz) 250 | (f32.mul (call $lab_to_xyz_component (local.get $fx)) (f32.const 0.96422)) 251 | (f32.mul (call $lab_to_xyz_component (local.get $fy)) (f32.const 1.0)) 252 | (f32.mul (call $lab_to_xyz_component (local.get $fz)) (f32.const 0.82521)) 253 | ) 254 | (func $xyz_to_lab_component (param $c f32) (result f32) 255 | (f32.gt (local.get $c) (f32.const 0.008856451679035631)) 256 | (if (result f32) 257 | (then 258 | (call $powf32 (local.get $c) (f32.div (f32.const 1.0) (f32.const 3.0))) 259 | ) 260 | (else 261 | (f32.div (f32.add (f32.mul (f32.const 903.2962962962963) (local.get $c)) (f32.const 16.0)) (f32.const 116.0)) 262 | ) 263 | ) 264 | ) 265 | (func $xyz_to_lab (param $x f32) (param $y f32) (param $z f32) (result f32 f32 f32) 266 | (local $f0 f32) 267 | (local $f1 f32) 268 | (local $f2 f32) 269 | (call $xyz_to_lab_component (f32.div (local.get $x) (f32.const 0.96422))) 270 | (local.set $f0) 271 | (call $xyz_to_lab_component (f32.div (local.get $y) (f32.const 1.0))) 272 | (local.set $f1) 273 | (call $xyz_to_lab_component (f32.div (local.get $z) (f32.const 0.82521))) 274 | (local.set $f2) 275 | (f32.sub (f32.mul (f32.const 116.0) (local.get $f1)) (f32.const 16.0)) 276 | (f32.mul (f32.const 500.0) (f32.sub (local.get $f0) (local.get $f1))) 277 | (f32.mul (f32.const 200.0) (f32.sub (local.get $f1) (local.get $f2))) 278 | ) 279 | (func $linear_rgb_to_srgb (param $r f32) (param $g f32) (param $b f32) (result f32 f32 f32) 280 | (call $linear_rgb_to_srgb_component (local.get $r)) 281 | (call $linear_rgb_to_srgb_component (local.get $g)) 282 | (call $linear_rgb_to_srgb_component (local.get $b)) 283 | ) 284 | (func $linear_rgb_to_srgb_component (param $c f32) (result f32) 285 | (f32.gt (local.get $c) (f32.const 0.0031308)) 286 | (if (result f32) 287 | (then 288 | (f32.sub (f32.mul (f32.const 1.055) (call $powf32 (local.get $c) (f32.div (f32.const 1.0) (f32.const 2.4)))) (f32.const 0.055)) 289 | ) 290 | (else 291 | (f32.mul (f32.const 12.92) (local.get $c)) 292 | ) 293 | ) 294 | ) 295 | (func $srgb_to_linear_rgb (param $r f32) (param $g f32) (param $b f32) (result f32 f32 f32) 296 | (call $srgb_to_linear_rgb_component (local.get $r)) 297 | (call $srgb_to_linear_rgb_component (local.get $g)) 298 | (call $srgb_to_linear_rgb_component (local.get $b)) 299 | ) 300 | (func $srgb_to_linear_rgb_component (param $c f32) (result f32) 301 | (f32.lt (local.get $c) (f32.const 0.04045)) 302 | (if (result f32) 303 | (then 304 | (f32.div (local.get $c) (f32.const 12.92)) 305 | ) 306 | (else 307 | (call $powf32 (f32.div (f32.add (local.get $c) (f32.const 0.055)) (f32.const 1.055)) (f32.const 2.4)) 308 | ) 309 | ) 310 | ) 311 | (func $xyz_to_linear_rgb (param $x f32) (param $y f32) (param $z f32) (result f32 f32 f32) 312 | (f32.sub (f32.sub (f32.mul (local.get $x) (f32.const 3.1338561)) (f32.mul (local.get $y) (f32.const 1.6168667))) (f32.mul (f32.const 0.4906146) (local.get $z))) 313 | (f32.add (f32.add (f32.mul (local.get $x) (f32.const -0.9787684)) (f32.mul (local.get $y) (f32.const 1.9161415))) (f32.mul (f32.const 0.033454) (local.get $z))) 314 | (f32.add (f32.sub (f32.mul (local.get $x) (f32.const 0.0719453)) (f32.mul (local.get $y) (f32.const 0.2289914))) (f32.mul (f32.const 1.4052427) (local.get $z))) 315 | ) 316 | (func $xyz_to_srgb (param $x f32) (param $y f32) (param $z f32) (result f32 f32 f32) 317 | (call $xyz_to_linear_rgb (local.get $x) (local.get $y) (local.get $z)) 318 | (call $linear_rgb_to_srgb) 319 | ) 320 | (func $linear_srgb_to_xyz (param $r f32) (param $g f32) (param $b f32) (result f32 f32 f32) 321 | (f32.add (f32.add (f32.mul (f32.const 0.4360747) (local.get $r)) (f32.mul (f32.const 0.3850649) (local.get $g))) (f32.mul (f32.const 0.1430804) (local.get $b))) 322 | (f32.add (f32.add (f32.mul (f32.const 0.2225045) (local.get $r)) (f32.mul (f32.const 0.7168786) (local.get $g))) (f32.mul (f32.const 0.0606169) (local.get $b))) 323 | (f32.add (f32.add (f32.mul (f32.const 0.0139322) (local.get $r)) (f32.mul (f32.const 0.0971045) (local.get $g))) (f32.mul (f32.const 0.7141733) (local.get $b))) 324 | ) 325 | (func $srgb_to_xyz (param $r f32) (param $g f32) (param $b f32) (result f32 f32 f32) 326 | (call $srgb_to_linear_rgb (local.get $r) (local.get $g) (local.get $b)) 327 | (call $linear_srgb_to_xyz) 328 | ) 329 | (func $lab_to_srgb (param $l f32) (param $a f32) (param $b f32) (result f32 f32 f32) 330 | (call $lab_to_xyz (local.get $l) (local.get $a) (local.get $b)) 331 | (call $xyz_to_srgb) 332 | ) 333 | (func $srgb_to_lab (param $r f32) (param $g f32) (param $b f32) (result f32 f32 f32) 334 | (call $srgb_to_xyz (local.get $r) (local.get $g) (local.get $b)) 335 | (call $xyz_to_lab) 336 | ) 337 | (func $to_svg (export "to_svg") (result i32) 338 | (call $bump_write_start) 339 | (call $do_linear_gradient) 340 | drop 341 | (call $bump_write_done) 342 | ) 343 | (func $do_linear_gradient (result i32) 344 | (local $i i32) 345 | (call $bump_write_start) 346 | (call $bump_write_str (i32.const 255)) 347 | (call $bump_write_str (i32.const 276)) 348 | (call $bump_write_str (i32.const 291)) 349 | (loop $Stops 350 | (i32.add (local.get $i) (i32.const 1)) 351 | (local.set $i) 352 | (i32.lt_s (local.get $i) (i32.const 20)) 353 | br_if $Stops 354 | ) 355 | (call $do_linear_gradient_stop (f32.const 0.0) (f32.const 0.0) (f32.const 0.0) (f32.const 0.0)) 356 | drop 357 | (call $do_linear_gradient_stop (f32.const 1.0) (f32.const 1.0) (f32.const 1.0) (f32.const 1.0)) 358 | drop 359 | (call $bump_write_str (i32.const 340)) 360 | (call $bump_write_done) 361 | ) 362 | (func $do_linear_gradient_stop (param $fraction f32) (param $r f32) (param $g f32) (param $b f32) (result i32) 363 | (call $bump_write_start) 364 | (call $bump_write_str (i32.const 360)) 365 | (i32.add (call $format_f32 (f32.mul (local.get $fraction) (f32.const 100.0)) (global.get $bump_offset)) (global.get $bump_offset)) 366 | (global.set $bump_offset) 367 | (call $bump_write_str (i32.const 375)) 368 | (call $bump_write_str (i32.const 390)) 369 | (i32.add (call $format_f32 (f32.nearest (f32.mul (local.get $r) (f32.const 255.0))) (global.get $bump_offset)) (global.get $bump_offset)) 370 | (global.set $bump_offset) 371 | (call $bump_write_str (i32.const 396)) 372 | (i32.add (call $format_f32 (f32.nearest (f32.mul (local.get $g) (f32.const 255.0))) (global.get $bump_offset)) (global.get $bump_offset)) 373 | (global.set $bump_offset) 374 | (call $bump_write_str (i32.const 396)) 375 | (i32.add (call $format_f32 (f32.nearest (f32.mul (local.get $b) (f32.const 255.0))) (global.get $bump_offset)) (global.get $bump_offset)) 376 | (global.set $bump_offset) 377 | (call $bump_write_str (i32.const 398)) 378 | (call $bump_write_str (i32.const 402)) 379 | (call $bump_write_done) 380 | ) 381 | ) 382 | """ 383 | 384 | def wat(), do: @wat 385 | end 386 | -------------------------------------------------------------------------------- /lib/orb_wasmtime/wasm.ex: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Wasm do 2 | @moduledoc """ 3 | Take a WebAssembly module and list its exports or call a one-shot function. 4 | """ 5 | 6 | alias OrbWasmtime.Wasm.Decode 7 | alias OrbWasmtime.Rust 8 | 9 | alias __MODULE__ 10 | 11 | defmacro __using__(_) do 12 | quote location: :keep do 13 | use Orb 14 | import Orb 15 | alias Orb.{I32, F32} 16 | require Orb.I32 17 | 18 | require Logger 19 | # TODO: allow use Wasm.Instance? 20 | # alias ComponentsGuide.Wasm.Instance 21 | 22 | # @on_load :validate_definition! 23 | # @after_compile __MODULE__ 24 | 25 | # def __after_compile__(_env, _bytecode) do 26 | # validate_definition!() 27 | # end 28 | 29 | # FIXME: this blows up 30 | # def validate_definition! do 31 | # ComponentsGuide.Wasm.validate_definition!(__MODULE__.to_wat()) 32 | # end 33 | 34 | def to_wasm() do 35 | Wasm.wat2wasm(__MODULE__) 36 | end 37 | 38 | def exports() do 39 | Wasm.list_exports(__MODULE__) 40 | end 41 | 42 | def import_types() do 43 | Wasm.list_import_types(__MODULE__) 44 | end 45 | 46 | def start() do 47 | # if Module.defines?(__MODULE__, {:to_wat, 0}, :def) do 48 | try do 49 | # Wasm.Instance.run(__MODULE__) 50 | Wasm.run_instance(__MODULE__) 51 | rescue 52 | x in [RuntimeError] -> 53 | # IO.puts(__MODULE__.to_wat()) 54 | Logger.error(__MODULE__.to_wat()) 55 | raise x 56 | end 57 | 58 | # end 59 | end 60 | 61 | defoverridable start: 0 62 | end 63 | end 64 | 65 | def list_exports(source) do 66 | {_, source} = process_source2(source) 67 | 68 | Rust.wasm_list_exports(source) 69 | end 70 | 71 | def grouped_exports(source) do 72 | exports = list_exports(source) 73 | 74 | for export <- exports, reduce: %{global: %{}, memory: %{}, func: %{}} do 75 | acc -> 76 | type = elem(export, 0) 77 | name = elem(export, 1) 78 | put_in(acc, [type, name], export |> Tuple.delete_at(0) |> Tuple.delete_at(0)) 79 | end 80 | end 81 | 82 | def list_import_types(source) do 83 | {_, source} = process_source2(source) 84 | 85 | case Rust.wasm_list_imports(source) do 86 | {:error, reason} -> raise reason 87 | other -> other 88 | end 89 | end 90 | 91 | def wat2wasm(source), do: process_source(source) |> Rust.wat2wasm() 92 | 93 | def to_wasm(source), do: wat2wasm(source) 94 | 95 | def validate_definition!(source) do 96 | source = {:wat, source} 97 | 98 | case Rust.validate_module_definition(source) do 99 | {:error, reason} -> raise reason 100 | _ -> nil 101 | end 102 | end 103 | 104 | def call(source, f) do 105 | call_apply(source, f, []) 106 | end 107 | 108 | def call(source, f, a) do 109 | call_apply(source, f, [a]) 110 | end 111 | 112 | def call(source, f, a, b) do 113 | call_apply(source, f, [a, b]) 114 | end 115 | 116 | def call(source, f, a, b, c) do 117 | call_apply(source, f, [a, b, c]) 118 | end 119 | 120 | def capture(source, f, arity) do 121 | # call = Function.capture(__MODULE__, :call, arity + 2) 122 | case arity do 123 | 0 -> fn -> call(source, f) end 124 | 1 -> fn a -> call(source, f, a) end 125 | 2 -> fn a, b -> call(source, f, a, b) end 126 | 3 -> fn a, b, c -> call(source, f, a, b, c) end 127 | end 128 | end 129 | 130 | def call_apply(source, f, args) do 131 | args = Enum.map(args, &transform32/1) 132 | call_apply_raw(source, f, args) 133 | end 134 | 135 | defp call_apply_raw(source, f, args) do 136 | f = to_string(f) 137 | process_source(source) |> Rust.wasm_call(f, args) |> Wasm.Decode.process_list_result() 138 | end 139 | 140 | # defp transform32(a) 141 | defp transform32(a), do: Decode.transform32(a) 142 | 143 | def call_string(source, f), do: process_source(source) |> Rust.wasm_call_i32_string(f, []) 144 | 145 | def call_string(source, f, a), 146 | do: process_source(source) |> Rust.wasm_call_i32_string(f, [a]) 147 | 148 | def call_string(source, f, a, b), 149 | do: process_source(source) |> Rust.wasm_call_i32_string(f, [a, b]) 150 | 151 | def steps(source, steps) do 152 | wat = process_source(source) 153 | results = wat |> Rust.wasm_steps(steps) 154 | 155 | case results do 156 | {:error, reason} -> 157 | {:error, reason, wat} 158 | 159 | results when is_list(results) -> 160 | for result <- results do 161 | case result do 162 | [] -> nil 163 | list when is_list(list) -> IO.iodata_to_binary(list) 164 | other -> Decode.process_term_result(other) 165 | end 166 | end 167 | end 168 | end 169 | 170 | defmodule FuncImport do 171 | defstruct unique_id: 0, 172 | module_name: "", 173 | name: "", 174 | param_types: [], 175 | result_types: [], 176 | # do: fn -> nil end 177 | do: &Function.identity/1 178 | end 179 | 180 | defmodule ReplyServer do 181 | use GenServer 182 | 183 | def start_link(imports) when is_list(imports) do 184 | case imports do 185 | [] -> 186 | # pid = :erlang.alias() 187 | # :erlang.unalias(pid) 188 | # pid 189 | 190 | # Process.spawn(fn -> nil end, []) 191 | 192 | GenServer.start_link(__MODULE__, imports) 193 | 194 | imports -> 195 | GenServer.start_link(__MODULE__, imports) 196 | end 197 | end 198 | 199 | @impl true 200 | def init(imports) do 201 | # IO.puts("Starting ReplyServer") 202 | # IO.inspect(imports) 203 | {:ok, %{imports: imports}} 204 | end 205 | 206 | # @impl true 207 | # def handle_info({:set_instance, instance}, imports) do 208 | # {:noreply, %{state | instance: instance}} 209 | # end 210 | 211 | @impl true 212 | def handle_call({:started_instance, instance}, _from, state) do 213 | # %{state | instance: instance} 214 | state = put_in(state[:instance], instance) 215 | {:reply, :ok, state} 216 | end 217 | 218 | @impl true 219 | def handle_info( 220 | {:reply_to_func_call_out, func_id, resource, term}, 221 | state = %{imports: imports} 222 | ) do 223 | # IO.inspect(func_id, label: "reply_to_func_call_out func_id") 224 | # IO.inspect(resource, label: "reply_to_func_call_out resource") 225 | 226 | {handler, params_arity} = 227 | imports 228 | |> Enum.find_value(fn 229 | %FuncImport{unique_id: ^func_id, do: handler, param_types: nil} -> 230 | {handler, 0} 231 | 232 | %FuncImport{unique_id: ^func_id, do: handler, param_types: params} 233 | when is_atom(params) -> 234 | {handler, 1} 235 | 236 | %FuncImport{unique_id: ^func_id, do: handler, param_types: params} 237 | when is_tuple(params) -> 238 | {handler, tuple_size(params)} 239 | 240 | %FuncImport{unique_id: ^func_id, do: handler, param_types: params} 241 | when is_list(params) -> 242 | {handler, length(params)} 243 | 244 | _ -> 245 | nil 246 | end) || 247 | raise "Expected imported function #{func_id} to be provided." 248 | 249 | # IO.inspect(handler, label: "reply_to_func_call_out found handler") 250 | # IO.inspect(term, label: "reply_to_func_call_out term") 251 | 252 | # IO.inspect(handler, label: "reply_to_func_call_out found func") 253 | 254 | # TODO: wrap in try/catch 255 | # and call wasm_call_out_reply_failure when it fails. 256 | # TODO: pass correct params 257 | # TODO: pass instance to func, so it can read memory 258 | 259 | input = Wasm.Decode.process_list_result(term) 260 | 261 | args = 262 | case input do 263 | input when is_tuple(input) -> Tuple.to_list(input) 264 | input -> List.wrap(input) 265 | end 266 | 267 | params_plus_caller_arity = params_arity + 1 268 | 269 | output = 270 | case Function.info(handler, :arity) do 271 | {:arity, ^params_arity} -> 272 | apply(handler, args) 273 | 274 | {:arity, ^params_plus_caller_arity} -> 275 | apply(handler, [resource | args]) 276 | 277 | {:arity, arity} -> 278 | raise "Expected import callback to have arity #{params_arity} or #{params_plus_caller_arity}, instead have #{arity}." 279 | end 280 | 281 | # output = output |> List.wrap() |> Enum.map(&Decode.transform32/1) 282 | 283 | output = 284 | case output do 285 | nil -> [] 286 | t when is_tuple(t) -> t |> Tuple.to_list() |> Enum.map(&Decode.transform32/1) 287 | value -> [Decode.transform32(value)] 288 | end 289 | 290 | # IO.inspect(output, label: "reply_to_func_call_out output") 291 | Rust.wasm_call_out_reply(resource, output) 292 | 293 | {:noreply, state} 294 | end 295 | end 296 | 297 | defp process_imports(import_types, imports) do 298 | # {"http", "get", {:func, %{params: [:i32], results: [:i32]}}} 299 | 300 | imports = 301 | Map.new(imports, fn {mod, name, func} -> 302 | {{Atom.to_string(mod), Atom.to_string(name)}, func} 303 | end) 304 | 305 | for {{mod, name, {:func, func_type}}, index} <- Enum.with_index(import_types) do 306 | %{params: params, results: results} = func_type 307 | func = Map.fetch!(imports, {mod, name}) 308 | 309 | {:arity, callback_arity} = Function.info(func, :arity) 310 | params_count = Enum.count(params) 311 | 312 | if params_count != callback_arity and params_count + 1 != callback_arity do 313 | IO.inspect(IEx.Info.info(params_count)) 314 | IO.inspect(IEx.Info.info(callback_arity)) 315 | IO.inspect(callback_arity, label: "callback arity") 316 | IO.inspect(params_count, label: "params count") 317 | IO.inspect(callback_arity == params_count) 318 | 319 | raise "Function arity #{inspect(callback_arity)} must match WebAssembly params count #{inspect(params_count)}." 320 | end 321 | 322 | %FuncImport{ 323 | unique_id: index, 324 | module_name: mod, 325 | name: name, 326 | param_types: params, 327 | result_types: results, 328 | do: func 329 | } 330 | end 331 | end 332 | 333 | def run_instance(source, imports \\ []) do 334 | import_types = list_import_types(source) 335 | imports = process_imports(import_types, imports) 336 | 337 | {:ok, pid} = ReplyServer.start_link(imports) 338 | {identifier, source} = process_source2(source) 339 | instance = Rust.wasm_run_instance(source, identifier, imports, pid) 340 | 341 | GenServer.call(pid, {:started_instance, instance}) 342 | 343 | # receive do 344 | # :run_instance_start -> 345 | # nil 346 | # after 347 | # 5000 -> 348 | # IO.puts(:stderr, "No message in 5 seconds") 349 | # end 350 | 351 | instance 352 | end 353 | 354 | defp get_instance_handle(%{handle: handle}), do: handle 355 | defp get_instance_handle(instance), do: instance 356 | 357 | def instance_get_global(instance, global_name), 358 | do: 359 | Rust.wasm_instance_get_global_i32( 360 | get_instance_handle(instance), 361 | to_string(global_name) 362 | ) 363 | 364 | def instance_set_global(instance, global_name, new_value), 365 | do: 366 | Rust.wasm_instance_set_global_i32( 367 | get_instance_handle(instance), 368 | to_string(global_name), 369 | new_value 370 | ) 371 | 372 | defp do_instance_call(instance, f, args) do 373 | f = to_string(f) 374 | args = Enum.map(args, &transform32/1) 375 | # Rust.wasm_instance_call_func(instance, f, args) 376 | get_instance_handle(instance) 377 | |> Rust.wasm_instance_call_func(f, args) 378 | |> Decode.process_term_result() 379 | end 380 | 381 | # def instance_call(instance, f), do: Rust.wasm_instance_call_func(instance, f) 382 | def instance_call(instance, f), do: do_instance_call(instance, f, []) 383 | def instance_call(instance, f, a), do: do_instance_call(instance, f, [a]) 384 | def instance_call(instance, f, a, b), do: do_instance_call(instance, f, [a, b]) 385 | def instance_call(instance, f, a, b, c), do: do_instance_call(instance, f, [a, b, c]) 386 | 387 | defp do_instance_call_returning_string(instance, f, args) do 388 | f = to_string(f) 389 | Rust.wasm_instance_call_func_i32_string(get_instance_handle(instance), f, args) 390 | end 391 | 392 | def instance_call_returning_string(instance, f), 393 | do: do_instance_call_returning_string(instance, f, []) 394 | 395 | def instance_call_returning_string(instance, f, a), 396 | do: do_instance_call_returning_string(instance, f, [a]) 397 | 398 | def instance_call_returning_string(instance, f, a, b), 399 | do: do_instance_call_returning_string(instance, f, [a, b]) 400 | 401 | def instance_call_returning_string(instance, f, a, b, c), 402 | do: do_instance_call_returning_string(instance, f, [a, b, c]) 403 | 404 | def instance_call_stream_string_chunks(instance, f) do 405 | Stream.unfold(0, fn n -> 406 | case instance_call_returning_string(instance, f) do 407 | "" -> nil 408 | s -> {s, n + 1} 409 | end 410 | end) 411 | end 412 | 413 | def instance_call_joining_string_chunks(instance, f) do 414 | instance_call_stream_string_chunks(instance, f) |> Enum.join() 415 | end 416 | 417 | def instance_cast(instance, f) do 418 | instance_cast_apply(instance, f, []) 419 | end 420 | 421 | def instance_cast(instance, f, a) do 422 | instance_cast_apply(instance, f, [a]) 423 | end 424 | 425 | def instance_cast(instance, f, a, b) do 426 | instance_cast_apply(instance, f, [a, b]) 427 | end 428 | 429 | def instance_cast(instance, f, a, b, c) do 430 | instance_cast_apply(instance, f, [a, b, c]) 431 | end 432 | 433 | def instance_cast_apply(instance, f, args) do 434 | f = to_string(f) 435 | args = Enum.map(args, &transform32/1) 436 | get_instance_handle(instance) |> Rust.wasm_instance_cast_func_i32(f, args) 437 | end 438 | 439 | def instance_write_i32(instance, memory_offset, value) 440 | when is_integer(memory_offset) and is_integer(value) do 441 | Rust.wasm_instance_write_i32(get_instance_handle(instance), memory_offset, value) 442 | end 443 | 444 | def instance_write_i64(instance, memory_offset, value) 445 | when is_integer(memory_offset) and is_integer(value) do 446 | Rust.wasm_instance_write_i64(get_instance_handle(instance), memory_offset, value) 447 | end 448 | 449 | def instance_write_memory(instance, memory_offset, bytes) 450 | when is_integer(memory_offset) do 451 | Rust.wasm_instance_write_memory(get_instance_handle(instance), memory_offset, bytes) 452 | end 453 | 454 | def instance_write_string_nul_terminated(instance, memory_offset, string) 455 | when is_integer(memory_offset) do 456 | Rust.wasm_instance_write_string_nul_terminated( 457 | get_instance_handle(instance), 458 | memory_offset, 459 | string 460 | ) 461 | end 462 | 463 | def instance_write_string_nul_terminated(instance, global_name, string) 464 | when is_atom(global_name) do 465 | memory_offset = 466 | Rust.wasm_instance_get_global_i32( 467 | get_instance_handle(instance), 468 | to_string(global_name) 469 | ) 470 | 471 | Rust.wasm_instance_write_string_nul_terminated( 472 | get_instance_handle(instance), 473 | memory_offset, 474 | string 475 | ) 476 | end 477 | 478 | def instance_read_memory(instance, start, length) 479 | when is_integer(start) and is_integer(length) do 480 | Rust.wasm_instance_read_memory(get_instance_handle(instance), start, length) 481 | |> IO.iodata_to_binary() 482 | end 483 | 484 | def instance_read_memory(instance, start_global_name, length) 485 | when is_atom(start_global_name) and is_integer(length) do 486 | start = Rust.wasm_instance_get_global_i32(instance, to_string(start_global_name)) 487 | 488 | Rust.wasm_instance_read_memory(get_instance_handle(instance), start, length) 489 | |> IO.iodata_to_binary() 490 | end 491 | 492 | def instance_read_string_nul_terminated(instance, memory_offset) 493 | when is_integer(memory_offset) do 494 | Rust.wasm_instance_read_string_nul_terminated( 495 | get_instance_handle(instance), 496 | memory_offset 497 | ) 498 | end 499 | 500 | defp process_source2(<<"\0asm", 0x01000000::32>> <> _rest = wasm), 501 | do: {"unknown", {:wasm, wasm}} 502 | 503 | defp process_source2(string) when is_binary(string), do: {"unknown", {:wat, string}} 504 | 505 | defp process_source2({:wat, string} = value) when is_binary(string), do: {"unknown", value} 506 | defp process_source2(atom) when is_atom(atom), do: {to_string(atom), {:wat, atom.to_wat()}} 507 | 508 | defp process_source(source) do 509 | {_identifier, {_, source}} = process_source2(source) 510 | source 511 | end 512 | end 513 | 514 | defmodule OrbWasmtime.Wasm.Decode do 515 | def transform32(a) when is_integer(a) and a < 0, do: {:i32, a} 516 | def transform32(a) when is_integer(a), do: {:u32, a} 517 | def transform32(a) when is_float(a), do: {:f32, a} 518 | def transform32(a) when is_tuple(a), do: a 519 | 520 | defp process_value({:i32, a}), do: a 521 | defp process_value({:i64, a}), do: a 522 | defp process_value({:f32, a}), do: a 523 | defp process_value({:f64, a}), do: a 524 | 525 | def process_list_result([]), do: nil 526 | def process_list_result([a]), do: process_value(a) 527 | 528 | def process_list_result(multiple_items) when is_list(multiple_items), 529 | do: multiple_items |> Enum.map(&process_value/1) |> List.to_tuple() 530 | 531 | def process_list_result({:error, "failed to parse WebAssembly module"}), do: {:error, :parse} 532 | def process_list_result({:error, s}), do: {:error, s} 533 | 534 | def process_term_result(nil), do: nil 535 | def process_term_result({:i32, a}), do: a 536 | def process_term_result({:i64, a}), do: a 537 | def process_term_result({:f32, a}), do: a 538 | def process_term_result({:f64, a}), do: a 539 | 540 | def process_term_result({:error, "failed to parse WebAssembly module"}), do: {:error, :parse} 541 | def process_term_result({:error, s}), do: {:error, s} 542 | 543 | # I’m not happy with how the previous two cases {:i32, a} and this are both tuples. It’s confusing. 544 | def process_term_result(tuple) when is_tuple(tuple) do 545 | tuple |> Tuple.to_list() |> Enum.map(&process_value/1) |> List.to_tuple() 546 | end 547 | end 548 | -------------------------------------------------------------------------------- /test/orb_wasmtime/wasm_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Wasm.Test do 2 | use ExUnit.Case, async: true 3 | 4 | alias OrbWasmtime.Wasm 5 | 6 | test "wasm_list_exports/1 single func" do 7 | wasm_source = """ 8 | (module $single_func 9 | (func (export "answer") (result i32) 10 | i32.const 42 11 | ) 12 | ) 13 | """ 14 | 15 | assert Wasm.list_exports({:wat, wasm_source}) == [{:func, "answer"}] 16 | end 17 | 18 | test "wasm_list_exports/1 two funcs" do 19 | wasm_source = """ 20 | (module $two_funcs 21 | (func (export "answer") (result i32) 22 | i32.const 42 23 | ) 24 | (memory (export "mem") 1) 25 | (func (export "get_pi") (result f32) 26 | f32.const 3.14 27 | ) 28 | (func $internal (result f32) 29 | f32.const 99 30 | ) 31 | ) 32 | """ 33 | 34 | assert Wasm.list_exports({:wat, wasm_source}) == [ 35 | {:func, "answer"}, 36 | {:memory, "mem"}, 37 | {:func, "get_pi"} 38 | ] 39 | end 40 | 41 | test "call/2" do 42 | wasm_source = """ 43 | (module $single_func 44 | (func (export "answer") (result i32) 45 | i32.const 42 46 | ) 47 | ) 48 | """ 49 | 50 | assert Wasm.call(wasm_source, "answer") == 42 51 | end 52 | 53 | test "call/2 i64" do 54 | wasm_source = """ 55 | (module $single_func 56 | (func (export "answer") (result i64) 57 | i64.const 42 58 | ) 59 | ) 60 | """ 61 | 62 | assert Wasm.call(wasm_source, "answer") == 42 63 | end 64 | 65 | test "instance_call/2" do 66 | wasm_source = """ 67 | (module $single_func 68 | (func (export "answer") (result i32) 69 | i32.const 42 70 | ) 71 | ) 72 | """ 73 | 74 | instance = Wasm.run_instance(wasm_source) 75 | assert Wasm.instance_call(instance, "answer") == 42 76 | end 77 | 78 | test "instance_call/2 i64" do 79 | wasm_source = """ 80 | (module $single_func 81 | (func (export "answer") (result i64) 82 | i64.const 42 83 | ) 84 | ) 85 | """ 86 | 87 | instance = Wasm.run_instance(wasm_source) 88 | assert Wasm.instance_call(instance, "answer") == 42 89 | end 90 | 91 | test "run_instance with wasm" do 92 | wat_source = """ 93 | (module $single_func 94 | (func (export "answer") (result i32) 95 | i32.const 42 96 | ) 97 | ) 98 | """ 99 | 100 | wasm_source = wat_source |> Wasm.to_wasm() 101 | 102 | instance = Wasm.run_instance(wasm_source) 103 | assert Wasm.instance_call(instance, "answer") == 42 104 | end 105 | 106 | test "call/2 uninitialized local" do 107 | wasm_source = """ 108 | (module $single_func 109 | (func (export "answer") (result i32) 110 | (local $a i32) 111 | local.get $a 112 | ) 113 | ) 114 | """ 115 | 116 | assert Wasm.call(wasm_source, "answer") == 0 117 | end 118 | 119 | test "call/2 mutating a param" do 120 | wasm_source = """ 121 | (module $single_func 122 | (func (export "answer") (param $a i32) (result i32) 123 | (i32.const 42) 124 | (local.set $a) 125 | (local.get $a) 126 | ) 127 | ) 128 | """ 129 | 130 | assert Wasm.call(wasm_source, "answer", 17) === 42 131 | end 132 | 133 | test "call/4 adding two numbers" do 134 | wasm_source = """ 135 | (module $add_func 136 | (func $add (param $a i32) (param $b i32) (result i32) 137 | (local.get $a) 138 | (local.get $b) 139 | (i32.add) 140 | ) 141 | (export "add" (func $add)) 142 | ) 143 | """ 144 | 145 | assert Wasm.call(wasm_source, "add", 7, 5) === 12 146 | end 147 | 148 | test "call/4 multiplying two i32s" do 149 | wasm_source = """ 150 | (module $multiply_func 151 | (func $multiply (param $a i32) (param $b i32) (result i32) 152 | (local.get $a) 153 | (local.get $b) 154 | (i32.mul) 155 | ) 156 | (export "multiply" (func $multiply)) 157 | ) 158 | """ 159 | 160 | assert Wasm.call(wasm_source, "multiply", 7, 5) === 35 161 | end 162 | 163 | test "call/4 swapping two i32s" do 164 | wasm_source = """ 165 | (module 166 | (func $swap (param $a i32) (param $b i32) (result i32 i32) 167 | (local.get $b) 168 | (local.get $a) 169 | ) 170 | (export "swap" (func $swap)) 171 | ) 172 | """ 173 | 174 | assert Wasm.call(wasm_source, "swap", 7, 5) === {5, 7} 175 | end 176 | 177 | test "call/4 multiplying two f32s" do 178 | wasm_source = """ 179 | (module $multiply_func 180 | (func $multiply (param $a f32) (param $b f32) (result f32) 181 | (local.get $a) 182 | (local.get $b) 183 | (f32.mul) 184 | ) 185 | (export "multiply" (func $multiply)) 186 | ) 187 | """ 188 | 189 | assert Wasm.call(wasm_source, "multiply", 7.0, 5.0) === 35.0 190 | end 191 | 192 | test "call/4 swapping two f32s" do 193 | wasm_source = """ 194 | (module 195 | (func $swap (param $a f32) (param $b f32) (result f32 f32) 196 | (local.get $b) 197 | (local.get $a) 198 | ) 199 | (export "swap" (func $swap)) 200 | ) 201 | """ 202 | 203 | assert Wasm.call(wasm_source, "swap", 7.0, 5.0) === {5.0, 7.0} 204 | end 205 | 206 | test "call/3 checking a number is within a range" do 207 | wasm_source = """ 208 | (module $range_func 209 | (func $validate (param $num i32) (result i32) 210 | (local $lt i32) 211 | (local $gt i32) 212 | (i32.lt_s (local.get $num) (i32.const 1)) 213 | (local.set $lt) 214 | (i32.gt_s (local.get $num) (i32.const 255)) 215 | (local.set $gt) 216 | (i32.or (local.get $lt) (local.get $gt)) 217 | (i32.eqz) 218 | ) 219 | (export "validate" (func $validate)) 220 | ) 221 | """ 222 | 223 | validate = &Wasm.call(wasm_source, "validate", &1) 224 | assert validate.(-1) === 0 225 | assert validate.(0) === 0 226 | assert validate.(1) === 1 227 | assert validate.(2) === 1 228 | assert validate.(10) === 1 229 | assert validate.(13) === 1 230 | assert validate.(255) === 1 231 | assert validate.(256) === 0 232 | assert validate.(257) === 0 233 | assert validate.(2000) === 0 234 | 235 | instance = Wasm.run_instance(wasm_source) 236 | # validate = Wasm.instance_get_func_i32(validate: 1) 237 | validate = &Wasm.instance_call(instance, "validate", &1) 238 | assert validate.(0) === 0 239 | assert validate.(1) === 1 240 | assert validate.(255) === 1 241 | assert validate.(256) === 0 242 | end 243 | 244 | test "wasm_string/2 spits out string" do 245 | wasm_source = """ 246 | (module $string_start_end 247 | (import "env" "buffer" (memory 1)) 248 | (data (i32.const 256) "Know the length of this string") 249 | (func (export "main") (result i32 i32) 250 | (i32.const 256) (i32.const 30) 251 | ) 252 | ) 253 | """ 254 | 255 | assert Wasm.call(wasm_source, "main") == {256, 30} 256 | assert Wasm.call_string(wasm_source, "main") == "Know the length of this string" 257 | end 258 | 259 | test "wasm_string/2 spits out null-terminated string" do 260 | wasm_source = """ 261 | (module $string_null_terminated 262 | (import "env" "buffer" (memory 1)) 263 | (data (i32.const 256) "No need to know the length of this string") 264 | (func (export "main") (result i32) 265 | (i32.const 256) 266 | ) 267 | ) 268 | """ 269 | 270 | assert Wasm.call(wasm_source, "main") == 256 271 | assert Wasm.call_string(wasm_source, "main") == "No need to know the length of this string" 272 | end 273 | 274 | test "wasm_string/2 spits out HTML strings" do 275 | wasm_source = """ 276 | (module $string_html 277 | (import "env" "buffer" (memory 1)) 278 | (global $doctype (mut i32) (i32.const 65536)) 279 | (data (i32.const 65536) "") 280 | (func (export "main") (result i32 i32) 281 | (global.get $doctype) (i32.const 15) 282 | ) 283 | ) 284 | """ 285 | 286 | assert Wasm.call(wasm_source, "main") == {65536, 15} 287 | assert Wasm.call_string(wasm_source, "main") == "" 288 | end 289 | 290 | test "wasm_string/2 looks up HTTP status" do 291 | wasm_source = ~s""" 292 | (module $string_html 293 | (import "env" "buffer" (memory 1)) 294 | (data (i32.const #{200 * 24}) "OK\\00") 295 | (data (i32.const #{201 * 24}) "Created\\00") 296 | (data (i32.const #{204 * 24}) "No Content\\00") 297 | (data (i32.const #{205 * 24}) "Reset Content\\00") 298 | (data (i32.const #{301 * 24}) "Moved Permanently\\00") 299 | (data (i32.const #{302 * 24}) "Found\\00") 300 | (data (i32.const #{303 * 24}) "See Other\\00") 301 | (data (i32.const #{304 * 24}) "Not Modified\\00") 302 | (data (i32.const #{307 * 24}) "Temporary Redirect\\00") 303 | (data (i32.const #{400 * 24}) "Bad Request\\00") 304 | (data (i32.const #{401 * 24}) "Unauthorized\\00") 305 | (data (i32.const #{403 * 24}) "Forbidden\\00") 306 | (data (i32.const #{404 * 24}) "Not Found\\00") 307 | (data (i32.const #{405 * 24}) "Method Not Allowed\\00") 308 | (data (i32.const #{409 * 24}) "Conflict\\00") 309 | (data (i32.const #{412 * 24}) "Precondition Failed\\00") 310 | (data (i32.const #{413 * 24}) "Payload Too Large\\00") 311 | (data (i32.const #{422 * 24}) "Unprocessable Entity\\00") 312 | (data (i32.const #{429 * 24}) "Too Many Requests\\00") 313 | (func (export "lookup") (param $status i32) (result i32) 314 | (local.get $status) 315 | (i32.const 24) 316 | (i32.mul) 317 | ) 318 | ) 319 | """ 320 | 321 | assert Wasm.call_string(wasm_source, "lookup", 200) == "OK" 322 | assert Wasm.call_string(wasm_source, "lookup", 201) == "Created" 323 | assert Wasm.call_string(wasm_source, "lookup", 204) == "No Content" 324 | assert Wasm.call_string(wasm_source, "lookup", 205) == "Reset Content" 325 | assert Wasm.call_string(wasm_source, "lookup", 301) == "Moved Permanently" 326 | assert Wasm.call_string(wasm_source, "lookup", 302) == "Found" 327 | assert Wasm.call_string(wasm_source, "lookup", 303) == "See Other" 328 | assert Wasm.call_string(wasm_source, "lookup", 304) == "Not Modified" 329 | assert Wasm.call_string(wasm_source, "lookup", 307) == "Temporary Redirect" 330 | assert Wasm.call_string(wasm_source, "lookup", 401) == "Unauthorized" 331 | assert Wasm.call_string(wasm_source, "lookup", 403) == "Forbidden" 332 | assert Wasm.call_string(wasm_source, "lookup", 404) == "Not Found" 333 | assert Wasm.call_string(wasm_source, "lookup", 405) == "Method Not Allowed" 334 | assert Wasm.call_string(wasm_source, "lookup", 409) == "Conflict" 335 | assert Wasm.call_string(wasm_source, "lookup", 412) == "Precondition Failed" 336 | assert Wasm.call_string(wasm_source, "lookup", 413) == "Payload Too Large" 337 | assert Wasm.call_string(wasm_source, "lookup", 422) == "Unprocessable Entity" 338 | assert Wasm.call_string(wasm_source, "lookup", 429) == "Too Many Requests" 339 | assert Wasm.call_string(wasm_source, "lookup", 100) == "" 340 | # Crashes: 341 | # assert Wasm.call_string(wasm_source, "lookup", -1) == "" 342 | end 343 | 344 | @wasm_calculate_mean """ 345 | (module $CalculateMean 346 | (import "env" "buffer" (memory 1)) 347 | (global $count (mut i32) (i32.const 0)) 348 | (global $tally (mut i32) (i32.const 0)) 349 | (func $insert (export "insert") (param $element i32) 350 | (i32.add (global.get $count) (i32.const 1)) 351 | (global.set $count) 352 | (i32.add (global.get $tally) (local.get $element)) 353 | (global.set $tally) 354 | ) 355 | (func $calculate_mean (export "calculate_mean") (result i32) 356 | (i32.div_u (global.get $tally) (global.get $count)) 357 | ) 358 | ) 359 | """ 360 | 361 | test "steps/2 global calculates mean" do 362 | [nil, nil, nil, result] = 363 | Wasm.steps(@wasm_calculate_mean, [ 364 | {:call, "insert", [5]}, 365 | {:call, "insert", [7]}, 366 | {:call, "insert", [9]}, 367 | {:call, "calculate_mean", []} 368 | ]) 369 | 370 | assert result == 7 371 | end 372 | 373 | defmodule FileNameSafe do 374 | def to_wat() do 375 | """ 376 | (module $FileNameSafe 377 | (memory (export "memory") 2) 378 | (func $get_is_valid (export "get_is_valid") (result i32) 379 | (local $str i32) 380 | (local $char i32) 381 | (i32.const 1024) 382 | (local.set $str) 383 | (loop $EachChar (result i32) 384 | (i32.load8_u (local.get $str)) 385 | (local.set $char) 386 | (i32.eq (local.get $char) (i32.const 47)) 387 | (if 388 | (then 389 | (return (i32.const 0)) 390 | ) 391 | ) 392 | (i32.eqz (local.get $char)) 393 | (if 394 | (then 395 | (return (i32.const 1)) 396 | ) 397 | ) 398 | (i32.add (local.get $str) (i32.const 1)) 399 | (local.set $str) 400 | br $EachChar 401 | ) 402 | ) 403 | ) 404 | """ 405 | end 406 | end 407 | 408 | test "returns if a string is file name safe" do 409 | [result] = 410 | Wasm.steps(FileNameSafe, [ 411 | {:write_string_nul_terminated, 1024, "good", true}, 412 | {:call, "get_is_valid", []} 413 | ]) 414 | 415 | assert result == 1 416 | 417 | [result] = 418 | Wasm.steps(FileNameSafe, [ 419 | {:write_string_nul_terminated, 1024, "has/slashes", true}, 420 | {:call, "get_is_valid", []} 421 | ]) 422 | 423 | assert result == 0 424 | end 425 | 426 | defmodule CopyString do 427 | def to_wat() do 428 | """ 429 | (module $CopyString 430 | (import "env" "buffer" (memory 2)) 431 | (func $do_copy (export "do_copy") (result i32) 432 | (local $read_offset i32) 433 | (local $char i32) 434 | (i32.const 1024) 435 | (local.set $read_offset) 436 | (loop $EachChar (result i32) 437 | (block $Outer 438 | (i32.load8_u (local.get $read_offset)) 439 | (local.set $char) 440 | (i32.store8 (i32.add (local.get $read_offset) (i32.const 1024)) (local.get $char)) 441 | (local.get $char) 442 | br_if $Outer 443 | (i32.sub (local.get $read_offset) (i32.const 1024)) 444 | return 445 | ) 446 | (i32.add (local.get $read_offset) (i32.const 1)) 447 | (local.set $read_offset) 448 | br $EachChar 449 | ) 450 | ) 451 | ) 452 | """ 453 | end 454 | end 455 | 456 | test "copies string bytes" do 457 | [len, result] = 458 | Wasm.steps(CopyString, [ 459 | {:write_string_nul_terminated, 1024, "good", true}, 460 | {:call, "do_copy", []}, 461 | {:read_memory, 2048, 4} 462 | ]) 463 | 464 | assert len == 4 465 | assert result == "good" 466 | end 467 | 468 | defmodule EscapeHTML do 469 | def to_wat() do 470 | """ 471 | (module $EscapeHTML 472 | (import "env" "buffer" (memory 2)) 473 | (func $escape_html (export "escape_html") (result i32) 474 | (local $read_offset i32) 475 | (local $write_offset i32) 476 | (local $char i32) 477 | (i32.const 1024) 478 | (local.set $read_offset) 479 | (i32.const 2048) 480 | (local.set $write_offset) 481 | (loop $EachChar (result i32) 482 | (block $Outer 483 | (i32.load8_u (local.get $read_offset)) 484 | (local.set $char) 485 | (i32.eq (local.get $char) (i32.const 38)) 486 | (if 487 | (then 488 | (i32.store8 (local.get $write_offset) (i32.const 38)) 489 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 1)) (i32.const 97)) 490 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 2)) (i32.const 109)) 491 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 3)) (i32.const 112)) 492 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 4)) (i32.const 59)) 493 | (i32.add (local.get $write_offset) (i32.const 4)) 494 | (local.set $write_offset) 495 | br $Outer 496 | ) 497 | ) 498 | (i32.eq (local.get $char) (i32.const 60)) 499 | (if 500 | (then 501 | (i32.store8 (local.get $write_offset) (i32.const 38)) 502 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 1)) (i32.const 108)) 503 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 2)) (i32.const 116)) 504 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 3)) (i32.const 59)) 505 | (i32.add (local.get $write_offset) (i32.const 3)) 506 | (local.set $write_offset) 507 | br $Outer 508 | ) 509 | ) 510 | (i32.eq (local.get $char) (i32.const 62)) 511 | (if 512 | (then 513 | (i32.store8 (local.get $write_offset) (i32.const 38)) 514 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 1)) (i32.const 103)) 515 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 2)) (i32.const 116)) 516 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 3)) (i32.const 59)) 517 | (i32.add (local.get $write_offset) (i32.const 3)) 518 | (local.set $write_offset) 519 | br $Outer 520 | ) 521 | ) 522 | (i32.eq (local.get $char) (i32.const 34)) 523 | (if 524 | (then 525 | (i32.store8 (local.get $write_offset) (i32.const 38)) 526 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 1)) (i32.const 113)) 527 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 2)) (i32.const 117)) 528 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 3)) (i32.const 111)) 529 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 4)) (i32.const 116)) 530 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 5)) (i32.const 59)) 531 | (i32.add (local.get $write_offset) (i32.const 5)) 532 | (local.set $write_offset) 533 | br $Outer 534 | ) 535 | ) 536 | (i32.eq (local.get $char) (i32.const 39)) 537 | (if 538 | (then 539 | (i32.store8 (local.get $write_offset) (i32.const 38)) 540 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 1)) (i32.const 35)) 541 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 2)) (i32.const 51)) 542 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 3)) (i32.const 57)) 543 | (i32.store8 (i32.add (local.get $write_offset) (i32.const 4)) (i32.const 59)) 544 | (i32.add (local.get $write_offset) (i32.const 4)) 545 | (local.set $write_offset) 546 | br $Outer 547 | ) 548 | ) 549 | (i32.store8 (local.get $write_offset) (local.get $char)) 550 | (local.get $char) 551 | br_if $Outer 552 | (i32.sub (local.get $write_offset) (i32.const 2048)) 553 | return 554 | ) 555 | (i32.add (local.get $read_offset) (i32.const 1)) 556 | (local.set $read_offset) 557 | (i32.add (local.get $write_offset) (i32.const 1)) 558 | (local.set $write_offset) 559 | br $EachChar 560 | ) 561 | ) 562 | ) 563 | """ 564 | end 565 | end 566 | 567 | test "escapes html" do 568 | # dbg(EscapeHTML.to_wat()) 569 | 570 | [count, result] = 571 | Wasm.steps(EscapeHTML, [ 572 | {:write_string_nul_terminated, 1024, "hello", true}, 573 | {:call, "escape_html", []}, 574 | {:read_memory, 2048, 5} 575 | ]) 576 | 577 | assert count == 5 578 | assert result == "hello" 579 | 580 | [count, result] = 581 | Wasm.steps(EscapeHTML, [ 582 | {:write_string_nul_terminated, 1024, "Hall & Oates like M&Ms", true}, 583 | {:call, "escape_html", []}, 584 | {:read_memory, 2048, 40} 585 | ]) 586 | 587 | result = String.trim_trailing(result, <<0>>) 588 | 589 | assert count == 30 590 | assert result == "Hall & Oates like M&Ms" 591 | 592 | [count, result] = 593 | Wasm.steps(EscapeHTML, [ 594 | {:write_string_nul_terminated, 1024, ~s[1 < 2 & 2 > 1 "double quotes" 'single quotes'], 595 | true}, 596 | {:call, "escape_html", []}, 597 | {:read_memory, 2048, 100} 598 | ]) 599 | 600 | result = String.trim_trailing(result, <<0>>) 601 | 602 | assert count == 73 603 | assert result == "1 < 2 & 2 > 1 "double quotes" 'single quotes'" 604 | end 605 | end 606 | -------------------------------------------------------------------------------- /test/orb_wasmtime/instance/url_encoded_test.exs: -------------------------------------------------------------------------------- 1 | defmodule OrbWasmtime.Instance.URLEncodedTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias OrbWasmtime.Wasm 5 | alias OrbWasmtime.Instance 6 | 7 | test "url_encoded_count" do 8 | inst = Instance.run(url_encoded_wat()) 9 | count = Instance.capture(inst, :url_encoded_count, 1) 10 | 11 | assert count.("") == 0 12 | assert count.("a") == 1 13 | assert count.("a&") == 1 14 | assert count.("a&&") == 1 15 | assert count.("&a&") == 1 16 | assert count.("&&a&&") == 1 17 | assert count.("a=1") == 1 18 | assert count.("a=1&") == 1 19 | assert count.("a=1&&") == 1 20 | assert count.("&&a=1&&") == 1 21 | assert count.("a=1&b=2") == 2 22 | assert count.("a=1&&b=2") == 2 23 | assert count.("a=1&&b=2&") == 2 24 | assert count.("a=1&&b=2&&") == 2 25 | assert count.("&&a=1&&b=2&&") == 2 26 | end 27 | 28 | test "url_encoded_empty?" do 29 | inst = Instance.run(url_encoded_wat()) 30 | empty? = Instance.capture(inst, :url_encoded_empty?, 1) 31 | 32 | assert empty?.("") == 1 33 | assert empty?.("a") == 0 34 | assert empty?.("a&") == 0 35 | assert empty?.("a&&") == 0 36 | assert empty?.("&a&") == 0 37 | assert empty?.("&&a&&") == 0 38 | assert empty?.("a=1") == 0 39 | assert empty?.("a=1&") == 0 40 | assert empty?.("a=1&&") == 0 41 | assert empty?.("&&a=1&&") == 0 42 | assert empty?.("a=1&b=2") == 0 43 | assert empty?.("a=1&&b=2") == 0 44 | assert empty?.("a=1&&b=2&") == 0 45 | assert empty?.("a=1&&b=2&&") == 0 46 | assert empty?.("&&a=1&&b=2&&") == 0 47 | end 48 | 49 | test "url_encoded_first_value_offset" do 50 | inst = Instance.run(url_encoded_wat()) 51 | 52 | first_value_offset = 53 | Instance.capture(inst, :url_encoded_first_value_offset, 1) 54 | 55 | first_value = 56 | Instance.capture(inst, String, :url_encoded_first_value_offset, 1) 57 | 58 | assert first_value_offset.("") == 0 59 | assert first_value_offset.("a") == 0 60 | assert first_value_offset.("a&") == 0 61 | assert first_value_offset.("a&&") == 0 62 | assert first_value_offset.("&a&") == 0 63 | assert first_value_offset.("&&a&&") == 0 64 | assert first_value_offset.("a=") == 0 65 | assert first_value_offset.("a=1") > 0 66 | assert first_value_offset.("a=1&") > 0 67 | assert first_value_offset.("a=1&&") > 0 68 | assert first_value_offset.("&&a=1&&") > 0 69 | assert first_value_offset.("a=1&b=2") > 0 70 | assert first_value_offset.("a=1&&b=2") > 0 71 | assert first_value_offset.("a=1&&b=2&") > 0 72 | assert first_value_offset.("a=1&&b=2&&") > 0 73 | assert first_value_offset.("&&a=1&&b=2&&") > 0 74 | assert first_value_offset.("urls%5B%5D=https%3A") > 0 75 | 76 | assert first_value.("") == "" 77 | assert first_value.("a") == "" 78 | assert first_value.("a&") == "" 79 | assert first_value.("a&&") == "" 80 | assert first_value.("&a&") == "" 81 | assert first_value.("&&a&&") == "" 82 | assert first_value.("a=") == "" 83 | assert first_value.("a=1") == "1" 84 | assert first_value.("a=1&") == "1&" 85 | assert first_value.("a=1&&") == "1&&" 86 | assert first_value.("&&a=1&&") == "1&&" 87 | assert first_value.("a=1&b=2") == "1&b=2" 88 | assert first_value.("a=1&&b=2") == "1&&b=2" 89 | assert first_value.("a=1&&b=2&") == "1&&b=2&" 90 | assert first_value.("a=1&&b=2&&") == "1&&b=2&&" 91 | assert first_value.("&&a=1&&b=2&&") == "1&&b=2&&" 92 | assert first_value.("urls%5B%5D=https%3A") == "https%3A" 93 | end 94 | 95 | test "url_encoded_clone_first" do 96 | inst = Instance.run(url_encoded_wat()) 97 | 98 | clone_first = 99 | Instance.capture(inst, String, :url_encoded_clone_first, 1) 100 | 101 | assert clone_first.("") == "" 102 | assert clone_first.("a") == "a" 103 | assert clone_first.("a&") == "a" 104 | assert clone_first.("a&&") == "a" 105 | assert clone_first.("&a&") == "a" 106 | assert clone_first.("&&a&&") == "a" 107 | assert clone_first.("a=1") == "a=1" 108 | assert clone_first.("a=1&") == "a=1" 109 | assert clone_first.("a=1&&") == "a=1" 110 | assert clone_first.("&&a=1&&") == "a=1" 111 | assert clone_first.("a=1&b=2") == "a=1" 112 | assert clone_first.("a=1&&b=2") == "a=1" 113 | assert clone_first.("a=1&&b=2&") == "a=1" 114 | assert clone_first.("a=1&&b=2&&") == "a=1" 115 | assert clone_first.("&&a=1&&b=2&&") == "a=1" 116 | end 117 | 118 | test "url_encoded_rest" do 119 | inst = Instance.run(url_encoded_wat()) 120 | 121 | rest = 122 | Instance.capture(inst, String, :url_encoded_rest, 1) 123 | 124 | assert rest.("") == "" 125 | assert rest.("&") == "" 126 | assert rest.("a") == "" 127 | assert rest.("a&") == "&" 128 | assert rest.("a&&") == "&&" 129 | assert rest.("&a&") == "&" 130 | assert rest.("&&a&&") == "&&" 131 | assert rest.("a=") == "" 132 | assert rest.("a=&") == "&" 133 | assert rest.("a=1") == "" 134 | assert rest.("a=1&") == "&" 135 | assert rest.("a=1&&") == "&&" 136 | assert rest.("&&a=1&&") == "&&" 137 | assert rest.("a=1&b=2") == "&b=2" 138 | assert rest.("a=1&&b=2") == "&&b=2" 139 | assert rest.("a=1&&b=2&") == "&&b=2&" 140 | assert rest.("a=1&&b=2&&") == "&&b=2&&" 141 | assert rest.("&&a=1&&b=2&&") == "&&b=2&&" 142 | end 143 | 144 | test "url_encode_rfc3986" do 145 | inst = Instance.run(url_encoded_wat()) 146 | url_encode = Instance.capture(inst, String, :url_encode_rfc3986, 1) 147 | 148 | assert url_encode.("0123456789") == "0123456789" 149 | assert url_encode.("abcxyzABCXYZ") == "abcxyzABCXYZ" 150 | assert url_encode.("two words") == "two%20words" 151 | assert url_encode.("TWO WORDS") == "TWO%20WORDS" 152 | assert url_encode.("`") == "%60" 153 | assert url_encode.("<>`") == "%3C%3E%60" 154 | assert url_encode.("put it+й") == "put%20it+%D0%B9" 155 | 156 | assert url_encode.("ftp://s-ite.tld/?value=put it+й") == 157 | "ftp://s-ite.tld/?value=put%20it+%D0%B9" 158 | 159 | assert url_encode.("ftp://s-ite.tld/?value=put it+й") == 160 | URI.encode("ftp://s-ite.tld/?value=put it+й") 161 | 162 | assert url_encode.(":/?#[]@!$&\'()*+,;=~_-.") == ":/?#[]@!$&\'()*+,;=~_-." 163 | assert url_encode.(":/?#[]@!$&\'()*+,;=~_-.") == URI.encode(":/?#[]@!$&\'()*+,;=~_-.") 164 | 165 | assert url_encode.("😀") == "%F0%9F%98%80" 166 | assert url_encode.("💪🏾") == "%F0%9F%92%AA%F0%9F%8F%BE" 167 | end 168 | 169 | test "url_encode_www_form" do 170 | inst = Instance.run(url_encoded_wat()) 171 | url_encode = Instance.capture(inst, String, :url_encode_www_form, 1) 172 | 173 | assert url_encode.("0123456789") == "0123456789" 174 | assert url_encode.("abcxyzABCXYZ") == "abcxyzABCXYZ" 175 | assert url_encode.("two words") == "two+words" 176 | assert url_encode.("TWO WORDS") == "TWO+WORDS" 177 | assert url_encode.("+") == "%2B" 178 | assert url_encode.("`") == "%60" 179 | assert url_encode.("<>`") == "%3C%3E%60" 180 | assert url_encode.("put it+й") == "put+it%2B%D0%B9" 181 | 182 | assert url_encode.("ftp://s-ite.tld/?value=put it+й") == 183 | "ftp%3A%2F%2Fs-ite.tld%2F%3Fvalue%3Dput+it%2B%D0%B9" 184 | 185 | assert url_encode.("ftp://s-ite.tld/?value=put it+й") == 186 | URI.encode_www_form("ftp://s-ite.tld/?value=put it+й") 187 | 188 | assert url_encode.(":/?#[]@!$&\'()*,;=~_-.") == 189 | "%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2C%3B%3D~_-." 190 | 191 | assert url_encode.(":/?#[]@!$&\'()*,;=~_-.") == 192 | URI.encode_www_form(":/?#[]@!$&\'()*,;=~_-.") 193 | 194 | assert url_encode.("😀") == "%F0%9F%98%80" 195 | assert url_encode.("💪🏾") == "%F0%9F%92%AA%F0%9F%8F%BE" 196 | end 197 | 198 | @tag :skip 199 | test "append_url_encode_query_pair_www_form" do 200 | inst = Instance.run(url_encoded_wat()) 201 | append_query = Instance.capture(inst, String, :append_url_encode_query_pair_www_form, 2) 202 | build_start = Instance.capture(inst, :bump_write_start, 0) 203 | build_done = Instance.capture(inst, String, :bump_write_done, 0) 204 | 205 | a = Instance.alloc_string(inst, "a") 206 | b = Instance.alloc_string(inst, "b") 207 | 208 | build_start.() 209 | append_query.(a, b) 210 | s = build_done.() 211 | 212 | assert s == "&a=b" 213 | end 214 | 215 | @tag :skip 216 | test "url_encode_query_www_form" do 217 | inst = Instance.run(url_encoded_wat()) 218 | url_encode_query = Instance.capture(inst, String, :url_encode_query_www_form, 1) 219 | 220 | # {list, bytes, list_bytes} = Instance.alloc_list(inst, [["a", "1"], ["b", "2"]]) 221 | # assert list == [[<<97, 0>>, <<49, 0>>], [<<98, 0>>, <<50, 0>>]] 222 | # assert bytes == <<97, 0, 49, 0, 98, 0, 50, 0>> 223 | # assert list_bytes == <<65540::little-size(32), 65552::little-size(32)>> 224 | list_ptr = Instance.alloc_list(inst, [["a", "1"], ["b", "2"]]) 225 | assert url_encode_query.(list_ptr) == "a=1&b=2" 226 | 227 | # result = Instance.call( 228 | # URLEncoded.url_encode_query_www_form([ 229 | # a: 1, 230 | # b: 1, 231 | # ]) 232 | # ) 233 | end 234 | 235 | @tag :skip 236 | test "wasm byte size" do 237 | assert byte_size(Wasm.to_wasm(url_encoded_wat())) == 1985 238 | end 239 | 240 | defp url_encoded_wat() do 241 | """ 242 | (module $URLEncoded 243 | (memory (export "memory") 2) 244 | (global $bump_write_level (mut i32) (i32.const 0)) 245 | (global $bump_offset (mut i32) (i32.const 65536)) 246 | (global $bump_mark (mut i32) (i32.const 0)) 247 | (func $alloc (export "alloc") (param $size i32) (result i32) 248 | (call $bump_alloc (local.get $size)) 249 | ) 250 | (func $free_all (export "free_all") 251 | (i32.const 65536) 252 | (global.set $bump_offset) 253 | ) 254 | (func $bump_write_start 255 | (i32.eqz (global.get $bump_write_level)) 256 | (if 257 | (then 258 | (global.get $bump_offset) 259 | (global.set $bump_mark) 260 | ) 261 | ) 262 | (i32.add (global.get $bump_write_level) (i32.const 1)) 263 | (global.set $bump_write_level) 264 | ) 265 | (func $bump_write_done (result i32) 266 | (global.get $bump_write_level) 267 | (if 268 | (then 269 | nop 270 | ) 271 | (else 272 | unreachable 273 | ) 274 | ) 275 | (i32.sub (global.get $bump_write_level) (i32.const 1)) 276 | (global.set $bump_write_level) 277 | (i32.eqz (global.get $bump_write_level)) 278 | (if 279 | (then 280 | (i32.store8 (global.get $bump_offset) (i32.const 0)) 281 | (i32.add (global.get $bump_offset) (i32.const 1)) 282 | (global.set $bump_offset) 283 | ) 284 | ) 285 | (global.get $bump_mark) 286 | ) 287 | (func $bump_write_str (param $str_ptr i32) 288 | (local $len i32) 289 | (i32.eq (local.get $str_ptr) (global.get $bump_mark)) 290 | (if 291 | (then 292 | return 293 | ) 294 | ) 295 | (call $strlen (local.get $str_ptr)) 296 | (local.set $len) 297 | (call $memcpy (global.get $bump_offset) (local.get $str_ptr) (local.get $len)) 298 | (i32.add (global.get $bump_offset) (local.get $len)) 299 | (global.set $bump_offset) 300 | ) 301 | (func $u32toa_count (param $value i32) (result i32) 302 | (local $digit_count i32) 303 | (local $digit i32) 304 | (loop $Digits 305 | (i32.add (local.get $digit_count) (i32.const 1)) 306 | (local.set $digit_count) 307 | (i32.rem_u (local.get $value) (i32.const 10)) 308 | (local.set $digit) 309 | (i32.div_u (local.get $value) (i32.const 10)) 310 | (local.set $value) 311 | (i32.gt_u (local.get $value) (i32.const 0)) 312 | br_if $Digits 313 | ) 314 | (local.get $digit_count) 315 | ) 316 | (func $u32toa (param $value i32) (param $end_offset i32) (result i32) 317 | (local $working_offset i32) 318 | (local $digit i32) 319 | (local.get $end_offset) 320 | (local.set $working_offset) 321 | (loop $Digits 322 | (i32.sub (local.get $working_offset) (i32.const 1)) 323 | (local.set $working_offset) 324 | (i32.rem_u (local.get $value) (i32.const 10)) 325 | (local.set $digit) 326 | (i32.div_u (local.get $value) (i32.const 10)) 327 | (local.set $value) 328 | (i32.store8 (local.get $working_offset) (i32.add (i32.const 48) (local.get $digit))) 329 | (i32.gt_u (local.get $value) (i32.const 0)) 330 | br_if $Digits 331 | ) 332 | (local.get $working_offset) 333 | ) 334 | (func $write_u32 (param $value i32) (param $str_ptr i32) (result i32) 335 | (local $working_offset i32) 336 | (local $last_offset i32) 337 | (local $digit i32) 338 | (i32.add (local.get $str_ptr) (call $u32toa_count (local.get $value))) 339 | (local.set $last_offset) 340 | (local.get $last_offset) 341 | (local.set $working_offset) 342 | (loop $Digits 343 | (i32.sub (local.get $working_offset) (i32.const 1)) 344 | (local.set $working_offset) 345 | (i32.rem_u (local.get $value) (i32.const 10)) 346 | (local.set $digit) 347 | (i32.div_u (local.get $value) (i32.const 10)) 348 | (local.set $value) 349 | (i32.store8 (local.get $working_offset) (i32.add (i32.const 48) (local.get $digit))) 350 | (i32.gt_u (local.get $value) (i32.const 0)) 351 | br_if $Digits 352 | ) 353 | (local.get $last_offset) 354 | ) 355 | (func $streq (param $address_a i32) (param $address_b i32) (result i32) 356 | (local $i i32) 357 | (local $byte_a i32) 358 | (local $byte_b i32) 359 | (loop $EachByte (result i32) 360 | (i32.load8_u (i32.add (local.get $address_a) (local.get $i))) 361 | (local.set $byte_a) 362 | (i32.load8_u (i32.add (local.get $address_b) (local.get $i))) 363 | (local.set $byte_b) 364 | (i32.eqz (local.get $byte_a)) 365 | (if 366 | (then 367 | (return (i32.eqz (local.get $byte_b))) 368 | ) 369 | ) 370 | (i32.eq (local.get $byte_a) (local.get $byte_b)) 371 | (if 372 | (then 373 | (i32.add (local.get $i) (i32.const 1)) 374 | (local.set $i) 375 | br $EachByte 376 | ) 377 | ) 378 | (return (i32.const 0)) 379 | ) 380 | ) 381 | (func $strlen (param $string_ptr i32) (result i32) 382 | (local $count i32) 383 | (loop $EachChar 384 | (i32.load8_u (i32.add (local.get $string_ptr) (local.get $count))) 385 | (if 386 | (then 387 | (i32.add (local.get $count) (i32.const 1)) 388 | (local.set $count) 389 | br $EachChar 390 | ) 391 | ) 392 | ) 393 | (local.get $count) 394 | ) 395 | (func $memcpy (param $dest i32) (param $src i32) (param $byte_count i32) 396 | (local $i i32) 397 | (loop $EachByte 398 | (i32.eq (local.get $i) (local.get $byte_count)) 399 | (if 400 | (then 401 | return 402 | ) 403 | ) 404 | (i32.store8 (i32.add (local.get $dest) (local.get $i)) (i32.load8_u (i32.add (local.get $src) (local.get $i)))) 405 | (i32.add (local.get $i) (i32.const 1)) 406 | (local.set $i) 407 | br $EachByte 408 | ) 409 | ) 410 | (func $memset (param $dest i32) (param $u8 i32) (param $byte_count i32) 411 | (local $i i32) 412 | (loop $EachByte 413 | (i32.eq (local.get $i) (local.get $byte_count)) 414 | (if 415 | (then 416 | return 417 | ) 418 | ) 419 | (i32.store8 (i32.add (local.get $dest) (local.get $i)) (local.get $u8)) 420 | (i32.add (local.get $i) (i32.const 1)) 421 | (local.set $i) 422 | br $EachByte 423 | ) 424 | ) 425 | (func $bump_alloc (param $size i32) (result i32) 426 | (global.get $bump_offset) 427 | (i32.add (global.get $bump_offset) (local.get $size)) 428 | (global.set $bump_offset) 429 | 430 | ) 431 | (func $url_encode_rfc3986 (export "url_encode_rfc3986") (param $str_ptr i32) (result i32) 432 | (local $char i32) 433 | (local $abc i32) 434 | (local $__dup_32 i32) 435 | (call $bump_write_start) 436 | (loop $EachByte 437 | (i32.load8_u (local.get $str_ptr)) 438 | (local.set $char) 439 | (local.get $char) 440 | (if 441 | (then 442 | (i32.or (i32.or (i32.or (i32.and (i32.ge_u (local.get $char) (i32.const 97)) (i32.le_u (local.get $char) (i32.const 122))) (i32.and (i32.ge_u (local.get $char) (i32.const 65)) (i32.le_u (local.get $char) (i32.const 90)))) (i32.and (i32.ge_u (local.get $char) (i32.const 48)) (i32.le_u (local.get $char) (i32.const 57)))) (i32.eq (local.get $char) (i32.const 43)) 443 | (i32.eq (local.get $char) (i32.const 58)) 444 | (i32.or) 445 | (i32.eq (local.get $char) (i32.const 47)) 446 | (i32.or) 447 | (i32.eq (local.get $char) (i32.const 63)) 448 | (i32.or) 449 | (i32.eq (local.get $char) (i32.const 35)) 450 | (i32.or) 451 | (i32.eq (local.get $char) (i32.const 91)) 452 | (i32.or) 453 | (i32.eq (local.get $char) (i32.const 93)) 454 | (i32.or) 455 | (i32.eq (local.get $char) (i32.const 64)) 456 | (i32.or) 457 | (i32.eq (local.get $char) (i32.const 33)) 458 | (i32.or) 459 | (i32.eq (local.get $char) (i32.const 36)) 460 | (i32.or) 461 | (i32.eq (local.get $char) (i32.const 38)) 462 | (i32.or) 463 | (i32.eq (local.get $char) (i32.const 92)) 464 | (i32.or) 465 | (i32.eq (local.get $char) (i32.const 39)) 466 | (i32.or) 467 | (i32.eq (local.get $char) (i32.const 40)) 468 | (i32.or) 469 | (i32.eq (local.get $char) (i32.const 41)) 470 | (i32.or) 471 | (i32.eq (local.get $char) (i32.const 42)) 472 | (i32.or) 473 | (i32.eq (local.get $char) (i32.const 44)) 474 | (i32.or) 475 | (i32.eq (local.get $char) (i32.const 59)) 476 | (i32.or) 477 | (i32.eq (local.get $char) (i32.const 61)) 478 | (i32.or) 479 | (i32.eq (local.get $char) (i32.const 126)) 480 | (i32.or) 481 | (i32.eq (local.get $char) (i32.const 95)) 482 | (i32.or) 483 | (i32.eq (local.get $char) (i32.const 45)) 484 | (i32.or) 485 | (i32.eq (local.get $char) (i32.const 46)) 486 | (i32.or)) 487 | (if 488 | (then 489 | (i32.store8 (global.get $bump_offset) (local.get $char)) 490 | (i32.add (global.get $bump_offset) (i32.const 1)) 491 | (global.set $bump_offset) 492 | ) 493 | (else 494 | (i32.store8 (global.get $bump_offset) (i32.const 37)) 495 | (i32.add (global.get $bump_offset) (i32.const 1)) 496 | (global.set $bump_offset) 497 | (i32.store8 (global.get $bump_offset) (i32.add (i32.shr_u (local.get $char) (i32.const 4)) (i32.le_u (i32.shr_u (local.get $char) (i32.const 4)) (i32.const 9)) 498 | (if (result i32) 499 | (then 500 | (i32.const 48) 501 | ) 502 | (else 503 | (i32.const 55) 504 | ) 505 | ))) 506 | (i32.add (global.get $bump_offset) (i32.const 1)) 507 | (global.set $bump_offset) 508 | (i32.store8 (global.get $bump_offset) (i32.add (i32.and (local.get $char) (i32.const 15)) (i32.le_u (i32.and (local.get $char) (i32.const 15)) (i32.const 9)) 509 | (if (result i32) 510 | (then 511 | (i32.const 48) 512 | ) 513 | (else 514 | (i32.const 55) 515 | ) 516 | ))) 517 | (i32.add (global.get $bump_offset) (i32.const 1)) 518 | (global.set $bump_offset) 519 | ) 520 | ) 521 | (i32.add (local.get $str_ptr) (i32.const 1)) 522 | (local.set $str_ptr) 523 | br $EachByte 524 | ) 525 | ) 526 | ) 527 | (call $bump_write_done) 528 | ) 529 | (func $append_url_encode_www_form (export "append_url_encode_www_form") (param $str_ptr i32) 530 | (local $char i32) 531 | (local $abc i32) 532 | (local $__dup_32 i32) 533 | (loop $EachByte 534 | (i32.load8_u (local.get $str_ptr)) 535 | (local.set $char) 536 | (local.get $char) 537 | (if 538 | (then 539 | (i32.eq (local.get $char) (i32.const 32)) 540 | (if 541 | (then 542 | (i32.store8 (global.get $bump_offset) (i32.const 43)) 543 | (i32.add (global.get $bump_offset) (i32.const 1)) 544 | (global.set $bump_offset) 545 | ) 546 | (else 547 | (i32.or (i32.or (i32.or (i32.and (i32.ge_u (local.get $char) (i32.const 97)) (i32.le_u (local.get $char) (i32.const 122))) (i32.and (i32.ge_u (local.get $char) (i32.const 65)) (i32.le_u (local.get $char) (i32.const 90)))) (i32.and (i32.ge_u (local.get $char) (i32.const 48)) (i32.le_u (local.get $char) (i32.const 57)))) (i32.eq (local.get $char) (i32.const 126)) 548 | (i32.eq (local.get $char) (i32.const 95)) 549 | (i32.or) 550 | (i32.eq (local.get $char) (i32.const 45)) 551 | (i32.or) 552 | (i32.eq (local.get $char) (i32.const 46)) 553 | (i32.or)) 554 | (if 555 | (then 556 | (i32.store8 (global.get $bump_offset) (local.get $char)) 557 | (i32.add (global.get $bump_offset) (i32.const 1)) 558 | (global.set $bump_offset) 559 | ) 560 | (else 561 | (i32.store8 (global.get $bump_offset) (i32.const 37)) 562 | (i32.add (global.get $bump_offset) (i32.const 1)) 563 | (global.set $bump_offset) 564 | (i32.store8 (global.get $bump_offset) (i32.add (i32.shr_u (local.get $char) (i32.const 4)) (i32.le_u (i32.shr_u (local.get $char) (i32.const 4)) (i32.const 9)) 565 | (if (result i32) 566 | (then 567 | (i32.const 48) 568 | ) 569 | (else 570 | (i32.const 55) 571 | ) 572 | ))) 573 | (i32.add (global.get $bump_offset) (i32.const 1)) 574 | (global.set $bump_offset) 575 | (i32.store8 (global.get $bump_offset) (i32.add (i32.and (local.get $char) (i32.const 15)) (i32.le_u (i32.and (local.get $char) (i32.const 15)) (i32.const 9)) 576 | (if (result i32) 577 | (then 578 | (i32.const 48) 579 | ) 580 | (else 581 | (i32.const 55) 582 | ) 583 | ))) 584 | (i32.add (global.get $bump_offset) (i32.const 1)) 585 | (global.set $bump_offset) 586 | ) 587 | ) 588 | ) 589 | ) 590 | (i32.add (local.get $str_ptr) (i32.const 1)) 591 | (local.set $str_ptr) 592 | br $EachByte 593 | ) 594 | ) 595 | ) 596 | ) 597 | (func $append_url_encode_query_pair_www_form (export "append_url_encode_query_pair_www_form") (param $key i32) (param $value i32) 598 | (i32.store8 (global.get $bump_offset) (i32.const 38)) 599 | (i32.add (global.get $bump_offset) (i32.const 1)) 600 | (global.set $bump_offset) 601 | (call $append_url_encode_www_form (local.get $key)) 602 | (i32.store8 (global.get $bump_offset) (i32.const 61)) 603 | (i32.add (global.get $bump_offset) (i32.const 1)) 604 | (global.set $bump_offset) 605 | (call $append_url_encode_www_form (local.get $value)) 606 | ) 607 | (func $url_encode_www_form (export "url_encode_www_form") (param $str_ptr i32) (result i32) 608 | (local $char i32) 609 | (local $abc i32) 610 | (local $__dup_32 i32) 611 | (call $bump_write_start) 612 | (call $append_url_encode_www_form (local.get $str_ptr)) 613 | (call $bump_write_done) 614 | ) 615 | (func $decode_char_www_form (export "decode_char_www_form") (param $str i32) (result i32) 616 | (local $c0 i32) 617 | (local $c1 i32) 618 | (local $c2 i32) 619 | (i32.load8_u (local.get $str)) 620 | (local.set $c0) 621 | (i32.eqz (local.get $c0)) 622 | (if 623 | (then 624 | (return (i32.const 0)) 625 | ) 626 | ) 627 | (block $i32_match (result i32) 628 | (i32.eq (local.get $c0) (i32.const 37)) 629 | (if 630 | (then 631 | (i32.load8_u (i32.add (local.get $str) (i32.const 1))) 632 | (local.set $c1) 633 | (i32.eqz (local.get $c1)) 634 | (if (result i32) 635 | (then 636 | (i32.const 0) 637 | ) 638 | (else 639 | (i32.load8_u (i32.add (local.get $str) (i32.const 2))) 640 | (local.set $c2) 641 | (i32.add (i32.shl (i32.add (i32.and (local.get $c1) (i32.const 15)) (i32.mul (i32.shr_u (local.get $c1) (i32.const 6)) (i32.const 9))) (i32.const 4)) (i32.add (i32.and (local.get $c2) (i32.const 15)) (i32.mul (i32.shr_u (local.get $c2) (i32.const 6)) (i32.const 9)))) 642 | ) 643 | ) 644 | br $i32_match 645 | ) 646 | ) 647 | (local.get $c0) 648 | 649 | ) 650 | ) 651 | (func $url_encoded_count (export "url_encoded_count") (param $url_encoded i32) (result i32) 652 | (local $char i32) 653 | (local $count i32) 654 | (local $pair_char_len i32) 655 | (loop $EachByte 656 | (i32.load8_u (local.get $url_encoded)) 657 | (local.set $char) 658 | (i32.eq (local.get $char) (i32.const 38)) 659 | (i32.eqz (local.get $char)) 660 | (i32.or) 661 | (if 662 | (then 663 | (i32.add (local.get $count) (i32.gt_u (local.get $pair_char_len) (i32.const 0))) 664 | (local.set $count) 665 | (i32.const 0) 666 | (local.set $pair_char_len) 667 | ) 668 | (else 669 | (i32.add (local.get $pair_char_len) (i32.const 1)) 670 | (local.set $pair_char_len) 671 | ) 672 | ) 673 | (i32.add (local.get $url_encoded) (i32.const 1)) 674 | (local.set $url_encoded) 675 | (local.get $char) 676 | br_if $EachByte 677 | ) 678 | (local.get $count) 679 | ) 680 | (func $url_encoded_empty? (export "url_encoded_empty?") (param $url_encoded i32) (result i32) 681 | (local $char i32) 682 | (local $pair_char_len i32) 683 | (loop $EachByte (result i32) 684 | (i32.load8_u (local.get $url_encoded)) 685 | (local.set $char) 686 | (i32.eqz (local.get $char)) 687 | (if 688 | (then 689 | (return (i32.const 1)) 690 | ) 691 | ) 692 | (i32.eqz (i32.eq (local.get $char) (i32.const 38))) 693 | (if 694 | (then 695 | (return (i32.const 0)) 696 | ) 697 | ) 698 | (i32.add (local.get $url_encoded) (i32.const 1)) 699 | (local.set $url_encoded) 700 | br $EachByte 701 | ) 702 | ) 703 | (func $url_encoded_clone_first (export "url_encoded_clone_first") (param $url_encoded i32) (result i32) 704 | (local $char i32) 705 | (local $len i32) 706 | (call $bump_write_start) 707 | (loop $EachByte (result i32) 708 | (i32.load8_u (local.get $url_encoded)) 709 | (local.set $char) 710 | (i32.or (i32.eqz (local.get $char)) (i32.and (i32.eq (local.get $char) (i32.const 38)) (i32.gt_u (local.get $len) (i32.const 0)))) 711 | (if 712 | (then 713 | (call $bump_write_done) 714 | return 715 | ) 716 | ) 717 | (i32.eqz (i32.eq (local.get $char) (i32.const 38))) 718 | (if 719 | (then 720 | (i32.store8 (global.get $bump_offset) (local.get $char)) 721 | (i32.add (global.get $bump_offset) (i32.const 1)) 722 | (global.set $bump_offset) 723 | (i32.add (local.get $len) (i32.const 1)) 724 | (local.set $len) 725 | ) 726 | ) 727 | (i32.add (local.get $url_encoded) (i32.const 1)) 728 | (local.set $url_encoded) 729 | br $EachByte 730 | ) 731 | ) 732 | (func $url_encoded_rest (export "url_encoded_rest") (param $url_encoded i32) (result i32) 733 | (local $char i32) 734 | (local $len i32) 735 | (loop $EachByte (result i32) 736 | (i32.load8_u (local.get $url_encoded)) 737 | (local.set $char) 738 | (i32.or (i32.eqz (local.get $char)) (i32.and (i32.eq (local.get $char) (i32.const 38)) (i32.gt_u (local.get $len) (i32.const 0)))) 739 | (if 740 | (then 741 | (local.get $url_encoded) 742 | return 743 | ) 744 | ) 745 | (i32.eqz (i32.eq (local.get $char) (i32.const 38))) 746 | (if 747 | (then 748 | (i32.add (local.get $len) (i32.const 1)) 749 | (local.set $len) 750 | ) 751 | ) 752 | (i32.add (local.get $url_encoded) (i32.const 1)) 753 | (local.set $url_encoded) 754 | br $EachByte 755 | ) 756 | ) 757 | (func $url_encoded_decode_first_value_www_form (export "url_encoded_decode_first_value_www_form") (param $url_encoded i32) (param $key i32) (result i32) 758 | (i32.const 0) 759 | ) 760 | (func $url_encoded_first_value_offset (export "url_encoded_first_value_offset") (param $url_encoded i32) (result i32) 761 | (local $char i32) 762 | (local $len i32) 763 | (loop $EachByte (result i32) 764 | (i32.load8_u (local.get $url_encoded)) 765 | (local.set $char) 766 | (i32.or (i32.eqz (local.get $char)) (i32.and (i32.eq (local.get $char) (i32.const 38)) (i32.gt_u (local.get $len) (i32.const 0)))) 767 | (if 768 | (then 769 | (i32.const 0) 770 | return 771 | ) 772 | ) 773 | (i32.eq (local.get $char) (i32.const 61)) 774 | (if 775 | (then 776 | (i32.add (local.get $url_encoded) (i32.const 1)) 777 | (local.set $url_encoded) 778 | (i32.load8_u (local.get $url_encoded)) 779 | (local.set $char) 780 | (i32.eqz (local.get $char)) 781 | (i32.eq (local.get $char) (i32.const 38)) 782 | (i32.or) 783 | (if (result i32) 784 | (then 785 | (i32.const 0) 786 | ) 787 | (else 788 | (local.get $url_encoded) 789 | ) 790 | ) 791 | return 792 | ) 793 | ) 794 | (i32.eqz (i32.eq (local.get $char) (i32.const 38))) 795 | (if 796 | (then 797 | (i32.add (local.get $len) (i32.const 1)) 798 | (local.set $len) 799 | ) 800 | ) 801 | (i32.add (local.get $url_encoded) (i32.const 1)) 802 | (local.set $url_encoded) 803 | br $EachByte 804 | ) 805 | ) 806 | (func $_url_encoded_value_next_char (export "_url_encoded_value_next_char") (param $ptr i32) (result i32) 807 | (local $next i32) 808 | (local $next_char i32) 809 | (i32.add (i32.eq (i32.load8_u (local.get $ptr)) (i32.const 37)) 810 | (if (result i32) 811 | (then 812 | (i32.const 3) 813 | ) 814 | (else 815 | (i32.const 1) 816 | ) 817 | ) (local.get $ptr)) 818 | (local.set $next) 819 | (i32.load8_u (local.get $next)) 820 | (local.set $next_char) 821 | (i32.eqz (local.get $next_char)) 822 | (i32.eq (local.get $next_char) (i32.const 38)) 823 | (i32.or) 824 | (if (result i32) 825 | (then 826 | (i32.const 0) 827 | ) 828 | (else 829 | (local.get $next) 830 | ) 831 | ) 832 | ) 833 | ) 834 | """ 835 | end 836 | end 837 | -------------------------------------------------------------------------------- /native/orb_wasmtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | // See: https://hansihe.com/posts/rustler-safe-erlang-elixir-nifs-in-rust/ 2 | 3 | pub mod atom; 4 | 5 | use std::convert::{TryFrom, TryInto}; 6 | use std::ffi::CStr; 7 | use std::time::Duration; 8 | // use std::rc::Rc; 9 | use anyhow::anyhow; 10 | use crossbeam_channel::bounded; 11 | use std::slice; 12 | use std::sync::{Arc, RwLock}; 13 | use std::thread; 14 | // use log::{info, warn}; 15 | use wasmtime::*; 16 | //use anyhow::Error as anyhowError; 17 | use rustler::{ 18 | nif, Binary, Encoder, Env, Error, LocalPid, NewBinary, NifStruct, NifTaggedEnum, NifTuple, 19 | NifUnitEnum, OwnedBinary, OwnedEnv, ResourceArc, Term, 20 | }; 21 | use wabt::wat2wasm_with_features; 22 | 23 | #[nif] 24 | fn add(a: i64, b: i64) -> i64 { 25 | a + b 26 | } 27 | 28 | #[nif] 29 | fn reverse_string(a: String) -> String { 30 | // return a; 31 | return a.chars().rev().collect(); 32 | } 33 | 34 | #[nif] 35 | fn strlen(a: String) -> usize { 36 | return a.chars().count(); 37 | } 38 | 39 | fn string_error(error: T) -> Error { 40 | return Error::Term(Box::new(error.to_string())); 41 | } 42 | 43 | fn map_return_values_i32(env: Env, values: Vec) -> Term { 44 | match values[..] { 45 | [] => rustler::types::atom::nil().encode(env), 46 | [a] => a.encode(env), 47 | [a, b] => (a, b).encode(env), 48 | [a, b, c] => (a, b, c).encode(env), 49 | [a, b, c, d] => (a, b, c, d).encode(env), 50 | [..] => values.encode(env), 51 | } 52 | } 53 | 54 | #[derive(NifTaggedEnum)] 55 | enum WasmModuleDefinition<'a> { 56 | Wat(String), 57 | Wasm(Binary<'a>), 58 | } 59 | 60 | // #[derive(NifTuple)] 61 | // struct WasmExport { 62 | // type: i32, 63 | // name: String, 64 | // } 65 | 66 | #[derive(Clone, NifUnitEnum)] 67 | enum GlobalType { 68 | I32, 69 | I64, 70 | F32, 71 | F64, 72 | } 73 | impl TryFrom for GlobalType { 74 | type Error = anyhow::Error; 75 | 76 | fn try_from(val: wasmtime::ValType) -> Result { 77 | Ok(match val { 78 | ValType::I32 => GlobalType::I32, 79 | ValType::I64 => GlobalType::I64, 80 | ValType::F32 => GlobalType::F32, 81 | ValType::F64 => GlobalType::F64, 82 | _ => anyhow::bail!("Unsupported global type {}", val), 83 | }) 84 | } 85 | } 86 | // TODO: remove this one 87 | impl TryFrom<&wasmtime::ValType> for GlobalType { 88 | type Error = anyhow::Error; 89 | 90 | fn try_from(val: &wasmtime::ValType) -> Result { 91 | Ok(match val { 92 | ValType::I32 => GlobalType::I32, 93 | ValType::I64 => GlobalType::I64, 94 | ValType::F32 => GlobalType::F32, 95 | ValType::F64 => GlobalType::F64, 96 | _ => anyhow::bail!("Unsupported global type {}", val), 97 | }) 98 | } 99 | } 100 | impl From for wasmtime::ValType { 101 | fn from(val: GlobalType) -> wasmtime::ValType { 102 | match val { 103 | GlobalType::I32 => ValType::I32, 104 | GlobalType::I64 => ValType::I64, 105 | GlobalType::F32 => ValType::F32, 106 | GlobalType::F64 => ValType::F64, 107 | } 108 | } 109 | } 110 | 111 | #[derive(NifTaggedEnum)] 112 | enum WasmExternType { 113 | Func { 114 | params: Vec, 115 | results: Vec, 116 | }, 117 | // Func(GlobalType), 118 | Global(GlobalType), 119 | Memory(), 120 | Table(), 121 | // Baz{ a: i32, b: i32 }, 122 | } 123 | 124 | impl TryFrom for WasmExternType { 125 | type Error = anyhow::Error; 126 | 127 | fn try_from(val: ExternType) -> Result { 128 | Ok(match val { 129 | ExternType::Func(f) => { 130 | let params: Result> = 131 | f.params().into_iter().map(|p| p.try_into()).collect(); 132 | let results: Result> = 133 | f.results().into_iter().map(|p| p.try_into()).collect(); 134 | WasmExternType::Func { 135 | params: params?, 136 | results: results?, 137 | } 138 | } 139 | ExternType::Global(g) => WasmExternType::Global(g.content().try_into()?), 140 | ExternType::Memory(_m) => WasmExternType::Memory(), 141 | ExternType::Table(_t) => WasmExternType::Table(), 142 | }) 143 | } 144 | } 145 | 146 | #[derive(NifTuple)] 147 | struct WasmImport { 148 | module: String, 149 | name: String, 150 | extern_type: WasmExternType, 151 | } 152 | 153 | #[derive(NifTaggedEnum)] 154 | enum WasmExport { 155 | Func(String), 156 | Global(String, GlobalType), 157 | Memory(String), 158 | Table(String), 159 | // Baz{ a: i32, b: i32 }, 160 | } 161 | 162 | #[derive(NifTaggedEnum, Debug, Copy, Clone)] 163 | enum WasmSupportedValue { 164 | U32(u32), 165 | U64(u64), 166 | I32(i32), 167 | I64(i64), 168 | F32(f32), 169 | F64(f64), 170 | } 171 | 172 | impl TryFrom<&wasmtime::Val> for WasmSupportedValue { 173 | type Error = anyhow::Error; 174 | 175 | fn try_from(val: &wasmtime::Val) -> Result { 176 | Ok(match val { 177 | Val::I32(i) => WasmSupportedValue::I32(*i), 178 | Val::I64(i) => WasmSupportedValue::I64(*i), 179 | Val::F32(_f) => WasmSupportedValue::F32(val.unwrap_f32()), 180 | Val::F64(_f) => WasmSupportedValue::F64(val.unwrap_f64()), 181 | val => anyhow::bail!("Unsupported val type"), 182 | }) 183 | } 184 | } 185 | 186 | impl Into for WasmSupportedValue { 187 | fn into(self) -> wasmtime::Val { 188 | match self { 189 | WasmSupportedValue::U32(i) => Val::I32(i as i32), 190 | WasmSupportedValue::U64(i) => Val::I64(i as i64), 191 | WasmSupportedValue::I32(i) => Val::I32(i), 192 | WasmSupportedValue::I64(i) => Val::I64(i), 193 | WasmSupportedValue::F32(f) => f.into(), 194 | WasmSupportedValue::F64(f) => f.into(), 195 | } 196 | } 197 | } 198 | 199 | impl WasmSupportedValue { 200 | fn map_return_values(env: Env, values: Vec) -> Term { 201 | match values[..] { 202 | [] => rustler::types::atom::nil().encode(env), 203 | [a] => a.encode(env), 204 | [a, b] => (a, b).encode(env), 205 | [a, b, c] => (a, b, c).encode(env), 206 | [a, b, c, d] => (a, b, c, d).encode(env), 207 | [..] => values.encode(env), // Gets encoded as a list 208 | } 209 | } 210 | } 211 | 212 | impl AsRef<[u8]> for WasmModuleDefinition<'_> { 213 | fn as_ref(&self) -> &[u8] { 214 | match self { 215 | WasmModuleDefinition::Wat(s) => s.as_ref(), 216 | WasmModuleDefinition::Wasm(b) => &*b, 217 | } 218 | } 219 | } 220 | 221 | #[nif] 222 | fn wasm_list_exports(source: WasmModuleDefinition) -> Result, Error> { 223 | let engine = Engine::default(); 224 | let module = Module::new(&engine, &source).map_err(string_error)?; 225 | let exports = module.exports(); 226 | 227 | let exports: Result, anyhow::Error> = exports 228 | .into_iter() 229 | .map(|export| { 230 | let name = export.name().to_string(); 231 | Ok(match export.ty() { 232 | ExternType::Func(_f) => WasmExport::Func(name), 233 | ExternType::Global(g) => WasmExport::Global(name, g.content().try_into()?), 234 | ExternType::Memory(_m) => WasmExport::Memory(name), 235 | ExternType::Table(_t) => WasmExport::Table(name), 236 | }) 237 | }) 238 | .collect(); 239 | 240 | exports.map_err(string_error) 241 | } 242 | 243 | #[nif(schedule = "DirtyCpu")] 244 | fn wasm_list_imports(source: WasmModuleDefinition) -> Result, Error> { 245 | let engine = Engine::default(); 246 | let module = Module::new(&engine, &source).map_err(string_error)?; 247 | let imports = module.imports(); 248 | 249 | let imports: Result, anyhow::Error> = imports 250 | .into_iter() 251 | .map(|import| { 252 | let module_name = import.module().to_string(); 253 | let name = import.name().to_string(); 254 | Ok(WasmImport { 255 | module: module_name, 256 | name: name, 257 | extern_type: import.ty().try_into()?, 258 | }) 259 | }) 260 | .collect(); 261 | 262 | imports.map_err(string_error) 263 | } 264 | 265 | #[nif(schedule = "DirtyCpu")] 266 | fn wasm_call( 267 | wat_source: String, 268 | f: String, 269 | args: Vec, 270 | ) -> Result, Error> { 271 | let source = WasmModuleDefinition::Wat(wat_source); 272 | RunningInstance::new(source) 273 | .and_then(|mut i| i.call(f, args)) 274 | .map_err(string_error) 275 | } 276 | 277 | #[nif] 278 | fn wasm_call_i32_string(wat_source: String, f: String, args: Vec) -> Result { 279 | let source = WasmModuleDefinition::Wat(wat_source); 280 | RunningInstance::new(source) 281 | .and_then(|mut i| i.call_i32_string(f, args)) 282 | .map_err(string_error) 283 | } 284 | 285 | fn wasm_read_memory( 286 | store: &Store, 287 | memory: &Memory, 288 | start: u32, 289 | length: u32, 290 | ) -> Result, anyhow::Error> { 291 | let start: usize = start.try_into().unwrap(); 292 | let length: usize = length.try_into().unwrap(); 293 | 294 | let mut string_buffer: Vec = Vec::with_capacity(length); 295 | string_buffer.resize(length, 0); 296 | memory.read(&store, start, &mut string_buffer)?; 297 | return Ok(string_buffer); 298 | } 299 | 300 | fn wasm_extract_string( 301 | // store: &Store, 302 | store: impl AsContext, 303 | memory: &Memory, 304 | result: Vec, 305 | ) -> Result { 306 | match result.as_slice() { 307 | [] => anyhow::bail!("Receive empty result"), 308 | [start, length] => { 309 | let start = *start; 310 | let length = *length; 311 | let start: usize = start.try_into().unwrap(); 312 | let length: usize = length.try_into().unwrap(); 313 | 314 | let mut string_buffer: Vec = Vec::with_capacity(length); 315 | string_buffer.resize(length, 0); 316 | memory.read(&store, start, &mut string_buffer)?; 317 | let string = String::from_utf8(string_buffer)?; 318 | return Ok(string); 319 | } 320 | [start] => { 321 | let start = *start; 322 | let start: usize = start.try_into().unwrap(); 323 | 324 | let data = &memory.data(&store)[start..]; 325 | let data: &[i8] = 326 | unsafe { slice::from_raw_parts(data.as_ptr() as *const i8, data.len()) }; 327 | 328 | let cstr = unsafe { CStr::from_ptr(data.as_ptr().cast()) }; 329 | let string = String::from_utf8_lossy(cstr.to_bytes()).to_string(); 330 | 331 | return Ok(string); 332 | } 333 | _other_number_of_items => anyhow::bail!("Received result with too many items"), 334 | } 335 | } 336 | 337 | #[derive(NifTaggedEnum)] 338 | enum WasmStepInstruction { 339 | // TODO: change from u32 to WasmSupportedValue 340 | Call(String, Vec), 341 | CallString(String, Vec), 342 | WriteStringNulTerminated(u32, String, bool), 343 | ReadMemory(u32, u32), 344 | // Baz{ a: i32, b: i32 }, 345 | } 346 | 347 | #[nif] 348 | fn wasm_steps( 349 | env: Env, 350 | wat_source: String, 351 | steps: Vec, 352 | ) -> Result, Error> { 353 | wasm_steps_internal(env, wat_source, steps).map_err(string_error) 354 | } 355 | 356 | fn wasm_steps_internal( 357 | env: Env, 358 | wat_source: String, 359 | steps: Vec, 360 | ) -> Result, anyhow::Error> { 361 | let mut running_instance = RunningInstance::new(WasmModuleDefinition::Wat(wat_source))?; 362 | 363 | let mut results: Vec = Vec::with_capacity(steps.len()); 364 | for step in steps { 365 | match step { 366 | WasmStepInstruction::Call(f, args) => { 367 | let args = args 368 | .into_iter() 369 | .map(|t| WasmSupportedValue::U32(t)) 370 | .collect(); 371 | let result = running_instance.call(f, args)?; 372 | results.push(WasmSupportedValue::map_return_values(env, result)); 373 | } 374 | WasmStepInstruction::CallString(f, args) => { 375 | let string = running_instance.call_i32_string(f, args)?; 376 | results.push(string.encode(env)); 377 | } 378 | WasmStepInstruction::WriteStringNulTerminated(offset, string, _null_terminated) => { 379 | running_instance.write_string_nul_terminated(offset, string)?; 380 | } 381 | WasmStepInstruction::ReadMemory(start, length) => { 382 | let bytes = running_instance.read_memory(start, length)?; 383 | results.push(bytes.encode(env)); 384 | } 385 | }; 386 | } 387 | 388 | return Ok(results); 389 | } 390 | 391 | struct RunningInstanceResource { 392 | identifier: String, 393 | // lock: RwLock, 394 | lock: Arc>, 395 | } 396 | 397 | impl RunningInstanceResource { 398 | fn new(identifier: String, running_instance: RunningInstance) -> Self { 399 | Self { 400 | identifier: identifier, 401 | // lock: RwLock::new(running_instance), 402 | lock: Arc::new(RwLock::new(running_instance)), 403 | } 404 | } 405 | } 406 | 407 | struct RunningInstance { 408 | store: Store<()>, 409 | memory: Memory, 410 | instance: Instance, 411 | } 412 | 413 | #[derive(Clone, NifStruct)] 414 | #[module = "OrbWasmtime.Wasm.FuncImport"] 415 | struct FuncImport { 416 | unique_id: i64, 417 | module_name: String, 418 | name: String, 419 | param_types: Vec, 420 | result_types: Vec, 421 | } 422 | 423 | impl Into<(Vec, Vec)> for FuncImport { 424 | fn into(self) -> (Vec, Vec) { 425 | let params: Vec = self.param_types.into_iter().map(|t| t.into()).collect(); 426 | let results: Vec = self.result_types.into_iter().map(|t| t.into()).collect(); 427 | 428 | (params, results) 429 | } 430 | } 431 | 432 | // impl TryInto for FuncImport { 433 | // type Error = anyhow::Error; 434 | 435 | // fn try_into(self) -> Result { 436 | // let params: Vec = self.param_types.into_iter().map(|t| t.into()).collect(); 437 | // let results: Vec = self.result_types.into_iter().map(|t| t.into()).collect(); 438 | 439 | // Ok(FuncType::new(params, results)) 440 | // } 441 | // } 442 | 443 | struct ImportsTable 444 | // where T: Fn(&[Val], &mut [Val]) -> Result<()>, 445 | { 446 | funcs: Vec, 447 | } 448 | 449 | trait CallbackReceiver: Send + Sync + Clone + Copy { 450 | fn pid(self) -> Option; 451 | // fn env(self) -> Option; 452 | fn receive(self, func_id: i64, params: &[Val], results: &mut [Val]) -> Result<()>; 453 | } 454 | 455 | #[derive(Clone, Copy)] 456 | struct NoopCallbackReceiver {} 457 | impl CallbackReceiver for NoopCallbackReceiver { 458 | fn pid(self) -> Option { 459 | None 460 | } 461 | // fn env(self) -> Option { 462 | // None 463 | // } 464 | fn receive(self, _func_id: i64, _params: &[Val], _results: &mut [Val]) -> Result<()> { 465 | // TODO: this should return an error "To use imported functions run an instance." 466 | // Do nothing 467 | Ok(()) 468 | } 469 | } 470 | 471 | #[derive(Clone, Copy)] 472 | struct EnvCallbackReceiver<'a> { 473 | env: Env<'a>, 474 | gen_pid: LocalPid, 475 | } 476 | impl<'a> CallbackReceiver for EnvCallbackReceiver<'a> { 477 | fn pid(self) -> Option { 478 | // Some(self.env.pid()) 479 | Some(self.gen_pid) 480 | } 481 | // fn env(self) -> Option> { 482 | // Some(self.env) 483 | // } 484 | fn receive(self, func_id: i64, params: &[Val], results: &mut [Val]) -> Result<()> { 485 | // TODO 486 | Ok(()) 487 | } 488 | } 489 | 490 | unsafe impl<'a> Send for EnvCallbackReceiver<'a> {} 491 | unsafe impl<'a> Sync for EnvCallbackReceiver<'a> {} 492 | // impl Send for EnvCallbackReceiver {} 493 | 494 | struct CallOutToFuncReply { 495 | // recv: std::sync::mpsc::Receiver, 496 | // lock: RwLock, 497 | func_id: i64, 498 | lock: RwLock>, 499 | // sender: crossbeam_channel::Sender, 500 | sender: crossbeam_channel::Sender>, 501 | 502 | memory_ptr_and_size: Option<(std::sync::atomic::AtomicPtr, usize)>, 503 | } 504 | unsafe impl Send for CallOutToFuncReply {} 505 | 506 | impl CallOutToFuncReply { 507 | fn new( 508 | func_id: i64, 509 | sender: crossbeam_channel::Sender>, 510 | caller: Caller<()>, 511 | memory: Option, 512 | ) -> Self { 513 | // Self { func_id: func_id, lock: RwLock::new(OwnedBinary::new(0)) } 514 | Self { 515 | func_id: func_id, 516 | lock: RwLock::new(None), 517 | sender: sender, 518 | memory_ptr_and_size: memory 519 | .map(|memory| (memory.data_ptr(&caller).into(), memory.data_size(&caller))), 520 | } 521 | } 522 | } 523 | 524 | impl ImportsTable { 525 | fn define( 526 | self, 527 | engine: &Engine, 528 | linker: &mut Linker<()>, 529 | callback_receiver: T, 530 | ) -> Result<()> { 531 | // let mut env = OwnedEnv::new(); 532 | let rc = Arc::new(callback_receiver); 533 | 534 | for fi in self.funcs { 535 | let (params, results): (Vec, Vec) = fi.clone().into(); 536 | let ft = FuncType::new(engine, params, results); 537 | // let ft: FuncType = fi.clone().try_into()?; 538 | // let ft: FuncType = FuncType::new(vec![ValType::I32], vec![ValType::I32]); 539 | let func_id = fi.unique_id; 540 | 541 | // let r = rc.clone(); 542 | let r = Arc::downgrade(&rc); 543 | let pid = callback_receiver.pid().unwrap(); 544 | 545 | let func_module_name = Arc::new(fi.module_name.clone()); 546 | let func_name = Arc::new(fi.name.clone()); 547 | let func_module_name2 = Arc::new(fi.module_name.clone()); 548 | let func_name2 = Arc::new(fi.name.clone()); 549 | // let func_module_name = Arc::new(fi.module_name.as_str()); 550 | // let func_name = Arc::new(fi.name.as_str()); 551 | 552 | linker.func_new( 553 | &fi.module_name, 554 | &fi.name, 555 | ft, 556 | move |mut caller, params, results| { 557 | // results[0] = Val::I32(42); 558 | // eprintln!("Error: Could not complete task"); 559 | 560 | let result_count = results.len(); 561 | 562 | if result_count > 0 { 563 | results[0] = Val::I32(42); 564 | } 565 | let mut owned_env = OwnedEnv::new(); 566 | 567 | // let (sender, recv) = bounded::(1); 568 | let (sender, recv) = bounded::>(1); 569 | 570 | // let params2 = params.clone(); 571 | let params2: Result> = params.iter().map(|v| v.try_into()).collect(); 572 | let params2 = params2.expect("Params could not be converted into supported values"); 573 | 574 | let memory = caller.get_export("memory").and_then(|export| export.into_memory()); 575 | let reply = ResourceArc::new(CallOutToFuncReply::new(func_id, sender, caller, memory)); 576 | 577 | // let mut owned_env2 = owned_env.clone(); 578 | thread::spawn(move || { 579 | let mut owned_env = OwnedEnv::new(); 580 | 581 | // let reply = ResourceArc::new(CallOutToFuncReply::new(func_id, sender, caller, memory)); 582 | owned_env.run(|env| { 583 | let func_module_name2 = ""; 584 | let func_name2 = ""; 585 | // eprintln!("Sending :reply_to_func_call_out func#{func_id} {func_module_name2} {func_name2}"); 586 | env.send( 587 | &pid, 588 | (atom::reply_to_func_call_out(), func_id, reply, params2).encode(env), 589 | ); 590 | }); 591 | // let reply_binary = recv.recv().expect("Did not write back reply value 1"); 592 | // let reply_binary = recv.recv_timeout(Duration::from_secs(5)).expect("Did not recv reply value in time"); 593 | 594 | // // let reply_binary2 = reply_binary 595 | // // .as_ref() 596 | // // .expect("Did not write back reply value 2"); 597 | // eprintln!("Got reply"); 598 | // // reply_binary2.to_vec() 599 | 600 | // let number = owned_env.run(|env| { 601 | // let (term, _size) = env 602 | // .binary_to_term(reply_binary.as_ref()) 603 | // .expect("Could not decode term"); 604 | // let number: i32 = term.decode().expect("Not a i32"); 605 | // number 606 | // }); 607 | // number 608 | }); 609 | 610 | let reply_binary = recv 611 | .recv_timeout(Duration::from_secs(5)) 612 | .unwrap_or_else(|error| panic!("Did not recv reply value in time calling imported func {}.{}: {error:?}", func_module_name, func_name)); 613 | // .expect("Did not recv reply value in time"); 614 | 615 | // eprintln!("Got reply {reply_binary}"); 616 | 617 | // let reply_binary2 = reply_binary 618 | // .as_ref() 619 | // .expect("Did not write back reply value 2"); 620 | // reply_binary2.to_vec() 621 | 622 | let number = reply_binary; 623 | 624 | // let number = owned_env.run(|env| { 625 | // let (term, _size) = env 626 | // .binary_to_term(reply_binary.as_ref()) 627 | // .expect("Could not decode term"); 628 | // let number: u32 = term.decode().expect("Not a u32"); 629 | // number 630 | // }); 631 | 632 | 633 | // let number = reply_value.join().expect("Thread failed."); 634 | 635 | // let number = owned_env.run(|env| { 636 | // let reply_binary2 = reply_binary2.join().expect("Thread failed."); 637 | // let (term, _size) = env 638 | // .binary_to_term(reply_binary2.as_ref()) 639 | // .expect("Could not decode term"); 640 | // let number: i32 = term.decode().expect("Not a i32"); 641 | // number 642 | // }); 643 | 644 | // let mut owned_env = OwnedEnv::new(); 645 | // let env = owned_env.run(|env| env.clone()); 646 | // let reply_saved_term = reply_saved_term.join()?; 647 | // let number: i32 = reply_term.decode()?; 648 | 649 | if result_count > 0 { 650 | for (i, val) in number.iter().enumerate() { 651 | results[i] = (*val).into(); 652 | } 653 | } 654 | Ok(()) 655 | 656 | // receiver.receive(func_id, params, result) 657 | // r.upgrade() 658 | // .expect("Still alive") 659 | // .receive(func_id, params, result) 660 | 661 | // func.write().unwrap()(params, result) 662 | // TODO 663 | // env.send_and_clear(&pid, |env| { 664 | // (atom::call_exfn(), func_id, sval_vec_to_term(env, values)).encode(env) 665 | // }); 666 | // Ok(()) 667 | }, 668 | )?; 669 | // linker.define(&store, fi.module_name, fi.name, memory)?; 670 | } 671 | Ok(()) 672 | } 673 | } 674 | 675 | impl RunningInstance { 676 | fn new_with_imports( 677 | wat_source: WasmModuleDefinition, 678 | imports: ImportsTable, 679 | receiver: T, 680 | ) -> Result { 681 | let engine = Engine::default(); 682 | // let engine = Engine::new(Config::new().async_support(true))?; 683 | let module = Module::new(&engine, &wat_source)?; 684 | 685 | // A `Store` is what will own instances, functions, globals, etc. All wasm 686 | // items are stored within a `Store`, and it's what we'll always be using to 687 | // interact with the wasm world. Custom data can be stored in stores but for 688 | // now we just use `()`. 689 | let mut store = Store::new(&engine, ()); 690 | let mut linker = Linker::new(&engine); 691 | 692 | let has_exported_memory: bool = module.exports().any(|export| match export.ty() { 693 | ExternType::Memory(_) => true, 694 | _ => false, 695 | }); 696 | 697 | // TODO: remove this and just rely on exported memory 698 | let mut imported_memory: Option = match has_exported_memory { 699 | true => None, 700 | false => { 701 | let memory_ty = MemoryType::new(3, None); 702 | let memory = Memory::new(&mut store, memory_ty)?; 703 | linker.define(&store, "env", "buffer", memory)?; 704 | Some(memory) 705 | } 706 | }; 707 | 708 | imports.define(&engine, &mut linker, receiver)?; 709 | 710 | let instance = linker.instantiate(&mut store, &module)?; 711 | 712 | let memory = imported_memory 713 | .or_else(|| instance.get_memory(&mut store, "memory")) 714 | .expect("Expected memory to be exported."); 715 | 716 | return Ok(Self { 717 | store: store, 718 | memory: memory, 719 | instance: instance, 720 | }); 721 | } 722 | 723 | fn new(wat_source: WasmModuleDefinition) -> Result { 724 | let imports_table = ImportsTable { 725 | funcs: Vec::default(), 726 | }; 727 | let noop = NoopCallbackReceiver {}; 728 | Self::new_with_imports(wat_source, imports_table, noop) 729 | } 730 | 731 | // TODO: make return WasmSupportedValue 732 | fn get_global_value_i32(&mut self, global_name: String) -> Result { 733 | let global_val = self 734 | .instance 735 | .get_global(&mut self.store, &global_name) 736 | .map(|g| g.get(&mut self.store)); 737 | 738 | match global_val { 739 | Some(Val::I32(i)) => Ok(i), 740 | Some(other_val) => Err(anyhow!( 741 | "Only I32 globals are supported, got {} instead", 742 | other_val.ty(&self.store) 743 | )), 744 | None => Err(anyhow!("{} was not an exported global", global_name)), 745 | } 746 | } 747 | 748 | // TODO: make accept WasmSupportedValue 749 | fn set_global_value_i32( 750 | &mut self, 751 | global_name: String, 752 | new_value: u32, 753 | ) -> Result<(), anyhow::Error> { 754 | let global = self 755 | .instance 756 | .get_global(&mut self.store, &global_name) 757 | .ok_or(anyhow!("{} was not an exported global", global_name))?; 758 | 759 | return global.set(&mut self.store, Val::I32(new_value as i32)); 760 | } 761 | 762 | fn call( 763 | &mut self, 764 | f: String, 765 | args: Vec, 766 | ) -> Result, anyhow::Error> { 767 | let func = self 768 | .instance 769 | .get_func(&mut self.store, &f) 770 | .expect(&format!("{} was not an exported function", f)); 771 | 772 | let func_type = func.ty(&self.store); 773 | let args: Vec = args.into_iter().map(|v| v.into()).collect(); 774 | 775 | let mut result: Vec = Vec::with_capacity(16); 776 | let result_length = func_type.results().len(); 777 | result.resize(result_length, Val::I32(0)); 778 | 779 | func.call(&mut self.store, &args, &mut result)?; 780 | 781 | let result: Result> = result.iter().map(|v| v.try_into()).collect(); 782 | result 783 | } 784 | 785 | // TODO: change to accept Vec 786 | fn call_i32_string(&mut self, f: String, args: Vec) -> Result { 787 | let func = self 788 | .instance 789 | .get_func(&mut self.store, &f) 790 | .expect(&format!("{} was not an exported function", f)); 791 | 792 | let func_type = func.ty(&self.store); 793 | let args: Vec = args.into_iter().map(|i| Val::I32(i as i32)).collect(); 794 | 795 | let mut result: Vec = Vec::with_capacity(16); 796 | let result_length = func_type.results().len(); 797 | result.resize(result_length, Val::I32(0)); 798 | 799 | func.call(&mut self.store, &args, &mut result)?; 800 | 801 | let result: Vec = result.iter().map(|v| v.unwrap_i32() as u32).collect(); 802 | let s = wasm_extract_string(&self.store, &self.memory, result)?; 803 | Ok(s) 804 | } 805 | 806 | // fn cast( 807 | // &mut self, 808 | // env: Env, 809 | // f: String, 810 | // args: Vec, 811 | // ) -> Result<(), anyhow::Error> { 812 | // env.send_and_clear(&pid, |thread_env| { 813 | // let func = self 814 | // .instance 815 | // .get_func(&mut self.store, &f) 816 | // .expect(&format!("{} was not an exported function", f)); 817 | // 818 | // let func_type = func.ty(&self.store); 819 | // let args: Vec = args.into_iter().map(|v| v.into()).collect(); 820 | // 821 | // let mut result: Vec = Vec::with_capacity(16); 822 | // let result_length = func_type.results().len(); 823 | // result.resize(result_length, Val::I32(0)); 824 | // 825 | // func.call(&mut self.store, &args, &mut result)?; 826 | // 827 | // let result: Result> = result.iter().map(|v| v.try_into()).collect(); 828 | // let result = result?; 829 | // 830 | // (atom::reply_to_func_cast(), f, result).encode(thread_env) 831 | // }); 832 | // 833 | // Ok(()) 834 | // } 835 | 836 | // TODO: use WasmSupportedValue 837 | fn write_i32(&mut self, memory_offset: u32, value: u32) -> Result<(), anyhow::Error> { 838 | let offset: usize = memory_offset.try_into().unwrap(); 839 | let bytes = value.to_le_bytes(); 840 | self.memory.write(&mut self.store, offset, &bytes)?; 841 | Ok(()) 842 | } 843 | 844 | // TODO: use WasmSupportedValue 845 | fn write_i64(&mut self, memory_offset: u32, value: u64) -> Result<(), anyhow::Error> { 846 | let offset: usize = memory_offset.try_into().unwrap(); 847 | let bytes = value.to_le_bytes(); 848 | self.memory.write(&mut self.store, offset, &bytes)?; 849 | Ok(()) 850 | } 851 | 852 | fn write_memory(&mut self, memory_offset: u32, bytes: Vec) -> Result<(), anyhow::Error> { 853 | let offset: usize = memory_offset.try_into().unwrap(); 854 | self.memory.write(&mut self.store, offset, &bytes)?; 855 | Ok(()) 856 | } 857 | 858 | fn write_string_nul_terminated( 859 | &mut self, 860 | memory_offset: u32, 861 | string: String, 862 | ) -> Result { 863 | let offset: usize = memory_offset.try_into().unwrap(); 864 | // let mut bytes = string.as_bytes().clone(); 865 | let mut bytes = string.as_bytes().to_vec(); 866 | // let mut s = string.clone(); 867 | // let vec = s.as_mut_vec(); 868 | bytes.push(0); 869 | self.memory.write(&mut self.store, offset, &bytes)?; 870 | Ok(bytes.len().try_into().unwrap()) 871 | } 872 | 873 | fn read_memory(&self, start: u32, length: u32) -> Result, anyhow::Error> { 874 | wasm_read_memory(&self.store, &self.memory, start, length) 875 | } 876 | 877 | fn read_string_nul_terminated(&self, start: u32) -> Result { 878 | let s = wasm_extract_string(&self.store, &self.memory, vec![start])?; 879 | Ok(s) 880 | } 881 | } 882 | 883 | #[nif] 884 | fn wasm_run_instance( 885 | env: Env, 886 | source: WasmModuleDefinition, 887 | identifier: String, 888 | func_imports: Vec, 889 | gen_pid: LocalPid, 890 | ) -> Result, Error> { 891 | env.send(&env.pid(), atom::run_instance_start().encode(env)); 892 | let imports_table = ImportsTable { 893 | funcs: func_imports, 894 | }; 895 | let receiver = EnvCallbackReceiver { 896 | env: env, 897 | gen_pid: gen_pid, 898 | }; 899 | let running_instance = 900 | RunningInstance::new_with_imports(source, imports_table, receiver).map_err(string_error)?; 901 | 902 | // info!("running_instance: {}", running_instance); 903 | 904 | let resource = ResourceArc::new(RunningInstanceResource::new(identifier, running_instance)); 905 | 906 | Ok(resource) 907 | } 908 | 909 | #[nif] 910 | fn wasm_instance_get_global_i32( 911 | env: Env, 912 | resource: ResourceArc, 913 | global_name: String, 914 | ) -> Result { 915 | let mut instance = resource.lock.write().map_err(string_error)?; 916 | let result = instance 917 | .get_global_value_i32(global_name) 918 | .map_err(string_error)?; 919 | 920 | Ok(result) 921 | } 922 | 923 | #[nif] 924 | fn wasm_instance_set_global_i32( 925 | env: Env, 926 | resource: ResourceArc, 927 | global_name: String, 928 | new_value: u32, 929 | ) -> Result<(), Error> { 930 | let mut instance = resource.lock.write().map_err(string_error)?; 931 | let result = instance 932 | .set_global_value_i32(global_name, new_value) 933 | .map_err(string_error)?; 934 | 935 | return Ok(result); 936 | } 937 | 938 | #[nif(schedule = "DirtyCpu")] 939 | fn wasm_instance_call_func( 940 | env: Env, 941 | resource: ResourceArc, 942 | f: String, 943 | args: Vec, 944 | ) -> Result { 945 | let mut instance = resource.lock.write().map_err(string_error)?; 946 | let result = instance.call(f, args).map_err(string_error)?; 947 | Ok(WasmSupportedValue::map_return_values(env, result)) 948 | } 949 | 950 | #[nif(schedule = "DirtyCpu")] 951 | fn wasm_instance_call_func_i32_string( 952 | env: Env, 953 | resource: ResourceArc, 954 | f: String, 955 | args: Vec, 956 | ) -> Result { 957 | let mut instance = resource.lock.write().map_err(string_error)?; 958 | return instance.call_i32_string(f, args).map_err(string_error); 959 | } 960 | 961 | #[nif] 962 | fn wasm_instance_cast_func_i32( 963 | env: Env, 964 | resource: ResourceArc, 965 | f: String, 966 | args: Vec, 967 | ) -> Result<(), Error> { 968 | eprintln!("CAST! {f}"); 969 | // let c_lock = Arc::clone(&resource.lock); 970 | 971 | thread::spawn(move || { 972 | eprintln!("CAST in thread!"); 973 | let mut instance = resource 974 | .lock 975 | .write() 976 | .expect("Could not get a hold of resource."); 977 | eprintln!("Got instance"); 978 | // let mut instance = c_lock.write().expect("Could not get a hold of resource."); 979 | let result = instance.call(f, args).expect("WebAssembly call failed."); 980 | eprintln!("CAST call done!"); 981 | // return Ok(map_return_values_i32(env, result)); 982 | }); 983 | 984 | Ok(()) 985 | } 986 | 987 | #[nif] 988 | fn wasm_instance_write_i32( 989 | env: Env, 990 | resource: ResourceArc, 991 | memory_offset: u32, 992 | value: u32, 993 | ) -> Result<(), Error> { 994 | let mut instance = resource.lock.write().map_err(string_error)?; 995 | instance 996 | .write_i32(memory_offset, value) 997 | .map_err(string_error) 998 | } 999 | 1000 | #[nif] 1001 | fn wasm_instance_write_i64( 1002 | env: Env, 1003 | resource: ResourceArc, 1004 | memory_offset: u32, 1005 | value: u64, 1006 | ) -> Result<(), Error> { 1007 | let mut instance = resource.lock.write().map_err(string_error)?; 1008 | instance 1009 | .write_i64(memory_offset, value) 1010 | .map_err(string_error) 1011 | } 1012 | 1013 | #[nif] 1014 | fn wasm_instance_write_string_nul_terminated( 1015 | env: Env, 1016 | resource: ResourceArc, 1017 | memory_offset: u32, 1018 | string: String, 1019 | ) -> Result { 1020 | let mut instance = resource.lock.write().map_err(string_error)?; 1021 | let result = instance 1022 | .write_string_nul_terminated(memory_offset, string) 1023 | .map_err(string_error)?; 1024 | 1025 | return Ok(result); 1026 | } 1027 | 1028 | #[nif] 1029 | fn wasm_instance_write_memory( 1030 | env: Env, 1031 | resource: ResourceArc, 1032 | memory_offset: u32, 1033 | bytes: Vec, 1034 | ) -> Result<(), Error> { 1035 | let mut instance = resource.lock.write().map_err(string_error)?; 1036 | instance 1037 | .write_memory(memory_offset, bytes) 1038 | .map_err(string_error) 1039 | } 1040 | 1041 | #[nif] 1042 | fn wasm_instance_read_memory( 1043 | resource: ResourceArc, 1044 | start: u32, 1045 | length: u32, 1046 | ) -> Result, Error> { 1047 | // let instance = resource.lock.read().map_err(string_error)?; 1048 | let instance = resource.lock.try_read().map_err(string_error)?; 1049 | let result = instance.read_memory(start, length).map_err(string_error)?; 1050 | 1051 | return Ok(result); 1052 | } 1053 | 1054 | #[nif(schedule = "DirtyCpu")] 1055 | fn wasm_instance_read_string_nul_terminated( 1056 | env: Env, 1057 | resource: ResourceArc, 1058 | memory_offset: u32, 1059 | ) -> Result { 1060 | // drop(resource.lock.try_read().map_err(string_error)?); 1061 | 1062 | let mut instance = resource.lock.try_write().map_err(string_error)?; 1063 | let result = instance 1064 | .read_string_nul_terminated(memory_offset) 1065 | .map_err(string_error)?; 1066 | 1067 | return Ok(result); 1068 | } 1069 | 1070 | #[nif] 1071 | fn wasm_call_out_reply( 1072 | env: Env, 1073 | resource: ResourceArc, 1074 | reply: Vec, 1075 | // reply: Term, 1076 | ) -> Result<(), Error> { 1077 | // let mut binary = resource.lock.write().map_err(string_error)?; 1078 | // *binary = Some(reply.to_binary()); 1079 | 1080 | // eprintln!("Received reply in Rust! {reply}"); 1081 | 1082 | resource.sender.send(reply).map_err(string_error)?; 1083 | 1084 | return Ok(()); 1085 | } 1086 | 1087 | #[nif] 1088 | fn wasm_caller_read_string_nul_terminated( 1089 | env: Env, 1090 | resource: ResourceArc, 1091 | memory_offset: u32, // reply: Term, 1092 | ) -> Result { 1093 | let memory_ptr_and_size = resource 1094 | .memory_ptr_and_size 1095 | .as_ref() 1096 | .expect("Must have memory in order to read string."); 1097 | 1098 | let memory_ptr = &memory_ptr_and_size.0; 1099 | let memory_size = memory_ptr_and_size.1; 1100 | 1101 | let memory_ptr = memory_ptr.load(std::sync::atomic::Ordering::Relaxed); 1102 | // let data = &memory.data(&store)[start..]; 1103 | let data: &[i8] = unsafe { slice::from_raw_parts(memory_ptr as *const i8, memory_size) }; 1104 | 1105 | let memory_offset: usize = memory_offset.try_into().unwrap(); 1106 | let data = &data[memory_offset..]; 1107 | 1108 | let cstr = unsafe { CStr::from_ptr(data.as_ptr().cast()) }; 1109 | let string = String::from_utf8_lossy(cstr.to_bytes()).to_string(); 1110 | 1111 | // let s = wasm_extract_string(&caller, &memory, vec![memory_offset])?; 1112 | 1113 | return Ok(string); 1114 | } 1115 | 1116 | #[nif] 1117 | fn wasm_caller_write_string_nul_terminated( 1118 | env: Env, 1119 | resource: ResourceArc, 1120 | memory_offset: u32, 1121 | string: String, 1122 | ) -> Result { 1123 | let memory_ptr_and_size = resource 1124 | .memory_ptr_and_size 1125 | .as_ref() 1126 | .expect("Must have memory in order to read string."); 1127 | 1128 | let memory_ptr = &memory_ptr_and_size.0; 1129 | let memory_size = memory_ptr_and_size.1; 1130 | 1131 | let memory_ptr = memory_ptr.load(std::sync::atomic::Ordering::Relaxed); 1132 | // let data = &memory.data(&store)[start..]; 1133 | let data: &mut [u8] = unsafe { slice::from_raw_parts_mut(memory_ptr as *mut u8, memory_size) }; 1134 | 1135 | let mut bytes = string.as_bytes().to_vec(); 1136 | bytes.push(0); 1137 | let memory_offset = memory_offset as usize; 1138 | let len = bytes.len(); 1139 | data[memory_offset..][..len].copy_from_slice(&bytes[..]); 1140 | // self.memory.write(&mut self.store, offset, &bytes)?; 1141 | 1142 | Ok(bytes.len().try_into().unwrap()) 1143 | } 1144 | 1145 | // The `'a` in this function definition is something called a lifetime. 1146 | // This will inform the Rust compiler of how long different things are 1147 | // allowed to live. Don't worry too much about this, as this will be the 1148 | // exact same for most function definitions. 1149 | fn load<'a>(env: Env<'a>, _load_info: Term<'a>) -> bool { 1150 | // This macro will take care of defining and initializing a new resource 1151 | // object type. 1152 | rustler::resource!(RunningInstanceResource, env); 1153 | rustler::resource!(CallOutToFuncReply, env); 1154 | true 1155 | } 1156 | 1157 | #[nif(schedule = "DirtyCpu")] 1158 | // fn wat2wasm(wat_source: String) -> Result, Error> { 1159 | fn wat2wasm(env: Env, wat_source: String) -> Result { 1160 | // let wat2wasm = Wat2Wasm::new(); 1161 | 1162 | // wat2wasm.features.enable_multi_value(); 1163 | 1164 | // let result = wat2wasm 1165 | // // .canonicalize_lebs(true) 1166 | // // .write_debug_names(true) 1167 | // .convert(wat_source); 1168 | 1169 | let mut features = wabt::Features::new(); 1170 | features.enable_multi_value(); 1171 | features.enable_simd(); 1172 | let result: Result, wabt::Error> = wat2wasm_with_features(wat_source, features); 1173 | 1174 | return match result { 1175 | Ok(v) => { 1176 | let mut b = NewBinary::new(env, v.len()); 1177 | b.as_mut_slice().copy_from_slice(&v); 1178 | let b2: Binary = b.into(); 1179 | Ok(b2) 1180 | } 1181 | Err(e) => Err(string_error(e)), 1182 | }; 1183 | } 1184 | 1185 | #[nif(schedule = "DirtyCpu")] 1186 | // #[nif] 1187 | fn validate_module_definition(env: Env, source: WasmModuleDefinition) -> Result<(), Error> { 1188 | let module = match source { 1189 | WasmModuleDefinition::Wat(s) => { 1190 | let s = s.clone(); 1191 | let source: &[u8] = s.as_ref(); 1192 | wabt::Module::parse_wat("hello.wat", source, wabt::Features::new()) 1193 | } 1194 | WasmModuleDefinition::Wasm(b) => { 1195 | wabt::Module::read_binary(b.as_slice(), &wabt::ReadBinaryOptions::default()) 1196 | } 1197 | } 1198 | .map_err(string_error)?; 1199 | 1200 | let result = module.validate().map_err(string_error); 1201 | result 1202 | // Ok(()) 1203 | } 1204 | 1205 | rustler::init!( 1206 | "Elixir.OrbWasmtime.Rust", 1207 | [ 1208 | add, 1209 | reverse_string, 1210 | strlen, 1211 | wasm_list_exports, 1212 | wasm_list_imports, 1213 | wasm_call, 1214 | wasm_call_i32_string, 1215 | wasm_steps, 1216 | wasm_run_instance, 1217 | wasm_instance_get_global_i32, 1218 | wasm_instance_set_global_i32, 1219 | wasm_instance_call_func, 1220 | wasm_instance_call_func_i32_string, 1221 | wasm_instance_cast_func_i32, 1222 | wasm_instance_write_i32, 1223 | wasm_instance_write_i64, 1224 | wasm_instance_write_string_nul_terminated, 1225 | wasm_instance_write_memory, 1226 | wasm_instance_read_memory, 1227 | wasm_instance_read_string_nul_terminated, 1228 | wasm_call_out_reply, 1229 | wasm_caller_read_string_nul_terminated, 1230 | wasm_caller_write_string_nul_terminated, 1231 | wat2wasm, 1232 | validate_module_definition 1233 | ], 1234 | load = load 1235 | ); 1236 | -------------------------------------------------------------------------------- /native/orb_wasmtime/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "ahash" 16 | version = "0.8.11" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 19 | dependencies = [ 20 | "cfg-if", 21 | "once_cell", 22 | "version_check", 23 | "zerocopy", 24 | ] 25 | 26 | [[package]] 27 | name = "aho-corasick" 28 | version = "1.1.3" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 31 | dependencies = [ 32 | "memchr", 33 | ] 34 | 35 | [[package]] 36 | name = "anyhow" 37 | version = "1.0.81" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" 40 | 41 | [[package]] 42 | name = "arbitrary" 43 | version = "1.3.2" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" 46 | 47 | [[package]] 48 | name = "async-trait" 49 | version = "0.1.79" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" 52 | dependencies = [ 53 | "proc-macro2", 54 | "quote", 55 | "syn", 56 | ] 57 | 58 | [[package]] 59 | name = "autocfg" 60 | version = "1.2.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" 63 | 64 | [[package]] 65 | name = "base64" 66 | version = "0.21.7" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 69 | 70 | [[package]] 71 | name = "bincode" 72 | version = "1.3.3" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 75 | dependencies = [ 76 | "serde", 77 | ] 78 | 79 | [[package]] 80 | name = "bitflags" 81 | version = "2.5.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 84 | 85 | [[package]] 86 | name = "block-buffer" 87 | version = "0.10.4" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 90 | dependencies = [ 91 | "generic-array", 92 | ] 93 | 94 | [[package]] 95 | name = "bumpalo" 96 | version = "3.15.4" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" 99 | 100 | [[package]] 101 | name = "byteorder" 102 | version = "1.5.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 105 | 106 | [[package]] 107 | name = "cc" 108 | version = "1.0.90" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" 111 | dependencies = [ 112 | "jobserver", 113 | "libc", 114 | ] 115 | 116 | [[package]] 117 | name = "cfg-if" 118 | version = "1.0.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 121 | 122 | [[package]] 123 | name = "cmake" 124 | version = "0.1.50" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" 127 | dependencies = [ 128 | "cc", 129 | ] 130 | 131 | [[package]] 132 | name = "cpp_demangle" 133 | version = "0.4.3" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" 136 | dependencies = [ 137 | "cfg-if", 138 | ] 139 | 140 | [[package]] 141 | name = "cpufeatures" 142 | version = "0.2.12" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 145 | dependencies = [ 146 | "libc", 147 | ] 148 | 149 | [[package]] 150 | name = "cranelift-bforest" 151 | version = "0.106.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "6a535eb1cf5a6003197dc569320c40c1cb2d2f97ef5d5348eebf067f20957381" 154 | dependencies = [ 155 | "cranelift-entity", 156 | ] 157 | 158 | [[package]] 159 | name = "cranelift-codegen" 160 | version = "0.106.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "11b5066db32cec1492573827183af2142d2d88fe85a83cfc9e73f0f63d3788d4" 163 | dependencies = [ 164 | "bumpalo", 165 | "cranelift-bforest", 166 | "cranelift-codegen-meta", 167 | "cranelift-codegen-shared", 168 | "cranelift-control", 169 | "cranelift-entity", 170 | "cranelift-isle", 171 | "gimli", 172 | "hashbrown 0.14.3", 173 | "log", 174 | "regalloc2", 175 | "smallvec", 176 | "target-lexicon", 177 | ] 178 | 179 | [[package]] 180 | name = "cranelift-codegen-meta" 181 | version = "0.106.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "64942e5774308e835fbad4dd25f253105412c90324631910e1ec27963147bddb" 184 | dependencies = [ 185 | "cranelift-codegen-shared", 186 | ] 187 | 188 | [[package]] 189 | name = "cranelift-codegen-shared" 190 | version = "0.106.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "c39c33db9a86dd6d8d04166a10c53deb477aeea3500eaaefca682e4eda9bb986" 193 | 194 | [[package]] 195 | name = "cranelift-control" 196 | version = "0.106.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "4b7fc4937613aea3156a0538800a17bf56f345a5da2e79ae3df58488c93d867f" 199 | dependencies = [ 200 | "arbitrary", 201 | ] 202 | 203 | [[package]] 204 | name = "cranelift-entity" 205 | version = "0.106.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "f85575e79a153ce1ddbfb7fe1813519b4bfe1eb200cc9c8353b45ad123ae4d36" 208 | dependencies = [ 209 | "serde", 210 | "serde_derive", 211 | ] 212 | 213 | [[package]] 214 | name = "cranelift-frontend" 215 | version = "0.106.0" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "bbc31d6c0ab2249fe0c21e988256b42f5f401ab2673b4fc40076c82a698bdfb9" 218 | dependencies = [ 219 | "cranelift-codegen", 220 | "log", 221 | "smallvec", 222 | "target-lexicon", 223 | ] 224 | 225 | [[package]] 226 | name = "cranelift-isle" 227 | version = "0.106.0" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "dc14f37e3314c0e4c53779c2f46753bf242efff76ee9473757a1fff3b495ad37" 230 | 231 | [[package]] 232 | name = "cranelift-native" 233 | version = "0.106.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "2ea5375f76ab31f9800a23fb2b440810286a6f669a3eb467cdd7ff255ea64268" 236 | dependencies = [ 237 | "cranelift-codegen", 238 | "libc", 239 | "target-lexicon", 240 | ] 241 | 242 | [[package]] 243 | name = "cranelift-wasm" 244 | version = "0.106.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "79851dba01b1fa83fad95134aa27beca88dc4b027121d92ab19788582389dc5f" 247 | dependencies = [ 248 | "cranelift-codegen", 249 | "cranelift-entity", 250 | "cranelift-frontend", 251 | "itertools", 252 | "log", 253 | "smallvec", 254 | "wasmparser", 255 | "wasmtime-types", 256 | ] 257 | 258 | [[package]] 259 | name = "crc32fast" 260 | version = "1.4.0" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" 263 | dependencies = [ 264 | "cfg-if", 265 | ] 266 | 267 | [[package]] 268 | name = "crossbeam-channel" 269 | version = "0.5.12" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" 272 | dependencies = [ 273 | "crossbeam-utils", 274 | ] 275 | 276 | [[package]] 277 | name = "crossbeam-deque" 278 | version = "0.8.5" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 281 | dependencies = [ 282 | "crossbeam-epoch", 283 | "crossbeam-utils", 284 | ] 285 | 286 | [[package]] 287 | name = "crossbeam-epoch" 288 | version = "0.9.18" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 291 | dependencies = [ 292 | "crossbeam-utils", 293 | ] 294 | 295 | [[package]] 296 | name = "crossbeam-utils" 297 | version = "0.8.19" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 300 | 301 | [[package]] 302 | name = "crypto-common" 303 | version = "0.1.6" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 306 | dependencies = [ 307 | "generic-array", 308 | "typenum", 309 | ] 310 | 311 | [[package]] 312 | name = "debugid" 313 | version = "0.8.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" 316 | dependencies = [ 317 | "uuid", 318 | ] 319 | 320 | [[package]] 321 | name = "digest" 322 | version = "0.10.7" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 325 | dependencies = [ 326 | "block-buffer", 327 | "crypto-common", 328 | ] 329 | 330 | [[package]] 331 | name = "directories-next" 332 | version = "2.0.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" 335 | dependencies = [ 336 | "cfg-if", 337 | "dirs-sys-next", 338 | ] 339 | 340 | [[package]] 341 | name = "dirs-sys-next" 342 | version = "0.1.2" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 345 | dependencies = [ 346 | "libc", 347 | "redox_users", 348 | "winapi", 349 | ] 350 | 351 | [[package]] 352 | name = "either" 353 | version = "1.10.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" 356 | 357 | [[package]] 358 | name = "encoding_rs" 359 | version = "0.8.33" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" 362 | dependencies = [ 363 | "cfg-if", 364 | ] 365 | 366 | [[package]] 367 | name = "equivalent" 368 | version = "1.0.1" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 371 | 372 | [[package]] 373 | name = "errno" 374 | version = "0.3.8" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 377 | dependencies = [ 378 | "libc", 379 | "windows-sys", 380 | ] 381 | 382 | [[package]] 383 | name = "fallible-iterator" 384 | version = "0.3.0" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 387 | 388 | [[package]] 389 | name = "fxhash" 390 | version = "0.2.1" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 393 | dependencies = [ 394 | "byteorder", 395 | ] 396 | 397 | [[package]] 398 | name = "fxprof-processed-profile" 399 | version = "0.6.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" 402 | dependencies = [ 403 | "bitflags", 404 | "debugid", 405 | "fxhash", 406 | "serde", 407 | "serde_json", 408 | ] 409 | 410 | [[package]] 411 | name = "generic-array" 412 | version = "0.14.7" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 415 | dependencies = [ 416 | "typenum", 417 | "version_check", 418 | ] 419 | 420 | [[package]] 421 | name = "getrandom" 422 | version = "0.2.12" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 425 | dependencies = [ 426 | "cfg-if", 427 | "libc", 428 | "wasi", 429 | ] 430 | 431 | [[package]] 432 | name = "gimli" 433 | version = "0.28.1" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 436 | dependencies = [ 437 | "fallible-iterator", 438 | "indexmap", 439 | "stable_deref_trait", 440 | ] 441 | 442 | [[package]] 443 | name = "glob" 444 | version = "0.2.11" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 447 | 448 | [[package]] 449 | name = "hashbrown" 450 | version = "0.13.2" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 453 | dependencies = [ 454 | "ahash", 455 | ] 456 | 457 | [[package]] 458 | name = "hashbrown" 459 | version = "0.14.3" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 462 | dependencies = [ 463 | "ahash", 464 | ] 465 | 466 | [[package]] 467 | name = "heck" 468 | version = "0.4.1" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 471 | 472 | [[package]] 473 | name = "id-arena" 474 | version = "2.2.1" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" 477 | 478 | [[package]] 479 | name = "indexmap" 480 | version = "2.2.6" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 483 | dependencies = [ 484 | "equivalent", 485 | "hashbrown 0.14.3", 486 | "serde", 487 | ] 488 | 489 | [[package]] 490 | name = "itertools" 491 | version = "0.12.1" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 494 | dependencies = [ 495 | "either", 496 | ] 497 | 498 | [[package]] 499 | name = "itoa" 500 | version = "1.0.11" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 503 | 504 | [[package]] 505 | name = "ittapi" 506 | version = "0.4.0" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" 509 | dependencies = [ 510 | "anyhow", 511 | "ittapi-sys", 512 | "log", 513 | ] 514 | 515 | [[package]] 516 | name = "ittapi-sys" 517 | version = "0.4.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" 520 | dependencies = [ 521 | "cc", 522 | ] 523 | 524 | [[package]] 525 | name = "jobserver" 526 | version = "0.1.28" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" 529 | dependencies = [ 530 | "libc", 531 | ] 532 | 533 | [[package]] 534 | name = "lazy_static" 535 | version = "1.4.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 538 | 539 | [[package]] 540 | name = "leb128" 541 | version = "0.2.5" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 544 | 545 | [[package]] 546 | name = "libc" 547 | version = "0.2.153" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 550 | 551 | [[package]] 552 | name = "libredox" 553 | version = "0.1.3" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 556 | dependencies = [ 557 | "bitflags", 558 | "libc", 559 | ] 560 | 561 | [[package]] 562 | name = "linux-raw-sys" 563 | version = "0.4.13" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 566 | 567 | [[package]] 568 | name = "log" 569 | version = "0.4.21" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 572 | 573 | [[package]] 574 | name = "mach" 575 | version = "0.3.2" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" 578 | dependencies = [ 579 | "libc", 580 | ] 581 | 582 | [[package]] 583 | name = "memchr" 584 | version = "2.7.2" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 587 | 588 | [[package]] 589 | name = "memfd" 590 | version = "0.6.4" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" 593 | dependencies = [ 594 | "rustix", 595 | ] 596 | 597 | [[package]] 598 | name = "memoffset" 599 | version = "0.9.1" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 602 | dependencies = [ 603 | "autocfg", 604 | ] 605 | 606 | [[package]] 607 | name = "object" 608 | version = "0.32.2" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 611 | dependencies = [ 612 | "crc32fast", 613 | "hashbrown 0.14.3", 614 | "indexmap", 615 | "memchr", 616 | ] 617 | 618 | [[package]] 619 | name = "once_cell" 620 | version = "1.19.0" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 623 | 624 | [[package]] 625 | name = "orb_wasmtime" 626 | version = "0.1.17" 627 | dependencies = [ 628 | "anyhow", 629 | "crossbeam-channel", 630 | "rustler", 631 | "wabt", 632 | "wasmtime", 633 | ] 634 | 635 | [[package]] 636 | name = "paste" 637 | version = "1.0.14" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 640 | 641 | [[package]] 642 | name = "pkg-config" 643 | version = "0.3.30" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 646 | 647 | [[package]] 648 | name = "proc-macro2" 649 | version = "1.0.79" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" 652 | dependencies = [ 653 | "unicode-ident", 654 | ] 655 | 656 | [[package]] 657 | name = "psm" 658 | version = "0.1.21" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" 661 | dependencies = [ 662 | "cc", 663 | ] 664 | 665 | [[package]] 666 | name = "quote" 667 | version = "1.0.35" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 670 | dependencies = [ 671 | "proc-macro2", 672 | ] 673 | 674 | [[package]] 675 | name = "rayon" 676 | version = "1.10.0" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 679 | dependencies = [ 680 | "either", 681 | "rayon-core", 682 | ] 683 | 684 | [[package]] 685 | name = "rayon-core" 686 | version = "1.12.1" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 689 | dependencies = [ 690 | "crossbeam-deque", 691 | "crossbeam-utils", 692 | ] 693 | 694 | [[package]] 695 | name = "redox_users" 696 | version = "0.4.5" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" 699 | dependencies = [ 700 | "getrandom", 701 | "libredox", 702 | "thiserror", 703 | ] 704 | 705 | [[package]] 706 | name = "regalloc2" 707 | version = "0.9.3" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" 710 | dependencies = [ 711 | "hashbrown 0.13.2", 712 | "log", 713 | "rustc-hash", 714 | "slice-group-by", 715 | "smallvec", 716 | ] 717 | 718 | [[package]] 719 | name = "regex" 720 | version = "1.10.4" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" 723 | dependencies = [ 724 | "aho-corasick", 725 | "memchr", 726 | "regex-automata", 727 | "regex-syntax", 728 | ] 729 | 730 | [[package]] 731 | name = "regex-automata" 732 | version = "0.4.6" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 735 | dependencies = [ 736 | "aho-corasick", 737 | "memchr", 738 | "regex-syntax", 739 | ] 740 | 741 | [[package]] 742 | name = "regex-syntax" 743 | version = "0.8.3" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" 746 | 747 | [[package]] 748 | name = "rustc-demangle" 749 | version = "0.1.23" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 752 | 753 | [[package]] 754 | name = "rustc-hash" 755 | version = "1.1.0" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 758 | 759 | [[package]] 760 | name = "rustix" 761 | version = "0.38.32" 762 | source = "registry+https://github.com/rust-lang/crates.io-index" 763 | checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" 764 | dependencies = [ 765 | "bitflags", 766 | "errno", 767 | "libc", 768 | "linux-raw-sys", 769 | "windows-sys", 770 | ] 771 | 772 | [[package]] 773 | name = "rustler" 774 | version = "0.29.1" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "0884cb623b9f43d3e2c51f9071c5e96a5acf3e6e6007866812884ff0cb983f1e" 777 | dependencies = [ 778 | "lazy_static", 779 | "rustler_codegen", 780 | "rustler_sys", 781 | ] 782 | 783 | [[package]] 784 | name = "rustler_codegen" 785 | version = "0.29.1" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "50e277af754f2560cf4c4ebedb68c1a735292fb354505c6133e47ec406e699cf" 788 | dependencies = [ 789 | "heck", 790 | "proc-macro2", 791 | "quote", 792 | "syn", 793 | ] 794 | 795 | [[package]] 796 | name = "rustler_sys" 797 | version = "2.3.2" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "ff76ba8524729d7c9db2b3e80f2269d1fdef39b5a60624c33fd794797e69b558" 800 | dependencies = [ 801 | "regex", 802 | "unreachable", 803 | ] 804 | 805 | [[package]] 806 | name = "ryu" 807 | version = "1.0.17" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 810 | 811 | [[package]] 812 | name = "semver" 813 | version = "1.0.22" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 816 | 817 | [[package]] 818 | name = "serde" 819 | version = "1.0.197" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" 822 | dependencies = [ 823 | "serde_derive", 824 | ] 825 | 826 | [[package]] 827 | name = "serde_derive" 828 | version = "1.0.197" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" 831 | dependencies = [ 832 | "proc-macro2", 833 | "quote", 834 | "syn", 835 | ] 836 | 837 | [[package]] 838 | name = "serde_json" 839 | version = "1.0.115" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" 842 | dependencies = [ 843 | "itoa", 844 | "ryu", 845 | "serde", 846 | ] 847 | 848 | [[package]] 849 | name = "serde_spanned" 850 | version = "0.6.5" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" 853 | dependencies = [ 854 | "serde", 855 | ] 856 | 857 | [[package]] 858 | name = "sha2" 859 | version = "0.10.8" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 862 | dependencies = [ 863 | "cfg-if", 864 | "cpufeatures", 865 | "digest", 866 | ] 867 | 868 | [[package]] 869 | name = "slice-group-by" 870 | version = "0.3.1" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" 873 | 874 | [[package]] 875 | name = "smallvec" 876 | version = "1.13.2" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 879 | 880 | [[package]] 881 | name = "sptr" 882 | version = "0.3.2" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" 885 | 886 | [[package]] 887 | name = "stable_deref_trait" 888 | version = "1.2.0" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 891 | 892 | [[package]] 893 | name = "syn" 894 | version = "2.0.57" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" 897 | dependencies = [ 898 | "proc-macro2", 899 | "quote", 900 | "unicode-ident", 901 | ] 902 | 903 | [[package]] 904 | name = "target-lexicon" 905 | version = "0.12.14" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" 908 | 909 | [[package]] 910 | name = "thiserror" 911 | version = "1.0.58" 912 | source = "registry+https://github.com/rust-lang/crates.io-index" 913 | checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" 914 | dependencies = [ 915 | "thiserror-impl", 916 | ] 917 | 918 | [[package]] 919 | name = "thiserror-impl" 920 | version = "1.0.58" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" 923 | dependencies = [ 924 | "proc-macro2", 925 | "quote", 926 | "syn", 927 | ] 928 | 929 | [[package]] 930 | name = "toml" 931 | version = "0.8.12" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" 934 | dependencies = [ 935 | "serde", 936 | "serde_spanned", 937 | "toml_datetime", 938 | "toml_edit", 939 | ] 940 | 941 | [[package]] 942 | name = "toml_datetime" 943 | version = "0.6.5" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" 946 | dependencies = [ 947 | "serde", 948 | ] 949 | 950 | [[package]] 951 | name = "toml_edit" 952 | version = "0.22.9" 953 | source = "registry+https://github.com/rust-lang/crates.io-index" 954 | checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" 955 | dependencies = [ 956 | "indexmap", 957 | "serde", 958 | "serde_spanned", 959 | "toml_datetime", 960 | "winnow", 961 | ] 962 | 963 | [[package]] 964 | name = "typenum" 965 | version = "1.17.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 968 | 969 | [[package]] 970 | name = "unicode-ident" 971 | version = "1.0.12" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 974 | 975 | [[package]] 976 | name = "unicode-width" 977 | version = "0.1.11" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 980 | 981 | [[package]] 982 | name = "unicode-xid" 983 | version = "0.2.4" 984 | source = "registry+https://github.com/rust-lang/crates.io-index" 985 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 986 | 987 | [[package]] 988 | name = "unreachable" 989 | version = "1.0.0" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 992 | dependencies = [ 993 | "void", 994 | ] 995 | 996 | [[package]] 997 | name = "uuid" 998 | version = "1.8.0" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" 1001 | 1002 | [[package]] 1003 | name = "version_check" 1004 | version = "0.9.4" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1007 | 1008 | [[package]] 1009 | name = "void" 1010 | version = "1.0.2" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1013 | 1014 | [[package]] 1015 | name = "wabt" 1016 | version = "0.10.0" 1017 | source = "registry+https://github.com/rust-lang/crates.io-index" 1018 | checksum = "00bef93d5e6c81a293bccf107cf43aa47239382f455ba14869d36695d8963b9c" 1019 | dependencies = [ 1020 | "serde", 1021 | "serde_derive", 1022 | "serde_json", 1023 | "wabt-sys", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "wabt-sys" 1028 | version = "0.8.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "1a4e043159f63e16986e713e9b5e1c06043df4848565bf672e27c523864c7791" 1031 | dependencies = [ 1032 | "cc", 1033 | "cmake", 1034 | "glob", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "wasi" 1039 | version = "0.11.0+wasi-snapshot-preview1" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1042 | 1043 | [[package]] 1044 | name = "wasm-encoder" 1045 | version = "0.201.0" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" 1048 | dependencies = [ 1049 | "leb128", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "wasm-encoder" 1054 | version = "0.202.0" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a" 1057 | dependencies = [ 1058 | "leb128", 1059 | ] 1060 | 1061 | [[package]] 1062 | name = "wasmparser" 1063 | version = "0.201.0" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" 1066 | dependencies = [ 1067 | "bitflags", 1068 | "indexmap", 1069 | "semver", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "wasmprinter" 1074 | version = "0.201.0" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265" 1077 | dependencies = [ 1078 | "anyhow", 1079 | "wasmparser", 1080 | ] 1081 | 1082 | [[package]] 1083 | name = "wasmtime" 1084 | version = "19.0.0" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "6a08af88fa3d324cc5cf6d388d90ef396a787b3fb4bbd51ba185f8645dc0f02c" 1087 | dependencies = [ 1088 | "addr2line", 1089 | "anyhow", 1090 | "async-trait", 1091 | "bincode", 1092 | "bumpalo", 1093 | "cfg-if", 1094 | "encoding_rs", 1095 | "fxprof-processed-profile", 1096 | "gimli", 1097 | "indexmap", 1098 | "ittapi", 1099 | "libc", 1100 | "log", 1101 | "object", 1102 | "once_cell", 1103 | "paste", 1104 | "rayon", 1105 | "rustix", 1106 | "semver", 1107 | "serde", 1108 | "serde_derive", 1109 | "serde_json", 1110 | "target-lexicon", 1111 | "wasm-encoder 0.201.0", 1112 | "wasmparser", 1113 | "wasmtime-cache", 1114 | "wasmtime-component-macro", 1115 | "wasmtime-component-util", 1116 | "wasmtime-cranelift", 1117 | "wasmtime-environ", 1118 | "wasmtime-fiber", 1119 | "wasmtime-jit-debug", 1120 | "wasmtime-jit-icache-coherence", 1121 | "wasmtime-runtime", 1122 | "wasmtime-slab", 1123 | "wasmtime-winch", 1124 | "wat", 1125 | "windows-sys", 1126 | ] 1127 | 1128 | [[package]] 1129 | name = "wasmtime-asm-macros" 1130 | version = "19.0.0" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "16cdbfcf28542bcda0b5fd68d44603e53e5ad126cbe7b9f25c130e1249fd8211" 1133 | dependencies = [ 1134 | "cfg-if", 1135 | ] 1136 | 1137 | [[package]] 1138 | name = "wasmtime-cache" 1139 | version = "19.0.0" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "546b85be1a1d380ba821aeb0252b656359d0ce027b16fef8fc0e2f2b9159e193" 1142 | dependencies = [ 1143 | "anyhow", 1144 | "base64", 1145 | "bincode", 1146 | "directories-next", 1147 | "log", 1148 | "rustix", 1149 | "serde", 1150 | "serde_derive", 1151 | "sha2", 1152 | "toml", 1153 | "windows-sys", 1154 | "zstd", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "wasmtime-component-macro" 1159 | version = "19.0.0" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "0cdcf690257c623506eeec3a502864b282aab0fdfd6981c1ebb63c7e98f4a23a" 1162 | dependencies = [ 1163 | "anyhow", 1164 | "proc-macro2", 1165 | "quote", 1166 | "syn", 1167 | "wasmtime-component-util", 1168 | "wasmtime-wit-bindgen", 1169 | "wit-parser", 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "wasmtime-component-util" 1174 | version = "19.0.0" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "ab3ae7bf66e2fae1e332ab3634f332d7422e878a6eecc47c8f8f78cc1f24e501" 1177 | 1178 | [[package]] 1179 | name = "wasmtime-cranelift" 1180 | version = "19.0.0" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "67ea025c969a09117818732fa6f96848e858a7953d4659dab8081a6eea3c0523" 1183 | dependencies = [ 1184 | "anyhow", 1185 | "cfg-if", 1186 | "cranelift-codegen", 1187 | "cranelift-control", 1188 | "cranelift-entity", 1189 | "cranelift-frontend", 1190 | "cranelift-native", 1191 | "cranelift-wasm", 1192 | "gimli", 1193 | "log", 1194 | "object", 1195 | "target-lexicon", 1196 | "thiserror", 1197 | "wasmparser", 1198 | "wasmtime-cranelift-shared", 1199 | "wasmtime-environ", 1200 | "wasmtime-versioned-export-macros", 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "wasmtime-cranelift-shared" 1205 | version = "19.0.0" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "dcd6dd2f8d8d4860b384f61f89b597633a5b5f0943c546210e5084c5d321fe20" 1208 | dependencies = [ 1209 | "anyhow", 1210 | "cranelift-codegen", 1211 | "cranelift-control", 1212 | "cranelift-native", 1213 | "gimli", 1214 | "object", 1215 | "target-lexicon", 1216 | "wasmtime-environ", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "wasmtime-environ" 1221 | version = "19.0.0" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "7f60f3f717658dd77745de03b750d5852126e9be6dad465848c77f90387c44c9" 1224 | dependencies = [ 1225 | "anyhow", 1226 | "bincode", 1227 | "cpp_demangle", 1228 | "cranelift-entity", 1229 | "gimli", 1230 | "indexmap", 1231 | "log", 1232 | "object", 1233 | "rustc-demangle", 1234 | "serde", 1235 | "serde_derive", 1236 | "target-lexicon", 1237 | "thiserror", 1238 | "wasm-encoder 0.201.0", 1239 | "wasmparser", 1240 | "wasmprinter", 1241 | "wasmtime-component-util", 1242 | "wasmtime-types", 1243 | ] 1244 | 1245 | [[package]] 1246 | name = "wasmtime-fiber" 1247 | version = "19.0.0" 1248 | source = "registry+https://github.com/rust-lang/crates.io-index" 1249 | checksum = "bf8cd22ab1041bf0e54b6283e57824557902e4fed8b1f3a7eef29cbaba89eebf" 1250 | dependencies = [ 1251 | "anyhow", 1252 | "cc", 1253 | "cfg-if", 1254 | "rustix", 1255 | "wasmtime-asm-macros", 1256 | "wasmtime-versioned-export-macros", 1257 | "windows-sys", 1258 | ] 1259 | 1260 | [[package]] 1261 | name = "wasmtime-jit-debug" 1262 | version = "19.0.0" 1263 | source = "registry+https://github.com/rust-lang/crates.io-index" 1264 | checksum = "8753654f698354950a557d0d0cbdecf356c973659920091cf3d5fada74183e02" 1265 | dependencies = [ 1266 | "object", 1267 | "once_cell", 1268 | "rustix", 1269 | "wasmtime-versioned-export-macros", 1270 | ] 1271 | 1272 | [[package]] 1273 | name = "wasmtime-jit-icache-coherence" 1274 | version = "19.0.0" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "2796e4b4989db62899d2117e1e0258b839d088c044591b14e3a0396e7b3ae53a" 1277 | dependencies = [ 1278 | "cfg-if", 1279 | "libc", 1280 | "windows-sys", 1281 | ] 1282 | 1283 | [[package]] 1284 | name = "wasmtime-runtime" 1285 | version = "19.0.0" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "4bf2b7745df452a4f41b9aab21d3f7ba1347b12da2fdc5241e59306127884a68" 1288 | dependencies = [ 1289 | "anyhow", 1290 | "cc", 1291 | "cfg-if", 1292 | "encoding_rs", 1293 | "indexmap", 1294 | "libc", 1295 | "log", 1296 | "mach", 1297 | "memfd", 1298 | "memoffset", 1299 | "paste", 1300 | "psm", 1301 | "rustix", 1302 | "sptr", 1303 | "wasm-encoder 0.201.0", 1304 | "wasmtime-asm-macros", 1305 | "wasmtime-environ", 1306 | "wasmtime-fiber", 1307 | "wasmtime-jit-debug", 1308 | "wasmtime-versioned-export-macros", 1309 | "wasmtime-wmemcheck", 1310 | "windows-sys", 1311 | ] 1312 | 1313 | [[package]] 1314 | name = "wasmtime-slab" 1315 | version = "19.0.0" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "83448ef600ad95977019ebaea84a5516fdbc9561d0a8e26b1e099351f993b527" 1318 | 1319 | [[package]] 1320 | name = "wasmtime-types" 1321 | version = "19.0.0" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "cf6fe7ed3fd18ed4b1e4465fe5c8674acc9f03523fca5b1b9f975b2560cd741b" 1324 | dependencies = [ 1325 | "cranelift-entity", 1326 | "serde", 1327 | "serde_derive", 1328 | "thiserror", 1329 | "wasmparser", 1330 | ] 1331 | 1332 | [[package]] 1333 | name = "wasmtime-versioned-export-macros" 1334 | version = "19.0.0" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "6d6d967f01032da7d4c6303da32f6a00d5efe1bac124b156e7342d8ace6ffdfc" 1337 | dependencies = [ 1338 | "proc-macro2", 1339 | "quote", 1340 | "syn", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "wasmtime-winch" 1345 | version = "19.0.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "eb8b3fcbc455105760e4a2aa8ee3f39b8357183a62201383b3f72d4836ca2be8" 1348 | dependencies = [ 1349 | "anyhow", 1350 | "cranelift-codegen", 1351 | "gimli", 1352 | "object", 1353 | "target-lexicon", 1354 | "wasmparser", 1355 | "wasmtime-cranelift-shared", 1356 | "wasmtime-environ", 1357 | "winch-codegen", 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "wasmtime-wit-bindgen" 1362 | version = "19.0.0" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "96326c9800fb6c099f50d1bd2126d636fc2f96950e1675acf358c0f52516cd38" 1365 | dependencies = [ 1366 | "anyhow", 1367 | "heck", 1368 | "indexmap", 1369 | "wit-parser", 1370 | ] 1371 | 1372 | [[package]] 1373 | name = "wasmtime-wmemcheck" 1374 | version = "19.0.0" 1375 | source = "registry+https://github.com/rust-lang/crates.io-index" 1376 | checksum = "36bd91a4dc55af0bf55e9e2ab0ea13724cfb5c5a1acdf8873039769208f59490" 1377 | 1378 | [[package]] 1379 | name = "wast" 1380 | version = "202.0.0" 1381 | source = "registry+https://github.com/rust-lang/crates.io-index" 1382 | checksum = "1fbcb11204515c953c9b42ede0a46a1c5e17f82af05c4fae201a8efff1b0f4fe" 1383 | dependencies = [ 1384 | "bumpalo", 1385 | "leb128", 1386 | "memchr", 1387 | "unicode-width", 1388 | "wasm-encoder 0.202.0", 1389 | ] 1390 | 1391 | [[package]] 1392 | name = "wat" 1393 | version = "1.202.0" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "4de4b15a47135c56a3573406e9977b9518787a6154459b4842a9b9d3d1684848" 1396 | dependencies = [ 1397 | "wast", 1398 | ] 1399 | 1400 | [[package]] 1401 | name = "winapi" 1402 | version = "0.3.9" 1403 | source = "registry+https://github.com/rust-lang/crates.io-index" 1404 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1405 | dependencies = [ 1406 | "winapi-i686-pc-windows-gnu", 1407 | "winapi-x86_64-pc-windows-gnu", 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "winapi-i686-pc-windows-gnu" 1412 | version = "0.4.0" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1415 | 1416 | [[package]] 1417 | name = "winapi-x86_64-pc-windows-gnu" 1418 | version = "0.4.0" 1419 | source = "registry+https://github.com/rust-lang/crates.io-index" 1420 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1421 | 1422 | [[package]] 1423 | name = "winch-codegen" 1424 | version = "0.17.0" 1425 | source = "registry+https://github.com/rust-lang/crates.io-index" 1426 | checksum = "d285c833af9453c037cd220765f86c5c9961e8906a815829107c8801d535b8e4" 1427 | dependencies = [ 1428 | "anyhow", 1429 | "cranelift-codegen", 1430 | "gimli", 1431 | "regalloc2", 1432 | "smallvec", 1433 | "target-lexicon", 1434 | "wasmparser", 1435 | "wasmtime-environ", 1436 | ] 1437 | 1438 | [[package]] 1439 | name = "windows-sys" 1440 | version = "0.52.0" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1443 | dependencies = [ 1444 | "windows-targets", 1445 | ] 1446 | 1447 | [[package]] 1448 | name = "windows-targets" 1449 | version = "0.52.4" 1450 | source = "registry+https://github.com/rust-lang/crates.io-index" 1451 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 1452 | dependencies = [ 1453 | "windows_aarch64_gnullvm", 1454 | "windows_aarch64_msvc", 1455 | "windows_i686_gnu", 1456 | "windows_i686_msvc", 1457 | "windows_x86_64_gnu", 1458 | "windows_x86_64_gnullvm", 1459 | "windows_x86_64_msvc", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "windows_aarch64_gnullvm" 1464 | version = "0.52.4" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 1467 | 1468 | [[package]] 1469 | name = "windows_aarch64_msvc" 1470 | version = "0.52.4" 1471 | source = "registry+https://github.com/rust-lang/crates.io-index" 1472 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 1473 | 1474 | [[package]] 1475 | name = "windows_i686_gnu" 1476 | version = "0.52.4" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 1479 | 1480 | [[package]] 1481 | name = "windows_i686_msvc" 1482 | version = "0.52.4" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 1485 | 1486 | [[package]] 1487 | name = "windows_x86_64_gnu" 1488 | version = "0.52.4" 1489 | source = "registry+https://github.com/rust-lang/crates.io-index" 1490 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 1491 | 1492 | [[package]] 1493 | name = "windows_x86_64_gnullvm" 1494 | version = "0.52.4" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 1497 | 1498 | [[package]] 1499 | name = "windows_x86_64_msvc" 1500 | version = "0.52.4" 1501 | source = "registry+https://github.com/rust-lang/crates.io-index" 1502 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 1503 | 1504 | [[package]] 1505 | name = "winnow" 1506 | version = "0.6.5" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" 1509 | dependencies = [ 1510 | "memchr", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "wit-parser" 1515 | version = "0.201.0" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" 1518 | dependencies = [ 1519 | "anyhow", 1520 | "id-arena", 1521 | "indexmap", 1522 | "log", 1523 | "semver", 1524 | "serde", 1525 | "serde_derive", 1526 | "serde_json", 1527 | "unicode-xid", 1528 | "wasmparser", 1529 | ] 1530 | 1531 | [[package]] 1532 | name = "zerocopy" 1533 | version = "0.7.32" 1534 | source = "registry+https://github.com/rust-lang/crates.io-index" 1535 | checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" 1536 | dependencies = [ 1537 | "zerocopy-derive", 1538 | ] 1539 | 1540 | [[package]] 1541 | name = "zerocopy-derive" 1542 | version = "0.7.32" 1543 | source = "registry+https://github.com/rust-lang/crates.io-index" 1544 | checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" 1545 | dependencies = [ 1546 | "proc-macro2", 1547 | "quote", 1548 | "syn", 1549 | ] 1550 | 1551 | [[package]] 1552 | name = "zstd" 1553 | version = "0.13.1" 1554 | source = "registry+https://github.com/rust-lang/crates.io-index" 1555 | checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" 1556 | dependencies = [ 1557 | "zstd-safe", 1558 | ] 1559 | 1560 | [[package]] 1561 | name = "zstd-safe" 1562 | version = "7.1.0" 1563 | source = "registry+https://github.com/rust-lang/crates.io-index" 1564 | checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" 1565 | dependencies = [ 1566 | "zstd-sys", 1567 | ] 1568 | 1569 | [[package]] 1570 | name = "zstd-sys" 1571 | version = "2.0.10+zstd.1.5.6" 1572 | source = "registry+https://github.com/rust-lang/crates.io-index" 1573 | checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" 1574 | dependencies = [ 1575 | "cc", 1576 | "pkg-config", 1577 | ] 1578 | --------------------------------------------------------------------------------