├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile.toml ├── README.md ├── _typos.toml ├── examples ├── args_get.rs ├── args_get.wasm ├── fd_read.rs ├── fd_read.wasm ├── fd_write.rs ├── fd_write.wasm ├── fd_write.wat ├── fib.rs ├── fib.wasm ├── fib.wat ├── hello.rs ├── hello.wasm ├── risp.rs ├── risp.wasm ├── rjo.rs └── rjo.wasm ├── src ├── binary │ ├── error.rs │ ├── instruction.rs │ ├── mod.rs │ ├── module.rs │ ├── section.rs │ ├── snapshots │ │ ├── chibiwasm__binary__module__test__decode_module.snap │ │ ├── chibiwasm__binary__module__test__nested_if.snap │ │ └── chibiwasm__binary__module__test__return.snap │ └── types.rs ├── execution │ ├── error.rs │ ├── fixtures │ │ └── invoke.wat │ ├── float.rs │ ├── importer.rs │ ├── indices.rs │ ├── integer.rs │ ├── macros.rs │ ├── mod.rs │ ├── module.rs │ ├── op.rs │ ├── runtime.rs │ ├── store.rs │ └── value.rs ├── lib.rs ├── main.rs └── wasi │ ├── macros.rs │ ├── mod.rs │ └── wasi_snapshot_preview1 │ ├── file.rs │ ├── file_table.rs │ ├── mod.rs │ ├── preview1.rs │ ├── types.rs │ ├── virtual_file.rs │ └── wasi_file.rs └── tests └── spec.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | lint: 5 | runs-on: ubuntu-latest 6 | name: Run clippy 7 | steps: 8 | - name: checkout 9 | uses: actions/checkout@v3 10 | - name: Setup rust toolchain 11 | uses: dtolnay/rust-toolchain@stable 12 | - name: Run clippy 13 | run: cargo clippy --all-targets --all-features -- -D warnings 14 | test: 15 | runs-on: ubuntu-latest 16 | name: Run test 17 | steps: 18 | - name: checkout 19 | uses: actions/checkout@v3 20 | with: 21 | submodules: recursive 22 | - name: Setup rust toolchain 23 | uses: dtolnay/rust-toolchain@stable 24 | - name: Run test 25 | run: cargo test --all -- --nocapture 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/testsuite"] 2 | path = tests/testsuite 3 | url = https://github.com/skanehira/wasm-testsuite 4 | -------------------------------------------------------------------------------- /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 = "aho-corasick" 7 | version = "0.7.20" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.69" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" 19 | 20 | [[package]] 21 | name = "atty" 22 | version = "0.2.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 25 | dependencies = [ 26 | "hermit-abi 0.1.19", 27 | "libc", 28 | "winapi", 29 | ] 30 | 31 | [[package]] 32 | name = "autocfg" 33 | version = "1.1.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.3.2" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 42 | 43 | [[package]] 44 | name = "cc" 45 | version = "1.0.79" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 48 | 49 | [[package]] 50 | name = "cfg-if" 51 | version = "1.0.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 54 | 55 | [[package]] 56 | name = "chibiwasm" 57 | version = "0.1.0" 58 | dependencies = [ 59 | "anyhow", 60 | "clap", 61 | "insta", 62 | "leb128", 63 | "log", 64 | "num-derive", 65 | "num-traits", 66 | "paste", 67 | "pretty_assertions", 68 | "pretty_env_logger", 69 | "rand", 70 | "serde_json", 71 | "thiserror", 72 | "wabt", 73 | "wat", 74 | ] 75 | 76 | [[package]] 77 | name = "clap" 78 | version = "4.1.8" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" 81 | dependencies = [ 82 | "bitflags", 83 | "clap_derive", 84 | "clap_lex", 85 | "is-terminal", 86 | "once_cell", 87 | "strsim", 88 | "termcolor", 89 | ] 90 | 91 | [[package]] 92 | name = "clap_derive" 93 | version = "4.1.8" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" 96 | dependencies = [ 97 | "heck", 98 | "proc-macro-error", 99 | "proc-macro2", 100 | "quote", 101 | "syn", 102 | ] 103 | 104 | [[package]] 105 | name = "clap_lex" 106 | version = "0.3.2" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" 109 | dependencies = [ 110 | "os_str_bytes", 111 | ] 112 | 113 | [[package]] 114 | name = "cmake" 115 | version = "0.1.49" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" 118 | dependencies = [ 119 | "cc", 120 | ] 121 | 122 | [[package]] 123 | name = "console" 124 | version = "0.15.5" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" 127 | dependencies = [ 128 | "encode_unicode", 129 | "lazy_static", 130 | "libc", 131 | "windows-sys 0.42.0", 132 | ] 133 | 134 | [[package]] 135 | name = "ctor" 136 | version = "0.1.26" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" 139 | dependencies = [ 140 | "quote", 141 | "syn", 142 | ] 143 | 144 | [[package]] 145 | name = "diff" 146 | version = "0.1.13" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 149 | 150 | [[package]] 151 | name = "encode_unicode" 152 | version = "0.3.6" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 155 | 156 | [[package]] 157 | name = "env_logger" 158 | version = "0.7.1" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 161 | dependencies = [ 162 | "atty", 163 | "humantime", 164 | "log", 165 | "regex", 166 | "termcolor", 167 | ] 168 | 169 | [[package]] 170 | name = "errno" 171 | version = "0.3.5" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" 174 | dependencies = [ 175 | "libc", 176 | "windows-sys 0.48.0", 177 | ] 178 | 179 | [[package]] 180 | name = "getrandom" 181 | version = "0.2.11" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" 184 | dependencies = [ 185 | "cfg-if", 186 | "libc", 187 | "wasi", 188 | ] 189 | 190 | [[package]] 191 | name = "glob" 192 | version = "0.2.11" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 195 | 196 | [[package]] 197 | name = "heck" 198 | version = "0.4.1" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 201 | 202 | [[package]] 203 | name = "hermit-abi" 204 | version = "0.1.19" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 207 | dependencies = [ 208 | "libc", 209 | ] 210 | 211 | [[package]] 212 | name = "hermit-abi" 213 | version = "0.3.1" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 216 | 217 | [[package]] 218 | name = "humantime" 219 | version = "1.3.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 222 | dependencies = [ 223 | "quick-error", 224 | ] 225 | 226 | [[package]] 227 | name = "insta" 228 | version = "1.29.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "9a28d25139df397cbca21408bb742cf6837e04cdbebf1b07b760caf971d6a972" 231 | dependencies = [ 232 | "console", 233 | "lazy_static", 234 | "linked-hash-map", 235 | "similar", 236 | "yaml-rust", 237 | ] 238 | 239 | [[package]] 240 | name = "io-lifetimes" 241 | version = "1.0.6" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" 244 | dependencies = [ 245 | "libc", 246 | "windows-sys 0.45.0", 247 | ] 248 | 249 | [[package]] 250 | name = "is-terminal" 251 | version = "0.4.4" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" 254 | dependencies = [ 255 | "hermit-abi 0.3.1", 256 | "io-lifetimes", 257 | "rustix", 258 | "windows-sys 0.45.0", 259 | ] 260 | 261 | [[package]] 262 | name = "itoa" 263 | version = "1.0.6" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 266 | 267 | [[package]] 268 | name = "lazy_static" 269 | version = "1.4.0" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 272 | 273 | [[package]] 274 | name = "leb128" 275 | version = "0.2.5" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 278 | 279 | [[package]] 280 | name = "libc" 281 | version = "0.2.150" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" 284 | 285 | [[package]] 286 | name = "linked-hash-map" 287 | version = "0.5.6" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 290 | 291 | [[package]] 292 | name = "linux-raw-sys" 293 | version = "0.1.4" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 296 | 297 | [[package]] 298 | name = "log" 299 | version = "0.4.17" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 302 | dependencies = [ 303 | "cfg-if", 304 | ] 305 | 306 | [[package]] 307 | name = "memchr" 308 | version = "2.5.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 311 | 312 | [[package]] 313 | name = "num-derive" 314 | version = "0.3.3" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" 317 | dependencies = [ 318 | "proc-macro2", 319 | "quote", 320 | "syn", 321 | ] 322 | 323 | [[package]] 324 | name = "num-traits" 325 | version = "0.2.15" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 328 | dependencies = [ 329 | "autocfg", 330 | ] 331 | 332 | [[package]] 333 | name = "once_cell" 334 | version = "1.17.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 337 | 338 | [[package]] 339 | name = "os_str_bytes" 340 | version = "6.4.1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 343 | 344 | [[package]] 345 | name = "output_vt100" 346 | version = "0.1.3" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" 349 | dependencies = [ 350 | "winapi", 351 | ] 352 | 353 | [[package]] 354 | name = "paste" 355 | version = "1.0.12" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" 358 | 359 | [[package]] 360 | name = "ppv-lite86" 361 | version = "0.2.17" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 364 | 365 | [[package]] 366 | name = "pretty_assertions" 367 | version = "1.3.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" 370 | dependencies = [ 371 | "ctor", 372 | "diff", 373 | "output_vt100", 374 | "yansi", 375 | ] 376 | 377 | [[package]] 378 | name = "pretty_env_logger" 379 | version = "0.4.0" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" 382 | dependencies = [ 383 | "env_logger", 384 | "log", 385 | ] 386 | 387 | [[package]] 388 | name = "proc-macro-error" 389 | version = "1.0.4" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 392 | dependencies = [ 393 | "proc-macro-error-attr", 394 | "proc-macro2", 395 | "quote", 396 | "syn", 397 | "version_check", 398 | ] 399 | 400 | [[package]] 401 | name = "proc-macro-error-attr" 402 | version = "1.0.4" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 405 | dependencies = [ 406 | "proc-macro2", 407 | "quote", 408 | "version_check", 409 | ] 410 | 411 | [[package]] 412 | name = "proc-macro2" 413 | version = "1.0.52" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" 416 | dependencies = [ 417 | "unicode-ident", 418 | ] 419 | 420 | [[package]] 421 | name = "quick-error" 422 | version = "1.2.3" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 425 | 426 | [[package]] 427 | name = "quote" 428 | version = "1.0.26" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 431 | dependencies = [ 432 | "proc-macro2", 433 | ] 434 | 435 | [[package]] 436 | name = "rand" 437 | version = "0.8.5" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 440 | dependencies = [ 441 | "libc", 442 | "rand_chacha", 443 | "rand_core", 444 | ] 445 | 446 | [[package]] 447 | name = "rand_chacha" 448 | version = "0.3.1" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 451 | dependencies = [ 452 | "ppv-lite86", 453 | "rand_core", 454 | ] 455 | 456 | [[package]] 457 | name = "rand_core" 458 | version = "0.6.4" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 461 | dependencies = [ 462 | "getrandom", 463 | ] 464 | 465 | [[package]] 466 | name = "regex" 467 | version = "1.7.3" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" 470 | dependencies = [ 471 | "aho-corasick", 472 | "memchr", 473 | "regex-syntax", 474 | ] 475 | 476 | [[package]] 477 | name = "regex-syntax" 478 | version = "0.6.29" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 481 | 482 | [[package]] 483 | name = "rustix" 484 | version = "0.36.16" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "6da3636faa25820d8648e0e31c5d519bbb01f72fdf57131f0f5f7da5fed36eab" 487 | dependencies = [ 488 | "bitflags", 489 | "errno", 490 | "io-lifetimes", 491 | "libc", 492 | "linux-raw-sys", 493 | "windows-sys 0.45.0", 494 | ] 495 | 496 | [[package]] 497 | name = "ryu" 498 | version = "1.0.13" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 501 | 502 | [[package]] 503 | name = "serde" 504 | version = "1.0.156" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" 507 | 508 | [[package]] 509 | name = "serde_derive" 510 | version = "1.0.156" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" 513 | dependencies = [ 514 | "proc-macro2", 515 | "quote", 516 | "syn", 517 | ] 518 | 519 | [[package]] 520 | name = "serde_json" 521 | version = "1.0.94" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" 524 | dependencies = [ 525 | "itoa", 526 | "ryu", 527 | "serde", 528 | ] 529 | 530 | [[package]] 531 | name = "similar" 532 | version = "2.2.1" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" 535 | 536 | [[package]] 537 | name = "strsim" 538 | version = "0.10.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 541 | 542 | [[package]] 543 | name = "syn" 544 | version = "1.0.109" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 547 | dependencies = [ 548 | "proc-macro2", 549 | "quote", 550 | "unicode-ident", 551 | ] 552 | 553 | [[package]] 554 | name = "termcolor" 555 | version = "1.2.0" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 558 | dependencies = [ 559 | "winapi-util", 560 | ] 561 | 562 | [[package]] 563 | name = "thiserror" 564 | version = "1.0.39" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" 567 | dependencies = [ 568 | "thiserror-impl", 569 | ] 570 | 571 | [[package]] 572 | name = "thiserror-impl" 573 | version = "1.0.39" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" 576 | dependencies = [ 577 | "proc-macro2", 578 | "quote", 579 | "syn", 580 | ] 581 | 582 | [[package]] 583 | name = "unicode-ident" 584 | version = "1.0.8" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 587 | 588 | [[package]] 589 | name = "unicode-width" 590 | version = "0.1.10" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 593 | 594 | [[package]] 595 | name = "version_check" 596 | version = "0.9.4" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 599 | 600 | [[package]] 601 | name = "wabt" 602 | version = "0.10.0" 603 | source = "git+https://github.com/skanehira/wabt-rs#3d31ffb4e2038569f9b3e2c0e778cc4114488bb4" 604 | dependencies = [ 605 | "serde", 606 | "serde_derive", 607 | "serde_json", 608 | "wabt-sys", 609 | ] 610 | 611 | [[package]] 612 | name = "wabt-sys" 613 | version = "0.8.0" 614 | source = "git+https://github.com/skanehira/wabt-rs#3d31ffb4e2038569f9b3e2c0e778cc4114488bb4" 615 | dependencies = [ 616 | "cc", 617 | "cmake", 618 | "glob", 619 | ] 620 | 621 | [[package]] 622 | name = "wasi" 623 | version = "0.11.0+wasi-snapshot-preview1" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 626 | 627 | [[package]] 628 | name = "wasm-encoder" 629 | version = "0.25.0" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7" 632 | dependencies = [ 633 | "leb128", 634 | ] 635 | 636 | [[package]] 637 | name = "wast" 638 | version = "56.0.0" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "6b54185c051d7bbe23757d50fe575880a2426a2f06d2e9f6a10fd9a4a42920c0" 641 | dependencies = [ 642 | "leb128", 643 | "memchr", 644 | "unicode-width", 645 | "wasm-encoder", 646 | ] 647 | 648 | [[package]] 649 | name = "wat" 650 | version = "1.0.62" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "56681922808216ab86d96bb750f70d500b5a7800e41564290fd46bb773581299" 653 | dependencies = [ 654 | "wast", 655 | ] 656 | 657 | [[package]] 658 | name = "winapi" 659 | version = "0.3.9" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 662 | dependencies = [ 663 | "winapi-i686-pc-windows-gnu", 664 | "winapi-x86_64-pc-windows-gnu", 665 | ] 666 | 667 | [[package]] 668 | name = "winapi-i686-pc-windows-gnu" 669 | version = "0.4.0" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 672 | 673 | [[package]] 674 | name = "winapi-util" 675 | version = "0.1.5" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 678 | dependencies = [ 679 | "winapi", 680 | ] 681 | 682 | [[package]] 683 | name = "winapi-x86_64-pc-windows-gnu" 684 | version = "0.4.0" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 687 | 688 | [[package]] 689 | name = "windows-sys" 690 | version = "0.42.0" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 693 | dependencies = [ 694 | "windows_aarch64_gnullvm 0.42.2", 695 | "windows_aarch64_msvc 0.42.2", 696 | "windows_i686_gnu 0.42.2", 697 | "windows_i686_msvc 0.42.2", 698 | "windows_x86_64_gnu 0.42.2", 699 | "windows_x86_64_gnullvm 0.42.2", 700 | "windows_x86_64_msvc 0.42.2", 701 | ] 702 | 703 | [[package]] 704 | name = "windows-sys" 705 | version = "0.45.0" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 708 | dependencies = [ 709 | "windows-targets 0.42.2", 710 | ] 711 | 712 | [[package]] 713 | name = "windows-sys" 714 | version = "0.48.0" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 717 | dependencies = [ 718 | "windows-targets 0.48.5", 719 | ] 720 | 721 | [[package]] 722 | name = "windows-targets" 723 | version = "0.42.2" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 726 | dependencies = [ 727 | "windows_aarch64_gnullvm 0.42.2", 728 | "windows_aarch64_msvc 0.42.2", 729 | "windows_i686_gnu 0.42.2", 730 | "windows_i686_msvc 0.42.2", 731 | "windows_x86_64_gnu 0.42.2", 732 | "windows_x86_64_gnullvm 0.42.2", 733 | "windows_x86_64_msvc 0.42.2", 734 | ] 735 | 736 | [[package]] 737 | name = "windows-targets" 738 | version = "0.48.5" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 741 | dependencies = [ 742 | "windows_aarch64_gnullvm 0.48.5", 743 | "windows_aarch64_msvc 0.48.5", 744 | "windows_i686_gnu 0.48.5", 745 | "windows_i686_msvc 0.48.5", 746 | "windows_x86_64_gnu 0.48.5", 747 | "windows_x86_64_gnullvm 0.48.5", 748 | "windows_x86_64_msvc 0.48.5", 749 | ] 750 | 751 | [[package]] 752 | name = "windows_aarch64_gnullvm" 753 | version = "0.42.2" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 756 | 757 | [[package]] 758 | name = "windows_aarch64_gnullvm" 759 | version = "0.48.5" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 762 | 763 | [[package]] 764 | name = "windows_aarch64_msvc" 765 | version = "0.42.2" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 768 | 769 | [[package]] 770 | name = "windows_aarch64_msvc" 771 | version = "0.48.5" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 774 | 775 | [[package]] 776 | name = "windows_i686_gnu" 777 | version = "0.42.2" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 780 | 781 | [[package]] 782 | name = "windows_i686_gnu" 783 | version = "0.48.5" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 786 | 787 | [[package]] 788 | name = "windows_i686_msvc" 789 | version = "0.42.2" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 792 | 793 | [[package]] 794 | name = "windows_i686_msvc" 795 | version = "0.48.5" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 798 | 799 | [[package]] 800 | name = "windows_x86_64_gnu" 801 | version = "0.42.2" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 804 | 805 | [[package]] 806 | name = "windows_x86_64_gnu" 807 | version = "0.48.5" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 810 | 811 | [[package]] 812 | name = "windows_x86_64_gnullvm" 813 | version = "0.42.2" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 816 | 817 | [[package]] 818 | name = "windows_x86_64_gnullvm" 819 | version = "0.48.5" 820 | source = "registry+https://github.com/rust-lang/crates.io-index" 821 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 822 | 823 | [[package]] 824 | name = "windows_x86_64_msvc" 825 | version = "0.42.2" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 828 | 829 | [[package]] 830 | name = "windows_x86_64_msvc" 831 | version = "0.48.5" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 834 | 835 | [[package]] 836 | name = "yaml-rust" 837 | version = "0.4.5" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 840 | dependencies = [ 841 | "linked-hash-map", 842 | ] 843 | 844 | [[package]] 845 | name = "yansi" 846 | version = "0.5.1" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" 849 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chibiwasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.69" 10 | clap = { version = "4.1.8", features = ["derive"] } 11 | leb128 = "0.2.5" 12 | num-traits = "0.2" 13 | num-derive = "0.3" 14 | thiserror = "1.0.39" 15 | log = "0.4.17" 16 | pretty_env_logger = "0.4.0" 17 | rand = "0.8.5" 18 | 19 | [dev-dependencies] 20 | wat = "1.0.62" 21 | paste = "1.0.12" 22 | wabt = { git = "https://github.com/skanehira/wabt-rs" } 23 | insta = "1.29.0" 24 | pretty_assertions = "1.3.0" 25 | serde_json = "1.0" 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 skanehira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | 3 | [tasks.test] 4 | command = "cargo" 5 | args = ["test", "--all", "--", "--nocapture"] 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chibiwasm 2 | This repository was created for the purpose of learning how Wasm works. 3 | Please do not use it in production. 4 | 5 | If you want to learn how Wasm runtime works, I recommend this book :) 6 | 7 | https://skanehira.github.io/writing-a-wasm-runtime-in-rust/ 8 | 9 | ## Usage 10 | ```sh 11 | $ cat 12 | (module 13 | (func $add (export "add") (param i32 i32) (result i32) 14 | (local.get 0) 15 | (local.get 1) 16 | (i32.add) 17 | ) 18 | ) 19 | $ wat2wasm add.wat 20 | $ cargo run -- add.wasm add 1 2 21 | Finished dev [unoptimized + debuginfo] target(s) in 0.09s 22 | Running `target/debug/chibiwasm add.wasm add 1 2` 23 | 3 24 | ``` 25 | 26 | ## Use as a crate 27 | 28 | ```rust 29 | use chibiwasm::{Runtime, Value}; 30 | 31 | fn main() -> anyhow::Result<()> { 32 | let mut runtime = Runtime::from_file("examples/fib.wasm", None)?; 33 | if let Some(output) = runtime.call("fib".into(), vec![Value::I32(10)])? { 34 | println!("output: {}", output); 35 | } 36 | Ok(()) 37 | } 38 | ``` 39 | 40 | ```sh 41 | $ cargo run -q --example fib 42 | output: 89 43 | ``` 44 | 45 | ## Test 46 | ```sh 47 | $ cargo make test 48 | ``` 49 | 50 | ## Spec 51 | Base on core 1. 52 | 53 | https://www.w3.org/TR/wasm-core-1/ 54 | 55 | # tests 56 | The list is base on https://github.com/WebAssembly/spec/tree/wg-1.0/test/core 57 | 58 | NOTE: Checking only the test cases that have passed both assert_return and assert_trap. 59 | 60 | - [x] address.wast 61 | - [x] align.wast 62 | - [x] binary_leb128.wast 63 | - [x] binary.wast 64 | - [x] block.wast 65 | - [x] br.wast 66 | - [x] br_if.wast 67 | - [x] br_table.wast 68 | - [x] break_drop.wast 69 | - [x] call.wast 70 | - [x] call_indirect.wast 71 | - [x] comments.wast 72 | - [x] const.wast 73 | - [x] conversions.wast 74 | - [x] custom.wast 75 | - [x] data.wast 76 | - [x] elem.wast 77 | - [x] endianness.wast 78 | - [x] exports.wast 79 | - [x] f32.wast 80 | - [x] f32_bitwise.wast 81 | - [x] f32_cmp.wast 82 | - [x] f64.wast 83 | - [x] f64_bitwise.wast 84 | - [x] f64_cmp.wast 85 | - [x] fac.wast 86 | - [x] float_exprs.wast 87 | - [x] float_literals.wast 88 | - [x] float_memory.wast 89 | - [x] float_misc.wast 90 | - [x] forward.wast 91 | - [x] func.wast 92 | - [x] func_ptrs.wast 93 | - [x] globals.wast 94 | - [x] i32.wast 95 | - [x] i64.wast 96 | - [x] if.wast 97 | - [x] imports.wast 98 | - [x] inline_module.wast 99 | - [x] int_exprs.wast 100 | - [x] int_literals.wast 101 | - [x] labels.wast 102 | - [x] left_to_right.wast 103 | - [x] linking.wast 104 | - [x] load.wast 105 | - [x] local_get.wast 106 | - [x] local_set.wast 107 | - [x] local_tee.wast 108 | - [x] loop.wast 109 | - [x] memory.wast 110 | - [x] memory_grow.wast 111 | - [x] memory_redundancy.wast 112 | - [x] memory_size.wast 113 | - [x] memory_trap.wast 114 | - [x] names.wast 115 | - [x] nop.wast 116 | - [x] return.wast 117 | - [x] select.wast 118 | - [x] skip_stack_guard_page.wast 119 | - [x] stack.wast 120 | - [x] start.wast 121 | - [x] store.wast 122 | - [x] switch.wast 123 | - [ ] ~~token.wast~~ 124 | - [x] traps.wast 125 | - [x] type.wast 126 | - [x] unreachable.wast 127 | - [ ] ~~unreached_invalid.wast~~ 128 | - [x] unwind.wast 129 | - [ ] ~~utf8_custom_section_id.wast~~ 130 | - [ ] ~~utf8_import_field.wast~~ 131 | - [ ] ~~utf8_import_module.wast~~ 132 | - [ ] ~~utf8_invalid_encoding.wast~~ 133 | 134 | ## LICENSE 135 | This software includes the work that is distributed in the Apache License 2.0. 136 | -------------------------------------------------------------------------------- /_typos.toml: -------------------------------------------------------------------------------- 1 | [files] 2 | extend-exclude = ["tests/testsuite/*.wast"] 3 | 4 | [default.extend-words] 5 | fle = "fle" 6 | -------------------------------------------------------------------------------- /examples/args_get.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use chibiwasm::wasi::WasiSnapshotPreview1; 3 | use chibiwasm::Runtime; 4 | 5 | fn main() -> Result<()> { 6 | let wasi = WasiSnapshotPreview1::default(); 7 | let mut runtime = Runtime::from_file("examples/args_get.wasm", Some(vec![Box::new(wasi)]))?; 8 | runtime.call("_start".into(), vec![])?; 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /examples/args_get.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skanehira/chibiwasm/917be992646d0f6f8a13fd41e957b2d088cc941c/examples/args_get.wasm -------------------------------------------------------------------------------- /examples/fd_read.rs: -------------------------------------------------------------------------------- 1 | use chibiwasm::{wasi::WasiSnapshotPreview1, Runtime}; 2 | 3 | fn main() -> anyhow::Result<()> { 4 | let wasi = WasiSnapshotPreview1::default(); 5 | let mut runtime = Runtime::from_file("examples/fd_read.wasm", Some(vec![Box::new(wasi)]))?; 6 | runtime.call("_start".into(), vec![])?; 7 | Ok(()) 8 | } 9 | -------------------------------------------------------------------------------- /examples/fd_read.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skanehira/chibiwasm/917be992646d0f6f8a13fd41e957b2d088cc941c/examples/fd_read.wasm -------------------------------------------------------------------------------- /examples/fd_write.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use chibiwasm::wasi::WasiSnapshotPreview1; 3 | use chibiwasm::Runtime; 4 | 5 | fn main() -> Result<()> { 6 | let wasi = WasiSnapshotPreview1::default(); 7 | let mut runtime = Runtime::from_file("examples/fd_write.wasm", Some(vec![Box::new(wasi)]))?; 8 | runtime.call("_start".into(), vec![])?; 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /examples/fd_write.wasm: -------------------------------------------------------------------------------- 1 | asm ``#wasi_snapshot_preview1fd_writememory_start 2 | 0.AA6AA6AA6AA6A!A AA A Hello, World! 3 | -------------------------------------------------------------------------------- /examples/fd_write.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "wasi_snapshot_preview1" "fd_write" 3 | (func $fd_write (param i32 i32 i32 i32) (result i32)) 4 | ) 5 | (memory (export "memory") 1) 6 | (data (i32.const 0) "Hello, World!\n") 7 | 8 | (func $write_hello_world (result i32) 9 | (local $iovec i32) 10 | 11 | (i32.store (i32.const 16) (i32.const 0)) 12 | (i32.store (i32.const 20) (i32.const 7)) 13 | (i32.store (i32.const 24) (i32.const 7)) 14 | (i32.store (i32.const 28) (i32.const 7)) 15 | 16 | (local.set $iovec (i32.const 16)) 17 | 18 | (call $fd_write 19 | (i32.const 1) 20 | (local.get $iovec) 21 | (i32.const 2) 22 | (i32.const 28) 23 | ) 24 | ) 25 | (export "_start" (func $write_hello_world)) 26 | ) 27 | -------------------------------------------------------------------------------- /examples/fib.rs: -------------------------------------------------------------------------------- 1 | use chibiwasm::{Runtime, Value}; 2 | 3 | fn main() -> anyhow::Result<()> { 4 | let mut runtime = Runtime::from_file("examples/fib.wasm", None)?; 5 | if let Some(output) = runtime.call("fib".into(), vec![Value::I32(10)])? { 6 | println!("output: {}", output); 7 | } 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /examples/fib.wasm: -------------------------------------------------------------------------------- 1 | asm`fib 2 |  AMA Ak Akj -------------------------------------------------------------------------------- /examples/fib.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (func $fib (export "fib") (param i32) (result i32) 3 | (if (result i32) (i32.le_u (local.get 0) (i32.const 1)) 4 | (then (i32.const 1)) 5 | (else 6 | (i32.add 7 | (call $fib (i32.sub (local.get 0) (i32.const 2))) 8 | (call $fib (i32.sub (local.get 0) (i32.const 1))) 9 | ) 10 | ) 11 | ) 12 | ) 13 | ) 14 | -------------------------------------------------------------------------------- /examples/hello.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use chibiwasm::wasi::WasiSnapshotPreview1; 3 | use chibiwasm::Runtime; 4 | 5 | fn main() -> Result<()> { 6 | let wasi = WasiSnapshotPreview1::default(); 7 | let mut runtime = Runtime::from_file("examples/hello.wasm", Some(vec![Box::new(wasi)]))?; 8 | runtime.call("_start".into(), vec![])?; 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /examples/hello.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skanehira/chibiwasm/917be992646d0f6f8a13fd41e957b2d088cc941c/examples/hello.wasm -------------------------------------------------------------------------------- /examples/risp.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use chibiwasm::wasi::WasiSnapshotPreview1; 3 | use chibiwasm::Runtime; 4 | 5 | fn main() -> Result<()> { 6 | let wasi = WasiSnapshotPreview1::default(); 7 | let mut runtime = Runtime::from_file("examples/risp.wasm", Some(vec![Box::new(wasi)]))?; 8 | runtime.call("_start".into(), vec![])?; 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /examples/risp.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skanehira/chibiwasm/917be992646d0f6f8a13fd41e957b2d088cc941c/examples/risp.wasm -------------------------------------------------------------------------------- /examples/rjo.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use chibiwasm::wasi::WasiSnapshotPreview1; 3 | use chibiwasm::Runtime; 4 | 5 | fn main() -> Result<()> { 6 | let wasi = WasiSnapshotPreview1::default(); 7 | let mut runtime = Runtime::from_file("examples/rjo.wasm", Some(vec![Box::new(wasi)]))?; 8 | runtime.call("_start".into(), vec![])?; 9 | Ok(()) 10 | } 11 | -------------------------------------------------------------------------------- /examples/rjo.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skanehira/chibiwasm/917be992646d0f6f8a13fd41e957b2d088cc941c/examples/rjo.wasm -------------------------------------------------------------------------------- /src/binary/error.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::enum_variant_names)] 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum Error { 6 | #[error("invalid count of memory, must be 1")] 7 | InvalidMemoryCount, 8 | #[error("invalid count of table, must be 1")] 9 | InvalidTableCount, 10 | #[error("invalid elemtype of table, must be funcref, got {0}")] 11 | InvalidElmType(u8), 12 | #[error("invalid init expr instruction in expressions, got {0}")] 13 | InvalidInitExprOpcode(u8), 14 | #[error("invalid end instruction in expressions, got {0}")] 15 | InvalidInitExprEndOpcode(u8), 16 | #[error("invalid import kind at import section, got {0}")] 17 | InvalidImportKind(u8), 18 | #[error("invalid opecode: {0:x}")] 19 | InvalidOpcode(u8), 20 | } 21 | -------------------------------------------------------------------------------- /src/binary/instruction.rs: -------------------------------------------------------------------------------- 1 | use super::types::Block; 2 | use num_derive::FromPrimitive; 3 | 4 | #[derive(Debug, PartialEq, Clone)] 5 | pub struct MemoryArg { 6 | pub align: u32, 7 | pub offset: u32, 8 | } 9 | 10 | // https://webassembly.github.io/spec/core/binary/instructions.html#expressions 11 | #[derive(Debug, FromPrimitive, PartialEq)] 12 | #[repr(u8)] 13 | pub enum Opcode { 14 | Unreachable = 0x00, 15 | Nop = 0x01, 16 | Block = 0x02, 17 | Loop = 0x03, 18 | If = 0x04, 19 | Else = 0x05, 20 | End = 0x0B, 21 | Br = 0x0C, 22 | BrIf = 0x0D, 23 | BrTable = 0x0E, 24 | LocalGet = 0x20, 25 | LocalSet = 0x21, 26 | LocalTee = 0x22, 27 | GlobalGet = 0x23, 28 | GlobalSet = 0x24, 29 | Call = 0x10, 30 | CallIndirect = 0x11, 31 | I32Const = 0x41, 32 | I32Eqz = 0x45, 33 | I32Eq = 0x46, 34 | I32Ne = 0x47, 35 | I32LtS = 0x48, 36 | I32LtU = 0x49, 37 | I32GtS = 0x4A, 38 | I32GtU = 0x4B, 39 | I32LeS = 0x4C, 40 | I32LeU = 0x4D, 41 | I32GeS = 0x4E, 42 | I32GeU = 0x4F, 43 | I32Add = 0x6a, 44 | I32Sub = 0x6b, 45 | I32Mul = 0x6c, 46 | I32Clz = 0x67, 47 | I32Ctz = 0x68, 48 | I32Popcnt = 0x69, 49 | I32DivS = 0x6D, 50 | I32DivU = 0x6E, 51 | I32RemS = 0x6F, 52 | I32RemU = 0x70, 53 | I32And = 0x71, 54 | I32Or = 0x72, 55 | I32Xor = 0x73, 56 | I32ShL = 0x74, 57 | I32ShrS = 0x75, 58 | I32ShrU = 0x76, 59 | I32RtoL = 0x77, 60 | I32RtoR = 0x78, 61 | I32Extend8S = 0xC0, 62 | I32Extend16S = 0xC1, 63 | I64Const = 0x42, 64 | I64Eqz = 0x50, 65 | I64Eq = 0x51, 66 | I64Ne = 0x52, 67 | I64LtS = 0x53, 68 | I64LtU = 0x54, 69 | I64GtS = 0x55, 70 | I64GtU = 0x56, 71 | I64LeS = 0x57, 72 | I64LeU = 0x58, 73 | I64GeS = 0x59, 74 | I64GeU = 0x5A, 75 | I64Clz = 0x79, 76 | I64Ctz = 0x7A, 77 | I64Popcnt = 0x7B, 78 | I64Add = 0x7C, 79 | I64Sub = 0x7D, 80 | I64Mul = 0x7E, 81 | I64DivS = 0x7F, 82 | I64DivU = 0x80, 83 | I64RemS = 0x81, 84 | I64RemU = 0x82, 85 | I64And = 0x83, 86 | I64Or = 0x84, 87 | I64Xor = 0x85, 88 | I64ShL = 0x86, 89 | I64ShrS = 0x87, 90 | I64ShrU = 0x88, 91 | I64RtoL = 0x89, 92 | I64RtoR = 0x8A, 93 | I64Extend8S = 0xC2, 94 | I64Extend16S = 0xC3, 95 | I64Extend32S = 0xC4, 96 | F32Const = 0x43, 97 | F64Const = 0x44, 98 | F32Eq = 0x5B, 99 | F32Ne = 0x5C, 100 | F32Lt = 0x5D, 101 | F32Gt = 0x5E, 102 | F32Le = 0x5F, 103 | F32Ge = 0x60, 104 | F32Abs = 0x8B, 105 | F32Neg = 0x8C, 106 | F32Ceil = 0x8D, 107 | F32Floor = 0x8E, 108 | F32Trunc = 0x8F, 109 | F32Nearest = 0x90, 110 | F32Sqrt = 0x91, 111 | F32Add = 0x92, 112 | F32Sub = 0x93, 113 | F32Mul = 0x94, 114 | F32Div = 0x95, 115 | F32Min = 0x96, 116 | F32Max = 0x97, 117 | F64Abs = 0x99, 118 | F64Neg = 0x9A, 119 | F64Ceil = 0x9B, 120 | F64Floor = 0x9C, 121 | F64Trunc = 0x9D, 122 | F64Nearest = 0x9E, 123 | F64Sqrt = 0x9F, 124 | F64Add = 0xA0, 125 | F64Sub = 0xA1, 126 | F64Mul = 0xA2, 127 | F64Div = 0xA3, 128 | F64Min = 0xA4, 129 | F64Max = 0xA5, 130 | F64Copysign = 0xA6, 131 | I32WrapI64 = 0xA7, 132 | F64Eq = 0x61, 133 | F64Ne = 0x62, 134 | F64Lt = 0x63, 135 | F64Gt = 0x64, 136 | F64Le = 0x65, 137 | F64Ge = 0x66, 138 | F32Copysign = 0x98, 139 | Return = 0x0f, 140 | I32Load = 0x28, 141 | I64Load = 0x29, 142 | F32Load = 0x2A, 143 | F64Load = 0x2B, 144 | I32Load8S = 0x2C, 145 | I32Load8U = 0x2D, 146 | I32Load16S = 0x2E, 147 | I32Load16U = 0x2F, 148 | I64Load8S = 0x30, 149 | I64Load8U = 0x31, 150 | I64Load16S = 0x32, 151 | I64Load16U = 0x33, 152 | I64Load32S = 0x34, 153 | I64Load32U = 0x35, 154 | I32Store = 0x36, 155 | I64Store = 0x37, 156 | F32Store = 0x38, 157 | F64Store = 0x39, 158 | I32Store8 = 0x3A, 159 | I32Store16 = 0x3B, 160 | I64Store8 = 0x3C, 161 | I64Store16 = 0x3D, 162 | I64Store32 = 0x3E, 163 | MemorySize = 0x3F, 164 | MemoryGrow = 0x40, 165 | MmeoryCopyOrFill = 0xFC, 166 | Select = 0x1B, 167 | Drop = 0x1A, 168 | I32TruncF32S = 0xA8, 169 | I32TruncF32U = 0xA9, 170 | I32TruncF64S = 0xAA, 171 | I32TruncF64U = 0xAB, 172 | I64ExtendI32S = 0xAC, 173 | I64ExtendI32U = 0xAD, 174 | I64TruncF32S = 0xAE, 175 | I64TruncF32U = 0xAF, 176 | I64TruncF64S = 0xB0, 177 | I64TruncF64U = 0xB1, 178 | F32ConvertI32S = 0xB2, 179 | F32ConvertI32U = 0xB3, 180 | F32ConvertI64S = 0xB4, 181 | F32ConvertI64U = 0xB5, 182 | F32DemoteF64 = 0xB6, 183 | F64ConvertI32S = 0xB7, 184 | F64ConvertI32U = 0xB8, 185 | F64ConvertI64S = 0xB9, 186 | F64ConvertI64U = 0xBA, 187 | F64PromoteF32 = 0xBB, 188 | I32ReinterpretF32 = 0xBC, 189 | I64ReinterpretF64 = 0xBD, 190 | F32ReinterpretI32 = 0xBE, 191 | F64ReinterpretI64 = 0xBF, 192 | } 193 | 194 | #[derive(Debug, Clone)] 195 | pub enum Instruction { 196 | Unreachable, 197 | Nop, 198 | Block(Block), 199 | Loop(Block), 200 | If(Block), 201 | Else, 202 | End, 203 | Br(u32), 204 | BrIf(u32), 205 | BrTable(Vec, u32), // break depths, default 206 | LocalGet(u32), 207 | LocalSet(u32), 208 | LocalTee(u32), 209 | GlobalSet(u32), 210 | GlobalGet(u32), 211 | Call(u32), 212 | CallIndirect((u32, u32)), 213 | I32Const(i32), 214 | I32Eqz, 215 | I32Eq, 216 | I32Ne, 217 | I32LtS, 218 | I32LtU, 219 | I32GtS, 220 | I32GtU, 221 | I32LeS, 222 | I32LeU, 223 | I32GeS, 224 | I32GeU, 225 | I32Clz, 226 | I32Ctz, 227 | I32Popcnt, 228 | I32Add, 229 | I32Sub, 230 | I32Mul, 231 | I32DivS, 232 | I32DivU, 233 | I32RemS, 234 | I32RemU, 235 | I32And, 236 | I32Or, 237 | I32Xor, 238 | I32ShL, 239 | I32ShrS, 240 | I32ShrU, 241 | I32RtoL, 242 | I32RtoR, 243 | I32Extend8S, 244 | I32Extend16S, 245 | I64Const(i64), 246 | I64Eqz, 247 | I64Eq, 248 | I64Ne, 249 | I64LtS, 250 | I64LtU, 251 | I64GtS, 252 | I64GtU, 253 | I64LeS, 254 | I64LeU, 255 | I64GeS, 256 | I64GeU, 257 | I64Clz, 258 | I64Ctz, 259 | I64Popcnt, 260 | I64Add, 261 | I64Sub, 262 | I64Mul, 263 | I64DivS, 264 | I64DivU, 265 | I64RemS, 266 | I64RemU, 267 | I64And, 268 | I64Or, 269 | I64Xor, 270 | I64ShL, 271 | I64ShrS, 272 | I64ShrU, 273 | I64RtoL, 274 | I64RtoR, 275 | I64Extend8S, 276 | I64Extend16S, 277 | I64Extend32S, 278 | F32Const(f32), 279 | F32Eq, 280 | F32Ne, 281 | F32Lt, 282 | F32Gt, 283 | F32Le, 284 | F32Ge, 285 | F32Abs, 286 | F32Neg, 287 | F32Ceil, 288 | F32Floor, 289 | F32Trunc, 290 | F32Nearest, 291 | F32Sqrt, 292 | F32Add, 293 | F32Sub, 294 | F32Mul, 295 | F32Div, 296 | F32Min, 297 | F32Max, 298 | F32Copysign, 299 | F64Abs, 300 | F64Neg, 301 | F64Ceil, 302 | F64Floor, 303 | F64Trunc, 304 | F64Nearest, 305 | F64Sqrt, 306 | F64Add, 307 | F64Sub, 308 | F64Mul, 309 | F64Div, 310 | F64Min, 311 | F64Max, 312 | F64Copysign, 313 | I32WrapI64, 314 | F64Eq, 315 | F64Ne, 316 | F64Lt, 317 | F64Gt, 318 | F64Le, 319 | F64Ge, 320 | F64Const(f64), 321 | Return, 322 | I32Load(MemoryArg), 323 | I64Load(MemoryArg), 324 | F32Load(MemoryArg), 325 | F64Load(MemoryArg), 326 | I32Load8S(MemoryArg), 327 | I32Load8U(MemoryArg), 328 | I32Load16S(MemoryArg), 329 | I32Load16U(MemoryArg), 330 | I64Load8S(MemoryArg), 331 | I64Load8U(MemoryArg), 332 | I64Load16S(MemoryArg), 333 | I64Load16U(MemoryArg), 334 | I64Load32S(MemoryArg), 335 | I64Load32U(MemoryArg), 336 | I32Store(MemoryArg), 337 | I64Store(MemoryArg), 338 | F32Store(MemoryArg), 339 | F64Store(MemoryArg), 340 | I32Store8(MemoryArg), 341 | I32Store16(MemoryArg), 342 | I64Store8(MemoryArg), 343 | I64Store16(MemoryArg), 344 | I64Store32(MemoryArg), 345 | Select, 346 | MemoryGrow(u32), 347 | MemorySize, 348 | MemoryCopy(u32, u32), 349 | MemoryFill(u32), 350 | Drop, 351 | I32TruncF32S, 352 | I32TruncF32U, 353 | I32TruncF64S, 354 | I32TruncF64U, 355 | I64ExtendI32S, 356 | I64ExtendI32U, 357 | I64TruncF32S, 358 | I64TruncF32U, 359 | I64TruncF64S, 360 | I64TruncF64U, 361 | F32ConvertI32S, 362 | F32ConvertI32U, 363 | F32ConvertI64S, 364 | F32ConvertI64U, 365 | F32DemoteF64, 366 | F64ConvertI32S, 367 | F64ConvertI32U, 368 | F64ConvertI64S, 369 | F64ConvertI64U, 370 | F64PromoteF32, 371 | I32ReinterpretF32, 372 | I64ReinterpretF64, 373 | F32ReinterpretI32, 374 | F64ReinterpretI64, 375 | } 376 | -------------------------------------------------------------------------------- /src/binary/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod error; 2 | pub(crate) mod instruction; 3 | pub(crate) mod module; 4 | pub(crate) mod section; 5 | pub(crate) mod types; 6 | -------------------------------------------------------------------------------- /src/binary/module.rs: -------------------------------------------------------------------------------- 1 | use super::{section::*, types::*}; 2 | use anyhow::{bail, Result}; 3 | use num_traits::FromPrimitive; 4 | use std::io; 5 | use std::io::{BufRead, BufReader, Read}; 6 | 7 | #[derive(Debug, Default)] 8 | pub struct Module { 9 | pub magic: String, 10 | pub version: u32, 11 | pub custom_section: Option, 12 | pub type_section: Option>, 13 | pub import_section: Option>, 14 | pub function_section: Option>, 15 | pub table_section: Option>, 16 | pub memory_section: Option>, 17 | pub global_section: Option>, 18 | pub export_section: Option>, 19 | pub start_section: Option, 20 | pub element_section: Option>, 21 | pub data: Option>, 22 | pub code_section: Option>, 23 | } 24 | 25 | impl Module { 26 | pub fn add_section(&mut self, section: Section) { 27 | match section { 28 | Section::Custom(section) => self.custom_section = Some(section), 29 | Section::Type(section) => self.type_section = Some(section), 30 | Section::Import(section) => self.import_section = Some(section), 31 | Section::Function(section) => self.function_section = Some(section), 32 | Section::Table(section) => self.table_section = Some(section), 33 | Section::Memory(section) => self.memory_section = Some(section), 34 | Section::Global(section) => self.global_section = Some(section), 35 | Section::Export(section) => self.export_section = Some(section), 36 | Section::Code(section) => self.code_section = Some(section), 37 | Section::Element(section) => self.element_section = Some(section), 38 | Section::Data(section) => self.data = Some(section), 39 | Section::Start(section) => self.start_section = Some(section), 40 | }; 41 | } 42 | } 43 | 44 | pub struct Decoder { 45 | reader: BufReader, 46 | } 47 | 48 | impl Decoder { 49 | pub fn new(reader: R) -> Self { 50 | let reader = BufReader::new(reader); 51 | Self { reader } 52 | } 53 | 54 | fn is_end(&mut self) -> Result { 55 | Ok(self.reader.fill_buf().map(|b| !b.is_empty())?) 56 | } 57 | 58 | fn byte(&mut self) -> Result { 59 | let mut buf = [0u8; 1]; 60 | self.reader.read_exact(&mut buf)?; 61 | Ok(buf[0]) 62 | } 63 | 64 | fn bytes(&mut self, num: usize) -> Result> { 65 | let mut buf = vec![0u8; num]; 66 | self.reader.read_exact(&mut buf)?; 67 | Ok(buf) 68 | } 69 | 70 | fn decode_to_u32(&mut self) -> Result { 71 | Ok(u32::from_le_bytes(self.bytes(4)?.as_slice().try_into()?)) 72 | } 73 | 74 | fn decode_to_string(&mut self, num: usize) -> Result { 75 | let str = String::from_utf8_lossy(self.bytes(num)?.as_slice()).to_string(); 76 | Ok(str) 77 | } 78 | 79 | fn u32(&mut self) -> Result { 80 | let num = leb128::read::unsigned(&mut self.reader)?; 81 | let num = u32::try_from(num)?; 82 | Ok(num) 83 | } 84 | 85 | pub fn decode_section_header(&mut self) -> Result<(SectionID, usize)> { 86 | let id: SectionID = FromPrimitive::from_u8(self.byte()?).unwrap(); 87 | let size = self.u32()? as usize; 88 | Ok((id, size)) 89 | } 90 | 91 | pub fn decode_header(&mut self) -> Result<(String, u32)> { 92 | let magic = self.decode_to_string(4)?; 93 | if magic != "\0asm" { 94 | bail!("invalid binary magic") 95 | } 96 | 97 | let version = self.decode_to_u32()?; 98 | if version != 1 { 99 | bail!("invalid binary version") 100 | } 101 | Ok((magic, version)) 102 | } 103 | 104 | pub fn decode(&mut self) -> Result { 105 | let (magic, version) = self.decode_header()?; 106 | let mut module = Module { 107 | magic, 108 | version, 109 | ..Module::default() 110 | }; 111 | while self.is_end()? { 112 | let (id, size) = self.decode_section_header()?; 113 | let bytes = self.bytes(size)?; 114 | let section = decode(id, &bytes)?; 115 | module.add_section(section); 116 | } 117 | Ok(module) 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | mod test { 123 | use super::Decoder; 124 | use anyhow::Result; 125 | use insta::assert_debug_snapshot; 126 | use wabt::wat2wasm; 127 | 128 | #[test] 129 | fn test_decode_module() -> Result<()> { 130 | let source = r#" 131 | (module 132 | ;; import section 133 | (import "test" "print_i32" (func $print_i32 (param i32))) 134 | (import "test" "memory-2-inf" (table 10 funcref)) 135 | (import "test" "global-i32" (global i32)) 136 | 137 | ;; memory section 138 | (memory 1 256) 139 | 140 | ;; table section 141 | (table 1 256 funcref) 142 | 143 | ;; global section 144 | (global $a i32 (i32.const -2)) 145 | (global $x (mut f32) (f32.const 5.5)) 146 | 147 | ;; function section 148 | (func (export "test") (param i32) 149 | (i32.add 150 | (local.get 0) 151 | (i32.const 1) 152 | ) 153 | (drop) 154 | ) 155 | (func (export "test2") (param i32) (param i32) (result i32) 156 | (i32.add 157 | (local.get 0) 158 | (local.get 1) 159 | ) 160 | ) 161 | (func $main (call $print_i32 (i32.const 2))) 162 | 163 | ;; data section 164 | (data (memory 0x0) (i32.const 1) "a" "" "bcd") 165 | 166 | ;; element_section 167 | (elem (i32.const 0) $main) 168 | 169 | ;; start section 170 | (start $main) 171 | ) 172 | "#; 173 | let wasm = wat2wasm(source.as_bytes())?; 174 | 175 | let reader = std::io::Cursor::new(wasm); 176 | let mut decoder = Decoder::new(reader); 177 | let module = decoder.decode()?; 178 | 179 | assert_debug_snapshot!(module); 180 | 181 | Ok(()) 182 | } 183 | 184 | #[test] 185 | fn test_nested_if() -> Result<()> { 186 | let source = r#" 187 | (module 188 | ;; Auxiliary definition 189 | (memory 1) 190 | 191 | (func $dummy) 192 | (func (export "nested") (param i32 i32) (result i32) 193 | (if (result i32) (local.get 0) 194 | (then 195 | (if (local.get 1) (then (call $dummy) (block) (nop))) 196 | (if (local.get 1) (then) (else (call $dummy) (block) (nop))) 197 | (if (result i32) (local.get 1) 198 | (then (call $dummy) (i32.const 9)) 199 | (else (call $dummy) (i32.const 10)) 200 | ) 201 | ) 202 | (else 203 | (if (local.get 1) (then (call $dummy) (block) (nop))) 204 | (if (local.get 1) (then) (else (call $dummy) (block) (nop))) 205 | (if (result i32) (local.get 1) 206 | (then (call $dummy) (i32.const 10)) 207 | (else (call $dummy) (i32.const 11)) 208 | ) 209 | ) 210 | ) 211 | ) 212 | ) 213 | "#; 214 | let wasm = wat2wasm(source.as_bytes())?; 215 | 216 | let reader = std::io::Cursor::new(wasm); 217 | let mut decoder = Decoder::new(reader); 218 | let module = decoder.decode()?; 219 | 220 | assert_debug_snapshot!(module); 221 | 222 | Ok(()) 223 | } 224 | 225 | #[test] 226 | fn test_return() -> Result<()> { 227 | let source = r#" 228 | (module 229 | (func (export "type-i32-value") (result i32) 230 | (block (result i32) (i32.ctz (return (i32.const 1)))) 231 | ) 232 | ) 233 | "#; 234 | let wasm = wat2wasm(source.as_bytes())?; 235 | 236 | let reader = std::io::Cursor::new(wasm); 237 | let mut decoder = Decoder::new(reader); 238 | let module = decoder.decode()?; 239 | 240 | assert_debug_snapshot!(module); 241 | 242 | Ok(()) 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/binary/section.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_range_loop)] 2 | 3 | use super::error::Error; 4 | use super::instruction::{Instruction, MemoryArg, Opcode}; 5 | use super::types::*; 6 | use anyhow::{bail, Context as _, Result}; 7 | use num_derive::FromPrimitive; 8 | use num_traits::FromPrimitive as _; 9 | use std::io::{BufRead, Cursor, Read}; 10 | 11 | #[derive(Debug, PartialEq, Eq, FromPrimitive)] 12 | pub enum SectionID { 13 | Custom = 0x00, 14 | Type = 0x01, 15 | Import = 0x02, 16 | Function = 0x03, 17 | Table = 0x04, 18 | Memory = 0x05, 19 | Global = 0x06, 20 | Export = 0x07, 21 | Start = 0x08, 22 | Element = 0x09, 23 | Code = 0x0a, 24 | Data = 0x0b, 25 | } 26 | 27 | impl From for SectionID { 28 | fn from(id: u8) -> Self { 29 | match id { 30 | 0x00 => SectionID::Custom, 31 | 0x01 => SectionID::Type, 32 | 0x02 => SectionID::Import, 33 | 0x03 => SectionID::Function, 34 | 0x04 => SectionID::Table, 35 | 0x05 => SectionID::Memory, 36 | 0x06 => SectionID::Global, 37 | 0x07 => SectionID::Export, 38 | 0x08 => SectionID::Start, 39 | 0x09 => SectionID::Element, 40 | 0x0b => SectionID::Data, 41 | 0x0a => SectionID::Code, 42 | _ => panic!("unknown section id: {}", id), 43 | } 44 | } 45 | } 46 | 47 | pub struct SectionReader<'a> { 48 | buf: Cursor<&'a [u8]>, 49 | } 50 | 51 | impl<'a> SectionReader<'a> { 52 | fn new(buf: &'a [u8]) -> Self { 53 | Self { 54 | buf: Cursor::new(buf), 55 | } 56 | } 57 | 58 | fn byte(&mut self) -> Result { 59 | let mut buf = [0u8; 1]; 60 | self.buf.read_exact(&mut buf)?; 61 | Ok(buf[0]) 62 | } 63 | 64 | fn u32(&mut self) -> Result { 65 | let num = leb128::read::unsigned(&mut self.buf)?; 66 | let num = u32::try_from(num)?; 67 | Ok(num) 68 | } 69 | 70 | // https://www.w3.org/TR/wasm-core-1/#floating-point%E2%91%A4 71 | fn f32(&mut self) -> Result { 72 | let buf = &mut [0u8; 4]; 73 | self.buf.read_exact(buf)?; 74 | Ok(f32::from_le_bytes(*buf)) 75 | } 76 | 77 | fn f64(&mut self) -> Result { 78 | let buf = &mut [0u8; 8]; 79 | self.buf.read_exact(buf)?; 80 | Ok(f64::from_le_bytes(*buf)) 81 | } 82 | 83 | // https://www.w3.org/TR/wasm-core-1/#integers%E2%91%A4 84 | fn i32(&mut self) -> Result { 85 | let num = leb128::read::signed(&mut self.buf)?; 86 | let num = i32::try_from(num)?; 87 | Ok(num) 88 | } 89 | 90 | fn i64(&mut self) -> Result { 91 | let num = leb128::read::signed(&mut self.buf)?; 92 | Ok(num) 93 | } 94 | 95 | fn bytes(&mut self, num: usize) -> Result> { 96 | let mut buf = vec![0u8; num]; 97 | self.buf.read_exact(&mut buf)?; 98 | Ok(buf) 99 | } 100 | 101 | fn string(&mut self, size: usize) -> Result { 102 | let bytes = self.bytes(size)?; 103 | let string = String::from_utf8(bytes)?; 104 | Ok(string) 105 | } 106 | 107 | fn is_end(&mut self) -> Result { 108 | Ok(self.buf.fill_buf().map(|b| !b.is_empty())?) 109 | } 110 | } 111 | 112 | // https://webassembly.github.io/spec/core/binary/modules.html#sections 113 | #[derive(Debug)] 114 | pub enum Section { 115 | Custom(Custom), 116 | Type(Vec), 117 | Import(Vec), 118 | Function(Vec), 119 | Table(Vec), 120 | Memory(Vec), // only 1 memory for now 121 | Global(Vec), 122 | Export(Vec), 123 | Start(u32), 124 | Element(Vec), 125 | Data(Vec), 126 | Code(Vec), 127 | } 128 | 129 | pub fn decode(id: SectionID, data: &[u8]) -> Result
{ 130 | let mut reader = SectionReader::new(data); 131 | let section = match id { 132 | SectionID::Custom => decode_custom_section(&mut reader)?, 133 | SectionID::Type => decode_type_section(&mut reader)?, 134 | SectionID::Import => decode_import_section(&mut reader)?, 135 | SectionID::Function => decode_function_section(&mut reader)?, 136 | SectionID::Table => decode_table_secttion(&mut reader)?, 137 | SectionID::Memory => decode_memory_section(&mut reader)?, 138 | SectionID::Global => decode_global_section(&mut reader)?, 139 | SectionID::Export => decode_export_section(&mut reader)?, 140 | SectionID::Start => decode_start_section(&mut reader)?, 141 | SectionID::Element => decode_element_section(&mut reader)?, 142 | SectionID::Data => decode_data_section(&mut reader)?, 143 | SectionID::Code => decode_code_section(&mut reader)?, 144 | }; 145 | Ok(section) 146 | } 147 | 148 | fn decode_custom_section(reader: &mut SectionReader) -> Result
{ 149 | let name_size = reader.u32()?; 150 | let name = reader.string(name_size as usize)?; 151 | let data = reader.bytes(reader.buf.get_ref().len() - reader.buf.position() as usize)?; 152 | Ok(Section::Custom(Custom { name, data })) 153 | } 154 | 155 | fn decode_data_section(reader: &mut SectionReader) -> Result
{ 156 | let mut data = vec![]; 157 | let count = reader.u32()?; 158 | for _ in 0..count { 159 | let memory_index = reader.u32()?; 160 | let offset = decode_expr(reader)?; 161 | let size = reader.u32()?; 162 | let init = reader.bytes(size as usize)?; 163 | data.push(Data { 164 | memory_index, 165 | offset, 166 | init, 167 | }); 168 | } 169 | 170 | Ok(Section::Data(data)) 171 | } 172 | 173 | fn decode_element_section(reader: &mut SectionReader) -> Result
{ 174 | let mut elements = vec![]; 175 | let count = reader.u32()?; 176 | for _ in 0..count { 177 | let mut init = vec![]; 178 | let table_index = reader.u32()?; 179 | let offset = decode_expr(reader)?; 180 | let count = reader.u32()?; 181 | for _ in 0..count { 182 | let index = reader.u32()?; 183 | init.push(index); 184 | } 185 | elements.push(Element { 186 | table_index, 187 | offset, 188 | init, 189 | }); 190 | } 191 | 192 | Ok(Section::Element(elements)) 193 | } 194 | 195 | fn decode_start_section(reader: &mut SectionReader) -> Result
{ 196 | let index = reader.u32()?; 197 | Ok(Section::Start(index)) 198 | } 199 | 200 | fn decode_import_section(reader: &mut SectionReader) -> Result
{ 201 | let count = reader.u32()?; 202 | let mut imports = vec![]; 203 | 204 | for _ in 0..count { 205 | // module name 206 | let size = reader.u32()? as usize; 207 | let module = reader.string(size)?; 208 | 209 | // field name 210 | let size = reader.u32()? as usize; 211 | let field = reader.string(size)?; 212 | 213 | // implrt kind 214 | let import_kind = reader.byte()?; 215 | let kind = match import_kind { 216 | 0x00 => { 217 | // function 218 | let type_index = reader.u32()?; 219 | ImportKind::Func(type_index) 220 | } 221 | 0x01 => { 222 | // table 223 | let table = decode_table(reader)?; 224 | ImportKind::Table(table) 225 | } 226 | 0x02 => { 227 | // memory 228 | let mem = decode_memory(reader)?; 229 | ImportKind::Memory(mem) 230 | } 231 | 0x03 => { 232 | // global 233 | let global_type = decode_global_type(reader)?; 234 | ImportKind::Global(global_type) 235 | } 236 | _ => bail!(Error::InvalidImportKind(import_kind)), 237 | }; 238 | 239 | imports.push(Import { 240 | module, 241 | field, 242 | kind, 243 | }) 244 | } 245 | 246 | Ok(Section::Import(imports)) 247 | } 248 | 249 | fn decode_global_type(reader: &mut SectionReader) -> Result { 250 | let value_type = reader.byte()?; 251 | let mutability = reader.byte()?; 252 | let global_type = GlobalType { 253 | value_type: value_type.into(), 254 | mutability: Mutability::from_u8(mutability).unwrap(), 255 | }; 256 | Ok(global_type) 257 | } 258 | 259 | fn decode_global_section(reader: &mut SectionReader) -> Result
{ 260 | let count = reader.u32()?; 261 | let mut globals = vec![]; 262 | for _ in 0..count { 263 | let global_type = decode_global_type(reader)?; 264 | let init_expr = decode_expr_value(reader)?; 265 | let global = Global { 266 | global_type, 267 | init_expr, 268 | }; 269 | globals.push(global); 270 | } 271 | Ok(Section::Global(globals)) 272 | } 273 | 274 | fn decode_expr_value(reader: &mut SectionReader) -> Result { 275 | let byte = reader.byte()?; 276 | let op: Opcode = Opcode::from_u8(byte).with_context(|| Error::InvalidOpcode(byte))?; 277 | let value = match op { 278 | Opcode::I32Const => { 279 | let value = reader.i32()?; 280 | ExprValue::I32(value) 281 | } 282 | Opcode::I64Const => { 283 | let value = reader.i64()?; 284 | ExprValue::I64(value) 285 | } 286 | Opcode::F32Const => { 287 | let value = reader.f32()?; 288 | ExprValue::F32(value) 289 | } 290 | Opcode::F64Const => { 291 | let value = reader.f64()?; 292 | ExprValue::F64(value) 293 | } 294 | _ => bail!(Error::InvalidInitExprOpcode(byte)), 295 | }; 296 | 297 | let byte = reader.byte()?; 298 | let op: Opcode = Opcode::from_u8(byte).with_context(|| Error::InvalidOpcode(byte))?; 299 | if op != Opcode::End { 300 | bail!(Error::InvalidInitExprEndOpcode(byte)); 301 | } 302 | Ok(value) 303 | } 304 | 305 | fn decode_expr(reader: &mut SectionReader) -> Result { 306 | let byte = reader.byte()?; 307 | let op = Opcode::from_u8(byte).with_context(|| Error::InvalidOpcode(byte))?; 308 | let value = match op { 309 | Opcode::I32Const => { 310 | let value = reader.i32()?; 311 | Expr::Value(ExprValue::I32(value)) 312 | } 313 | Opcode::I64Const => { 314 | let value = reader.i64()?; 315 | Expr::Value(ExprValue::I64(value)) 316 | } 317 | Opcode::F32Const => { 318 | let value = reader.f32()?; 319 | Expr::Value(ExprValue::F32(value)) 320 | } 321 | Opcode::F64Const => { 322 | let value = reader.f64()?; 323 | Expr::Value(ExprValue::F64(value)) 324 | } 325 | Opcode::GlobalGet => { 326 | let value = reader.u32()?; 327 | Expr::GlobalIndex(value as usize) 328 | } 329 | _ => bail!(Error::InvalidInitExprOpcode(byte)), 330 | }; 331 | 332 | let byte = reader.byte()?; 333 | let op = Opcode::from_u8(byte).with_context(|| Error::InvalidOpcode(byte))?; 334 | if op != Opcode::End { 335 | bail!(Error::InvalidInitExprEndOpcode(byte)); 336 | } 337 | Ok(value) 338 | } 339 | 340 | fn decode_table(reader: &mut SectionReader) -> Result
{ 341 | let elem_type = reader.byte()?; 342 | if elem_type != 0x70 { 343 | bail!(Error::InvalidElmType(elem_type)); 344 | } 345 | let limits = decode_limits(reader)?; 346 | let table = Table { 347 | elem_type: ElemType::from_u8(elem_type).unwrap(), 348 | limits, 349 | }; 350 | Ok(table) 351 | } 352 | 353 | fn decode_table_secttion(reader: &mut SectionReader) -> Result
{ 354 | let count = reader.u32()?; 355 | if count != 1 { 356 | bail!(Error::InvalidTableCount); 357 | } 358 | let mut tables = vec![]; 359 | for _ in 0..count { 360 | let table = decode_table(reader)?; 361 | tables.push(table); 362 | } 363 | Ok(Section::Table(tables)) 364 | } 365 | 366 | fn decode_limits(reader: &mut SectionReader) -> Result { 367 | let limits = reader.u32()?; 368 | let min = reader.u32()?; 369 | let max = if limits == 0x00 { 370 | None 371 | } else { 372 | let max = reader.u32()?; 373 | Some(max) 374 | }; 375 | Ok(Limits { min, max }) 376 | } 377 | 378 | fn decode_memory(reader: &mut SectionReader) -> Result { 379 | let limits = decode_limits(reader)?; 380 | Ok(Memory { limits }) 381 | } 382 | 383 | fn decode_memory_section(reader: &mut SectionReader) -> Result
{ 384 | let count = reader.u32()?; 385 | let mut mems: Vec = vec![]; 386 | if count != 1 { 387 | bail!(Error::InvalidMemoryCount); 388 | } 389 | for _ in 0..count { 390 | mems.push(decode_memory(reader)?); 391 | } 392 | Ok(Section::Memory(mems)) 393 | } 394 | 395 | fn decode_type_section(reader: &mut SectionReader) -> Result
{ 396 | let mut func_types: Vec = vec![]; 397 | 398 | // size of function types 399 | let count = reader.u32()?; 400 | 401 | // read each func types 402 | for _ in 0..count { 403 | let func_type = reader.byte()?; 404 | if 0x60 != func_type { 405 | bail!("invalid func type: {:x}", func_type); 406 | } 407 | let mut func = FuncType::default(); 408 | 409 | // read each params 410 | let size = reader.u32()?; 411 | for _ in 0..size { 412 | let value_type: ValueType = reader.byte()?.into(); 413 | func.params.push(value_type); 414 | } 415 | 416 | // read each results 417 | let size = reader.u32()?; 418 | for _ in 0..size { 419 | let value_type: ValueType = reader.byte()?.into(); 420 | func.results.push(value_type); 421 | } 422 | 423 | func_types.push(func) 424 | } 425 | Ok(Section::Type(func_types)) 426 | } 427 | 428 | fn decode_function_section(reader: &mut SectionReader) -> Result
{ 429 | let mut func_idx: Vec = vec![]; 430 | let count = reader.u32()?; 431 | for _ in 0..count { 432 | func_idx.push(reader.u32()?); 433 | } 434 | Ok(Section::Function(func_idx)) 435 | } 436 | 437 | fn decode_export_section(reader: &mut SectionReader) -> Result
{ 438 | let count = reader.u32()?; 439 | let mut exports: Vec = vec![]; 440 | for _ in 0..count { 441 | // name of exported function 442 | let str_len = reader.u32()?; 443 | let name = String::from_utf8(reader.bytes(str_len as usize)?)?; 444 | let exportkind = reader.byte()?; 445 | let idx = reader.u32()?; 446 | let desc = match exportkind { 447 | 0x00 => ExportDesc::Func(idx), 448 | 0x01 => ExportDesc::Table(idx), 449 | 0x02 => ExportDesc::Memory(idx), 450 | 0x03 => ExportDesc::Global(idx), 451 | _ => bail!("unknown export kind: {:x}", exportkind), 452 | }; 453 | exports.push(Export { name, desc }) 454 | } 455 | Ok(Section::Export(exports)) 456 | } 457 | 458 | fn decode_code_section(reader: &mut SectionReader) -> Result
{ 459 | let mut functions: Vec = vec![]; 460 | // size of function 461 | let count = reader.u32()?; 462 | 463 | for _ in 0..count { 464 | let func_body_size = reader.u32()?; 465 | let bytes = reader.bytes(func_body_size as usize)?; 466 | let mut body = SectionReader::new(&bytes); 467 | functions.push(decode_function_body(&mut body)?); 468 | } 469 | Ok(Section::Code(functions)) 470 | } 471 | 472 | fn decode_function_body(reader: &mut SectionReader) -> Result { 473 | let mut function_body = FunctionBody::default(); 474 | 475 | // count of local variable declarations 476 | let count = reader.u32()?; 477 | for _ in 0..count { 478 | let type_count = reader.u32()?; 479 | let value_type: ValueType = reader.byte()?.into(); 480 | function_body.locals.push(FunctionLocal { 481 | type_count, 482 | value_type, 483 | }) 484 | } 485 | 486 | while reader.is_end()? { 487 | let inst = decode_instruction(reader)?; 488 | function_body.code.push(inst); 489 | } 490 | 491 | Ok(function_body) 492 | } 493 | 494 | fn decode_block_type(reader: &mut SectionReader) -> Result { 495 | let byte = reader.byte()?; 496 | let block_type = if byte == 0x40 { 497 | BlockType::Empty 498 | } else { 499 | let value_type = byte.into(); 500 | BlockType::Value(vec![value_type]) 501 | }; 502 | Ok(block_type) 503 | } 504 | 505 | fn decode_block(reader: &mut SectionReader) -> Result { 506 | let block_type = decode_block_type(reader)?; 507 | let block = Block { block_type }; 508 | Ok(block) 509 | } 510 | 511 | fn decode_instruction(reader: &mut SectionReader) -> Result { 512 | let byte = reader.byte()?; 513 | let op: Opcode = Opcode::from_u8(byte).with_context(|| Error::InvalidOpcode(byte))?; 514 | //trace!("decode opcode: {:?}", op); 515 | let inst = match op { 516 | Opcode::Unreachable => Instruction::Unreachable, 517 | Opcode::Nop => Instruction::Nop, 518 | Opcode::Block => Instruction::Block(decode_block(reader)?), 519 | Opcode::Loop => Instruction::Loop(decode_block(reader)?), 520 | Opcode::If => Instruction::If(decode_block(reader)?), 521 | Opcode::Else => Instruction::Else, 522 | Opcode::End => Instruction::End, 523 | Opcode::Br => Instruction::Br(reader.u32()?), 524 | Opcode::BrIf => Instruction::BrIf(reader.u32()?), 525 | Opcode::BrTable => { 526 | let count = reader.u32()? as usize; 527 | let mut indexes = vec![0; count]; 528 | for i in 0..count { 529 | let index = reader.u32()?; 530 | indexes[i] = index; 531 | } 532 | let default = reader.u32()?; 533 | Instruction::BrTable(indexes, default) 534 | } 535 | Opcode::Call => { 536 | let local_idx = reader.u32()?; 537 | Instruction::Call(local_idx) 538 | } 539 | // first u32 is function signature index, second u32 is table index 540 | Opcode::CallIndirect => Instruction::CallIndirect((reader.u32()?, reader.u32()?)), 541 | Opcode::Return => Instruction::Return, 542 | Opcode::LocalGet => { 543 | let local_idx = reader.u32()?; 544 | Instruction::LocalGet(local_idx) 545 | } 546 | Opcode::LocalSet => Instruction::LocalSet(reader.u32()?), 547 | Opcode::LocalTee => Instruction::LocalTee(reader.u32()?), 548 | Opcode::GlobalSet => Instruction::GlobalSet(reader.u32()?), 549 | Opcode::GlobalGet => Instruction::GlobalGet(reader.u32()?), 550 | Opcode::I32Sub => Instruction::I32Sub, 551 | Opcode::I32Add => Instruction::I32Add, 552 | Opcode::I32Mul => Instruction::I32Mul, 553 | Opcode::I32Clz => Instruction::I32Clz, 554 | Opcode::I32Ctz => Instruction::I32Ctz, 555 | Opcode::I32DivU => Instruction::I32DivU, 556 | Opcode::I32DivS => Instruction::I32DivS, 557 | Opcode::I32Eq => Instruction::I32Eq, 558 | Opcode::I32Eqz => Instruction::I32Eqz, 559 | Opcode::I32Ne => Instruction::I32Ne, 560 | Opcode::I32LtS => Instruction::I32LtS, 561 | Opcode::I32LtU => Instruction::I32LtU, 562 | Opcode::I32GtS => Instruction::I32GtS, 563 | Opcode::I32GtU => Instruction::I32GtU, 564 | Opcode::I32LeS => Instruction::I32LeS, 565 | Opcode::I32GeU => Instruction::I32GeU, 566 | Opcode::I32GeS => Instruction::I32GeS, 567 | Opcode::I32LeU => Instruction::I32LeU, 568 | Opcode::I32Popcnt => Instruction::I32Popcnt, 569 | Opcode::I32RemS => Instruction::I32RemS, 570 | Opcode::I32RemU => Instruction::I32RemU, 571 | Opcode::I32And => Instruction::I32And, 572 | Opcode::I32Or => Instruction::I32Or, 573 | Opcode::I32Xor => Instruction::I32Xor, 574 | Opcode::I32ShL => Instruction::I32ShL, 575 | Opcode::I32ShrS => Instruction::I32ShrS, 576 | Opcode::I32ShrU => Instruction::I32ShrU, 577 | Opcode::I32RtoL => Instruction::I32RtoL, 578 | Opcode::I32RtoR => Instruction::I32RtoR, 579 | Opcode::I32Extend8S => Instruction::I32Extend8S, 580 | Opcode::I32Extend16S => Instruction::I32Extend16S, 581 | Opcode::I32Const => { 582 | let value = reader.i32()?; 583 | Instruction::I32Const(value) 584 | } 585 | Opcode::I64Sub => Instruction::I64Sub, 586 | Opcode::I64Add => Instruction::I64Add, 587 | Opcode::I64Mul => Instruction::I64Mul, 588 | Opcode::I64Clz => Instruction::I64Clz, 589 | Opcode::I64Ctz => Instruction::I64Ctz, 590 | Opcode::I64DivU => Instruction::I64DivU, 591 | Opcode::I64DivS => Instruction::I64DivS, 592 | Opcode::I64Eq => Instruction::I64Eq, 593 | Opcode::I64Eqz => Instruction::I64Eqz, 594 | Opcode::I64Ne => Instruction::I64Ne, 595 | Opcode::I64LtS => Instruction::I64LtS, 596 | Opcode::I64LtU => Instruction::I64LtU, 597 | Opcode::I64GtS => Instruction::I64GtS, 598 | Opcode::I64GtU => Instruction::I64GtU, 599 | Opcode::I64LeS => Instruction::I64LeS, 600 | Opcode::I64GeU => Instruction::I64GeU, 601 | Opcode::I64GeS => Instruction::I64GeS, 602 | Opcode::I64LeU => Instruction::I64LeU, 603 | Opcode::I64Popcnt => Instruction::I64Popcnt, 604 | Opcode::I64RemS => Instruction::I64RemS, 605 | Opcode::I64RemU => Instruction::I64RemU, 606 | Opcode::I64And => Instruction::I64And, 607 | Opcode::I64Or => Instruction::I64Or, 608 | Opcode::I64Xor => Instruction::I64Xor, 609 | Opcode::I64ShL => Instruction::I64ShL, 610 | Opcode::I64ShrS => Instruction::I64ShrS, 611 | Opcode::I64ShrU => Instruction::I64ShrU, 612 | Opcode::I64RtoL => Instruction::I64RtoL, 613 | Opcode::I64RtoR => Instruction::I64RtoR, 614 | Opcode::I64Extend8S => Instruction::I64Extend8S, 615 | Opcode::I64Extend16S => Instruction::I64Extend16S, 616 | Opcode::I64Extend32S => Instruction::I64Extend32S, 617 | Opcode::I64Const => { 618 | let value = reader.i64()?; 619 | Instruction::I64Const(value) 620 | } 621 | Opcode::F32Const => { 622 | let num = reader.f32()?; 623 | Instruction::F32Const(num) 624 | } 625 | Opcode::F32Eq => Instruction::F32Eq, 626 | Opcode::F32Ne => Instruction::F32Ne, 627 | Opcode::F32Lt => Instruction::F32Lt, 628 | Opcode::F32Gt => Instruction::F32Gt, 629 | Opcode::F32Le => Instruction::F32Le, 630 | Opcode::F32Ge => Instruction::F32Ge, 631 | Opcode::F32Abs => Instruction::F32Abs, 632 | Opcode::F32Neg => Instruction::F32Neg, 633 | Opcode::F32Ceil => Instruction::F32Ceil, 634 | Opcode::F32Floor => Instruction::F32Floor, 635 | Opcode::F32Trunc => Instruction::F32Trunc, 636 | Opcode::F32Nearest => Instruction::F32Nearest, 637 | Opcode::F32Sqrt => Instruction::F32Sqrt, 638 | Opcode::F32Add => Instruction::F32Add, 639 | Opcode::F32Sub => Instruction::F32Sub, 640 | Opcode::F32Mul => Instruction::F32Mul, 641 | Opcode::F32Div => Instruction::F32Div, 642 | Opcode::F32Min => Instruction::F32Min, 643 | Opcode::F32Max => Instruction::F32Max, 644 | Opcode::F32Copysign => Instruction::F32Copysign, 645 | Opcode::I32WrapI64 => Instruction::I32WrapI64, 646 | Opcode::F64Eq => Instruction::F64Eq, 647 | Opcode::F64Ne => Instruction::F64Ne, 648 | Opcode::F64Lt => Instruction::F64Lt, 649 | Opcode::F64Gt => Instruction::F64Gt, 650 | Opcode::F64Le => Instruction::F64Le, 651 | Opcode::F64Ge => Instruction::F64Ge, 652 | Opcode::F64Abs => Instruction::F64Abs, 653 | Opcode::F64Neg => Instruction::F64Neg, 654 | Opcode::F64Ceil => Instruction::F64Ceil, 655 | Opcode::F64Floor => Instruction::F64Floor, 656 | Opcode::F64Trunc => Instruction::F64Trunc, 657 | Opcode::F64Nearest => Instruction::F64Nearest, 658 | Opcode::F64Sqrt => Instruction::F64Sqrt, 659 | Opcode::F64Add => Instruction::F64Add, 660 | Opcode::F64Sub => Instruction::F64Sub, 661 | Opcode::F64Mul => Instruction::F64Mul, 662 | Opcode::F64Div => Instruction::F64Div, 663 | Opcode::F64Min => Instruction::F64Min, 664 | Opcode::F64Max => Instruction::F64Max, 665 | Opcode::F64Copysign => Instruction::F64Copysign, 666 | Opcode::F64Const => { 667 | let num = reader.f64()?; 668 | Instruction::F64Const(num) 669 | } 670 | Opcode::Drop => Instruction::Drop, 671 | Opcode::I32Load => Instruction::I32Load(read_memory_arg(reader)?), 672 | Opcode::I64Load => Instruction::I64Load(read_memory_arg(reader)?), 673 | Opcode::F32Load => Instruction::F32Load(read_memory_arg(reader)?), 674 | Opcode::F64Load => Instruction::F64Load(read_memory_arg(reader)?), 675 | Opcode::I32Load8S => Instruction::I32Load8S(read_memory_arg(reader)?), 676 | Opcode::I32Load8U => Instruction::I32Load8U(read_memory_arg(reader)?), 677 | Opcode::I32Load16S => Instruction::I32Load16S(read_memory_arg(reader)?), 678 | Opcode::I32Load16U => Instruction::I32Load16U(read_memory_arg(reader)?), 679 | Opcode::I64Load8S => Instruction::I64Load8S(read_memory_arg(reader)?), 680 | Opcode::I64Load8U => Instruction::I64Load8U(read_memory_arg(reader)?), 681 | Opcode::I64Load16S => Instruction::I64Load16S(read_memory_arg(reader)?), 682 | Opcode::I64Load16U => Instruction::I64Load16U(read_memory_arg(reader)?), 683 | Opcode::I64Load32S => Instruction::I64Load32S(read_memory_arg(reader)?), 684 | Opcode::I64Load32U => Instruction::I64Load32U(read_memory_arg(reader)?), 685 | Opcode::I32Store => Instruction::I32Store(read_memory_arg(reader)?), 686 | Opcode::I64Store => Instruction::I64Store(read_memory_arg(reader)?), 687 | Opcode::F32Store => Instruction::F32Store(read_memory_arg(reader)?), 688 | Opcode::F64Store => Instruction::F64Store(read_memory_arg(reader)?), 689 | Opcode::I32Store8 => Instruction::I32Store8(read_memory_arg(reader)?), 690 | Opcode::I32Store16 => Instruction::I32Store16(read_memory_arg(reader)?), 691 | Opcode::I64Store8 => Instruction::I64Store8(read_memory_arg(reader)?), 692 | Opcode::I64Store16 => Instruction::I64Store16(read_memory_arg(reader)?), 693 | Opcode::I64Store32 => Instruction::I64Store32(read_memory_arg(reader)?), 694 | Opcode::MemoryGrow => Instruction::MemoryGrow(reader.u32()?), 695 | Opcode::MemorySize => { 696 | // NOTE: memory index is always 0 now 697 | let _ = reader.byte(); 698 | Instruction::MemorySize 699 | } 700 | // TODO: improve instruction decoding because opecode maybe tow bytes in the version 2 701 | // this instruction is defined in the version2 of the spec 702 | Opcode::MmeoryCopyOrFill => { 703 | let kind = reader.byte()?; 704 | match kind { 705 | 0x0A => { 706 | let src_memidx = reader.u32()?; 707 | let dest_memidx = reader.u32()?; 708 | Instruction::MemoryCopy(src_memidx, dest_memidx) 709 | } 710 | 0x0B => { 711 | let memidx = reader.u32()?; 712 | Instruction::MemoryFill(memidx) 713 | } 714 | _ => unreachable!(), 715 | } 716 | } 717 | Opcode::Select => Instruction::Select, 718 | Opcode::I32TruncF32S => Instruction::I32TruncF32S, 719 | Opcode::I32TruncF32U => Instruction::I32TruncF32U, 720 | Opcode::I32TruncF64S => Instruction::I32TruncF64S, 721 | Opcode::I32TruncF64U => Instruction::I32TruncF64U, 722 | Opcode::I64ExtendI32S => Instruction::I64ExtendI32S, 723 | Opcode::I64ExtendI32U => Instruction::I64ExtendI32U, 724 | Opcode::I64TruncF32S => Instruction::I64TruncF32S, 725 | Opcode::I64TruncF32U => Instruction::I64TruncF32U, 726 | Opcode::I64TruncF64S => Instruction::I64TruncF64S, 727 | Opcode::I64TruncF64U => Instruction::I64TruncF64U, 728 | Opcode::F32ConvertI32S => Instruction::F32ConvertI32S, 729 | Opcode::F32ConvertI32U => Instruction::F32ConvertI32U, 730 | Opcode::F32ConvertI64S => Instruction::F32ConvertI64S, 731 | Opcode::F32ConvertI64U => Instruction::F32ConvertI64U, 732 | Opcode::F32DemoteF64 => Instruction::F32DemoteF64, 733 | Opcode::F64ConvertI32S => Instruction::F64ConvertI32S, 734 | Opcode::F64ConvertI32U => Instruction::F64ConvertI32U, 735 | Opcode::F64ConvertI64S => Instruction::F64ConvertI64S, 736 | Opcode::F64ConvertI64U => Instruction::F64ConvertI64U, 737 | Opcode::F64PromoteF32 => Instruction::F64PromoteF32, 738 | Opcode::I32ReinterpretF32 => Instruction::I32ReinterpretF32, 739 | Opcode::I64ReinterpretF64 => Instruction::I64ReinterpretF64, 740 | Opcode::F32ReinterpretI32 => Instruction::F32ReinterpretI32, 741 | Opcode::F64ReinterpretI64 => Instruction::F64ReinterpretI64, 742 | }; 743 | Ok(inst) 744 | } 745 | 746 | fn read_memory_arg(reader: &mut SectionReader) -> Result { 747 | let arg = MemoryArg { 748 | align: reader.u32()?, 749 | offset: reader.u32()?, 750 | }; 751 | Ok(arg) 752 | } 753 | -------------------------------------------------------------------------------- /src/binary/snapshots/chibiwasm__binary__module__test__decode_module.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/binary/module.rs 3 | expression: module 4 | --- 5 | Module { 6 | magic: "\0asm", 7 | version: 1, 8 | custom_section: None, 9 | type_section: Some( 10 | [ 11 | FuncType { 12 | params: [ 13 | I32, 14 | ], 15 | results: [], 16 | }, 17 | FuncType { 18 | params: [ 19 | I32, 20 | I32, 21 | ], 22 | results: [ 23 | I32, 24 | ], 25 | }, 26 | FuncType { 27 | params: [], 28 | results: [], 29 | }, 30 | ], 31 | ), 32 | import_section: Some( 33 | [ 34 | Import { 35 | module: "test", 36 | field: "print_i32", 37 | kind: Func( 38 | 0, 39 | ), 40 | }, 41 | Import { 42 | module: "test", 43 | field: "memory-2-inf", 44 | kind: Table( 45 | Table { 46 | elem_type: FuncRef, 47 | limits: Limits { 48 | min: 10, 49 | max: None, 50 | }, 51 | }, 52 | ), 53 | }, 54 | Import { 55 | module: "test", 56 | field: "global-i32", 57 | kind: Global( 58 | GlobalType { 59 | value_type: I32, 60 | mutability: Const, 61 | }, 62 | ), 63 | }, 64 | ], 65 | ), 66 | function_section: Some( 67 | [ 68 | 0, 69 | 1, 70 | 2, 71 | ], 72 | ), 73 | table_section: Some( 74 | [ 75 | Table { 76 | elem_type: FuncRef, 77 | limits: Limits { 78 | min: 1, 79 | max: Some( 80 | 256, 81 | ), 82 | }, 83 | }, 84 | ], 85 | ), 86 | memory_section: Some( 87 | [ 88 | Memory { 89 | limits: Limits { 90 | min: 1, 91 | max: Some( 92 | 256, 93 | ), 94 | }, 95 | }, 96 | ], 97 | ), 98 | global_section: Some( 99 | [ 100 | Global { 101 | global_type: GlobalType { 102 | value_type: I32, 103 | mutability: Const, 104 | }, 105 | init_expr: I32( 106 | -2, 107 | ), 108 | }, 109 | Global { 110 | global_type: GlobalType { 111 | value_type: F32, 112 | mutability: Var, 113 | }, 114 | init_expr: F32( 115 | 5.5, 116 | ), 117 | }, 118 | ], 119 | ), 120 | export_section: Some( 121 | [ 122 | Export { 123 | name: "test", 124 | desc: Func( 125 | 1, 126 | ), 127 | }, 128 | Export { 129 | name: "test2", 130 | desc: Func( 131 | 2, 132 | ), 133 | }, 134 | ], 135 | ), 136 | start_section: Some( 137 | 3, 138 | ), 139 | element_section: Some( 140 | [ 141 | Element { 142 | table_index: 0, 143 | offset: Value( 144 | I32( 145 | 0, 146 | ), 147 | ), 148 | init: [ 149 | 3, 150 | ], 151 | }, 152 | ], 153 | ), 154 | data: Some( 155 | [ 156 | Data { 157 | memory_index: 0, 158 | offset: Value( 159 | I32( 160 | 1, 161 | ), 162 | ), 163 | init: [ 164 | 97, 165 | 98, 166 | 99, 167 | 100, 168 | ], 169 | }, 170 | ], 171 | ), 172 | code_section: Some( 173 | [ 174 | FunctionBody { 175 | locals: [], 176 | code: [ 177 | LocalGet( 178 | 0, 179 | ), 180 | I32Const( 181 | 1, 182 | ), 183 | I32Add, 184 | Drop, 185 | End, 186 | ], 187 | }, 188 | FunctionBody { 189 | locals: [], 190 | code: [ 191 | LocalGet( 192 | 0, 193 | ), 194 | LocalGet( 195 | 1, 196 | ), 197 | I32Add, 198 | End, 199 | ], 200 | }, 201 | FunctionBody { 202 | locals: [], 203 | code: [ 204 | I32Const( 205 | 2, 206 | ), 207 | Call( 208 | 0, 209 | ), 210 | End, 211 | ], 212 | }, 213 | ], 214 | ), 215 | } 216 | -------------------------------------------------------------------------------- /src/binary/snapshots/chibiwasm__binary__module__test__nested_if.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/binary/module.rs 3 | expression: module 4 | --- 5 | Module { 6 | magic: "\0asm", 7 | version: 1, 8 | custom_section: None, 9 | type_section: Some( 10 | [ 11 | FuncType { 12 | params: [], 13 | results: [], 14 | }, 15 | FuncType { 16 | params: [ 17 | I32, 18 | I32, 19 | ], 20 | results: [ 21 | I32, 22 | ], 23 | }, 24 | ], 25 | ), 26 | import_section: None, 27 | function_section: Some( 28 | [ 29 | 0, 30 | 1, 31 | ], 32 | ), 33 | table_section: None, 34 | memory_section: Some( 35 | [ 36 | Memory { 37 | limits: Limits { 38 | min: 1, 39 | max: None, 40 | }, 41 | }, 42 | ], 43 | ), 44 | global_section: None, 45 | export_section: Some( 46 | [ 47 | Export { 48 | name: "nested", 49 | desc: Func( 50 | 1, 51 | ), 52 | }, 53 | ], 54 | ), 55 | start_section: None, 56 | element_section: None, 57 | data: None, 58 | code_section: Some( 59 | [ 60 | FunctionBody { 61 | locals: [], 62 | code: [ 63 | End, 64 | ], 65 | }, 66 | FunctionBody { 67 | locals: [], 68 | code: [ 69 | LocalGet( 70 | 0, 71 | ), 72 | If( 73 | Block { 74 | block_type: Value( 75 | [ 76 | I32, 77 | ], 78 | ), 79 | }, 80 | ), 81 | LocalGet( 82 | 1, 83 | ), 84 | If( 85 | Block { 86 | block_type: Empty, 87 | }, 88 | ), 89 | Call( 90 | 0, 91 | ), 92 | Block( 93 | Block { 94 | block_type: Empty, 95 | }, 96 | ), 97 | End, 98 | Nop, 99 | End, 100 | LocalGet( 101 | 1, 102 | ), 103 | If( 104 | Block { 105 | block_type: Empty, 106 | }, 107 | ), 108 | Else, 109 | Call( 110 | 0, 111 | ), 112 | Block( 113 | Block { 114 | block_type: Empty, 115 | }, 116 | ), 117 | End, 118 | Nop, 119 | End, 120 | LocalGet( 121 | 1, 122 | ), 123 | If( 124 | Block { 125 | block_type: Value( 126 | [ 127 | I32, 128 | ], 129 | ), 130 | }, 131 | ), 132 | Call( 133 | 0, 134 | ), 135 | I32Const( 136 | 9, 137 | ), 138 | Else, 139 | Call( 140 | 0, 141 | ), 142 | I32Const( 143 | 10, 144 | ), 145 | End, 146 | Else, 147 | LocalGet( 148 | 1, 149 | ), 150 | If( 151 | Block { 152 | block_type: Empty, 153 | }, 154 | ), 155 | Call( 156 | 0, 157 | ), 158 | Block( 159 | Block { 160 | block_type: Empty, 161 | }, 162 | ), 163 | End, 164 | Nop, 165 | End, 166 | LocalGet( 167 | 1, 168 | ), 169 | If( 170 | Block { 171 | block_type: Empty, 172 | }, 173 | ), 174 | Else, 175 | Call( 176 | 0, 177 | ), 178 | Block( 179 | Block { 180 | block_type: Empty, 181 | }, 182 | ), 183 | End, 184 | Nop, 185 | End, 186 | LocalGet( 187 | 1, 188 | ), 189 | If( 190 | Block { 191 | block_type: Value( 192 | [ 193 | I32, 194 | ], 195 | ), 196 | }, 197 | ), 198 | Call( 199 | 0, 200 | ), 201 | I32Const( 202 | 10, 203 | ), 204 | Else, 205 | Call( 206 | 0, 207 | ), 208 | I32Const( 209 | 11, 210 | ), 211 | End, 212 | End, 213 | End, 214 | ], 215 | }, 216 | ], 217 | ), 218 | } 219 | -------------------------------------------------------------------------------- /src/binary/snapshots/chibiwasm__binary__module__test__return.snap: -------------------------------------------------------------------------------- 1 | --- 2 | source: src/binary/module.rs 3 | expression: module 4 | --- 5 | Module { 6 | magic: "\0asm", 7 | version: 1, 8 | custom_section: None, 9 | type_section: Some( 10 | [ 11 | FuncType { 12 | params: [], 13 | results: [ 14 | I32, 15 | ], 16 | }, 17 | ], 18 | ), 19 | import_section: None, 20 | function_section: Some( 21 | [ 22 | 0, 23 | ], 24 | ), 25 | table_section: None, 26 | memory_section: None, 27 | global_section: None, 28 | export_section: Some( 29 | [ 30 | Export { 31 | name: "type-i32-value", 32 | desc: Func( 33 | 0, 34 | ), 35 | }, 36 | ], 37 | ), 38 | start_section: None, 39 | element_section: None, 40 | data: None, 41 | code_section: Some( 42 | [ 43 | FunctionBody { 44 | locals: [], 45 | code: [ 46 | Block( 47 | Block { 48 | block_type: Value( 49 | [ 50 | I32, 51 | ], 52 | ), 53 | }, 54 | ), 55 | I32Const( 56 | 1, 57 | ), 58 | Return, 59 | I32Ctz, 60 | End, 61 | End, 62 | ], 63 | }, 64 | ], 65 | ), 66 | } 67 | -------------------------------------------------------------------------------- /src/binary/types.rs: -------------------------------------------------------------------------------- 1 | use super::instruction::Instruction; 2 | use num_derive::FromPrimitive; 3 | 4 | // https://webassembly.github.io/spec/core/binary/types.html#value-types 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub enum ValueType { 7 | I32, // 0x7F 8 | I64, // 0x7E 9 | F32, // 0x7D 10 | F64, // 0x7C 11 | } 12 | 13 | impl From for ValueType { 14 | fn from(value_type: u8) -> Self { 15 | match value_type { 16 | 0x7F => Self::I32, 17 | 0x7E => Self::I64, 18 | 0x7D => Self::F32, 19 | 0x7C => Self::F64, 20 | _ => panic!("Invalid value type: {:X}", value_type), 21 | } 22 | } 23 | } 24 | 25 | // https://webassembly.github.io/spec/core/binary/types.html#function-types 26 | #[derive(Debug, Default, Clone, PartialEq)] 27 | pub struct FuncType { 28 | pub params: Vec, 29 | pub results: Vec, 30 | } 31 | 32 | // https://webassembly.github.io/spec/core/binary/modules.html#binary-codesec 33 | #[derive(Debug, Clone, PartialEq)] 34 | #[allow(dead_code)] 35 | pub struct FunctionLocal { 36 | pub type_count: u32, 37 | pub value_type: ValueType, 38 | } 39 | 40 | #[derive(Debug, Default, Clone)] 41 | pub struct FunctionBody { 42 | pub locals: Vec, 43 | pub code: Vec, 44 | } 45 | 46 | #[derive(Debug, Clone, PartialEq)] 47 | pub enum ExportDesc { 48 | Func(u32), 49 | Table(u32), 50 | Memory(u32), 51 | Global(u32), 52 | } 53 | 54 | #[derive(Debug, PartialEq)] 55 | pub struct Export { 56 | pub name: String, 57 | pub desc: ExportDesc, 58 | } 59 | 60 | #[derive(Debug, PartialEq, FromPrimitive)] 61 | pub enum ElemType { 62 | FuncRef = 0x70, 63 | } 64 | 65 | #[derive(Debug, PartialEq)] 66 | pub struct Table { 67 | pub elem_type: ElemType, 68 | pub limits: Limits, 69 | } 70 | 71 | #[derive(Debug, PartialEq)] 72 | pub struct Memory { 73 | pub limits: Limits, 74 | } 75 | 76 | #[derive(Debug, PartialEq)] 77 | pub struct Limits { 78 | pub min: u32, 79 | pub max: Option, 80 | } 81 | 82 | #[derive(Debug, PartialEq, FromPrimitive)] 83 | pub enum Mutability { 84 | Const = 0x00, 85 | Var = 0x01, 86 | } 87 | 88 | #[derive(Debug, PartialEq)] 89 | pub struct GlobalType { 90 | pub value_type: ValueType, 91 | pub mutability: Mutability, 92 | } 93 | 94 | #[derive(Debug, PartialEq, Clone)] 95 | pub enum ExprValue { 96 | I32(i32), 97 | I64(i64), 98 | F32(f32), 99 | F64(f64), 100 | } 101 | 102 | #[derive(Debug, PartialEq, Clone)] 103 | pub enum Expr { 104 | Value(ExprValue), 105 | GlobalIndex(usize), 106 | } 107 | 108 | macro_rules! from_expr_value { 109 | ($($ty:ty => $atrr:ident),+) => { 110 | $( 111 | impl From for $ty { 112 | fn from(value: ExprValue) -> Self { 113 | match value { 114 | ExprValue::$atrr(v) => v, 115 | _ => unreachable!(), 116 | } 117 | } 118 | } 119 | )+ 120 | }; 121 | } 122 | 123 | from_expr_value!(i32 => I32, i64 => I64, f32 => F32, f64 => F64); 124 | 125 | #[derive(Debug, PartialEq)] 126 | pub struct Global { 127 | pub global_type: GlobalType, 128 | pub init_expr: ExprValue, 129 | } 130 | 131 | #[derive(Debug, PartialEq)] 132 | pub enum ImportKind { 133 | Func(u32), 134 | Table(Table), 135 | Memory(Memory), 136 | Global(GlobalType), 137 | } 138 | 139 | #[derive(Debug, PartialEq)] 140 | pub struct Import { 141 | pub module: String, 142 | pub field: String, 143 | pub kind: ImportKind, 144 | } 145 | 146 | #[derive(Debug, PartialEq)] 147 | pub struct Element { 148 | pub table_index: u32, 149 | pub offset: Expr, // offset in table 150 | pub init: Vec, // index of function 151 | } 152 | 153 | #[derive(Debug, PartialEq)] 154 | pub struct Data { 155 | pub memory_index: u32, 156 | pub offset: Expr, 157 | pub init: Vec, 158 | } 159 | 160 | #[derive(Default, Debug, PartialEq)] 161 | pub struct Custom { 162 | pub name: String, 163 | pub data: Vec, 164 | } 165 | 166 | // https://www.w3.org/TR/wasm-core-1/#binary-blocktype 167 | #[derive(Debug, Clone, PartialEq)] 168 | pub enum BlockType { 169 | Empty, 170 | Value(Vec), // only one value type is allowed now 171 | } 172 | 173 | impl BlockType { 174 | pub fn result_count(&self) -> usize { 175 | match self { 176 | Self::Empty => 0, 177 | Self::Value(value_types) => value_types.len(), 178 | } 179 | } 180 | } 181 | 182 | #[derive(Debug, Clone, PartialEq)] 183 | pub struct Block { 184 | pub block_type: BlockType, 185 | } 186 | -------------------------------------------------------------------------------- /src/execution/error.rs: -------------------------------------------------------------------------------- 1 | use super::value::Value; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum Error { 6 | #[error("integer divide by zero")] 7 | IntegerDivideByZero, 8 | #[error("integer overflow")] 9 | IntegerOverflow, 10 | #[error("integer overflow")] 11 | DivisionOverflow, 12 | #[error("cannot pop value from stack")] 13 | StackPopError, 14 | #[error("memory size is not page aligned, page size is {0}")] 15 | MemorySizeNotPageAligned(u32), 16 | #[error("memory page is overflow. max is {0}, grow size is {1}")] 17 | MemoryPageOverflow(u32, u32), 18 | #[error("unexpected stack value type: {0:?}")] 19 | UnexpectedStackValueType(Value), 20 | #[error("not found local variable with index: {0}")] 21 | NotFoundLocalVariable(usize), 22 | #[error("not found global variable with index: {0}")] 23 | NotFoundGlobalVariable(usize), 24 | #[error("not found import module: {0}")] 25 | NotFoundImportModule(String), 26 | #[error("no any imports")] 27 | NoImports, 28 | #[error("not found instruction with pc: {0}")] 29 | NotFoundInstruction(usize), 30 | #[error("not found label with index: {0}")] 31 | NotFoundLabel(usize), 32 | #[error("cannot get start pc in the label")] 33 | NotFoundStartPc, 34 | #[error("not found exported instance by name: {0}")] 35 | NotFoundExportInstance(String), 36 | #[error("not found exported function by index: {0}")] 37 | NotFoundExportedFunction(u32), 38 | #[error("not found exported table by index: {0}")] 39 | NotFoundExportedTable(u32), 40 | #[error("not found exported memory by index: {0}")] 41 | NotFoundExportedMemory(u32), 42 | #[error("not found exported global by index: {0}")] 43 | NotFoundExportedGlobal(u32), 44 | #[error("not found memory by index: {0}")] 45 | NotFoundMemory(usize), 46 | #[error("cannot pop call stack when execute instruction: {0}")] 47 | CallStackPopError(String), 48 | #[error("invalid br_table index: {0}")] 49 | InvalidBrTableIndex(usize), 50 | #[error("cannot pop label when instruction: {0}")] 51 | LabelPopError(String), 52 | #[error("not found function by index: {0}")] 53 | NotFoundFunction(usize), 54 | #[error("not found table by index: {0}")] 55 | NotFoundTable(usize), 56 | #[error("undefined element")] 57 | UndefinedElement, 58 | #[error("uninitialized element {0}")] 59 | UninitializedElement(usize), 60 | #[error("not found function type by index: {0}")] 61 | NotFoundFuncType(usize), 62 | #[error("indirect call type mismatch")] 63 | TypeMismatchIndirectCall, 64 | #[error("not found type section")] 65 | NotFoundTypeSection, 66 | } 67 | -------------------------------------------------------------------------------- /src/execution/fixtures/invoke.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (memory (data "abcdefghijklmnopqrstuvwxyz")) 3 | (func $dummy) 4 | (func $i32.add (param $a i32) (param $b i32) (result i32) 5 | local.get $a 6 | local.get $b 7 | i32.add 8 | ) 9 | (func $call (export "call") (param $a i32) (param $b i32) (result i32) 10 | local.get $a 11 | local.get $b 12 | call $i32.add 13 | ) 14 | (func $return (export "return") (result i32) 15 | (return (i32.const 15)) 16 | ) 17 | (func $if (export "if") (param $a i32) (param $b i32) (result i32) 18 | (if 19 | (i32.eq (local.get $a) (local.get $b)) 20 | (then (return (i32.const 1))) 21 | ) 22 | (return (i32.const 0)) 23 | ) 24 | (func $fib (export "fib") (param $N i32) (result i32) 25 | (if 26 | (i32.eq (local.get $N) (i32.const 1)) 27 | (then (return (i32.const 1))) 28 | ) 29 | (if 30 | (i32.eq (local.get $N) (i32.const 2)) 31 | (then (return (i32.const 1))) 32 | ) 33 | (i32.add 34 | (call $fib (i32.sub (local.get $N) (i32.const 1))) 35 | (call $fib (i32.sub (local.get $N) (i32.const 2))) 36 | ) 37 | ) 38 | (func $if_else_empty (export "if_else_empty") 39 | (if (i32.const 1) 40 | (then) 41 | (else) 42 | ) 43 | ) 44 | (func (export "as-loop-first") (result i32) 45 | (loop (result i32) (block (result i32) (i32.const 1)) (call $dummy) (call $dummy)) 46 | ) 47 | (func (export "as-loop-mid") (result i32) 48 | (loop (result i32) (call $dummy) (block (result i32) (i32.const 1)) (call $dummy)) 49 | ) 50 | (func (export "as-loop-last") (result i32) 51 | (loop (result i32) (call $dummy) (call $dummy) (block (result i32) (i32.const 1))) 52 | ) 53 | (func (export "singular") (result i32) 54 | (block (nop)) 55 | (block (result i32) (i32.const 7)) 56 | ) 57 | (func (export "nested") (result i32) 58 | (block (result i32) 59 | (block (call $dummy) (block) (nop)) 60 | (block (result i32) (call $dummy) (i32.const 9)) 61 | ) 62 | ) 63 | (func (export "as-if-then") (result i32) 64 | (if (result i32) (i32.const 1) (then (block (result i32) (i32.const 1))) (else (i32.const 2))) 65 | ) 66 | (func (export "as-if-else") (result i32) 67 | (if (result i32) (i32.const 0) (then (i32.const 2)) (else (block (result i32) (i32.const 1)))) 68 | ) 69 | (func (export "as-br-value") (result i32) 70 | (block (result i32) (br 0 (br 0 (i32.const 9)))) 71 | ) 72 | (func (export "as-br-last") (result i32) 73 | (block (result i32) 74 | (loop (result i32) (nop) (call $dummy) (br 1 (i32.const 5))) 75 | ) 76 | ) 77 | (func (export "as-if-cond") (result i32) 78 | (block (result i32) 79 | (if (result i32) (br 0 (i32.const 2)) 80 | (then (i32.const 0)) 81 | (else (i32.const 1)) 82 | ) 83 | ) 84 | ) 85 | (func (export "as-br_if-value") (result i32) 86 | (block (result i32) 87 | (drop (br_if 0 (br 0 (i32.const 8)) (i32.const 1))) (i32.const 7) 88 | ) 89 | ) 90 | (func (export "while") (param i32) (result i32) 91 | (local i32) 92 | (local.set 1 (i32.const 1)) 93 | (block 94 | (loop 95 | (br_if 1 (i32.eqz (local.get 0))) 96 | (local.set 1 (i32.mul (local.get 0) (local.get 1))) 97 | (local.set 0 (i32.sub (local.get 0) (i32.const 1))) 98 | (br 0) 99 | ) 100 | ) 101 | (local.get 1) 102 | ) 103 | (func (export "as-if-then-return") (param i32 i32) (result i32) 104 | (if (result i32) 105 | (local.get 0) 106 | (then 107 | (i32.const 1) 108 | (i32.const 2) 109 | (return (i32.const 3)) 110 | ) 111 | (else (local.get 1)) 112 | ) 113 | ) 114 | 115 | (func (export "call-nested") (param i32 i32) (result i32) 116 | (if (result i32) (local.get 0) 117 | (then 118 | (if (local.get 1) (then (call $dummy) (block) (nop))) 119 | (if (local.get 1) (then) (else (call $dummy) (block) (nop))) 120 | (if (result i32) (local.get 1) 121 | (then (call $dummy) (i32.const 9)) 122 | (else (call $dummy) (i32.const 10)) 123 | ) 124 | ) 125 | (else 126 | (if (local.get 1) (then (call $dummy) (block) (nop))) 127 | (if (local.get 1) (then) (else (call $dummy) (block) (nop))) 128 | (if (result i32) (local.get 1) 129 | (then (call $dummy) (i32.const 10)) 130 | (else (call $dummy) (i32.const 11)) 131 | ) 132 | ) 133 | ) 134 | ) 135 | 136 | (func (export "br-nested") (result i32) 137 | (block (result i32) 138 | (block (result i32) 139 | (block (result i32) 140 | (block (result i32) 141 | (i32.const 1) 142 | br 3 143 | ) 144 | ) 145 | ) 146 | ) 147 | ) 148 | 149 | (func (export "if1") (result i32) 150 | (local $i i32) 151 | (local.set $i (i32.const 0)) 152 | (block 153 | (if $l 154 | (i32.const 1) 155 | (then (br $l) (local.set $i (i32.const 666))) 156 | ) 157 | (local.set $i (i32.add (local.get $i) (i32.const 1))) 158 | (if $l 159 | (i32.const 1) 160 | (then (br $l) (local.set $i (i32.const 666))) 161 | (else (local.set $i (i32.const 888))) 162 | ) 163 | (local.set $i (i32.add (local.get $i) (i32.const 1))) 164 | (if $l 165 | (i32.const 1) 166 | (then (br $l) (local.set $i (i32.const 666))) 167 | (else (local.set $i (i32.const 888))) 168 | ) 169 | (local.set $i (i32.add (local.get $i) (i32.const 1))) 170 | (if $l 171 | (i32.const 0) 172 | (then (local.set $i (i32.const 888))) 173 | (else (br $l) (local.set $i (i32.const 666))) 174 | ) 175 | (local.set $i (i32.add (local.get $i) (i32.const 1))) 176 | (if $l 177 | (i32.const 0) 178 | (then (local.set $i (i32.const 888))) 179 | (else (br $l) (local.set $i (i32.const 666))) 180 | ) 181 | (local.set $i (i32.add (local.get $i) (i32.const 1))) 182 | ) 183 | (local.get $i) 184 | ) 185 | (func (export "singleton") (param i32) (result i32) 186 | (block 187 | (block 188 | (br_table 1 0 (local.get 0)) 189 | (return (i32.const 21)) 190 | ) 191 | (return (i32.const 20)) 192 | ) 193 | (i32.const 22) 194 | ) 195 | (func (export "memsize") (result i32) (memory.size)) 196 | (func (export "i32.load") (result i32) 197 | (i32.const 0) 198 | (i32.load offset=1) 199 | ) 200 | (func (export "i64.load") (result i64) 201 | (i64.load offset=0 (i32.const 0)) ;; 0x6867666564636261 'abcdefgh' 202 | ) 203 | (func (export "f32.load") (result f32) 204 | (f32.load offset=0 (i32.const 0)) 205 | ) 206 | (func (export "f64.load") (result f64) 207 | (f64.load offset=0 (i32.const 0)) 208 | ) 209 | ) 210 | -------------------------------------------------------------------------------- /src/execution/float.rs: -------------------------------------------------------------------------------- 1 | use crate::{fbinop, frelop, funop}; 2 | use anyhow::Result; 3 | 4 | // Ref: https://www.w3.org/TR/wasm-core-1/#numeric-instructions%E2%91%A0 5 | pub trait Funop { 6 | fn abs(&self) -> Result 7 | where 8 | Self: Sized; 9 | fn neg(&self) -> Result 10 | where 11 | Self: Sized; 12 | fn sqrt(&self) -> Result 13 | where 14 | Self: Sized; 15 | fn ceil(&self) -> Result 16 | where 17 | Self: Sized; 18 | fn floor(&self) -> Result 19 | where 20 | Self: Sized; 21 | fn trunc(&self) -> Result 22 | where 23 | Self: Sized; 24 | fn nearest(&self) -> Result 25 | where 26 | Self: Sized; 27 | } 28 | 29 | pub trait Fbinop { 30 | fn add(&self, rhs: Self) -> Result 31 | where 32 | Self: Sized; 33 | fn sub(&self, rhs: Self) -> Result 34 | where 35 | Self: Sized; 36 | fn mul(&self, rhs: Self) -> Result 37 | where 38 | Self: Sized; 39 | fn div(&self, rhs: Self) -> Result 40 | where 41 | Self: Sized; 42 | fn min(&self, rhs: Self) -> Result 43 | where 44 | Self: Sized; 45 | fn max(&self, rhs: Self) -> Result 46 | where 47 | Self: Sized; 48 | fn copysign(&self, rhs: Self) -> Result 49 | where 50 | Self: Sized; 51 | } 52 | 53 | pub trait Frelop { 54 | fn equal(&self, rhs: Self) -> Result 55 | where 56 | Self: Sized; 57 | fn not_equal(&self, rhs: Self) -> Result 58 | where 59 | Self: Sized; 60 | fn flt(&self, rhs: Self) -> Result 61 | where 62 | Self: Sized; 63 | fn fgt(&self, rhs: Self) -> Result 64 | where 65 | Self: Sized; 66 | fn fle(&self, rhs: Self) -> Result 67 | where 68 | Self: Sized; 69 | fn fge(&self, rhs: Self) -> Result 70 | where 71 | Self: Sized; 72 | } 73 | 74 | funop!(f32, f64); 75 | fbinop!(f32, f64); 76 | frelop!(f32, f64); 77 | -------------------------------------------------------------------------------- /src/execution/importer.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | module::{ExternalFuncInst, FuncInst, GlobalInst, InternalMemoryInst, InternalTableInst}, 3 | Store, Value, 4 | }; 5 | use anyhow::Result; 6 | use std::{cell::RefCell, rc::Rc}; 7 | 8 | pub trait Importer { 9 | fn name(&self) -> &str; 10 | 11 | fn get(&self, _name: &str) -> Result>>> { 12 | Ok(None) 13 | } 14 | 15 | fn invoke( 16 | &self, 17 | store: Rc>, 18 | func: ExternalFuncInst, 19 | args: Vec, 20 | ) -> Result>; 21 | 22 | fn resolve_table( 23 | &self, 24 | _module: &str, 25 | _field: &str, 26 | ) -> Result>>> { 27 | Ok(None) 28 | } 29 | 30 | fn resolve_global(&self, _module: &str, _field: &str) -> Result> { 31 | Ok(None) 32 | } 33 | 34 | fn resolve_func(&self, _module: &str, _field: &str) -> Result> { 35 | Ok(None) 36 | } 37 | 38 | fn resolve_memory( 39 | &self, 40 | _name: &str, 41 | _field: &str, 42 | ) -> Result>>> { 43 | Ok(None) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/execution/indices.rs: -------------------------------------------------------------------------------- 1 | pub type TypeIdx = u32; 2 | pub type FuncIdx = u32; 3 | pub type TableIdx = u32; 4 | pub type MemoryIdx = u32; 5 | pub type GlobalIdx = u32; 6 | -------------------------------------------------------------------------------- /src/execution/integer.rs: -------------------------------------------------------------------------------- 1 | use super::error::Error; 2 | use crate::{ibinop, irelop, itestop, iunop}; 3 | use anyhow::{bail, Result}; 4 | 5 | pub trait Iunop { 6 | fn clz(&self) -> Result 7 | where 8 | Self: Sized; 9 | fn ctz(&self) -> Result 10 | where 11 | Self: Sized; 12 | fn extend8_s(&self) -> Result 13 | where 14 | Self: Sized; 15 | fn extend16_s(&self) -> Result 16 | where 17 | Self: Sized; 18 | } 19 | 20 | pub trait Ibinop { 21 | fn add(&self, rhs: Self) -> Result 22 | where 23 | Self: Sized; 24 | fn sub(&self, rhs: Self) -> Result 25 | where 26 | Self: Sized; 27 | fn mul(&self, rhs: Self) -> Result 28 | where 29 | Self: Sized; 30 | fn div_s(&self, rhs: Self) -> Result 31 | where 32 | Self: Sized; 33 | fn div_u(&self, rhs: Self) -> Result 34 | where 35 | Self: Sized; 36 | fn rem_s(&self, rhs: Self) -> Result 37 | where 38 | Self: Sized; 39 | fn rem_u(&self, rhs: Self) -> Result 40 | where 41 | Self: Sized; 42 | fn and(&self, rhs: Self) -> Result 43 | where 44 | Self: Sized; 45 | fn or(&self, rhs: Self) -> Result 46 | where 47 | Self: Sized; 48 | fn xor(&self, rhs: Self) -> Result 49 | where 50 | Self: Sized; 51 | fn shl(&self, rhs: Self) -> Result 52 | where 53 | Self: Sized; 54 | fn shr_s(&self, rhs: Self) -> Result 55 | where 56 | Self: Sized; 57 | fn shr_u(&self, rhs: Self) -> Result 58 | where 59 | Self: Sized; 60 | fn rotl(&self, rhs: Self) -> Result 61 | where 62 | Self: Sized; 63 | fn rotr(&self, rhs: Self) -> Result 64 | where 65 | Self: Sized; 66 | } 67 | 68 | pub trait Irelop { 69 | fn equal(&self, rhs: Self) -> Result 70 | where 71 | Self: Sized; 72 | fn not_equal(&self, rhs: Self) -> Result 73 | where 74 | Self: Sized; 75 | fn lt_s(&self, rhs: Self) -> Result 76 | where 77 | Self: Sized; 78 | fn lt_u(&self, rhs: Self) -> Result 79 | where 80 | Self: Sized; 81 | fn gt_s(&self, rhs: Self) -> Result 82 | where 83 | Self: Sized; 84 | fn gt_u(&self, rhs: Self) -> Result 85 | where 86 | Self: Sized; 87 | fn le_s(&self, rhs: Self) -> Result 88 | where 89 | Self: Sized; 90 | fn le_u(&self, rhs: Self) -> Result 91 | where 92 | Self: Sized; 93 | fn ge_s(&self, rhs: Self) -> Result 94 | where 95 | Self: Sized; 96 | fn ge_u(&self, rhs: Self) -> Result 97 | where 98 | Self: Sized; 99 | } 100 | 101 | pub trait Itestop { 102 | fn eqz(&self) -> Result 103 | where 104 | Self: Sized; 105 | } 106 | 107 | iunop!(i32); 108 | iunop!(i64); 109 | ibinop!(i32); 110 | ibinop!(i64); 111 | irelop!(i32); 112 | irelop!(i64); 113 | itestop!(); 114 | -------------------------------------------------------------------------------- /src/execution/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! load { 3 | ($stack: expr, $store: expr, $ty: ty, $arg: expr) => {{ 4 | let store = $store.borrow(); 5 | let memory = store 6 | .memory 7 | .get(0) 8 | .with_context(|| Error::NotFoundMemory(0))?; 9 | let memory = memory.borrow(); 10 | let addr = $stack.pop1::()? as usize; 11 | let value = memory.load::<$ty>(addr, $arg)?; 12 | $stack.push(value.into()); 13 | }}; 14 | ($stack: expr, $store: expr, $ty: ty, $arg: expr, $tz: ty) => {{ 15 | let addr = $stack.pop1::()? as usize; 16 | let store = $store.borrow(); 17 | let memory = store 18 | .memory 19 | .get(0) 20 | .with_context(|| Error::NotFoundMemory(0))?; 21 | let memory = memory.borrow(); 22 | let value = memory.load::<$ty>(addr, $arg)? as $tz; 23 | $stack.push(value.into()); 24 | }}; 25 | } 26 | 27 | #[macro_export] 28 | macro_rules! store { 29 | ($stack: expr, $store: expr, $ty: ty, $arg: expr) => {{ 30 | let store = $store.borrow(); 31 | let memory = store 32 | .memory 33 | .get(0) 34 | .with_context(|| Error::NotFoundMemory(0))?; 35 | let mut memory = memory.borrow_mut(); 36 | let value = $stack.pop1::<$ty>()?; 37 | let addr = $stack.pop1::()? as usize; 38 | memory.write(addr, $arg, value)?; 39 | }}; 40 | ($stack: expr, $store: expr, $ty: ty, $arg: expr, $tz: ty) => {{ 41 | let store = $store.borrow(); 42 | let memory = store 43 | .memory 44 | .get(0) 45 | .with_context(|| Error::NotFoundMemory(0))?; 46 | let mut memory = memory.borrow_mut(); 47 | let value = $stack.pop1::<$ty>()? as $tz; 48 | let addr = $stack.pop1::()? as usize; 49 | memory.write(addr, $arg, value)?; 50 | }}; 51 | } 52 | 53 | #[macro_export] 54 | macro_rules! impl_binary_operation { 55 | ($($op: ident),*) => { 56 | $( 57 | pub fn $op(stack: &mut impl StackAccess) -> Result<()> { 58 | let (r, l): (Value, Value) = stack.pop_rl()?; 59 | let value = l.$op(&r)?; 60 | stack.push(value); 61 | Ok(()) 62 | } 63 | )* 64 | }; 65 | } 66 | 67 | #[macro_export] 68 | macro_rules! impl_unary_operation { 69 | ($($op: ident),*) => { 70 | $( 71 | pub fn $op(stack: &mut impl StackAccess) -> Result<()> { 72 | let value: Value = stack.pop1()?; 73 | let value = value.$op()?; 74 | stack.push(value); 75 | Ok(()) 76 | } 77 | )* 78 | }; 79 | } 80 | 81 | #[macro_export] 82 | macro_rules! impl_cvtop_operation { 83 | ($($op: ident),*) => { 84 | $( 85 | pub fn $op(stack: &mut impl StackAccess) -> Result<()> { 86 | let value: Value = stack.pop1()?; 87 | let value = value.$op()?; 88 | stack.push(value); 89 | Ok(()) 90 | } 91 | )* 92 | }; 93 | } 94 | 95 | #[macro_export] 96 | macro_rules! funop { 97 | ($($ty: ty),+) => { 98 | $( 99 | impl Funop for $ty { 100 | fn abs(&self) -> Result { 101 | Ok((*self).abs()) 102 | } 103 | fn neg(&self) -> Result { 104 | Ok(-(*self)) 105 | } 106 | fn sqrt(&self) -> Result { 107 | if (*self) == 0.0 { 108 | return Ok(0.0); 109 | } 110 | Ok((*self).sqrt()) 111 | } 112 | fn ceil(&self) -> Result { 113 | Ok(num_traits::real::Real::ceil(*self)) 114 | } 115 | fn floor(&self) -> Result { 116 | Ok((*self).floor()) 117 | } 118 | fn trunc(&self) -> Result { 119 | Ok((*self).trunc()) 120 | } 121 | fn nearest(&self) -> Result { 122 | let abs = (*self).abs(); 123 | if (0.0..=0.5).contains(&abs) { 124 | return Ok(0.0); 125 | } 126 | let rounded = (*self).round(); 127 | let value = match rounded as i64 % 2 { 128 | r if r == 1 => self.floor().unwrap(), 129 | r if r == -1 => self.ceil().unwrap(), 130 | _ => rounded, 131 | }; 132 | Ok(value) 133 | } 134 | } 135 | )+ 136 | }; 137 | } 138 | 139 | #[macro_export] 140 | macro_rules! fbinop { 141 | ($($ty: ty),+) => { 142 | $( 143 | impl Fbinop for $ty { 144 | fn add(&self, rhs: Self) -> Result { 145 | Ok((*self) + rhs) 146 | } 147 | fn div(&self, rhs: Self) -> Result { 148 | Ok((*self) / rhs) 149 | } 150 | fn mul(&self, rhs: Self) -> Result { 151 | Ok((*self) * (rhs)) 152 | } 153 | fn sub(&self, rhs: Self) -> Result { 154 | Ok((*self) - rhs) 155 | } 156 | fn min(&self, rhs: Self) -> Result { 157 | Ok((*self).min(rhs)) 158 | } 159 | fn max(&self, rhs: Self) -> Result { 160 | Ok((*self).max(rhs)) 161 | } 162 | fn copysign(&self, rhs: Self) -> Result { 163 | Ok((*self).copysign(rhs)) 164 | } 165 | } 166 | )+ 167 | }; 168 | } 169 | 170 | #[macro_export] 171 | macro_rules! frelop { 172 | ($($ty: ty),+) => { 173 | $( 174 | impl Frelop for $ty { 175 | fn equal(&self, rhs: Self) -> Result { 176 | Ok(((*self) == rhs) as i32) 177 | } 178 | fn not_equal(&self, rhs: Self) -> Result { 179 | Ok(((*self) != rhs) as i32) 180 | } 181 | fn flt(&self, rhs: Self) -> Result { 182 | Ok(((*self) < rhs) as i32) 183 | } 184 | fn fgt(&self, rhs: Self) -> Result { 185 | Ok(((*self) > rhs) as i32) 186 | } 187 | fn fle(&self, rhs: Self) -> Result { 188 | Ok(((*self) <= rhs) as i32) 189 | } 190 | fn fge(&self, rhs: Self) -> Result { 191 | Ok(((*self) >= rhs) as i32) 192 | } 193 | } 194 | )+ 195 | }; 196 | } 197 | 198 | #[macro_export] 199 | macro_rules! iunop { 200 | () => { 201 | fn clz(&self) -> Result { 202 | Ok(self.leading_zeros() as Self) 203 | } 204 | fn ctz(&self) -> Result { 205 | Ok(self.trailing_zeros() as Self) 206 | } 207 | }; 208 | (i32) => { 209 | impl Iunop for i32 { 210 | iunop!(); 211 | fn extend8_s(&self) -> Result { 212 | Ok(self << 24 >> 24) 213 | } 214 | fn extend16_s(&self) -> Result { 215 | Ok(self << 16 >> 16) 216 | } 217 | } 218 | }; 219 | (i64) => { 220 | impl Iunop for i64 { 221 | iunop!(); 222 | fn extend8_s(&self) -> Result { 223 | Ok(self << 56 >> 56) 224 | } 225 | fn extend16_s(&self) -> Result { 226 | Ok(self << 48 >> 48) 227 | } 228 | } 229 | }; 230 | } 231 | 232 | #[macro_export] 233 | macro_rules! ibinop { 234 | () => { 235 | fn add(&self, rhs: Self) -> Result { 236 | Ok(self.wrapping_add(rhs)) 237 | } 238 | fn sub(&self, rhs: Self) -> Result { 239 | Ok(self.wrapping_sub(rhs)) 240 | } 241 | fn mul(&self, rhs: Self) -> Result { 242 | Ok(self.wrapping_mul(rhs)) 243 | } 244 | fn div_s(&self, rhs: Self) -> Result { 245 | if rhs == 0 { 246 | bail!(Error::IntegerDivideByZero); 247 | } 248 | match self.checked_div(rhs) { 249 | Some(v) => Ok(v), 250 | None => bail!(Error::DivisionOverflow), 251 | } 252 | } 253 | fn rem_s(&self, rhs: Self) -> Result { 254 | if rhs == 0 { 255 | bail!(Error::IntegerDivideByZero); 256 | } 257 | Ok(self.wrapping_rem(rhs) as Self) 258 | } 259 | fn and(&self, rhs: Self) -> Result { 260 | Ok((*self & rhs) as Self) 261 | } 262 | fn or(&self, rhs: Self) -> Result { 263 | Ok((*self | rhs) as Self) 264 | } 265 | fn xor(&self, rhs: Self) -> Result { 266 | Ok((*self ^ rhs) as Self) 267 | } 268 | fn shl(&self, rhs: Self) -> Result { 269 | Ok((*self).wrapping_shl(rhs as u32)) 270 | } 271 | fn shr_s(&self, rhs: Self) -> Result { 272 | Ok((*self).wrapping_shr(rhs as u32)) 273 | } 274 | fn rotl(&self, rhs: Self) -> Result { 275 | Ok((*self).rotate_left(rhs as u32)) 276 | } 277 | fn rotr(&self, rhs: Self) -> Result { 278 | Ok((*self).rotate_right(rhs as u32)) 279 | } 280 | }; 281 | (i32) => { 282 | impl Ibinop for i32 { 283 | ibinop!(); 284 | fn div_u(&self, rhs: Self) -> Result { 285 | if rhs == 0 { 286 | bail!(Error::IntegerDivideByZero); 287 | } 288 | Ok(u32::wrapping_div(*self as u32, rhs as u32) as Self) 289 | } 290 | fn rem_u(&self, rhs: Self) -> Result { 291 | if rhs == 0 { 292 | bail!(Error::IntegerDivideByZero); 293 | } 294 | Ok((*self as u32).wrapping_rem(rhs as u32) as Self) 295 | } 296 | fn shr_u(&self, rhs: Self) -> Result { 297 | Ok((*self as u32).wrapping_shr(rhs as u32) as Self) 298 | } 299 | } 300 | }; 301 | (i64) => { 302 | impl Ibinop for i64 { 303 | ibinop!(); 304 | fn div_u(&self, rhs: Self) -> Result { 305 | if rhs == 0 { 306 | bail!(Error::IntegerDivideByZero); 307 | } 308 | Ok(u64::wrapping_div(*self as u64, rhs as u64) as Self) 309 | } 310 | fn rem_u(&self, rhs: Self) -> Result { 311 | if rhs == 0 { 312 | bail!(Error::IntegerDivideByZero); 313 | } 314 | Ok((*self as u64).wrapping_rem(rhs as u64) as Self) 315 | } 316 | fn shr_u(&self, rhs: Self) -> Result { 317 | Ok((*self as u64).wrapping_shr(rhs as u32) as Self) 318 | } 319 | } 320 | }; 321 | } 322 | 323 | #[macro_export] 324 | macro_rules! irelop { 325 | () => { 326 | fn equal(&self, rhs: Self) -> Result { 327 | Ok((*self == rhs) as i32) 328 | } 329 | fn not_equal(&self, rhs: Self) -> Result { 330 | Ok((*self != rhs) as i32) 331 | } 332 | fn lt_s(&self, rhs: Self) -> Result { 333 | Ok((*self < rhs) as i32) 334 | } 335 | fn gt_s(&self, rhs: Self) -> Result { 336 | Ok((*self > rhs) as i32) 337 | } 338 | fn le_s(&self, rhs: Self) -> Result { 339 | Ok((*self <= rhs) as i32) 340 | } 341 | fn ge_s(&self, rhs: Self) -> Result { 342 | Ok((*self >= rhs) as i32) 343 | } 344 | }; 345 | (i32) => { 346 | impl Irelop for i32 { 347 | irelop!(); 348 | fn lt_u(&self, rhs: Self) -> Result { 349 | Ok((*self as u32).lt(&(rhs as u32)) as i32) 350 | } 351 | fn gt_u(&self, rhs: Self) -> Result { 352 | Ok((*self as u32).gt(&(rhs as u32)) as i32) 353 | } 354 | fn le_u(&self, rhs: Self) -> Result { 355 | Ok((*self as u32).le(&(rhs as u32)) as i32) 356 | } 357 | fn ge_u(&self, rhs: Self) -> Result { 358 | Ok((*self as u32).ge(&(rhs as u32)) as i32) 359 | } 360 | } 361 | }; 362 | (i64) => { 363 | impl Irelop for i64 { 364 | irelop!(); 365 | fn lt_u(&self, rhs: Self) -> Result { 366 | Ok((*self as u64).lt(&(rhs as u64)) as i32) 367 | } 368 | fn gt_u(&self, rhs: Self) -> Result { 369 | Ok((*self as u64).gt(&(rhs as u64)) as i32) 370 | } 371 | fn le_u(&self, rhs: Self) -> Result { 372 | Ok((*self as u64).le(&(rhs as u64)) as i32) 373 | } 374 | fn ge_u(&self, rhs: Self) -> Result { 375 | Ok((*self as u64).ge(&(rhs as u64)) as i32) 376 | } 377 | } 378 | }; 379 | } 380 | 381 | #[macro_export] 382 | macro_rules! itestop { 383 | () => { 384 | impl Itestop for i32 { 385 | fn eqz(&self) -> Result { 386 | Ok((*self == 0) as i32) 387 | } 388 | } 389 | 390 | impl Itestop for i64 { 391 | fn eqz(&self) -> Result { 392 | Ok((*self == 0) as i32) 393 | } 394 | } 395 | }; 396 | } 397 | -------------------------------------------------------------------------------- /src/execution/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub(crate) mod float; 3 | pub mod importer; 4 | pub(crate) mod indices; 5 | pub(crate) mod integer; 6 | mod macros; 7 | pub mod module; 8 | pub(crate) mod op; 9 | pub mod runtime; 10 | pub mod store; 11 | pub mod value; 12 | 13 | pub use importer::*; 14 | pub use runtime::*; 15 | pub use store::*; 16 | pub use value::*; 17 | -------------------------------------------------------------------------------- /src/execution/module.rs: -------------------------------------------------------------------------------- 1 | use super::indices::TypeIdx; 2 | use super::value::{ExternalVal, Numeric, Value}; 3 | use crate::binary::instruction::{Instruction, MemoryArg}; 4 | use crate::binary::module::Module; 5 | use crate::binary::types::{FuncType, ValueType}; 6 | use crate::execution::error::Error; 7 | use anyhow::{bail, Result}; 8 | use std::cell::RefCell; 9 | use std::collections::HashMap; 10 | use std::rc::Rc; 11 | 12 | // https://www.w3.org/TR/wasm-core-1/#memory-instances%E2%91%A0 13 | pub const PAGE_SIZE: u32 = 65536; // 64Ki 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct Func { 17 | pub type_idx: TypeIdx, 18 | pub locals: Vec, 19 | pub body: Vec, 20 | } 21 | 22 | #[derive(Debug, Clone)] 23 | pub struct InternalFuncInst { 24 | pub func_type: FuncType, 25 | pub code: Func, 26 | } 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct ExternalFuncInst { 30 | pub module: String, 31 | pub field: String, // function name 32 | pub func_type: FuncType, 33 | } 34 | 35 | #[derive(Debug, Clone)] 36 | pub enum FuncInst { 37 | Internal(InternalFuncInst), 38 | External(ExternalFuncInst), 39 | } 40 | 41 | #[derive(Debug, Clone, Default)] 42 | pub struct InternalTableInst { 43 | pub funcs: Vec>, 44 | pub max: Option, 45 | } 46 | pub type TableInst = Rc>; 47 | 48 | #[derive(Default, Debug, Clone)] 49 | pub struct InternalMemoryInst { 50 | pub data: Vec, 51 | pub max: Option, 52 | } 53 | pub type MemoryInst = Rc>; 54 | 55 | impl InternalMemoryInst { 56 | pub fn size(&self) -> usize { 57 | self.data.len() / PAGE_SIZE as usize 58 | } 59 | 60 | // https://www.w3.org/TR/wasm-core-1/#grow-mem 61 | pub fn grow(&mut self, grow_size: u32) -> Result<()> { 62 | let size = self.size() as u32; 63 | if size % PAGE_SIZE != 0 { 64 | bail!(Error::MemorySizeNotPageAligned(PAGE_SIZE)); 65 | } 66 | let len = size + grow_size; 67 | if let Some(max) = self.max { 68 | if max < len { 69 | bail!(Error::MemoryPageOverflow(max, len)); 70 | } 71 | } 72 | self.data.resize((len * PAGE_SIZE) as usize, 0); 73 | Ok(()) 74 | } 75 | 76 | pub fn load(&self, addr: usize, arg: &MemoryArg) -> Result { 77 | // TODO: check align and memory size 78 | let at = addr + arg.offset as usize; 79 | Numeric::read(&self.data, at) 80 | } 81 | 82 | pub fn write(&mut self, addr: usize, arg: &MemoryArg, value: T) -> Result<()> { 83 | // TODO: check align and memory size 84 | let at = addr + arg.offset as usize; 85 | Numeric::write(&mut self.data, at, value) 86 | } 87 | 88 | pub fn write_bytes(&mut self, addr: usize, bytes: &[u8]) -> Result<()> { 89 | let slice = &mut self.data[addr..addr + bytes.len()]; 90 | slice.copy_from_slice(bytes); 91 | Ok(()) 92 | } 93 | } 94 | 95 | pub type GlobalInst = Rc>; 96 | 97 | #[derive(Debug, Clone)] 98 | pub struct InternalGlobalInst { 99 | pub value: Value, 100 | pub mutability: bool, 101 | } 102 | 103 | #[derive(Debug, Clone)] 104 | pub struct ExportInst { 105 | pub name: String, 106 | pub desc: ExternalVal, 107 | } 108 | 109 | #[derive(Debug, Default, Clone)] 110 | pub struct ModuleInst { 111 | pub func_types: Vec, 112 | pub exports: HashMap, 113 | } 114 | 115 | impl ModuleInst { 116 | // https://www.w3.org/TR/wasm-core-1/#modules%E2%91%A6 117 | pub fn allocate(module: &Module) -> Self { 118 | // func types 119 | let mut func_types = vec![]; 120 | if let Some(ref section) = module.type_section { 121 | for ty in section { 122 | let func_type = FuncType { 123 | params: ty.params.clone(), 124 | results: ty.results.clone(), 125 | }; 126 | func_types.push(func_type); 127 | } 128 | }; 129 | 130 | // exports 131 | let mut exports = HashMap::default(); 132 | if let Some(ref sections) = module.export_section { 133 | for export in sections { 134 | let desc = match export.desc { 135 | crate::binary::types::ExportDesc::Func(idx) => ExternalVal::Func(idx), 136 | crate::binary::types::ExportDesc::Table(idx) => ExternalVal::Table(idx), 137 | crate::binary::types::ExportDesc::Memory(idx) => ExternalVal::Memory(idx), 138 | crate::binary::types::ExportDesc::Global(idx) => ExternalVal::Global(idx), 139 | }; 140 | let name = export.name.clone(); 141 | let export_inst = ExportInst { 142 | name: name.clone(), 143 | desc, 144 | }; 145 | exports.insert(name, export_inst); 146 | } 147 | }; 148 | 149 | ModuleInst { 150 | func_types, 151 | exports, 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/execution/op.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | module::{ExternalFuncInst, InternalFuncInst}, 3 | store::Store, 4 | value::{Frame, Label, LabelKind, StackAccess, Value}, 5 | }; 6 | use crate::{ 7 | binary::{instruction::Instruction, types::ValueType}, 8 | execution::error::Error, 9 | impl_binary_operation, impl_cvtop_operation, impl_unary_operation, 10 | }; 11 | use anyhow::{bail, Context as _, Result}; 12 | use log::trace; 13 | use std::{cell::RefCell, rc::Rc}; 14 | 15 | pub fn local_get(locals: &[Value], stack: &mut impl StackAccess, idx: usize) -> Result<()> { 16 | let value = locals 17 | .get(idx) 18 | .with_context(|| Error::NotFoundLocalVariable(idx))?; 19 | stack.push(value.clone()); 20 | Ok(()) 21 | } 22 | 23 | pub fn local_set(locals: &mut [Value], stack: &mut impl StackAccess, idx: usize) -> Result<()> { 24 | let value: Value = stack.pop1()?; 25 | locals[idx] = value; 26 | Ok(()) 27 | } 28 | 29 | pub fn local_tee(locals: &mut [Value], stack: &mut impl StackAccess, idx: usize) -> Result<()> { 30 | let value: Value = stack.pop1()?; 31 | stack.push(value.clone()); 32 | stack.push(value); 33 | local_set(locals, stack, idx)?; 34 | Ok(()) 35 | } 36 | 37 | pub fn global_set(store: &mut Store, stack: &mut impl StackAccess, idx: usize) -> Result<()> { 38 | let value: Value = stack.pop1().with_context(|| Error::StackPopError)?; 39 | let mut global = store 40 | .globals 41 | .get(idx) 42 | .with_context(|| Error::NotFoundGlobalVariable(idx))? 43 | .borrow_mut(); 44 | global.value = value; 45 | Ok(()) 46 | } 47 | 48 | pub fn global_get(store: &mut Store, stack: &mut impl StackAccess, idx: usize) -> Result<()> { 49 | let global = store 50 | .globals 51 | .get(idx) 52 | .with_context(|| Error::NotFoundGlobalVariable(idx))?; 53 | stack.push(global.borrow().value.clone()); 54 | Ok(()) 55 | } 56 | 57 | pub fn popcnt(stack: &mut impl StackAccess) -> Result<()> { 58 | let value = stack.pop1().with_context(|| Error::StackPopError)?; 59 | 60 | match value { 61 | Value::I32(v) => { 62 | stack.push(v.count_ones() as i32); 63 | } 64 | Value::I64(v) => { 65 | stack.push(v.count_ones() as i64); 66 | } 67 | _ => bail!(Error::UnexpectedStackValueType(value)), 68 | } 69 | Ok(()) 70 | } 71 | 72 | pub fn i64extend_32s(stack: &mut impl StackAccess) -> Result<()> { 73 | let value = stack.pop1()?; 74 | match value { 75 | Value::I64(v) => { 76 | let result = v << 32 >> 32; 77 | let value: Value = result.into(); 78 | stack.push(value); 79 | } 80 | _ => bail!(Error::UnexpectedStackValueType(value)), 81 | } 82 | Ok(()) 83 | } 84 | 85 | pub fn get_end_address(insts: &[Instruction], pc: isize) -> Result { 86 | let mut pc = pc as usize; 87 | let mut depth = 0; 88 | loop { 89 | pc += 1; 90 | let inst = insts 91 | .get(pc) 92 | .with_context(|| Error::NotFoundInstruction(pc))?; 93 | match inst { 94 | Instruction::If(_) | Instruction::Block(_) | Instruction::Loop(_) => depth += 1, 95 | Instruction::End => { 96 | if depth == 0 { 97 | return Ok(pc); 98 | } else { 99 | depth -= 1; 100 | } 101 | } 102 | _ => { 103 | // do nothing 104 | } 105 | } 106 | } 107 | } 108 | 109 | pub fn get_else_or_end_address(insts: &[Instruction], pc: isize) -> Result { 110 | let mut pc = pc as usize; 111 | let mut depth = 0; 112 | loop { 113 | pc += 1; 114 | let inst = insts 115 | .get(pc) 116 | .with_context(|| Error::NotFoundInstruction(pc))?; 117 | match inst { 118 | Instruction::If(_) => { 119 | depth += 1; 120 | } 121 | Instruction::Else => { 122 | if depth == 0 { 123 | return Ok(pc); 124 | } 125 | } 126 | Instruction::End => { 127 | if depth == 0 { 128 | return Ok(pc); 129 | } else { 130 | depth -= 1; 131 | } 132 | } 133 | _ => { 134 | // do nothing 135 | } 136 | } 137 | } 138 | } 139 | 140 | pub fn push_frame(stack: &mut Vec, call_stack: &mut Vec, func: &InternalFuncInst) { 141 | let arity = func.func_type.results.len(); 142 | let bottom = stack.len() - func.func_type.params.len(); 143 | let mut locals = stack.split_off(bottom); 144 | 145 | for local in func.code.locals.iter() { 146 | match local { 147 | ValueType::I32 => locals.push(Value::I32(0)), 148 | ValueType::I64 => locals.push(Value::I64(0)), 149 | ValueType::F32 => locals.push(Value::F32(0.0)), 150 | ValueType::F64 => locals.push(Value::F64(0.0)), 151 | } 152 | } 153 | 154 | let sp = stack.len(); 155 | let frame = Frame { 156 | pc: -1, 157 | sp, 158 | insts: func.code.body.clone(), 159 | arity, 160 | locals, 161 | labels: vec![], 162 | }; 163 | trace!("call internal function: {:?}", &frame); 164 | call_stack.push(frame); 165 | } 166 | 167 | pub fn stack_unwind(stack: &mut Vec, sp: usize, arity: usize) -> Result<()> { 168 | if arity > 0 { 169 | let value = stack.pop().with_context(|| Error::StackPopError)?; 170 | stack.drain(sp..); 171 | stack.push(value); 172 | } else { 173 | stack.drain(sp..); 174 | } 175 | Ok(()) 176 | } 177 | 178 | pub fn br(labels: &mut Vec