├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── example-output ├── assemblyscript.ts ├── cpp.h ├── markdown.md ├── overview.txt ├── rust.rs └── zig.zig ├── logo.png ├── src ├── assemblyscript │ ├── common.rs │ ├── function.rs │ ├── header.rs │ ├── mod.rs │ ├── struct.rs │ ├── tuple.rs │ └── union.rs ├── astype.rs ├── cpp │ ├── common.rs │ ├── function.rs │ ├── header.rs │ ├── mod.rs │ ├── struct.rs │ ├── tuple.rs │ └── union.rs ├── doc │ ├── common.rs │ ├── function.rs │ ├── mod.rs │ ├── struct.rs │ ├── tuple.rs │ └── union.rs ├── error.rs ├── lib.rs ├── main.rs ├── overview │ ├── common.rs │ ├── function.rs │ ├── mod.rs │ ├── struct.rs │ ├── tuple.rs │ └── union.rs ├── pretty_writer.rs ├── rust │ ├── common.rs │ ├── function.rs │ ├── header.rs │ ├── mod.rs │ ├── struct.rs │ ├── tuple.rs │ └── union.rs └── zig │ ├── common.rs │ ├── function.rs │ ├── header.rs │ ├── mod.rs │ ├── struct.rs │ ├── tuple.rs │ └── union.rs └── tests ├── integration.rs ├── test_module.witx ├── wasi_ephemeral_crypto_common.witx ├── wasi_ephemeral_crypto_symmetric.witx └── wasi_experimental_http.witx /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Build 16 | run: cargo build --verbose 17 | - name: Run unit tests 18 | run: cargo test --verbose 19 | - name: Run functional tests 20 | run: | 21 | cargo run -- --output-type=overview tests/*.witx > /dev/null 22 | cargo run -- --output-type=markdown tests/*.witx > /dev/null 23 | cargo run -- --output-type=assemblyscript tests/*.witx > /dev/null 24 | cargo run -- --output-type=zig tests/*.witx > /dev/null 25 | cargo run -- --output-type=rust tests/*.witx > /dev/null 26 | - name: Check that crates version works 27 | run: cargo install witx-docgen --debug 28 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Get the version 14 | id: get_version 15 | run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} 16 | 17 | - uses: actions/checkout@master 18 | 19 | - uses: hecrj/setup-rust-action@master 20 | with: 21 | rust-version: stable 22 | 23 | - name: Check Cargo availability 24 | run: cargo --version 25 | 26 | - name: Check Rustup default toolchain 27 | run: rustup default | grep stable 28 | 29 | - name: Install cargo-deb 30 | run: cargo install --debug cargo-deb 31 | 32 | - name: Build 33 | run: | 34 | env RUSTFLAGS="-C link-arg=-s" cargo build --release 35 | mkdir witx-codegen 36 | mv target/release/witx-codegen witx-codegen/ 37 | cp README.md witx-codegen/ 38 | tar cJpf witx-codegen_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2 witx-codegen 39 | - name: Debian package 40 | run: | 41 | cargo deb 42 | 43 | - name: Create release 44 | id: create_release 45 | uses: actions/create-release@v1 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | with: 49 | tag_name: ${{ github.ref }} 50 | release_name: Release ${{ github.ref }} 51 | draft: true 52 | prerelease: false 53 | 54 | - name: Upload Debian package 55 | id: upload-release-asset-debian 56 | uses: actions/upload-release-asset@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | upload_url: ${{ steps.create_release.outputs.upload_url }} 61 | asset_name: "witx-codegen_${{ steps.get_version.outputs.VERSION }}_amd64.deb" 62 | asset_path: "target/debian/witx-codegen_${{ steps.get_version.outputs.VERSION }}_amd64.deb" 63 | asset_content_type: application/x-debian-package 64 | 65 | - name: Upload tarball 66 | id: upload-release-asset-tarball 67 | uses: actions/upload-release-asset@v1 68 | env: 69 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 70 | with: 71 | upload_url: ${{ steps.create_release.outputs.upload_url }} 72 | asset_name: "witx-codegen_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2" 73 | asset_path: "witx-codegen_${{ steps.get_version.outputs.VERSION }}_linux-x86_64.tar.bz2" 74 | asset_content_type: application/x-tar 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | /target 3 | Cargo.lock 4 | zig-cache 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "witx-codegen" 3 | version = "0.11.3" 4 | description = "WITX code generator for WebAssembly guest modules." 5 | authors = ["Frank Denis "] 6 | edition = "2018" 7 | license = "MIT" 8 | repository = "https://github.com/jedisct1/witx-codegen" 9 | homepage = "https://github.com/jedisct1/witx-codegen" 10 | categories = ["wasm", "api-bindings", "web-programming"] 11 | keywords = ["witx", "webassembly", "wasm", "generator", "bindgen"] 12 | readme = "README.md" 13 | 14 | [dependencies] 15 | anyhow = "1.0.82" 16 | convert_case = "0.6.0" 17 | structopt = "0.3.26" 18 | strum = "0.26.2" 19 | strum_macros = "0.26.2" 20 | witx = { package = "witnext", version = "0.10.0-beta3" } 21 | 22 | [package.metadata.deb] 23 | extended-description = """\ 24 | A code generator for WITX (WebAssembly interface description language for WASI). 25 | 26 | WITX-CodeGen generates types and function prototypes for various languages targeting 27 | WebAssembly, from a common WITX interface description. 28 | """ 29 | assets = [ 30 | ["target/release/witx-codegen", "usr/bin/", "755"], 31 | ["README.md", "usr/share/doc/witx-codegen/README.md", "644"] 32 | ] 33 | section = "development" 34 | depends = "$auto" 35 | priority = "optional" 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2024 Frank Denis 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![WITX code generator](logo.png) 2 | ================================ 3 | 4 | ![CI status](https://github.com/jedisct1/witx-codegen/actions/workflows/ci.yml/badge.svg) 5 | [![crates.io](https://img.shields.io/crates/v/witx-codegen.svg)](https://crates.io/crates/witx-codegen) 6 | 7 | # WITX-CodeGen: A WITX code and documentation generator 8 | 9 | WITX is a way to describe types and function interfaces for WebAssembly modules. 10 | 11 | From this, code generators can produce code to access data, call or implement functions from different languages using the same layout and calling conventions. 12 | 13 | WITX-CodeGen doesn't do transformations when functions are called. Instead, it exposes types that have the same layout in all languages, like a zero-copy serialization format. Data can thus be easily shared between guests and hosts without any overhead. 14 | 15 | The generated code is compatible with the WebAssembly standard APIs ([WASI](https://wasi.dev)). 16 | 17 | This tool uses the next (as on June 9th, 2021) revision of the format definition, that will eventually be required for interface types. 18 | 19 | `witx-codegen` is currently written in Rust, but it is totally language-agnostic. It is also compatible with all WebAssembly runtimes. The generated code is optimized for simplicity and readability. 20 | 21 | The tool can also produce different documentation formats. 22 | 23 | `witx-codegen` supersedes `as-witx`, `zig-witx`, `witx-docgen`, `witx-overview-docgen` and `witx-generate-raw`. 24 | 25 | ## Installation 26 | 27 | * Via `cargo`: 28 | 29 | ```sh 30 | cargo install witx-codegen 31 | ``` 32 | 33 | * Precompiled binaries: tarballs and Debian/Ubuntu packages are available [here](https://github.com/jedisct1/witx-codegen/releases/latest). 34 | 35 | ## Usage 36 | 37 | ```text 38 | WITX code generator for WebAssembly guest modules 39 | 40 | USAGE: 41 | witx-codegen [FLAGS] [OPTIONS] ... 42 | 43 | FLAGS: 44 | -h, --help Prints help information 45 | -H, --skip-header Do not generate a header 46 | -I, --skip-imports Ignores imported types and functions 47 | -V, --version Prints version information 48 | 49 | OPTIONS: 50 | -m, --module-name 51 | Set the module name to use instead of reading it from the witx file 52 | 53 | -o, --output Output file, or - for the standard output 54 | -t, --output-type 55 | Output type. One in: {assemblyscript, zig, rust, overview, markdown} 56 | [default: assemblyscript] 57 | 58 | ARGS: 59 | ... WITX files 60 | ``` 61 | 62 | ## Backends 63 | 64 | * [X] Markdown documentation ([example](https://github.com/jedisct1/witx-codegen/blob/master/example-output/markdown.md)) 65 | * [X] API Overview ([example](https://github.com/jedisct1/witx-codegen/blob/master/example-output/overview.txt)) 66 | * [X] AssemblyScript ([example](https://github.com/jedisct1/witx-codegen/blob/master/example-output/assemblyscript.ts)) 67 | * [X] Zig ([example](https://github.com/jedisct1/witx-codegen/blob/master/example-output/zig.zig)) 68 | * [X] Rust ([example](https://github.com/jedisct1/witx-codegen/blob/master/example-output/rust.rs)) 69 | * [X] C++ ([example](https://github.com/jedisct1/witx-codegen/blob/master/example-output/cpp.h)) - Experimental 70 | * [ ] TinyGo 71 | * [ ] Swift 72 | * [ ] HTML documentation 73 | 74 | Support for additional languages is more than welcome! 75 | 76 | ## Example inputs 77 | 78 | See the [`tests`](https://github.com/jedisct1/witx-codegen/tree/master/tests) folder for examples of WITX input files. 79 | 80 | Other input formats may also be eventually supported, as well as extensions to produce more structured documentation. 81 | 82 | ## WITX format 83 | 84 | ### Basic types 85 | 86 | `bool`, `char`, `u8`, `u16`, `u32`, `u64`, `s8`, `s16`, `s32`, `s64` 87 | 88 | ### Other types 89 | 90 | * `string`: a read-only string. 91 | * `(in-buffer u8)`: a read-only buffer whose elements are of type `u8`. 92 | * `(out-buffer u8)`: a buffer whose elements are of type `u8`. 93 | * `(@witx const_pointer u8)`: a read-only `u8` pointer. 94 | * `(@witx pointer u8)`: a `u8` pointer. 95 | * `(@witx usize)`: an object size. 96 | 97 | ### Type aliases 98 | 99 | * `(typename $status_code u16)` 100 | * `(typename $size (@witx usize))` 101 | 102 | Note that returned values from function must all be aliases, not raw types. 103 | 104 | ### Handles 105 | 106 | Handles are opaque references to objects managed by the host. 107 | 108 | In order to use handles, a "resource" has to be declared: 109 | 110 | ``` 111 | (resource $http_handle) 112 | ``` 113 | 114 | A "resource" represent a group of handles. The same resource can be shared by all handle types from the same module. 115 | 116 | Each handle type can then be declared as aliases: 117 | 118 | ``` 119 | (typename $query (handle $http_handle)) 120 | (typename $response_handle (handle $http_handle)) 121 | ``` 122 | 123 | ### Constants 124 | 125 | ``` 126 | (typename $big_int u64) 127 | (@witx const $big_int $zero 0) 128 | (@witx const $big_int $a_hundred 100) 129 | (@witx const $big_int $a_big_value 0xff00000000000000) 130 | (@witx const $big_int $a_bigger_value 0xffffffffffffffff) 131 | ``` 132 | 133 | ### Structures 134 | 135 | ``` 136 | (typename $example_structure 137 | (record 138 | (field $first_member bool) 139 | (field $second_member u8) 140 | (field $third_member string) 141 | ) 142 | ) 143 | ``` 144 | 145 | Structures that only contain booleans are encoded as bit sets. 146 | 147 | ### Tuples 148 | 149 | ``` 150 | (typename $test_tuple (tuple $test_bool $test_medium_int $big_int)) 151 | ``` 152 | 153 | ### Tagged unions 154 | 155 | ``` 156 | (typename $test_tagged_union 157 | (variant (@witx tag u16) 158 | (case $first_choice u8) 159 | (case $second_choice string) 160 | (case $third_choice f32) 161 | (case $empty_choice) 162 | ) 163 | ) 164 | ``` 165 | 166 | This defines a union with a tag representing the active member. The example above generates a structure equivalent to: 167 | 168 | ```zig 169 | struct { 170 | tag: u16, 171 | member: union { 172 | first_choice: u8, 173 | second_choice: string, 174 | third_choice: f32, 175 | empty_choice: (), 176 | } 177 | } 178 | ``` 179 | 180 | ### Imports 181 | 182 | Import some aliases, or all of them, from `common.witx`: 183 | 184 | ``` 185 | (use $some_type, $some_other_type from $common) 186 | (use * from $common) 187 | ``` 188 | 189 | ### Modules 190 | 191 | Only one module can be present in a file, whose name must match the module name. A module is defined as follows: 192 | 193 | ``` 194 | (module $module_name 195 | ... 196 | ) 197 | ``` 198 | 199 | It contains everything: types, handles, functions and imports. 200 | 201 | ### Functions 202 | 203 | ``` 204 | (@interface func (export "symmetric_key_generate") 205 | (param $algorithm string) 206 | (param $options $opt_options) 207 | (result $error (expected $symmetric_key (error $crypto_errno))) 208 | ) 209 | ``` 210 | 211 | This declares a `symmetric_key_generate` function, with two input parameters (`algorithm` and `options` of type `string` and `opt_options`). 212 | 213 | The function returns an error code of type `$crypto_errno`. Or, if no error occurred, the function returns a value of type `$symmetric_key`. 214 | 215 | In Rust, an equivalent function would be: 216 | 217 | ```rust 218 | fn symmetric_key_generate(algorithm: &str, options: OptOptions) 219 | -> Result; 220 | ``` 221 | 222 | Returning multiple values: 223 | 224 | ``` 225 | (@interface func (export "symmetric_key_id") 226 | (param $key $symmetric_key) 227 | (param $key_id (@witx pointer u8)) 228 | (param $key_id_max_len $size) 229 | (result $error (expected (tuple $size $version) (error $crypto_errno))) 230 | ) 231 | ``` 232 | 233 | The function returns either an error, or two values, of type `$size` and `$version`. 234 | 235 | The above example is eauivalent to a declaration like that one in Rust: 236 | 237 | ```rust 238 | fn symmetric_key_id(key: SymmetricKey, key_id: *mut u8, key_id_max_len: usize) 239 | -> Result<(Size, Version), CryptoErrno>; 240 | ``` 241 | -------------------------------------------------------------------------------- /example-output/overview.txt: -------------------------------------------------------------------------------- 1 | * API overview * 2 | 3 | 4 | ---------------------- Module: [wasi_ephemeral_crypto_symmetric] ---------------------- 5 | 6 | enum crypto_errno: (tag: u16) 7 | - `success`: crypto_errno 8 | - `guest_error`: crypto_errno 9 | - `not_implemented`: crypto_errno 10 | - `unsupported_feature`: crypto_errno 11 | - `prohibited_operation`: crypto_errno 12 | - `unsupported_encoding`: crypto_errno 13 | - `unsupported_algorithm`: crypto_errno 14 | - `unsupported_option`: crypto_errno 15 | - `invalid_key`: crypto_errno 16 | - `invalid_length`: crypto_errno 17 | - `verification_failed`: crypto_errno 18 | - `rng_error`: crypto_errno 19 | - `algorithm_failure`: crypto_errno 20 | - `invalid_signature`: crypto_errno 21 | - `closed`: crypto_errno 22 | - `invalid_handle`: crypto_errno 23 | - `overflow`: crypto_errno 24 | - `internal_error`: crypto_errno 25 | - `too_many_handles`: crypto_errno 26 | - `key_not_supported`: crypto_errno 27 | - `key_required`: crypto_errno 28 | - `invalid_tag`: crypto_errno 29 | - `invalid_operation`: crypto_errno 30 | - `nonce_required`: crypto_errno 31 | - `invalid_nonce`: crypto_errno 32 | - `option_not_set`: crypto_errno 33 | - `not_found`: crypto_errno 34 | - `parameters_missing`: crypto_errno 35 | - `in_progress`: crypto_errno 36 | - `incompatible_keys`: crypto_errno 37 | - `expired`: crypto_errno 38 | 39 | enum keypair_encoding: (tag: u16) 40 | - `raw`: keypair_encoding 41 | - `pkcs8`: keypair_encoding 42 | - `pem`: keypair_encoding 43 | - `local`: keypair_encoding 44 | 45 | enum publickey_encoding: (tag: u16) 46 | - `raw`: publickey_encoding 47 | - `pkcs8`: publickey_encoding 48 | - `pem`: publickey_encoding 49 | - `sec`: publickey_encoding 50 | - `compressed_sec`: publickey_encoding 51 | - `local`: publickey_encoding 52 | 53 | enum secretkey_encoding: (tag: u16) 54 | - `raw`: secretkey_encoding 55 | - `pkcs8`: secretkey_encoding 56 | - `pem`: secretkey_encoding 57 | - `sec`: secretkey_encoding 58 | - `compressed_sec`: secretkey_encoding 59 | - `local`: secretkey_encoding 60 | 61 | enum signature_encoding: (tag: u16) 62 | - `raw`: signature_encoding 63 | - `der`: signature_encoding 64 | 65 | enum algorithm_type: (tag: u16) 66 | - `signatures`: algorithm_type 67 | - `symmetric`: algorithm_type 68 | - `key_exchange`: algorithm_type 69 | 70 | alias version = u64 71 | 72 | alias size = usize 73 | 74 | alias timestamp = u64 75 | 76 | alias u64 = u64 77 | 78 | alias array_output = handle 79 | 80 | alias options = handle 81 | 82 | alias secrets_manager = handle 83 | 84 | alias keypair = handle 85 | 86 | alias signature_state = handle 87 | 88 | alias signature = handle 89 | 90 | alias publickey = handle 91 | 92 | alias secretkey = handle 93 | 94 | alias signature_verification_state = handle 95 | 96 | alias symmetric_state = handle 97 | 98 | alias symmetric_key = handle 99 | 100 | alias symmetric_tag = handle 101 | 102 | enum opt_options_u: (tag: u8) 103 | - `some`: opt_options_u 104 | - `none`: opt_options_u 105 | 106 | union opt_options: (tag: u8) 107 | - `some`: options 108 | - `none`: (empty) 109 | 110 | enum opt_symmetric_key_u: (tag: u8) 111 | - `some`: opt_symmetric_key_u 112 | - `none`: opt_symmetric_key_u 113 | 114 | union opt_symmetric_key: (tag: u8) 115 | - `some`: symmetric_key 116 | - `none`: (empty) 117 | 118 | function symmetric_key_generate(): crypto_errno 119 | - Input: 120 | - `algorithm`: string 121 | - `options`: opt_options 122 | - Output: 123 | - mut_ptr 124 | 125 | function symmetric_key_import(): crypto_errno 126 | - Input: 127 | - `algorithm`: string 128 | - `raw`: ptr 129 | - `raw_len`: size 130 | - Output: 131 | - mut_ptr 132 | 133 | function symmetric_key_export(): crypto_errno 134 | - Input: 135 | - `symmetric_key`: symmetric_key 136 | - Output: 137 | - mut_ptr 138 | 139 | function symmetric_key_close(): crypto_errno 140 | - Input: 141 | - `symmetric_key`: symmetric_key 142 | - No output 143 | 144 | function symmetric_key_generate_managed(): crypto_errno 145 | - Input: 146 | - `secrets_manager`: secrets_manager 147 | - `algorithm`: string 148 | - `options`: opt_options 149 | - Output: 150 | - mut_ptr 151 | 152 | function symmetric_key_store_managed(): crypto_errno 153 | - Input: 154 | - `secrets_manager`: secrets_manager 155 | - `symmetric_key`: symmetric_key 156 | - `symmetric_key_id`: mut_ptr 157 | - `symmetric_key_id_max_len`: size 158 | - No output 159 | 160 | function symmetric_key_replace_managed(): crypto_errno 161 | - Input: 162 | - `secrets_manager`: secrets_manager 163 | - `symmetric_key_old`: symmetric_key 164 | - `symmetric_key_new`: symmetric_key 165 | - Output: 166 | - mut_ptr 167 | 168 | function symmetric_key_id(): crypto_errno 169 | - Input: 170 | - `symmetric_key`: symmetric_key 171 | - `symmetric_key_id`: mut_ptr 172 | - `symmetric_key_id_max_len`: size 173 | - Output: 174 | - mut_ptr 175 | - mut_ptr 176 | 177 | function symmetric_key_from_id(): crypto_errno 178 | - Input: 179 | - `secrets_manager`: secrets_manager 180 | - `symmetric_key_id`: ptr 181 | - `symmetric_key_id_len`: size 182 | - `symmetric_key_version`: version 183 | - Output: 184 | - mut_ptr 185 | 186 | function symmetric_state_open(): crypto_errno 187 | - Input: 188 | - `algorithm`: string 189 | - `key`: opt_symmetric_key 190 | - `options`: opt_options 191 | - Output: 192 | - mut_ptr 193 | 194 | function symmetric_state_options_get(): crypto_errno 195 | - Input: 196 | - `handle`: symmetric_state 197 | - `name`: string 198 | - `value`: mut_ptr 199 | - `value_max_len`: size 200 | - Output: 201 | - mut_ptr 202 | 203 | function symmetric_state_options_get_u64(): crypto_errno 204 | - Input: 205 | - `handle`: symmetric_state 206 | - `name`: string 207 | - Output: 208 | - mut_ptr 209 | 210 | function symmetric_state_close(): crypto_errno 211 | - Input: 212 | - `handle`: symmetric_state 213 | - No output 214 | 215 | function symmetric_state_absorb(): crypto_errno 216 | - Input: 217 | - `handle`: symmetric_state 218 | - `data`: ptr 219 | - `data_len`: size 220 | - No output 221 | 222 | function symmetric_state_squeeze(): crypto_errno 223 | - Input: 224 | - `handle`: symmetric_state 225 | - `out`: mut_ptr 226 | - `out_len`: size 227 | - No output 228 | 229 | function symmetric_state_squeeze_tag(): crypto_errno 230 | - Input: 231 | - `handle`: symmetric_state 232 | - Output: 233 | - mut_ptr 234 | 235 | function symmetric_state_squeeze_key(): crypto_errno 236 | - Input: 237 | - `handle`: symmetric_state 238 | - `alg_str`: string 239 | - Output: 240 | - mut_ptr 241 | 242 | function symmetric_state_max_tag_len(): crypto_errno 243 | - Input: 244 | - `handle`: symmetric_state 245 | - Output: 246 | - mut_ptr 247 | 248 | function symmetric_state_encrypt(): crypto_errno 249 | - Input: 250 | - `handle`: symmetric_state 251 | - `out`: mut_ptr 252 | - `out_len`: size 253 | - `data`: ptr 254 | - `data_len`: size 255 | - Output: 256 | - mut_ptr 257 | 258 | function symmetric_state_encrypt_detached(): crypto_errno 259 | - Input: 260 | - `handle`: symmetric_state 261 | - `out`: mut_ptr 262 | - `out_len`: size 263 | - `data`: ptr 264 | - `data_len`: size 265 | - Output: 266 | - mut_ptr 267 | 268 | function symmetric_state_decrypt(): crypto_errno 269 | - Input: 270 | - `handle`: symmetric_state 271 | - `out`: mut_ptr 272 | - `out_len`: size 273 | - `data`: ptr 274 | - `data_len`: size 275 | - Output: 276 | - mut_ptr 277 | 278 | function symmetric_state_decrypt_detached(): crypto_errno 279 | - Input: 280 | - `handle`: symmetric_state 281 | - `out`: mut_ptr 282 | - `out_len`: size 283 | - `data`: ptr 284 | - `data_len`: size 285 | - `raw_tag`: ptr 286 | - `raw_tag_len`: size 287 | - Output: 288 | - mut_ptr 289 | 290 | function symmetric_state_ratchet(): crypto_errno 291 | - Input: 292 | - `handle`: symmetric_state 293 | - No output 294 | 295 | function symmetric_tag_len(): crypto_errno 296 | - Input: 297 | - `symmetric_tag`: symmetric_tag 298 | - Output: 299 | - mut_ptr 300 | 301 | function symmetric_tag_pull(): crypto_errno 302 | - Input: 303 | - `symmetric_tag`: symmetric_tag 304 | - `buf`: mut_ptr 305 | - `buf_len`: size 306 | - Output: 307 | - mut_ptr 308 | 309 | function symmetric_tag_verify(): crypto_errno 310 | - Input: 311 | - `symmetric_tag`: symmetric_tag 312 | - `expected_raw_tag_ptr`: ptr 313 | - `expected_raw_tag_len`: size 314 | - No output 315 | 316 | function symmetric_tag_close(): crypto_errno 317 | - Input: 318 | - `symmetric_tag`: symmetric_tag 319 | - No output 320 | 321 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jedisct1/witx-codegen/b7f42105ba274637a1a55fcac4f86315084eb7b5/logo.png -------------------------------------------------------------------------------- /src/assemblyscript/common.rs: -------------------------------------------------------------------------------- 1 | use convert_case::{Case, Casing}; 2 | 3 | use super::tuple::Tuple; 4 | use crate::astype::*; 5 | 6 | pub trait IsNullable { 7 | fn is_nullable(&self) -> bool; 8 | } 9 | 10 | impl IsNullable for ASType { 11 | fn is_nullable(&self) -> bool { 12 | matches!( 13 | self, 14 | ASType::ConstPtr(_) 15 | | ASType::MutPtr(_) 16 | | ASType::ReadBuffer(_) 17 | | ASType::WriteBuffer(_) 18 | | ASType::Enum(_) 19 | | ASType::Struct(_) 20 | | ASType::Tuple(_) 21 | | ASType::Union(_) 22 | ) 23 | } 24 | } 25 | 26 | pub trait Normalize { 27 | fn as_str(&self) -> &str; 28 | 29 | fn as_type(&self) -> String { 30 | self.as_str().to_case(Case::Pascal) 31 | } 32 | 33 | fn as_fn(&self) -> String { 34 | self.as_str().to_case(Case::Camel) 35 | } 36 | 37 | fn as_fn_suffix(&self) -> String { 38 | self.as_str().to_case(Case::UpperCamel) 39 | } 40 | 41 | fn as_var(&self) -> String { 42 | escape_reserved_word(&self.as_str().to_case(Case::Snake)) 43 | } 44 | 45 | fn as_const(&self) -> String { 46 | self.as_str().to_case(Case::UpperSnake) 47 | } 48 | 49 | fn as_namespace(&self) -> String { 50 | self.as_str().to_case(Case::Pascal) 51 | } 52 | } 53 | 54 | impl> Normalize for T { 55 | fn as_str(&self) -> &str { 56 | self.as_ref() 57 | } 58 | } 59 | 60 | pub trait ToLanguageRepresentation { 61 | fn as_astype(&self) -> &ASType; 62 | 63 | fn to_string(&self) -> String { 64 | self.as_lang() 65 | } 66 | 67 | fn as_lang(&self) -> String { 68 | match self.as_astype() { 69 | ASType::Alias(alias) => alias.name.as_type(), 70 | ASType::Bool => "bool".to_string(), 71 | ASType::Char32 => "Char32".to_string(), 72 | ASType::Char8 => "Char8".to_string(), 73 | ASType::F32 => "f32".to_string(), 74 | ASType::F64 => "f64".to_string(), 75 | ASType::Handle(_resource_name) => "WasiHandle".to_string(), 76 | ASType::ConstPtr(pointee) => format!("WasiPtr<{}>", pointee.to_string()), 77 | ASType::MutPtr(pointee) => format!("WasiMutPtr<{}>", pointee.to_string()), 78 | ASType::Option(_) => todo!(), 79 | ASType::Result(_) => todo!(), 80 | ASType::S8 => "i8".to_string(), 81 | ASType::S16 => "i16".to_string(), 82 | ASType::S32 => "i32".to_string(), 83 | ASType::S64 => "i64".to_string(), 84 | ASType::U8 => "u8".to_string(), 85 | ASType::U16 => "u16".to_string(), 86 | ASType::U32 => "u32".to_string(), 87 | ASType::U64 => "u64".to_string(), 88 | ASType::USize => "usize".to_string(), 89 | ASType::Void => "void".to_string(), 90 | ASType::Constants(_) => unimplemented!(), 91 | ASType::Enum(enum_) => { 92 | format!("{} /* Enum */", enum_.repr.as_ref().as_lang()) 93 | } 94 | ASType::Struct(_) => unimplemented!(), 95 | ASType::Tuple(tuple_members) => Tuple::name_for(tuple_members).as_type(), 96 | ASType::Union(_) => unimplemented!(), 97 | ASType::Slice(element_type) => format!("WasiMutSlice<{}>", element_type.as_lang()), 98 | ASType::String(_) => "WasiString".to_string(), 99 | ASType::ReadBuffer(element_type) => format!("WasiSlice<{}>", element_type.as_lang()), 100 | ASType::WriteBuffer(element_type) => { 101 | format!("WasiMutSlice<{}>", element_type.to_string()) 102 | } 103 | } 104 | } 105 | } 106 | 107 | impl ToLanguageRepresentation for ASType { 108 | fn as_astype(&self) -> &ASType { 109 | self 110 | } 111 | } 112 | 113 | pub fn escape_reserved_word(word: &str) -> String { 114 | if RESERVED.iter().any(|k| *k == word) { 115 | // If the camel-cased string matched any strict or reserved keywords, then 116 | // append a trailing underscore to the identifier we generate. 117 | format!("{}_", word) 118 | } else { 119 | word.to_string() // Otherwise, use the string as is. 120 | } 121 | } 122 | 123 | /// Reserved Keywords. 124 | /// 125 | /// Source: [ECMAScript 2022 Language Specification](https://tc39.es/ecma262/#sec-keywords-and-reserved-words) 126 | const RESERVED: &[&str] = &[ 127 | "await", 128 | "break", 129 | "case", 130 | "catch", 131 | "class", 132 | "const", 133 | "continue", 134 | "debugger", 135 | "default", 136 | "delete", 137 | "do", 138 | "else", 139 | "enum", 140 | "export", 141 | "extends", 142 | "false", 143 | "finally", 144 | "for", 145 | "function", 146 | "if", 147 | "import", 148 | "in", 149 | "instanceof", 150 | "new", 151 | "null", 152 | "return", 153 | "super", 154 | "switch", 155 | "this", 156 | "throw", 157 | "true", 158 | "try", 159 | "typeof", 160 | "var", 161 | "void", 162 | "while", 163 | "with", 164 | "yield", 165 | "let", 166 | "static", 167 | "implements", 168 | "interface", 169 | "package", 170 | "private", 171 | "protected", 172 | "and", 173 | "public", 174 | ]; 175 | -------------------------------------------------------------------------------- /src/assemblyscript/function.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl AssemblyScriptGenerator { 6 | pub fn define_func( 7 | w: &mut PrettyWriter, 8 | module_name: &str, 9 | func_witx: &witx::Function, 10 | ) -> Result<(), Error> { 11 | assert_eq!(func_witx.abi, witx::Abi::Preview1); 12 | let name = func_witx.name.as_str().to_string(); 13 | let params_witx = &func_witx.params; 14 | let mut params = vec![]; 15 | for param_witx in params_witx { 16 | let param_name = param_witx.name.as_str(); 17 | let param_type = ASType::from(¶m_witx.tref); 18 | params.push((param_name.to_string(), param_type)); 19 | } 20 | 21 | let results_witx = &func_witx.results; 22 | assert_eq!(results_witx.len(), 1); 23 | let result_witx = &results_witx[0]; 24 | let result = ASType::from(&result_witx.tref); 25 | let result = match result { 26 | ASType::Result(result) => result, 27 | _ => unreachable!(), 28 | }; 29 | 30 | let ok_type = result.ok_type.clone(); 31 | 32 | let docs = &func_witx.docs; 33 | if !docs.is_empty() { 34 | Self::write_docs(w, docs)?; 35 | } 36 | 37 | let mut params_decomposed = vec![]; 38 | 39 | for param in ¶ms { 40 | let mut decomposed = param.1.decompose(¶m.0, false); 41 | params_decomposed.append(&mut decomposed); 42 | } 43 | 44 | let mut results = vec![]; 45 | // A tuple in a result is expanded into additional parameters, transformed to 46 | // pointers 47 | if let ASType::Tuple(tuple_members) = ok_type.as_ref().leaf() { 48 | for (i, tuple_member) in tuple_members.iter().enumerate() { 49 | let name = format!("result{}_ptr", i); 50 | results.push((name, tuple_member.type_.clone())); 51 | } 52 | } else { 53 | let name = "result_ptr"; 54 | results.push((name.to_string(), ok_type)); 55 | } 56 | for result in &results { 57 | let mut decomposed = result.1.decompose(&result.0, true); 58 | params_decomposed.append(&mut decomposed); 59 | } 60 | 61 | w.write_line("// @ts-ignore: decorator")? 62 | .write_line("@unsafe")? 63 | .write_line("// @ts-ignore: decorator")? 64 | .write_line(format!("@external(\"{}\", \"{}\")", module_name, name))? 65 | .indent()? 66 | .write(format!("export declare function {}(", name.as_fn()))?; 67 | 68 | if !params_decomposed.is_empty() || !results.is_empty() { 69 | w.eol()?; 70 | } 71 | for (i, param) in params_decomposed.iter().enumerate() { 72 | let eol = if i + 1 == params_decomposed.len() { 73 | "" 74 | } else { 75 | "," 76 | }; 77 | w.write_line_continued(format!( 78 | "{}: {}{}", 79 | param.name.as_var(), 80 | param.type_.as_lang(), 81 | eol 82 | ))?; 83 | } 84 | 85 | w.write_line(format!("): {};", result.error_type.as_lang()))?; 86 | w.eob()?; 87 | 88 | let signature_witx = func_witx.wasm_signature(witx::CallMode::DefinedImport); 89 | let params_count_witx = signature_witx.params.len() + signature_witx.results.len(); 90 | assert_eq!(params_count_witx, params_decomposed.len() + 1); 91 | 92 | Ok(()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/assemblyscript/header.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl AssemblyScriptGenerator { 6 | pub fn header(w: &mut PrettyWriter) -> Result<(), Error> { 7 | w.write_lines( 8 | " 9 | /* 10 | * This file was automatically generated by witx-codegen - Do not edit manually. 11 | */", 12 | )?; 13 | w.write_lines( 14 | " 15 | export type WasiHandle = i32; 16 | export type Char8 = u8; 17 | export type Char32 = u32; 18 | export type WasiPtr = usize; 19 | export type WasiMutPtr = usize; 20 | export type WasiStringBytesPtr = WasiPtr; 21 | ", 22 | )?; 23 | w.write_lines( 24 | " 25 | // @ts-ignore: decorator 26 | @unmanaged 27 | export class WasiString { 28 | ptr: WasiStringBytesPtr; 29 | length: usize; 30 | 31 | constructor(str: string) { 32 | let wasiString = String.UTF8.encode(str, false); 33 | // @ts-ignore: cast 34 | this.ptr = changetype(wasiString); 35 | this.length = wasiString.byteLength; 36 | } 37 | 38 | toString(): string { 39 | let tmp = new ArrayBuffer(this.length as u32); 40 | memory.copy(changetype(tmp), this.ptr, this.length); 41 | return String.UTF8.decode(tmp); 42 | } 43 | } 44 | 45 | // @ts-ignore: decorator 46 | @unmanaged 47 | export class WasiSlice { 48 | ptr: WasiPtr; 49 | length: usize; 50 | 51 | constructor(array: ArrayBufferView) { 52 | // @ts-ignore: cast 53 | this.ptr = array.dataStart; 54 | this.length = array.byteLength; 55 | } 56 | } 57 | 58 | // @ts-ignore: decorator 59 | @unmanaged 60 | export class WasiMutSlice { 61 | ptr: WasiMutPtr; 62 | length: usize; 63 | 64 | constructor(array: ArrayBufferView) { 65 | // @ts-ignore: cast 66 | this.ptr = array.dataStart; 67 | this.length = array.byteLength; 68 | } 69 | } 70 | ", 71 | )? 72 | .eob()?; 73 | Ok(()) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/assemblyscript/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod function; 3 | mod header; 4 | mod r#struct; 5 | mod tuple; 6 | mod union; 7 | 8 | use std::io::Write; 9 | 10 | use common::*; 11 | 12 | use super::*; 13 | use crate::astype::*; 14 | use crate::error::*; 15 | use crate::pretty_writer::PrettyWriter; 16 | 17 | pub struct AssemblyScriptGenerator { 18 | module_name: Option, 19 | } 20 | 21 | impl AssemblyScriptGenerator { 22 | pub fn new(module_name: Option) -> Self { 23 | AssemblyScriptGenerator { module_name } 24 | } 25 | } 26 | 27 | impl Generator for AssemblyScriptGenerator { 28 | fn generate( 29 | &self, 30 | writer: &mut T, 31 | module_witx: witx::Module, 32 | options: &Options, 33 | ) -> Result<(), Error> { 34 | let mut w = PrettyWriter::new(writer, " "); 35 | let module_name = match &self.module_name { 36 | None => module_witx.name().as_str().to_string(), 37 | Some(module_name) => module_name.to_string(), 38 | }; 39 | let module_id = module_witx.module_id(); 40 | let skip_imports = options.skip_imports; 41 | 42 | if !options.skip_header { 43 | Self::header(&mut w)?; 44 | } 45 | 46 | let module_title_comments = format!( 47 | "---------------------- Module: [{}] ----------------------", 48 | module_name 49 | ); 50 | Self::write_comments(&mut w, &module_title_comments)?; 51 | w.eob()?; 52 | 53 | for type_ in module_witx.typenames() { 54 | if skip_imports && &type_.module != module_id { 55 | continue; 56 | } 57 | let constants_for_type: Vec<_> = module_witx 58 | .constants() 59 | .filter_map(|x| { 60 | if x.ty == type_.name { 61 | Some(ASConstant { 62 | name: x.name.as_str().to_string(), 63 | value: x.value, 64 | }) 65 | } else { 66 | None 67 | } 68 | }) 69 | .collect(); 70 | Self::define_type(&mut w, type_.as_ref(), &constants_for_type)?; 71 | } 72 | 73 | for func in module_witx.funcs() { 74 | Self::define_func(&mut w, &module_name, func.as_ref())?; 75 | } 76 | 77 | Ok(()) 78 | } 79 | } 80 | 81 | impl AssemblyScriptGenerator { 82 | fn write_docs(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 83 | if docs.is_empty() { 84 | return Ok(()); 85 | } 86 | w.write_line("/**")?; 87 | for docs_line in docs.lines() { 88 | if docs_line.is_empty() { 89 | w.write_line(" *")?; 90 | } else { 91 | w.write_line(format!(" * {}", docs_line))?; 92 | } 93 | } 94 | w.write_line(" */")?; 95 | Ok(()) 96 | } 97 | 98 | fn write_comments(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 99 | if docs.is_empty() { 100 | return Ok(()); 101 | } 102 | w.write_line("/*")?; 103 | for docs_line in docs.lines() { 104 | if docs_line.is_empty() { 105 | w.write_line(" *")?; 106 | } else { 107 | w.write_line(format!(" * {}", docs_line))?; 108 | } 109 | } 110 | w.write_line(" */")?; 111 | Ok(()) 112 | } 113 | 114 | fn define_as_alias( 115 | w: &mut PrettyWriter, 116 | name: &str, 117 | other_type: &ASType, 118 | ) -> Result<(), Error> { 119 | w.write_line(format!( 120 | "export type {} = {};", 121 | name.as_type(), 122 | other_type.as_lang() 123 | ))?; 124 | Ok(()) 125 | } 126 | 127 | fn define_as_atom( 128 | w: &mut PrettyWriter, 129 | name: &str, 130 | type_: &ASType, 131 | ) -> Result<(), Error> { 132 | w.write_line(format!( 133 | "export type {} = {};", 134 | name.as_type(), 135 | type_.as_lang() 136 | ))?; 137 | Ok(()) 138 | } 139 | 140 | fn define_as_enum( 141 | w: &mut PrettyWriter, 142 | name: &str, 143 | enum_: &ASEnum, 144 | ) -> Result<(), Error> { 145 | let repr = enum_.repr.as_ref(); 146 | w.write_line(format!( 147 | "export type {} = {};", 148 | name.as_type(), 149 | repr.as_lang() 150 | ))?; 151 | w.eob()?; 152 | w.write_line(format!("export namespace {} {{", name.as_namespace()))?; 153 | { 154 | let mut w = w.new_block(); 155 | for choice in &enum_.choices { 156 | w.write_line(format!( 157 | "export const {}: {} = {};", 158 | choice.name.as_const(), 159 | name.as_type(), 160 | choice.value 161 | ))?; 162 | } 163 | } 164 | w.write_line("}")?; 165 | Ok(()) 166 | } 167 | 168 | fn define_as_constants( 169 | w: &mut PrettyWriter, 170 | name: &str, 171 | constants: &ASConstants, 172 | ) -> Result<(), Error> { 173 | let repr = constants.repr.as_ref(); 174 | w.write_line(format!( 175 | "export type {} = {};", 176 | name.as_type(), 177 | repr.as_lang() 178 | ))?; 179 | w.eob()?; 180 | Self::define_constants_for_type(w, name, &constants.constants)?; 181 | Ok(()) 182 | } 183 | 184 | fn define_as_type( 185 | w: &mut PrettyWriter, 186 | name: &str, 187 | type_: &ASType, 188 | ) -> Result<(), Error> { 189 | match type_ { 190 | ASType::Alias(_) 191 | | ASType::Bool 192 | | ASType::Char8 193 | | ASType::Char32 194 | | ASType::F32 195 | | ASType::F64 196 | | ASType::U8 197 | | ASType::U16 198 | | ASType::U32 199 | | ASType::U64 200 | | ASType::S8 201 | | ASType::S16 202 | | ASType::S32 203 | | ASType::S64 204 | | ASType::USize 205 | | ASType::Handle(_) 206 | | ASType::Slice(_) 207 | | ASType::String(_) 208 | | ASType::ReadBuffer(_) 209 | | ASType::WriteBuffer(_) => Self::define_as_atom(w, name, type_)?, 210 | ASType::Enum(enum_) => Self::define_as_enum(w, name, enum_)?, 211 | ASType::Union(union_) => Self::define_as_union(w, name, union_)?, 212 | ASType::Constants(constants) => Self::define_as_constants(w, name, constants)?, 213 | ASType::Tuple(members) => Self::define_as_tuple(w, name, members)?, 214 | ASType::Struct(members) => Self::define_as_struct(w, name, members)?, 215 | _ => { 216 | dbg!(type_); 217 | unimplemented!(); 218 | } 219 | } 220 | Ok(()) 221 | } 222 | 223 | fn define_constants_for_type( 224 | w: &mut PrettyWriter, 225 | type_name: &str, 226 | constants: &[ASConstant], 227 | ) -> Result<(), Error> { 228 | if constants.is_empty() { 229 | return Ok(()); 230 | } 231 | w.write_line(format!("export namespace {} {{", type_name.as_namespace()))?; 232 | { 233 | let mut w = w.new_block(); 234 | let mut hex = false; 235 | let mut single_bits: usize = 0; 236 | for constant in constants { 237 | if constant.value > 0xffff { 238 | hex = true; 239 | } 240 | if constant.value.count_ones() == 1 { 241 | single_bits += 1; 242 | } 243 | } 244 | if constants.len() > 2 && single_bits == constants.len() { 245 | hex = true; 246 | } 247 | for constant in constants { 248 | let value_s = if hex { 249 | format!("0x{:x}", constant.value) 250 | } else { 251 | format!("{}", constant.value) 252 | }; 253 | w.write_line(format!( 254 | "export const {}: {} = {};", 255 | constant.name.as_const(), 256 | type_name.as_type(), 257 | value_s 258 | ))?; 259 | } 260 | } 261 | w.write_line("}")?; 262 | w.eob()?; 263 | Ok(()) 264 | } 265 | 266 | fn define_type( 267 | w: &mut PrettyWriter, 268 | type_witx: &witx::NamedType, 269 | constants: &[ASConstant], 270 | ) -> Result<(), Error> { 271 | let docs = &type_witx.docs; 272 | if !docs.is_empty() { 273 | Self::write_docs(w, docs)?; 274 | } 275 | let type_name = type_witx.name.as_str(); 276 | let tref = &type_witx.tref; 277 | match tref { 278 | witx::TypeRef::Name(other_type) => { 279 | Self::define_as_alias(w, type_name, &ASType::from(&other_type.tref))? 280 | } 281 | witx::TypeRef::Value(type_witx) => { 282 | let t = ASType::from(type_witx.as_ref()); 283 | Self::define_as_type(w, type_name, &t)? 284 | } 285 | } 286 | w.eob()?; 287 | Self::define_constants_for_type(w, type_name, constants)?; 288 | Ok(()) 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/assemblyscript/struct.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl AssemblyScriptGenerator { 6 | pub fn define_as_struct( 7 | w: &mut PrettyWriter, 8 | name: &str, 9 | members: &[ASStructMember], 10 | ) -> Result<(), Error> { 11 | w.write_line("// @ts-ignore: decorator")? 12 | .write_line("@unmanaged")? 13 | .write_line(format!("export class {} {{", name.as_type()))?; 14 | { 15 | let mut w = w.new_block(); 16 | for member in members { 17 | let member_type = member.type_.as_ref(); 18 | w.write_line(format!( 19 | "{}: {};", 20 | member.name.as_var(), 21 | member_type.as_lang() 22 | ))?; 23 | 24 | let pad_len = member.padding; 25 | for i in 0..(pad_len & 1) { 26 | w.write_line(format!("private __pad8_{}: u8;", i))?; 27 | } 28 | for i in 0..(pad_len & 3) / 2 { 29 | w.write_line(format!("private __pad16_{}: u16;", i))?; 30 | } 31 | for i in 0..(pad_len & 7) / 4 { 32 | w.write_line(format!("private __pad32_{}: u32;", i))?; 33 | } 34 | for i in 0..pad_len / 8 { 35 | w.write_line(format!("private __pad64_{}: u64;", i))?; 36 | } 37 | } 38 | } 39 | w.write_line("}")?.eob()?; 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/assemblyscript/tuple.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | pub struct Tuple; 6 | 7 | impl Tuple { 8 | pub fn name_for(tuple_members: &[ASTupleMember]) -> String { 9 | format!( 10 | "WasiTuple{}{}", 11 | tuple_members.len(), 12 | tuple_members 13 | .iter() 14 | .map(|member| member.type_.to_string()) 15 | .collect::>() 16 | .join("_") 17 | ) 18 | } 19 | } 20 | 21 | impl AssemblyScriptGenerator { 22 | pub fn define_as_tuple( 23 | w: &mut PrettyWriter, 24 | name: &str, 25 | members: &[ASTupleMember], 26 | ) -> Result<(), Error> { 27 | w.write_line("// @ts-ignore: decorator")? 28 | .write_line("@unmanaged")? 29 | .write_line(format!("export class {} {{ // -- Tuple", name.as_type()))?; 30 | { 31 | let mut w = w.new_block(); 32 | for (i, member) in members.iter().enumerate() { 33 | let member_type = member.type_.as_ref(); 34 | w.write_line(format!("v{}: {};", i, member_type.as_lang()))?; 35 | 36 | let pad_len = member.padding; 37 | for i in 0..(pad_len & 1) { 38 | w.write_line(format!("private __pad8_{}: u8;", i))?; 39 | } 40 | for i in 0..(pad_len & 3) / 2 { 41 | w.write_line(format!("private __pad16_{}: u16;", i))?; 42 | } 43 | for i in 0..(pad_len & 7) / 4 { 44 | w.write_line(format!("private __pad32_{}: u32;", i))?; 45 | } 46 | for i in 0..pad_len / 8 { 47 | w.write_line(format!("private __pad64_{}: u64;", i))?; 48 | } 49 | } 50 | } 51 | w.write_line("}")?.eob()?; 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/assemblyscript/union.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl AssemblyScriptGenerator { 6 | fn define_union_member_accessors( 7 | w: &mut PrettyWriter, 8 | union_name: &str, 9 | i: usize, 10 | member: &ASUnionMember, 11 | ) -> Result<(), Error> { 12 | let member_type = member.type_.as_ref(); 13 | match member_type { 14 | ASType::Void => { 15 | w.write_line(format!( 16 | "static {}(): {} {{", 17 | member.name.as_fn(), 18 | union_name.as_type() 19 | ))? 20 | .indent()? 21 | .write_line(format!("return {}.new({});", union_name.as_type(), i))? 22 | .write_line("}")? 23 | .eob()?; 24 | 25 | w.write_line(format!("set{}(): void {{", member.name.as_fn_suffix()))? 26 | .indent()? 27 | .write_line(format!("this.tag = {};", i))? 28 | .write_line("}")? 29 | .eob()?; 30 | 31 | w.write_line(format!("is{}(): bool {{", member.name.as_fn_suffix()))? 32 | .indent()? 33 | .write_line(format!("return this.tag === {};", i))? 34 | .write_line("}")?; 35 | } 36 | _ => { 37 | w.write_line(format!( 38 | "static {}(val: {}): {} {{", 39 | member.name.as_fn(), 40 | member_type.as_lang(), 41 | union_name.as_type() 42 | ))?; 43 | w.new_block().write_line(format!( 44 | "return {}.new({}, val);", 45 | union_name.as_type(), 46 | i 47 | ))?; 48 | w.write_line("}")?.eob()?; 49 | 50 | w.write_line(format!( 51 | "set{}(val: {}): void {{", 52 | member.name.as_fn_suffix(), 53 | member_type.as_lang() 54 | ))?; 55 | { 56 | w.new_block() 57 | .write_line(format!("this.tag = {};", i))? 58 | .write_line("this.set(val);")?; 59 | } 60 | w.write_line("}")?.eob()?; 61 | 62 | w.write_line(format!("is{}(): bool {{", member.name.as_fn_suffix(),))? 63 | .indent()? 64 | .write_line(format!("return this.tag === {};", i))? 65 | .write_line("}")? 66 | .eob()?; 67 | 68 | if member_type.is_nullable() { 69 | w.write_line(format!( 70 | "get{}(): {} | null {{", 71 | member.name.as_fn_suffix(), 72 | member_type.as_lang() 73 | ))?; 74 | } else { 75 | w.write_line(format!( 76 | "get{}(): {} {{", 77 | member.name.as_fn_suffix(), 78 | member_type.as_lang() 79 | ))?; 80 | } 81 | { 82 | let mut w = w.new_block(); 83 | if member_type.is_nullable() { 84 | w.write_line(format!("if (this.tag !== {}) {{ return null; }}", i))?; 85 | } 86 | w.write_line(format!("return this.get<{}>();", member_type.as_lang()))?; 87 | } 88 | w.write_line("}")?; 89 | } 90 | } 91 | Ok(()) 92 | } 93 | 94 | fn define_union_member( 95 | w: &mut PrettyWriter, 96 | union_name: &str, 97 | i: usize, 98 | member: &ASUnionMember, 99 | ) -> Result<(), Error> { 100 | let member_type = member.type_.as_ref(); 101 | match member_type { 102 | ASType::Void => { 103 | w.write_line(format!( 104 | "// --- {}: (no associated content) if tag={}", 105 | member.name.as_var(), 106 | i 107 | ))?; 108 | } 109 | _ => { 110 | w.write_line(format!( 111 | "// --- {}: {} if tag={}", 112 | member.name.as_var(), 113 | member_type.as_lang(), 114 | i 115 | ))?; 116 | } 117 | } 118 | w.eob()?; 119 | Self::define_union_member_accessors(w, union_name, i, member)?; 120 | Ok(()) 121 | } 122 | 123 | pub fn define_as_union( 124 | w: &mut PrettyWriter, 125 | name: &str, 126 | union_: &ASUnion, 127 | ) -> Result<(), Error> { 128 | let tag_repr = union_.tag_repr.as_ref(); 129 | w.write_line("// @ts-ignore: decorator")? 130 | .write_line("@unmanaged")? 131 | .write_line(format!("export class {} {{", name.as_type()))?; 132 | { 133 | let mut w = w.new_block(); 134 | w.write_line(format!("tag: {};", tag_repr.as_lang()))?; 135 | let pad_len = union_.padding_after_tag; 136 | for i in 0..(pad_len & 1) { 137 | w.write_line(format!("private __pad8_{}: u8;", i))?; 138 | } 139 | for i in 0..(pad_len & 3) / 2 { 140 | w.write_line(format!("private __pad16_{}: u16;", i))?; 141 | } 142 | for i in 0..(pad_len & 7) / 4 { 143 | w.write_line(format!("private __pad32_{}: u32;", i))?; 144 | } 145 | for i in 0..pad_len / 8 { 146 | w.write_line(format!("private __pad64_{}: u64;", i))?; 147 | } 148 | w.eob()?; 149 | 150 | w.write_line(format!("constructor(tag: {}) {{", tag_repr.as_lang()))?; 151 | { 152 | let mut w = w.new_block(); 153 | w.write_line("this.tag = tag;")?.write_line(format!( 154 | "memory.fill(changetype(this) + {}, 0, {});", 155 | union_.member_offset, union_.max_member_size 156 | ))?; 157 | } 158 | w.write_line("}")?.eob()?; 159 | 160 | w.write_line("// @ts-ignore: default")?.write_line(format!( 161 | "static new(tag: {}, val: T = 0): {} {{", 162 | tag_repr.as_lang(), 163 | name.as_type() 164 | ))?; 165 | { 166 | let mut w = w.new_block(); 167 | w.write_line(format!("let tu = new {}(tag);", name.as_type()))? 168 | .write_line("tu.set(val);")? 169 | .write_line("return tu;")?; 170 | } 171 | w.write_line("}")?.eob()?; 172 | 173 | w.write_line("get(): T {")?; 174 | { 175 | let mut w = w.new_block(); 176 | w.write_line("// @ts-ignore: cast")? 177 | .write_line(format!( 178 | "let valBuf = changetype(this) + {};", 179 | union_.member_offset 180 | ))? 181 | .write_line("if (isReference()) {")?; 182 | w.new_block().write_line("return changetype(valBuf);")?; 183 | w.write_line("} else {")?; 184 | w.new_block().write_line("return load(valBuf);")?; 185 | w.write_line("}")?; 186 | } 187 | w.write_line("}")?.eob()?; 188 | 189 | w.write_line("// @ts-ignore: default")? 190 | .write_line("set(val: T = 0): void {")?; 191 | { 192 | let mut w = w.new_block(); 193 | w.write_line("// @ts-ignore: cast")? 194 | .write_line(format!( 195 | "let valBuf = changetype(this) + {};", 196 | union_.member_offset 197 | ))? 198 | .write_line(format!( 199 | "memory.fill(valBuf, 0, {});", 200 | union_.max_member_size 201 | ))? 202 | .write_line("if (isReference()) {")?; 203 | w.new_block().write_line( 204 | "(val !== null) && memory.copy(valBuf, changetype(val), offsetof());", 205 | )?; 206 | w.write_line("} else {")?; 207 | w.new_block().write_line("store(valBuf, val)")?; 208 | w.write_line("}")?; 209 | } 210 | w.write_line("}")?; 211 | 212 | for (i, member) in union_.members.iter().enumerate() { 213 | w.eob()?; 214 | Self::define_union_member(&mut w, name, i, member)?; 215 | } 216 | } 217 | w.write_line("}")?.eob()?; 218 | Ok(()) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/astype.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use witx::Layout as _; 4 | 5 | #[derive(Clone, Debug, Eq, PartialEq)] 6 | pub struct ASAlias { 7 | pub name: String, 8 | pub type_: Rc, 9 | } 10 | 11 | #[derive(Clone, Debug, Eq, PartialEq)] 12 | pub struct ASStructMember { 13 | pub name: String, 14 | pub offset: usize, 15 | pub type_: Rc, 16 | pub padding: usize, 17 | } 18 | 19 | #[derive(Clone, Debug, Eq, PartialEq)] 20 | pub struct ASEnumChoice { 21 | pub name: String, 22 | pub value: usize, 23 | } 24 | 25 | #[derive(Clone, Debug, Eq, PartialEq)] 26 | pub struct ASEnum { 27 | pub repr: Rc, 28 | pub choices: Vec, 29 | } 30 | 31 | #[derive(Clone, Debug, Eq, PartialEq)] 32 | pub struct ASUnionMember { 33 | pub name: String, 34 | pub type_: Rc, 35 | } 36 | 37 | #[derive(Clone, Debug, Eq, PartialEq)] 38 | pub struct ASUnion { 39 | pub tag_repr: Rc, 40 | pub members: Vec, 41 | pub member_offset: usize, 42 | pub padding_after_tag: usize, 43 | pub max_member_size: usize, 44 | } 45 | 46 | #[derive(Clone, Debug, Eq, PartialEq)] 47 | pub struct ASTupleMember { 48 | pub type_: Rc, 49 | pub offset: usize, 50 | pub padding: usize, 51 | } 52 | 53 | #[derive(Clone, Debug, Eq, PartialEq)] 54 | pub struct ASOption { 55 | pub tag_repr: Rc, 56 | pub type_: Rc, 57 | pub offset: usize, 58 | } 59 | 60 | #[derive(Clone, Debug, Eq, PartialEq)] 61 | pub struct ASResult { 62 | pub tag_repr: Rc, 63 | pub error_type: Rc, 64 | pub ok_type: Rc, 65 | pub result_offset: usize, 66 | pub padding_after_tag: usize, 67 | } 68 | 69 | #[derive(Clone, Debug, Eq, PartialEq)] 70 | pub struct ASConstant { 71 | pub name: String, 72 | pub value: u64, 73 | } 74 | 75 | #[derive(Clone, Debug, Eq, PartialEq)] 76 | pub struct ASConstants { 77 | pub repr: Rc, 78 | pub constants: Vec, 79 | } 80 | 81 | #[derive(Clone, Debug, Eq, PartialEq)] 82 | pub enum ASType { 83 | Void, 84 | Alias(ASAlias), 85 | Bool, 86 | Char8, 87 | Char32, 88 | USize, 89 | F32, 90 | F64, 91 | S8, 92 | S16, 93 | S32, 94 | S64, 95 | U8, 96 | U16, 97 | U32, 98 | U64, 99 | Constants(ASConstants), 100 | Result(ASResult), 101 | Option(ASOption), 102 | Handle(String), 103 | Enum(ASEnum), 104 | Tuple(Vec), 105 | ConstPtr(Rc), 106 | MutPtr(Rc), 107 | Union(ASUnion), 108 | Struct(Vec), 109 | Slice(Rc), 110 | String(Rc), 111 | ReadBuffer(Rc), 112 | WriteBuffer(Rc), 113 | } 114 | 115 | impl From for ASType { 116 | fn from(witx: witx::IntRepr) -> Self { 117 | match witx { 118 | witx::IntRepr::U8 => ASType::U8, 119 | witx::IntRepr::U16 => ASType::U16, 120 | witx::IntRepr::U32 => ASType::U32, 121 | witx::IntRepr::U64 => ASType::U64, 122 | } 123 | } 124 | } 125 | 126 | impl From<&witx::BuiltinType> for ASType { 127 | fn from(witx_builtin: &witx::BuiltinType) -> Self { 128 | match witx_builtin { 129 | witx::BuiltinType::Char => ASType::Char32, 130 | witx::BuiltinType::F32 => ASType::F32, 131 | witx::BuiltinType::F64 => ASType::F64, 132 | witx::BuiltinType::S8 => ASType::S8, 133 | witx::BuiltinType::S16 => ASType::S16, 134 | witx::BuiltinType::S32 => ASType::S32, 135 | witx::BuiltinType::S64 => ASType::S64, 136 | 137 | witx::BuiltinType::U8 { lang_c_char: false } => ASType::U8, 138 | witx::BuiltinType::U8 { lang_c_char: true } => ASType::Char8, 139 | witx::BuiltinType::U16 => ASType::U16, 140 | witx::BuiltinType::U32 { 141 | lang_ptr_size: false, 142 | } => ASType::U32, 143 | witx::BuiltinType::U32 { 144 | lang_ptr_size: true, 145 | } => ASType::USize, 146 | witx::BuiltinType::U64 => ASType::U64, 147 | } 148 | } 149 | } 150 | 151 | impl From<&witx::Type> for ASType { 152 | fn from(type_witx: &witx::Type) -> Self { 153 | match type_witx { 154 | witx::Type::Builtin(witx_builtin) => ASType::from(witx_builtin), 155 | witx::Type::ConstPointer(constptr_tref) => { 156 | let pointee = ASType::from(constptr_tref); 157 | ASType::ConstPtr(Rc::new(pointee)) 158 | } 159 | witx::Type::Pointer(constptr_tref) => { 160 | let pointee = ASType::from(constptr_tref); 161 | ASType::MutPtr(Rc::new(pointee)) 162 | } 163 | witx::Type::Handle(handle_data_type) => { 164 | // data type doesn't seem to be used for anything 165 | let resource_name = handle_data_type.resource_id.name.as_str().to_string(); 166 | ASType::Handle(resource_name) 167 | } 168 | witx::Type::Record(record) if record.is_tuple() => 169 | // Tuple 170 | { 171 | let mut tuple_members = vec![]; 172 | let layout_witx = &record.member_layout(true); 173 | for member_witx in layout_witx { 174 | let member_tref = &member_witx.member.tref; 175 | let member_offset = member_witx.offset; 176 | let member = ASTupleMember { 177 | offset: member_offset, 178 | type_: Rc::new(ASType::from(member_tref)), 179 | padding: 0, 180 | }; 181 | tuple_members.push(member); 182 | } 183 | // Perform a second pass to compute padding between members 184 | let n = if layout_witx.is_empty() { 185 | 0 186 | } else { 187 | layout_witx.len() - 1 188 | }; 189 | for (i, member_witx) in layout_witx.iter().enumerate().take(n) { 190 | let member_tref = &member_witx.member.tref; 191 | let member_size = member_tref.mem_size(true); 192 | let member_padding = 193 | layout_witx[i + 1].offset - member_witx.offset - member_size; 194 | tuple_members[i].padding = member_padding; 195 | } 196 | ASType::Tuple(tuple_members) 197 | } 198 | witx::Type::Record(record) if record.bitflags_repr().is_none() => 199 | // Struct 200 | { 201 | let mut struct_members = vec![]; 202 | let layout_witx = &record.member_layout(true); 203 | for member_witx in layout_witx { 204 | let member_name = member_witx.member.name.as_str().to_string(); 205 | let member_tref = &member_witx.member.tref; 206 | let member_offset = member_witx.offset; 207 | let member = ASStructMember { 208 | name: member_name, 209 | offset: member_offset, 210 | type_: Rc::new(ASType::from(member_tref)), 211 | padding: 0, 212 | }; 213 | struct_members.push(member); 214 | } 215 | // Perform a second pass to compute padding between members 216 | let n = if layout_witx.is_empty() { 217 | 0 218 | } else { 219 | layout_witx.len() - 1 220 | }; 221 | for (i, member_witx) in layout_witx.iter().enumerate().take(n) { 222 | let member_tref = &member_witx.member.tref; 223 | let member_size = member_tref.mem_size(true); 224 | let member_padding = 225 | layout_witx[i + 1].offset - member_witx.offset - member_size; 226 | struct_members[i].padding = member_padding; 227 | } 228 | ASType::Struct(struct_members) 229 | } 230 | witx::Type::Record(record) if record.bitflags_repr().is_some() => 231 | // Constants 232 | { 233 | let mut constants = vec![]; 234 | let constants_repr = ASType::from(record.bitflags_repr().unwrap()); 235 | for (idx, contants_witx) in record.member_layout(true).iter().enumerate() { 236 | let constant_name = contants_witx.member.name.as_str().to_string(); 237 | let constant = ASConstant { 238 | name: constant_name, 239 | value: 1u64 << idx, 240 | }; 241 | constants.push(constant); 242 | } 243 | ASType::Constants(ASConstants { 244 | repr: Rc::new(constants_repr), 245 | constants, 246 | }) 247 | } 248 | witx::Type::Record(record) => { 249 | dbg!(record); 250 | dbg!(record.bitflags_repr()); 251 | unreachable!() 252 | } 253 | witx::Type::Variant(variant) 254 | if (variant.is_enum() || variant.is_bool()) 255 | && variant.as_expected().is_none() 256 | && variant.as_option().is_none() => 257 | // Enum 258 | { 259 | let enum_repr = ASType::from(variant.tag_repr); 260 | let mut choices = vec![]; 261 | for (idx, choice_witx) in variant.cases.iter().enumerate() { 262 | let choice_name = choice_witx.name.as_str().to_string(); 263 | let choice = ASEnumChoice { 264 | name: choice_name, 265 | value: idx, 266 | }; 267 | choices.push(choice); 268 | } 269 | // WITX exposes booleans as enums 270 | if choices.len() == 2 271 | && choices[0].name == "false" 272 | && choices[0].value == 0 273 | && choices[1].name == "true" 274 | && choices[1].value == 1 275 | { 276 | ASType::Bool 277 | } else { 278 | ASType::Enum(ASEnum { 279 | repr: Rc::new(enum_repr), 280 | choices, 281 | }) 282 | } 283 | } 284 | witx::Type::Variant(variant) 285 | if variant.as_expected().is_none() && variant.as_option().is_some() => 286 | // Option 287 | { 288 | let tag_repr = ASType::from(variant.tag_repr); 289 | let option_offset = variant.payload_offset(true); 290 | assert_eq!(variant.cases.len(), 1); 291 | let option_tref = &variant.cases[0].tref; 292 | let option_type = match &option_tref { 293 | None => ASType::Void, 294 | Some(type_witx) => ASType::from(type_witx), 295 | }; 296 | ASType::Option(ASOption { 297 | tag_repr: Rc::new(tag_repr), 298 | offset: option_offset, 299 | type_: Rc::new(option_type), 300 | }) 301 | } 302 | witx::Type::Variant(variant) 303 | if variant.as_expected().is_some() && variant.as_option().is_none() => 304 | // Result 305 | { 306 | let tag_repr = ASType::from(variant.tag_repr); 307 | let result_offset = variant.payload_offset(true); 308 | assert_eq!(variant.cases.len(), 2); 309 | assert_eq!(variant.cases[0].name, "ok"); 310 | assert_eq!(variant.cases[1].name, "err"); 311 | let ok_tref = &variant.cases[0].tref; 312 | let ok_type = match &ok_tref { 313 | None => ASType::Void, 314 | Some(type_witx) => ASType::from(type_witx), 315 | }; 316 | let error_tref = &variant.cases[1].tref; 317 | let error_type = match &error_tref { 318 | None => ASType::Void, 319 | Some(type_witx) => ASType::from(type_witx), 320 | }; 321 | let full_size = variant.mem_size(true); 322 | let tag_size = variant.tag_repr.mem_size(true); 323 | let padding_after_tag = full_size - tag_size; 324 | ASType::Result(ASResult { 325 | tag_repr: Rc::new(tag_repr), 326 | result_offset, 327 | padding_after_tag, 328 | error_type: Rc::new(error_type), 329 | ok_type: Rc::new(ok_type), 330 | }) 331 | } 332 | witx::Type::Variant(variant) => 333 | // Tagged Union 334 | { 335 | let tag_repr = ASType::from(variant.tag_repr); 336 | let member_offset = variant.payload_offset(true); 337 | let mut members = vec![]; 338 | for member_witx in &variant.cases { 339 | let member_name = member_witx.name.as_str().to_string(); 340 | let member_type = match member_witx.tref.as_ref() { 341 | None => ASType::Void, 342 | Some(type_witx) => ASType::from(type_witx), 343 | }; 344 | let member = ASUnionMember { 345 | name: member_name, 346 | type_: Rc::new(member_type), 347 | }; 348 | members.push(member); 349 | } 350 | let full_size = variant.mem_size(true); 351 | let tag_size = variant.tag_repr.mem_size(true); 352 | let padding_after_tag = full_size - tag_size; 353 | let max_member_size = full_size - member_offset; 354 | ASType::Union(ASUnion { 355 | tag_repr: Rc::new(tag_repr), 356 | members, 357 | member_offset, 358 | padding_after_tag, 359 | max_member_size, 360 | }) 361 | } 362 | witx::Type::List(items_tref) => { 363 | let elements_type = ASType::from(items_tref); 364 | match elements_type { 365 | // The "string" keyword in WITX returns a Char32, even if the actual encoding is 366 | // expected to be UTF-8 367 | ASType::Char32 | ASType::Char8 => ASType::String(Rc::new(ASType::Char8)), 368 | _ => ASType::Slice(Rc::new(elements_type)), 369 | } 370 | } 371 | witx::Type::Buffer(buffer) if buffer.out => { 372 | let elements_type = ASType::from(&buffer.tref); 373 | ASType::WriteBuffer(Rc::new(elements_type)) 374 | } 375 | witx::Type::Buffer(buffer) => { 376 | let elements_typ = ASType::from(&buffer.tref); 377 | ASType::ReadBuffer(Rc::new(elements_typ)) 378 | } 379 | } 380 | } 381 | } 382 | 383 | impl From<&witx::TypeRef> for ASType { 384 | fn from(witx_tref: &witx::TypeRef) -> Self { 385 | match witx_tref { 386 | witx::TypeRef::Value(type_witx) => ASType::from(type_witx.as_ref()), 387 | witx::TypeRef::Name(alias_witx) => { 388 | let alias_witx = alias_witx.as_ref(); 389 | let alias_name = alias_witx.name.as_str().to_string(); 390 | let alias_target = ASType::from(&alias_witx.tref); 391 | ASType::Alias(ASAlias { 392 | name: alias_name, 393 | type_: Rc::new(alias_target), 394 | }) 395 | } 396 | } 397 | } 398 | } 399 | 400 | pub struct ASTypeDecomposed { 401 | pub name: String, 402 | pub type_: Rc, 403 | } 404 | 405 | impl ASType { 406 | pub fn leaf(&self) -> &ASType { 407 | if let ASType::Alias(alias) = self { 408 | alias.type_.as_ref() 409 | } else { 410 | self 411 | } 412 | } 413 | 414 | pub fn decompose(&self, name: &str, as_mut_pointers: bool) -> Vec { 415 | let leaf = self.leaf(); 416 | 417 | if as_mut_pointers { 418 | return match leaf { 419 | ASType::Void => vec![], 420 | _ => vec![ASTypeDecomposed { 421 | name: name.to_string(), 422 | type_: Rc::new(ASType::MutPtr(Rc::new(self.clone()))), 423 | }], 424 | }; 425 | } 426 | 427 | match leaf { 428 | ASType::Void => vec![], 429 | ASType::ReadBuffer(elements_type) 430 | | ASType::WriteBuffer(elements_type) 431 | | ASType::Slice(elements_type) 432 | | ASType::String(elements_type) => { 433 | let ptr_name = format!("{}_ptr", name); 434 | let len_name = format!("{}_len", name); 435 | let ptr_type = if let ASType::WriteBuffer(_) = leaf { 436 | ASType::MutPtr(elements_type.clone()) 437 | } else { 438 | ASType::ConstPtr(elements_type.clone()) 439 | }; 440 | let ptr_element = ASTypeDecomposed { 441 | name: ptr_name, 442 | type_: Rc::new(ptr_type), 443 | }; 444 | let len_element = ASTypeDecomposed { 445 | name: len_name, 446 | type_: Rc::new(ASType::USize), 447 | }; 448 | vec![ptr_element, len_element] 449 | } 450 | _ => { 451 | vec![ASTypeDecomposed { 452 | name: name.to_string(), 453 | type_: Rc::new(self.clone()), 454 | }] 455 | } 456 | } 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /src/cpp/common.rs: -------------------------------------------------------------------------------- 1 | use super::tuple::Tuple; 2 | use crate::astype::*; 3 | use convert_case::{Case, Casing}; 4 | 5 | pub trait IsNullable { 6 | fn is_nullable(&self) -> bool; 7 | } 8 | 9 | impl IsNullable for ASType { 10 | fn is_nullable(&self) -> bool { 11 | matches!( 12 | self, 13 | ASType::ConstPtr(_) 14 | | ASType::MutPtr(_) 15 | | ASType::ReadBuffer(_) 16 | | ASType::WriteBuffer(_) 17 | | ASType::Enum(_) 18 | | ASType::Struct(_) 19 | | ASType::Tuple(_) 20 | | ASType::Union(_) 21 | ) 22 | } 23 | } 24 | 25 | pub trait Normalize { 26 | fn as_str(&self) -> &str; 27 | 28 | fn as_type(&self) -> String { 29 | self.as_str().to_case(Case::Pascal) 30 | } 31 | 32 | fn as_fn(&self) -> String { 33 | self.as_str().to_case(Case::Snake) 34 | } 35 | 36 | fn as_fn_suffix(&self) -> String { 37 | self.as_str().to_case(Case::Snake) 38 | } 39 | 40 | fn as_var(&self) -> String { 41 | self.as_str().to_case(Case::Snake) 42 | } 43 | 44 | fn as_const(&self) -> String { 45 | self.as_str().to_case(Case::UpperSnake) 46 | } 47 | 48 | fn as_namespace(&self) -> String { 49 | self.as_str().to_case(Case::Pascal) 50 | } 51 | } 52 | 53 | impl> Normalize for T { 54 | fn as_str(&self) -> &str { 55 | self.as_ref() 56 | } 57 | } 58 | 59 | pub trait ToLanguageRepresentation { 60 | fn as_astype(&self) -> &ASType; 61 | 62 | fn to_string(&self) -> String { 63 | self.as_lang() 64 | } 65 | 66 | fn as_lang(&self) -> String { 67 | match self.as_astype() { 68 | ASType::Alias(alias) => alias.name.as_type(), 69 | ASType::Bool => "bool".to_string(), 70 | ASType::Char32 => "char32_t".to_string(), 71 | ASType::Char8 => "unsigned char".to_string(), 72 | ASType::F32 => "float".to_string(), 73 | ASType::F64 => "double".to_string(), 74 | ASType::Handle(_resource_name) => "WasiHandle".to_string(), 75 | ASType::ConstPtr(pointee) => format!("WasiPtr<{}>", pointee.to_string()), 76 | ASType::MutPtr(pointee) => format!("WasiMutPtr<{}>", pointee.to_string()), 77 | ASType::Option(_) => todo!(), 78 | ASType::Result(_) => todo!(), 79 | ASType::S8 => "int8_t".to_string(), 80 | ASType::S16 => "int16_t".to_string(), 81 | ASType::S32 => "int32_t".to_string(), 82 | ASType::S64 => "int64_t".to_string(), 83 | ASType::U8 => "uint8_t".to_string(), 84 | ASType::U16 => "uint16_t".to_string(), 85 | ASType::U32 => "uint32_t".to_string(), 86 | ASType::U64 => "uint64_t".to_string(), 87 | ASType::USize => "size_t".to_string(), 88 | ASType::Void => "void".to_string(), 89 | ASType::Constants(_) => unimplemented!(), 90 | ASType::Enum(enum_) => { 91 | format!("{} /* Enum */", enum_.repr.as_ref().as_lang()) 92 | } 93 | ASType::Struct(_) => unimplemented!(), 94 | ASType::Tuple(tuple_members) => Tuple::name_for(tuple_members).as_type(), 95 | ASType::Union(_) => unimplemented!(), 96 | ASType::Slice(element_type) => format!("WasiMutSlice<{}>", element_type.as_lang()), 97 | ASType::String(_) => "WasiString".to_string(), 98 | ASType::ReadBuffer(element_type) => format!("WasiSlice<{}>", element_type.as_lang()), 99 | ASType::WriteBuffer(element_type) => { 100 | format!("WasiMutSlice<{}>", element_type.to_string()) 101 | } 102 | } 103 | } 104 | } 105 | 106 | impl ToLanguageRepresentation for ASType { 107 | fn as_astype(&self) -> &ASType { 108 | self 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/cpp/function.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::io::Write; 3 | 4 | impl CppGenerator { 5 | pub fn define_func( 6 | w: &mut PrettyWriter, 7 | // module_name: &str, 8 | func_witx: &witx::Function, 9 | ) -> Result<(), Error> { 10 | assert_eq!(func_witx.abi, witx::Abi::Preview1); 11 | let name = func_witx.name.as_str().to_string(); 12 | let params_witx = &func_witx.params; 13 | let mut params = vec![]; 14 | for param_witx in params_witx { 15 | let param_name = param_witx.name.as_str(); 16 | let param_type = ASType::from(¶m_witx.tref); 17 | params.push((param_name.to_string(), param_type)); 18 | } 19 | 20 | let results_witx = &func_witx.results; 21 | assert_eq!(results_witx.len(), 1); 22 | let result_witx = &results_witx[0]; 23 | let result = ASType::from(&result_witx.tref); 24 | let result = match result { 25 | ASType::Result(result) => result, 26 | _ => unreachable!(), 27 | }; 28 | 29 | let ok_type = result.ok_type.clone(); 30 | 31 | let docs = &func_witx.docs; 32 | if !docs.is_empty() { 33 | Self::write_docs(w, docs)?; 34 | } 35 | 36 | let mut params_decomposed = vec![]; 37 | 38 | for param in ¶ms { 39 | let mut decomposed = param.1.decompose(¶m.0, false); 40 | params_decomposed.append(&mut decomposed); 41 | } 42 | 43 | let mut results = vec![]; 44 | // A tuple in a result is expanded into additional parameters, transformed to pointers 45 | if let ASType::Tuple(tuple_members) = ok_type.as_ref().leaf() { 46 | for (i, tuple_member) in tuple_members.iter().enumerate() { 47 | let name = format!("result{}_ptr", i); 48 | results.push((name, tuple_member.type_.clone())); 49 | } 50 | } else { 51 | let name = "result_ptr"; 52 | results.push((name.to_string(), ok_type)); 53 | } 54 | 55 | let mut results_decomposed = vec![]; 56 | for result in &results { 57 | let mut decomposed = result.1.decompose(&result.0, true); 58 | results_decomposed.append(&mut decomposed); 59 | } 60 | 61 | let results_decomposed_deref = results_decomposed 62 | .iter() 63 | .map(|result_ptr_type| match result_ptr_type.type_.as_ref() { 64 | ASType::MutPtr(result_type) => ASTypeDecomposed { 65 | name: result_ptr_type.name.clone(), 66 | type_: result_type.clone(), 67 | }, 68 | _ => panic!("Result type is not a pointer"), 69 | }) 70 | .collect::>(); 71 | let results_set = results_decomposed_deref 72 | .iter() 73 | .map(|result| result.type_.as_lang()) 74 | .collect::>(); 75 | let rust_fn_result_str = match results_set.len() { 76 | 0 => "std::monostate".to_string(), 77 | 1 => results_set[0].clone(), 78 | _ => format!("std::tuple<{}>", results_set.join(", ")), 79 | }; 80 | 81 | w.indent()?.write(format!( 82 | "Expected<{}, {}> {}(", 83 | rust_fn_result_str, 84 | result.error_type.as_lang(), 85 | name.as_fn() 86 | ))?; 87 | 88 | if !params_decomposed.is_empty() || !results.is_empty() { 89 | w.eol()?; 90 | } 91 | for (i, param) in params_decomposed.iter().enumerate() { 92 | let eol = if i + 1 == params_decomposed.len() { 93 | "" 94 | } else { 95 | "," 96 | }; 97 | w.write_line_continued(format!( 98 | "{} {}{}", 99 | param.type_.as_lang(), 100 | param.name.as_var(), 101 | eol 102 | ))?; 103 | } 104 | 105 | w.write_line(");")?; 106 | w.eob()?; 107 | 108 | let signature_witx = func_witx.wasm_signature(witx::CallMode::DefinedImport); 109 | let params_count_witx = signature_witx.params.len() + signature_witx.results.len(); 110 | assert_eq!( 111 | params_count_witx, 112 | params_decomposed.len() + results_decomposed.len() + 1 113 | ); 114 | 115 | Ok(()) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/cpp/header.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::io::Write; 3 | 4 | impl CppGenerator { 5 | pub fn header(w: &mut PrettyWriter) -> Result<(), Error> { 6 | w.write_lines( 7 | " 8 | /* 9 | * This file was automatically generated by witx-codegen - Do not edit manually. 10 | */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // namespace WitxCodegenHeader { 18 | using WasiHandle = int32_t; 19 | template using WasiPtr = T *const; 20 | template using WasiMutPtr = T *; 21 | using WasiStringBytesPtr = WasiPtr; 22 | 23 | template using Expected = std::variant; 24 | 25 | using WasiStringBytesPtr = WasiPtr; 26 | struct WasiString { 27 | WasiStringBytesPtr ptr; 28 | size_t length; 29 | }; 30 | 31 | template 32 | struct WasiSlice { 33 | WasiPtr ptr; 34 | size_t length; 35 | }; 36 | 37 | template 38 | struct WasiMutSlice { 39 | WasiMutPtr ptr; 40 | size_t length; 41 | }; 42 | 43 | // } 44 | ", 45 | )? 46 | .eob()?; 47 | Ok(()) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/cpp/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod function; 3 | mod header; 4 | mod r#struct; 5 | mod tuple; 6 | mod union; 7 | 8 | use super::*; 9 | use crate::astype::*; 10 | use crate::error::*; 11 | use crate::pretty_writer::PrettyWriter; 12 | use common::*; 13 | use std::io::Write; 14 | 15 | pub struct CppGenerator { 16 | module_name: Option, 17 | } 18 | 19 | impl CppGenerator { 20 | pub fn new(module_name: Option) -> Self { 21 | CppGenerator { module_name } 22 | } 23 | } 24 | 25 | impl Generator for CppGenerator { 26 | fn generate( 27 | &self, 28 | writer: &mut T, 29 | module_witx: witx::Module, 30 | options: &Options, 31 | ) -> Result<(), Error> { 32 | let mut w = PrettyWriter::new(writer, " "); 33 | let module_name = match &self.module_name { 34 | None => module_witx.name().as_str().to_string(), 35 | Some(module_name) => module_name.to_string(), 36 | }; 37 | let module_id = module_witx.module_id(); 38 | let skip_imports = options.skip_imports; 39 | 40 | if !options.skip_header { 41 | Self::header(&mut w)?; 42 | } 43 | 44 | let module_title_comments = format!( 45 | "---------------------- Module: [{}] ----------------------", 46 | module_name 47 | ); 48 | 49 | // w.write_lines(format!( 50 | // "namespace {} {{ 51 | // using namespace WitxCodegenHeader;", 52 | // &module_name.as_namespace() 53 | // ))?; 54 | 55 | // for r in module_witx.resources() { 56 | // w.write_line(format!("using namespace {};",r.resource_id.name.as_str()))?; 57 | // } 58 | 59 | Self::write_comments(&mut w, &module_title_comments)?; 60 | w.eob()?; 61 | 62 | for type_ in module_witx.typenames() { 63 | if skip_imports && &type_.module != module_id { 64 | continue; 65 | } 66 | let constants_for_type: Vec<_> = module_witx 67 | .constants() 68 | .filter_map(|x| { 69 | if x.ty == type_.name { 70 | Some(ASConstant { 71 | name: x.name.as_str().to_string(), 72 | value: x.value, 73 | }) 74 | } else { 75 | None 76 | } 77 | }) 78 | .collect(); 79 | Self::define_type(&mut w, type_.as_ref(), &constants_for_type)?; 80 | } 81 | 82 | for func in module_witx.funcs() { 83 | Self::define_func(&mut w, /* &module_name, */ func.as_ref())?; 84 | } 85 | 86 | // w.write_line("}")?; 87 | 88 | Ok(()) 89 | } 90 | } 91 | 92 | impl CppGenerator { 93 | fn write_docs(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 94 | if docs.is_empty() { 95 | return Ok(()); 96 | } 97 | w.write_line("/**")?; 98 | for docs_line in docs.lines() { 99 | w.write_line(format!("* {}", docs_line))?; 100 | } 101 | w.write_line("**/")?; 102 | Ok(()) 103 | } 104 | 105 | fn write_comments(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 106 | if docs.is_empty() { 107 | return Ok(()); 108 | } 109 | for docs_line in docs.lines() { 110 | w.write_line(format!("// {}", docs_line))?; 111 | } 112 | Ok(()) 113 | } 114 | 115 | fn define_as_alias( 116 | w: &mut PrettyWriter, 117 | name: &str, 118 | other_type: &ASType, 119 | ) -> Result<(), Error> { 120 | w.write_line(format!( 121 | "using {} = {};", 122 | name.as_type(), 123 | other_type.as_lang() 124 | ))?; 125 | Ok(()) 126 | } 127 | 128 | fn define_as_atom( 129 | w: &mut PrettyWriter, 130 | name: &str, 131 | type_: &ASType, 132 | ) -> Result<(), Error> { 133 | w.write_line(format!("using {} = {};", name.as_type(), type_.as_lang()))?; 134 | Ok(()) 135 | } 136 | 137 | fn define_as_enum( 138 | w: &mut PrettyWriter, 139 | name: &str, 140 | enum_: &ASEnum, 141 | ) -> Result<(), Error> { 142 | let repr = enum_.repr.as_ref(); 143 | w.write_line(format!( 144 | "enum class {} : {} {{", 145 | name.as_type(), 146 | repr.as_lang() 147 | ))?; 148 | { 149 | let mut w = w.new_block(); 150 | // w.write_line(format!("use super::{};", name.as_type()))?; 151 | for choice in &enum_.choices { 152 | w.write_line(format!("{} = {},", choice.name.as_const(), choice.value))?; 153 | } 154 | } 155 | w.write_line("};")?; 156 | Ok(()) 157 | } 158 | 159 | fn define_as_constants( 160 | w: &mut PrettyWriter, 161 | name: &str, 162 | constants: &ASConstants, 163 | ) -> Result<(), Error> { 164 | let repr = constants.repr.as_ref(); 165 | w.write_line(format!("using {} = {};", name.as_type(), repr.as_lang()))?; 166 | w.eob()?; 167 | Self::define_constants_for_type(w, name, &constants.constants)?; 168 | Ok(()) 169 | } 170 | 171 | fn define_as_type( 172 | w: &mut PrettyWriter, 173 | name: &str, 174 | type_: &ASType, 175 | ) -> Result<(), Error> { 176 | match type_ { 177 | ASType::Alias(_) 178 | | ASType::Bool 179 | | ASType::Char8 180 | | ASType::Char32 181 | | ASType::F32 182 | | ASType::F64 183 | | ASType::U8 184 | | ASType::U16 185 | | ASType::U32 186 | | ASType::U64 187 | | ASType::S8 188 | | ASType::S16 189 | | ASType::S32 190 | | ASType::S64 191 | | ASType::USize 192 | | ASType::Handle(_) 193 | | ASType::Slice(_) 194 | | ASType::String(_) 195 | | ASType::ReadBuffer(_) 196 | | ASType::WriteBuffer(_) => Self::define_as_atom(w, name, type_)?, 197 | ASType::Enum(enum_) => Self::define_as_enum(w, name, enum_)?, 198 | ASType::Union(union_) => Self::define_as_union(w, name, union_)?, 199 | ASType::Constants(constants) => Self::define_as_constants(w, name, constants)?, 200 | ASType::Tuple(members) => Self::define_as_tuple(w, name, members)?, 201 | ASType::Struct(members) => Self::define_as_struct(w, name, members)?, 202 | _ => { 203 | dbg!(type_); 204 | unimplemented!(); 205 | } 206 | } 207 | Ok(()) 208 | } 209 | 210 | fn define_constants_for_type( 211 | w: &mut PrettyWriter, 212 | type_name: &str, 213 | constants: &[ASConstant], 214 | ) -> Result<(), Error> { 215 | if constants.is_empty() { 216 | return Ok(()); 217 | } 218 | // w.write_line("#[allow(non_snake_case)]")?; 219 | w.write_line(format!("namespace {} {{", type_name.as_const()))?; 220 | { 221 | let mut w = w.new_block(); 222 | // w.write_line(format!("using {};", type_name.as_type()))?; 223 | 224 | let mut hex = false; 225 | let mut single_bits: usize = 0; 226 | for constant in constants { 227 | if constant.value > 0xffff { 228 | hex = true; 229 | } 230 | if constant.value.count_ones() == 1 { 231 | single_bits += 1; 232 | } 233 | } 234 | if constants.len() > 2 && single_bits == constants.len() { 235 | hex = true; 236 | } 237 | for constant in constants { 238 | let value_s = if hex { 239 | format!("0x{:x}", constant.value) 240 | } else { 241 | format!("{}", constant.value) 242 | }; 243 | w.write_line(format!( 244 | "const {} {} = {};", 245 | type_name.as_type(), 246 | constant.name.as_const(), 247 | value_s 248 | ))?; 249 | } 250 | } 251 | w.write_line("}")?; 252 | w.eob()?; 253 | Ok(()) 254 | } 255 | 256 | fn define_type( 257 | w: &mut PrettyWriter, 258 | type_witx: &witx::NamedType, 259 | constants: &[ASConstant], 260 | ) -> Result<(), Error> { 261 | let docs = &type_witx.docs; 262 | if !docs.is_empty() { 263 | Self::write_docs(w, docs)?; 264 | } 265 | let type_name = type_witx.name.as_str(); 266 | let tref = &type_witx.tref; 267 | match tref { 268 | witx::TypeRef::Name(other_type) => { 269 | Self::define_as_alias(w, type_name, &ASType::from(&other_type.tref))? 270 | } 271 | witx::TypeRef::Value(type_witx) => { 272 | let t = ASType::from(type_witx.as_ref()); 273 | Self::define_as_type(w, type_name, &t)? 274 | } 275 | } 276 | w.eob()?; 277 | Self::define_constants_for_type(w, type_name, constants)?; 278 | Ok(()) 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/cpp/struct.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::io::Write; 3 | 4 | impl CppGenerator { 5 | pub fn define_as_struct( 6 | w: &mut PrettyWriter, 7 | name: &str, 8 | members: &[ASStructMember], 9 | ) -> Result<(), Error> { 10 | w 11 | // .write_line("#[repr(C, packed)]")? 12 | .write_line(format!( 13 | "struct __attribute__((packed)) {} {{", 14 | name.as_type() 15 | ))?; 16 | { 17 | let mut w = w.new_block(); 18 | for member in members { 19 | let member_type = member.type_.as_ref(); 20 | w.write_line(format!( 21 | "{} {};", 22 | member_type.as_lang(), 23 | member.name.as_var() 24 | ))?; 25 | 26 | let pad_len = member.padding; 27 | for i in 0..(pad_len & 1) { 28 | w.write_line(format!("uint8_t __pad8_{};", i))?; 29 | } 30 | for i in 0..(pad_len & 3) / 2 { 31 | w.write_line(format!("uint16_t __pad16_{};", i))?; 32 | } 33 | for i in 0..(pad_len & 7) / 4 { 34 | w.write_line(format!("uint32_t __pad32_{};", i))?; 35 | } 36 | for i in 0..pad_len / 8 { 37 | w.write_line(format!("uint64_t __pad64_{};", i))?; 38 | } 39 | } 40 | } 41 | w.write_line("};")?.eob()?; 42 | 43 | for member in members { 44 | w.write_line(format!( 45 | "static_assert(offsetof({}, {}) == {}, \"Error layout\");", 46 | name.as_type(), 47 | member.name.as_var(), 48 | member.offset 49 | ))?; 50 | } 51 | 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/cpp/tuple.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::io::Write; 3 | 4 | pub struct Tuple; 5 | 6 | impl Tuple { 7 | pub fn name_for(tuple_members: &[ASTupleMember]) -> String { 8 | format!( 9 | "WasiTuple{}{}", 10 | tuple_members.len(), 11 | tuple_members 12 | .iter() 13 | .map(|member| member.type_.to_string()) 14 | .collect::>() 15 | .join("_") 16 | ) 17 | } 18 | } 19 | 20 | impl CppGenerator { 21 | pub fn define_as_tuple( 22 | w: &mut PrettyWriter, 23 | name: &str, 24 | members: &[ASTupleMember], 25 | ) -> Result<(), Error> { 26 | w.write_line(format!("struct {} {{ // -- Tuple", name.as_type()))?; 27 | { 28 | let mut w = w.new_block(); 29 | for (i, member) in members.iter().enumerate() { 30 | let member_type = member.type_.as_ref(); 31 | w.write_line(format!("{} v{};", member_type.as_lang(), i))?; 32 | 33 | let pad_len = member.padding; 34 | for i in 0..(pad_len & 1) { 35 | w.write_line(format!("uint8_t __pad8_{};", i))?; 36 | } 37 | for i in 0..(pad_len & 3) / 2 { 38 | w.write_line(format!("uint16_t __pad16_{};", i))?; 39 | } 40 | for i in 0..(pad_len & 7) / 4 { 41 | w.write_line(format!("uint32_t __pad32_{};", i))?; 42 | } 43 | for i in 0..pad_len / 8 { 44 | w.write_line(format!("uint64_t __pad64_{};", i))?; 45 | } 46 | } 47 | } 48 | w.write_line("};")?.eob()?; 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/cpp/union.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::io::Write; 3 | 4 | impl CppGenerator { 5 | fn define_union_member( 6 | w: &mut PrettyWriter, 7 | // union_name: &str, 8 | i: usize, 9 | member: &ASUnionMember, 10 | ) -> Result<(), Error> { 11 | let member_type = member.type_.as_ref(); 12 | match member_type { 13 | ASType::Void => { 14 | w.write_line(format!( 15 | "// {}: (no associated content) if tag={}", 16 | member.name.as_var(), 17 | i 18 | ))?; 19 | } 20 | _ => { 21 | w.write_line(format!( 22 | "{} {}; // if tag={}", 23 | member_type.as_lang(), 24 | member.name.as_var(), 25 | i 26 | ))?; 27 | } 28 | } 29 | // Self::define_union_member_accessors(w, union_name, i, member)?; 30 | Ok(()) 31 | } 32 | 33 | pub fn define_as_union( 34 | w: &mut PrettyWriter, 35 | name: &str, 36 | union_: &ASUnion, 37 | ) -> Result<(), Error> { 38 | let tag_repr = union_.tag_repr.as_ref(); 39 | let inner_name = format!("{}_member", name); 40 | w.write_line(format!("union {} {{", inner_name.as_type()))?; 41 | for (i, member) in union_.members.iter().enumerate() { 42 | // w.eob()?; 43 | Self::define_union_member(&mut w.new_block(), /* name, */ i, member)?; 44 | } 45 | w.write_line("};")?.eob()?; 46 | 47 | w.write_line(format!( 48 | "struct __attribute__((packed)) {} {{", 49 | name.as_type() 50 | ))?; 51 | { 52 | let mut w = w.new_block(); 53 | w.write_line(format!("{} tag;", tag_repr.as_lang()))?; 54 | let pad_len = union_.padding_after_tag; 55 | for i in 0..(pad_len & 1) { 56 | w.write_line(format!("uint8_t __pad8_{};", i))?; 57 | } 58 | for i in 0..(pad_len & 3) / 2 { 59 | w.write_line(format!("uint16_t __pad16_{};", i))?; 60 | } 61 | for i in 0..(pad_len & 7) / 4 { 62 | w.write_line(format!("uint32_t __pad32_{};", i))?; 63 | } 64 | for i in 0..pad_len / 8 { 65 | w.write_line(format!("uint64_t __pad64_{};", i))?; 66 | } 67 | 68 | w.write_line(format!("{} member;", inner_name.as_type()))?; 69 | } 70 | 71 | w.write_line("};")?.eob()?; 72 | Ok(()) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/doc/common.rs: -------------------------------------------------------------------------------- 1 | use crate::astype::*; 2 | 3 | pub trait Normalize { 4 | fn as_str(&self) -> &str; 5 | 6 | fn as_link(&self) -> String { 7 | let s = self.as_str().trim().to_lowercase(); 8 | let s = s.trim_matches(|c: char| !(c.is_alphanumeric() || c == '_' || c == '-')); 9 | format!("#{}", s) 10 | } 11 | 12 | fn as_type(&self) -> String { 13 | format!("_[`{}`]({})_", self.as_str(), self.as_link()) 14 | } 15 | 16 | fn as_fn(&self) -> String { 17 | format!("[`{}()`]({})", self.as_str(), self.as_link()) 18 | } 19 | 20 | fn as_fn_suffix(&self) -> String { 21 | self.as_str().to_string() 22 | } 23 | 24 | fn as_var(&self) -> String { 25 | format!("**`{}`**", self.as_str()) 26 | } 27 | 28 | fn as_const(&self) -> String { 29 | format!("**`{}`**", self.as_str()) 30 | } 31 | 32 | fn as_namespace(&self) -> String { 33 | format!("**[`{}`]({})**", self.as_str(), self.as_link()) 34 | } 35 | } 36 | 37 | impl> Normalize for T { 38 | fn as_str(&self) -> &str { 39 | self.as_ref() 40 | } 41 | } 42 | 43 | pub trait ToLanguageRepresentation { 44 | fn as_astype(&self) -> &ASType; 45 | 46 | fn to_string(&self) -> String { 47 | self.as_lang() 48 | } 49 | 50 | fn as_lang(&self) -> String { 51 | match self.as_astype() { 52 | ASType::Alias(alias) => alias.name.as_type(), 53 | ASType::Bool => "`bool`".to_string(), 54 | ASType::Char32 => "`char32`".to_string(), 55 | ASType::Char8 => "`char8`".to_string(), 56 | ASType::F32 => "`f32`".to_string(), 57 | ASType::F64 => "`f64`".to_string(), 58 | ASType::Handle(_resource_name) => "`handle`".to_string(), 59 | ASType::ConstPtr(pointee) => format!("{} pointer", pointee.to_string()), 60 | ASType::MutPtr(pointee) => format!("{} mutable pointer", pointee.to_string()), 61 | ASType::Option(_) => todo!(), 62 | ASType::Result(_) => todo!(), 63 | ASType::S8 => "`i8`".to_string(), 64 | ASType::S16 => "`i16`".to_string(), 65 | ASType::S32 => "`i32`".to_string(), 66 | ASType::S64 => "`i64`".to_string(), 67 | ASType::U8 => "`u8`".to_string(), 68 | ASType::U16 => "`u16`".to_string(), 69 | ASType::U32 => "`u32`".to_string(), 70 | ASType::U64 => "`u64`".to_string(), 71 | ASType::USize => "`usize`".to_string(), 72 | ASType::Void => "_(empty)_".to_string(), 73 | ASType::Constants(_) => unimplemented!(), 74 | ASType::Enum(enum_) => { 75 | format!("{} enumeration", enum_.repr.as_ref().as_lang()) 76 | } 77 | ASType::Struct(_) => unimplemented!(), 78 | ASType::Tuple(tuple_members) => { 79 | let tuple_types: Vec<_> = 80 | tuple_members.iter().map(|x| x.type_.to_string()).collect(); 81 | format!("({})", tuple_types.join(", ")) 82 | } 83 | ASType::Union(_) => unimplemented!(), 84 | ASType::Slice(element_type) => format!("{} mutable slice", element_type.as_lang()), 85 | ASType::String(_) => "`string`".to_string(), 86 | ASType::ReadBuffer(element_type) => format!("{} slice", element_type.as_lang()), 87 | ASType::WriteBuffer(element_type) => { 88 | format!("{} mutable slice", element_type.to_string()) 89 | } 90 | } 91 | } 92 | } 93 | 94 | impl ToLanguageRepresentation for ASType { 95 | fn as_astype(&self) -> &ASType { 96 | self 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/doc/function.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl DocGenerator { 6 | pub fn define_func( 7 | w: &mut PrettyWriter, 8 | _module_name: &str, 9 | func_witx: &witx::Function, 10 | ) -> Result<(), Error> { 11 | assert_eq!(func_witx.abi, witx::Abi::Preview1); 12 | let name = func_witx.name.as_str().to_string(); 13 | let params_witx = &func_witx.params; 14 | let mut params = vec![]; 15 | for param_witx in params_witx { 16 | let param_name = param_witx.name.as_str(); 17 | let param_type = ASType::from(¶m_witx.tref); 18 | params.push((param_name.to_string(), param_type)); 19 | } 20 | 21 | let results_witx = &func_witx.results; 22 | assert_eq!(results_witx.len(), 1); 23 | let result_witx = &results_witx[0]; 24 | let result = ASType::from(&result_witx.tref); 25 | let result = match result { 26 | ASType::Result(result) => result, 27 | _ => unreachable!(), 28 | }; 29 | 30 | let ok_type = result.ok_type.clone(); 31 | 32 | let mut results = vec![]; 33 | // A tuple in a result is expanded into additional parameters, transformed to 34 | // pointers 35 | if let ASType::Tuple(tuple_members) = ok_type.as_ref().leaf() { 36 | for (i, tuple_member) in tuple_members.iter().enumerate() { 37 | let name = format!("result{}_ptr", i); 38 | results.push((name, tuple_member.type_.clone())); 39 | } 40 | } else { 41 | let name = "result"; 42 | results.push((name.to_string(), ok_type)); 43 | } 44 | 45 | w.write_lines(format!( 46 | "### {}\nReturned error type: {}", 47 | name.as_fn(), 48 | result.error_type.as_lang() 49 | ))?; 50 | w.eob()?; 51 | if !params.is_empty() { 52 | w.write_line("#### Input:")?.eob()?; 53 | { 54 | let mut w = w.new_block(); 55 | for param in ¶ms { 56 | w.write_line(format!("{}: {}", param.0.as_var(), param.1.as_lang()))?; 57 | } 58 | } 59 | } 60 | w.eob()?; 61 | if !results.is_empty() { 62 | match results[0].1.as_ref() { 63 | ASType::Void if results.len() == 1 => { 64 | w.write_line("This function has no output.")?; 65 | } 66 | _ => { 67 | w.write_line("#### Output:")?.eob()?; 68 | { 69 | let mut w = w.new_block(); 70 | for result in &results { 71 | let result_as_ptr = ASType::MutPtr(result.1.clone()); 72 | w.write_line(&result_as_ptr.as_lang())?; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | let docs = &func_witx.docs; 80 | if !docs.is_empty() { 81 | Self::write_docs(w, docs)?; 82 | } 83 | 84 | w.eob()?.write_line("---")?.eob()?; 85 | Ok(()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/doc/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod function; 3 | mod r#struct; 4 | mod tuple; 5 | mod union; 6 | 7 | use std::io::Write; 8 | 9 | use common::*; 10 | 11 | use super::*; 12 | use crate::astype::*; 13 | use crate::error::*; 14 | use crate::pretty_writer::PrettyWriter; 15 | 16 | pub struct DocGenerator { 17 | module_name: Option, 18 | } 19 | 20 | impl DocGenerator { 21 | pub fn new(module_name: Option) -> Self { 22 | DocGenerator { module_name } 23 | } 24 | } 25 | 26 | impl Generator for DocGenerator { 27 | fn generate( 28 | &self, 29 | writer: &mut T, 30 | module_witx: witx::Module, 31 | options: &Options, 32 | ) -> Result<(), Error> { 33 | let mut w = PrettyWriter::new(writer, "* "); 34 | let module_name = match &self.module_name { 35 | None => module_witx.name().as_str().to_string(), 36 | Some(module_name) => module_name.to_string(), 37 | }; 38 | let module_id = module_witx.module_id(); 39 | let skip_imports = options.skip_imports; 40 | 41 | if !options.skip_header { 42 | Self::header(&mut w)?; 43 | } 44 | 45 | let module_title_doc = format!("# Module: {}", module_name); 46 | w.eob()?; 47 | w.write_line(module_title_doc)?; 48 | w.eob()?; 49 | 50 | w.write_line("## Table of contents")?.eob()?; 51 | w.write_line("### Types list:")?.eob()?; 52 | w.write("[**[All](#types)**]")?; 53 | for type_ in module_witx.typenames() { 54 | if skip_imports && &type_.module != module_id { 55 | continue; 56 | } 57 | let type_name = type_.name.as_str(); 58 | w.write(format!(" - [{}]", type_name.as_type()))?; 59 | } 60 | w.eol()?.eob()?; 61 | w.write_line("### Functions list:")?.eob()?; 62 | w.write("[**[All](#functions)**]")?; 63 | for func in module_witx.funcs() { 64 | let func_name = func.name.as_str(); 65 | w.write(format!(" - [{}]", func_name.as_fn()))?; 66 | } 67 | w.eol()?.eob()?; 68 | 69 | w.write_line("## Types")?.eob()?; 70 | 71 | for type_ in module_witx.typenames() { 72 | if skip_imports && &type_.module != module_id { 73 | continue; 74 | } 75 | let constants_for_type: Vec<_> = module_witx 76 | .constants() 77 | .filter_map(|x| { 78 | if x.ty == type_.name { 79 | Some(ASConstant { 80 | name: x.name.as_str().to_string(), 81 | value: x.value, 82 | }) 83 | } else { 84 | None 85 | } 86 | }) 87 | .collect(); 88 | Self::define_type(&mut w, type_.as_ref(), &constants_for_type)?; 89 | } 90 | 91 | w.write_line("## Functions")?.eob()?; 92 | 93 | for func in module_witx.funcs() { 94 | Self::define_func(&mut w, &module_name, func.as_ref())?; 95 | } 96 | 97 | Ok(()) 98 | } 99 | } 100 | 101 | impl DocGenerator { 102 | fn write_docs(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 103 | if docs.is_empty() { 104 | return Ok(()); 105 | } 106 | w.eob()?; 107 | for docs_line in docs.lines() { 108 | let docs_line = docs_line.trim().replace('<', "\\<").replace('>', "\\>"); 109 | w.write_line(format!("> {}", docs_line))?; 110 | } 111 | w.eob()?; 112 | Ok(()) 113 | } 114 | 115 | fn header(_w: &mut PrettyWriter) -> Result<(), Error> { 116 | Ok(()) 117 | } 118 | 119 | fn define_as_alias( 120 | w: &mut PrettyWriter, 121 | name: &str, 122 | other_type: &ASType, 123 | ) -> Result<(), Error> { 124 | w.write_lines(format!( 125 | "### {}\n\nAlias for {}.", 126 | name.as_type(), 127 | other_type.as_lang() 128 | ))? 129 | .eob()?; 130 | Ok(()) 131 | } 132 | 133 | fn define_as_atom( 134 | w: &mut PrettyWriter, 135 | name: &str, 136 | type_: &ASType, 137 | ) -> Result<(), Error> { 138 | w.write_line(format!( 139 | "### {}\nAlias for {}.", 140 | name.as_type(), 141 | type_.as_lang() 142 | ))? 143 | .eob()?; 144 | Ok(()) 145 | } 146 | 147 | fn define_as_enum( 148 | w: &mut PrettyWriter, 149 | name: &str, 150 | enum_: &ASEnum, 151 | ) -> Result<(), Error> { 152 | let repr = enum_.repr.as_ref(); 153 | w.write_lines(format!( 154 | "### {}\n\nEnumeration with tag type: {}, and the following members:", 155 | name.as_type(), 156 | repr.as_lang() 157 | ))? 158 | .eob()?; 159 | { 160 | let mut w = w.new_block(); 161 | for choice in &enum_.choices { 162 | w.write_line(format!("{}: {}", choice.name.as_const(), name.as_type()))?; 163 | } 164 | } 165 | Ok(()) 166 | } 167 | 168 | fn define_as_constants( 169 | w: &mut PrettyWriter, 170 | name: &str, 171 | constants: &ASConstants, 172 | ) -> Result<(), Error> { 173 | let repr = constants.repr.as_ref(); 174 | w.write_lines(format!( 175 | "### {}\n\nSet of constants, of type {}", 176 | name.as_type(), 177 | repr.as_lang() 178 | ))? 179 | .eob()?; 180 | Self::define_constants_for_type(w, name, &constants.constants)?; 181 | Ok(()) 182 | } 183 | 184 | fn define_as_type( 185 | w: &mut PrettyWriter, 186 | name: &str, 187 | type_: &ASType, 188 | ) -> Result<(), Error> { 189 | match type_ { 190 | ASType::Alias(_) 191 | | ASType::Bool 192 | | ASType::Char8 193 | | ASType::Char32 194 | | ASType::F32 195 | | ASType::F64 196 | | ASType::U8 197 | | ASType::U16 198 | | ASType::U32 199 | | ASType::U64 200 | | ASType::S8 201 | | ASType::S16 202 | | ASType::S32 203 | | ASType::S64 204 | | ASType::USize 205 | | ASType::Handle(_) 206 | | ASType::Slice(_) 207 | | ASType::String(_) 208 | | ASType::ReadBuffer(_) 209 | | ASType::WriteBuffer(_) => Self::define_as_atom(w, name, type_)?, 210 | ASType::Enum(enum_) => Self::define_as_enum(w, name, enum_)?, 211 | ASType::Union(union_) => Self::define_as_union(w, name, union_)?, 212 | ASType::Constants(constants) => Self::define_as_constants(w, name, constants)?, 213 | ASType::Tuple(members) => Self::define_as_tuple(w, name, members)?, 214 | ASType::Struct(members) => Self::define_as_struct(w, name, members)?, 215 | _ => { 216 | dbg!(type_); 217 | unimplemented!(); 218 | } 219 | } 220 | Ok(()) 221 | } 222 | 223 | fn define_constants_for_type( 224 | w: &mut PrettyWriter, 225 | type_name: &str, 226 | constants: &[ASConstant], 227 | ) -> Result<(), Error> { 228 | if constants.is_empty() { 229 | return Ok(()); 230 | } 231 | w.write_line(format!("Predefined constants for {}:", type_name.as_type()))? 232 | .eob()?; 233 | { 234 | let mut w = w.new_block(); 235 | let mut hex = false; 236 | let mut single_bits: usize = 0; 237 | for constant in constants { 238 | if constant.value > 0xffff { 239 | hex = true; 240 | } 241 | if constant.value.count_ones() == 1 { 242 | single_bits += 1; 243 | } 244 | } 245 | if constants.len() > 2 && single_bits == constants.len() { 246 | hex = true; 247 | } 248 | for constant in constants { 249 | let value_s = if hex { 250 | format!("0x{:x}", constant.value) 251 | } else { 252 | format!("{}", constant.value) 253 | }; 254 | w.write_line(format!("{} = `{}`", constant.name.as_const(), value_s))?; 255 | } 256 | } 257 | Ok(()) 258 | } 259 | 260 | fn define_type( 261 | w: &mut PrettyWriter, 262 | type_witx: &witx::NamedType, 263 | constants: &[ASConstant], 264 | ) -> Result<(), Error> { 265 | let type_name = type_witx.name.as_str(); 266 | let tref = &type_witx.tref; 267 | match tref { 268 | witx::TypeRef::Name(other_type) => { 269 | Self::define_as_alias(w, type_name, &ASType::from(&other_type.tref))? 270 | } 271 | witx::TypeRef::Value(type_witx) => { 272 | let t = ASType::from(type_witx.as_ref()); 273 | Self::define_as_type(w, type_name, &t)? 274 | } 275 | } 276 | Self::define_constants_for_type(w, type_name, constants)?; 277 | 278 | let docs = &type_witx.docs; 279 | if !docs.is_empty() { 280 | Self::write_docs(w, docs)?; 281 | } 282 | 283 | w.eob()?.write_line("---")?.eob()?; 284 | Ok(()) 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/doc/struct.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl DocGenerator { 6 | pub fn define_as_struct( 7 | w: &mut PrettyWriter, 8 | name: &str, 9 | members: &[ASStructMember], 10 | ) -> Result<(), Error> { 11 | w.write_lines(format!( 12 | "### {}\nStructure, with the following members:", 13 | name.as_type() 14 | ))? 15 | .eob()?; 16 | { 17 | let mut w = w.new_block(); 18 | for member in members { 19 | let member_type = member.type_.as_ref(); 20 | w.write_line(format!( 21 | "{}: {}", 22 | member.name.as_var(), 23 | member_type.as_lang() 24 | ))?; 25 | } 26 | } 27 | Ok(()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/doc/tuple.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl DocGenerator { 6 | pub fn define_as_tuple( 7 | w: &mut PrettyWriter, 8 | name: &str, 9 | members: &[ASTupleMember], 10 | ) -> Result<(), Error> { 11 | w.write_lines(format!( 12 | "### {}\nTuple, representing ({}).", 13 | name.as_type(), 14 | members 15 | .iter() 16 | .map(|member| { member.type_.as_lang() }) 17 | .collect::>() 18 | .join(", ") 19 | ))? 20 | .eob()?; 21 | Ok(()) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/doc/union.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl DocGenerator { 6 | fn define_union_member( 7 | w: &mut PrettyWriter, 8 | _union_name: &str, 9 | _i: usize, 10 | member: &ASUnionMember, 11 | ) -> Result<(), Error> { 12 | let member_type = member.type_.as_ref(); 13 | w.write_line(format!( 14 | "{}: {}", 15 | member.name.as_var(), 16 | member_type.as_lang(), 17 | ))?; 18 | Ok(()) 19 | } 20 | 21 | pub fn define_as_union( 22 | w: &mut PrettyWriter, 23 | name: &str, 24 | union_: &ASUnion, 25 | ) -> Result<(), Error> { 26 | let tag_repr = union_.tag_repr.as_ref(); 27 | w.write_lines(format!( 28 | "### {}\nTagged union with tag type: {} and the following possibilities:", 29 | name.as_type(), 30 | tag_repr.as_lang() 31 | ))? 32 | .eob()?; 33 | { 34 | let mut w = w.new_block(); 35 | for (i, member) in union_.members.iter().enumerate() { 36 | Self::define_union_member(&mut w, name, i, member)?; 37 | } 38 | } 39 | Ok(()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use witx::WitxError; 4 | 5 | #[derive(Debug)] 6 | pub enum Error { 7 | Witx(WitxError), 8 | Io(std::io::Error), 9 | } 10 | 11 | impl fmt::Display for Error { 12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 13 | write!(f, "{:?}", &self) 14 | } 15 | } 16 | 17 | impl std::error::Error for Error { 18 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 19 | None 20 | } 21 | } 22 | 23 | impl From for Error { 24 | fn from(e: std::io::Error) -> Self { 25 | Error::Io(e) 26 | } 27 | } 28 | 29 | impl From for Error { 30 | fn from(e: WitxError) -> Self { 31 | Self::Witx(e) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | 3 | use std::fs::File; 4 | use std::io::Write; 5 | 6 | use structopt::StructOpt; 7 | use strum::VariantNames; 8 | use strum_macros::{Display, EnumString, VariantNames}; 9 | 10 | mod assemblyscript; 11 | mod astype; 12 | mod cpp; 13 | mod doc; 14 | mod error; 15 | mod overview; 16 | mod pretty_writer; 17 | mod rust; 18 | mod zig; 19 | 20 | pub use crate::error::*; 21 | 22 | /// Generator output types 23 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Display, EnumString, VariantNames)] 24 | #[strum(serialize_all = "snake_case")] 25 | pub enum OutputType { 26 | #[strum(serialize = "assemblyscript")] 27 | AssemblyScript, 28 | Rust, 29 | Zig, 30 | Overview, 31 | #[strum(serialize = "doc", serialize = "markdown")] 32 | Doc, 33 | Cpp, 34 | } 35 | 36 | #[derive(Debug, Clone, PartialEq, Eq, StructOpt)] 37 | pub struct Config { 38 | /// Set the module name to use instead of reading it from the witx file 39 | #[structopt(short, long)] 40 | pub module_name: Option, 41 | 42 | /// Output file, or - for the standard output 43 | #[structopt(short, long)] 44 | pub output_file: Option, 45 | 46 | /// WITX files 47 | #[structopt()] 48 | pub witx_files: Vec, 49 | 50 | /// Output type 51 | #[structopt(short="t", long, possible_values=OutputType::VARIANTS)] 52 | pub output_type: OutputType, 53 | 54 | #[structopt(flatten)] 55 | pub flags: Options, 56 | } 57 | 58 | impl Default for Config { 59 | fn default() -> Self { 60 | Self { 61 | module_name: None, 62 | output_file: None, 63 | witx_files: vec![], 64 | output_type: OutputType::Doc, 65 | flags: Options { 66 | skip_header: false, 67 | skip_imports: false, 68 | }, 69 | } 70 | } 71 | } 72 | 73 | /// Options for WITX generators 74 | #[derive(Debug, Clone, PartialEq, Eq, StructOpt)] 75 | pub struct Options { 76 | /// Ignores imported types and functions 77 | #[structopt(short = "I", long)] 78 | skip_imports: bool, 79 | 80 | /// Do not generate a header 81 | #[structopt(short = "H", long)] 82 | skip_header: bool, 83 | } 84 | 85 | /// Abstract generator interface 86 | pub trait Generator { 87 | fn generate( 88 | &self, 89 | writer: &mut T, 90 | module_witx: witx::Module, 91 | options: &Options, 92 | ) -> Result<(), Error>; 93 | } 94 | 95 | fn get_generator(module: Option<&str>, output: OutputType) -> Box> { 96 | let m = module.map(|v| v.to_string()); 97 | 98 | match output { 99 | OutputType::AssemblyScript => Box::new(assemblyscript::AssemblyScriptGenerator::new(m)), 100 | OutputType::Zig => Box::new(zig::ZigGenerator::new(m)), 101 | OutputType::Rust => Box::new(rust::RustGenerator::new(m)), 102 | OutputType::Overview => Box::new(overview::OverviewGenerator::new(m)), 103 | OutputType::Doc => Box::new(doc::DocGenerator::new(m)), 104 | OutputType::Cpp => Box::new(cpp::CppGenerator::new(m)), 105 | } 106 | } 107 | 108 | /// Generate sources from WITX files using the provided config 109 | pub fn generate(cfg: &Config) -> Result<(), Error> { 110 | // generate all or generate no header no imports 111 | 112 | // Setup writer based on output file config 113 | let mut writer: Box = match cfg.output_file.as_deref() { 114 | None | Some("-") => Box::new(std::io::stdout()), 115 | Some(file) => Box::new(File::create(file).unwrap()), 116 | }; 117 | 118 | let mut flags = cfg.flags.clone(); 119 | 120 | for witx_file in &cfg.witx_files { 121 | // Parse WITX file 122 | let witx = witx::load(witx_file).unwrap(); 123 | 124 | // Create generator for the specified output type 125 | let generator = get_generator(cfg.module_name.as_deref(), cfg.output_type); 126 | 127 | // Generate output file 128 | generator.generate(&mut writer, witx, &flags).unwrap(); 129 | 130 | // Generate definitions only once if we have multiple input files 131 | flags.skip_imports = true; 132 | flags.skip_header = true; 133 | } 134 | 135 | Ok(()) 136 | } 137 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | 3 | use anyhow::Error; 4 | use structopt::StructOpt; 5 | 6 | fn main() -> Result<(), Error> { 7 | // Load options from CLI 8 | let cfg = witx_codegen::Config::from_args(); 9 | 10 | // Generate outputs 11 | witx_codegen::generate(&cfg)?; 12 | 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /src/overview/common.rs: -------------------------------------------------------------------------------- 1 | use crate::astype::*; 2 | 3 | pub trait Normalize { 4 | fn as_str(&self) -> &str; 5 | 6 | fn as_type(&self) -> String { 7 | self.as_str().to_string() 8 | } 9 | 10 | fn as_fn(&self) -> String { 11 | self.as_str().to_string() 12 | } 13 | 14 | fn as_fn_suffix(&self) -> String { 15 | self.as_str().to_string() 16 | } 17 | 18 | fn as_var(&self) -> String { 19 | self.as_str().to_string() 20 | } 21 | 22 | fn as_const(&self) -> String { 23 | self.as_str().to_string() 24 | } 25 | 26 | fn as_namespace(&self) -> String { 27 | self.as_str().to_string() 28 | } 29 | } 30 | 31 | impl> Normalize for T { 32 | fn as_str(&self) -> &str { 33 | self.as_ref() 34 | } 35 | } 36 | 37 | pub trait ToLanguageRepresentation { 38 | fn as_astype(&self) -> &ASType; 39 | 40 | fn to_string(&self) -> String { 41 | self.as_lang() 42 | } 43 | 44 | fn as_lang(&self) -> String { 45 | match self.as_astype() { 46 | ASType::Alias(alias) => alias.name.as_type(), 47 | ASType::Bool => "bool".to_string(), 48 | ASType::Char32 => "char32".to_string(), 49 | ASType::Char8 => "char8".to_string(), 50 | ASType::F32 => "f32".to_string(), 51 | ASType::F64 => "f64".to_string(), 52 | ASType::Handle(_resource_name) => "handle".to_string(), 53 | ASType::ConstPtr(pointee) => format!("ptr<{}>", pointee.to_string()), 54 | ASType::MutPtr(pointee) => format!("mut_ptr<{}>", pointee.to_string()), 55 | ASType::Option(_) => todo!(), 56 | ASType::Result(_) => todo!(), 57 | ASType::S8 => "i8".to_string(), 58 | ASType::S16 => "i16".to_string(), 59 | ASType::S32 => "i32".to_string(), 60 | ASType::S64 => "i64".to_string(), 61 | ASType::U8 => "u8".to_string(), 62 | ASType::U16 => "u16".to_string(), 63 | ASType::U32 => "u32".to_string(), 64 | ASType::U64 => "u64".to_string(), 65 | ASType::USize => "usize".to_string(), 66 | ASType::Void => "(empty)".to_string(), 67 | ASType::Constants(_) => unimplemented!(), 68 | ASType::Enum(enum_) => { 69 | format!("{} (enum)", enum_.repr.as_ref().as_lang()) 70 | } 71 | ASType::Struct(_) => unimplemented!(), 72 | ASType::Tuple(tuple_members) => { 73 | let tuple_types: Vec<_> = 74 | tuple_members.iter().map(|x| x.type_.to_string()).collect(); 75 | format!("({})", tuple_types.join(", ")) 76 | } 77 | ASType::Union(_) => unimplemented!(), 78 | ASType::Slice(element_type) => format!("mut_slice<{}>", element_type.as_lang()), 79 | ASType::String(_) => "string".to_string(), 80 | ASType::ReadBuffer(element_type) => format!("slice<{}>", element_type.as_lang()), 81 | ASType::WriteBuffer(element_type) => { 82 | format!("mut_slice<{}>", element_type.to_string()) 83 | } 84 | } 85 | } 86 | } 87 | 88 | impl ToLanguageRepresentation for ASType { 89 | fn as_astype(&self) -> &ASType { 90 | self 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/overview/function.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl OverviewGenerator { 6 | pub fn define_func( 7 | w: &mut PrettyWriter, 8 | _module_name: &str, 9 | func_witx: &witx::Function, 10 | ) -> Result<(), Error> { 11 | assert_eq!(func_witx.abi, witx::Abi::Preview1); 12 | let name = func_witx.name.as_str().to_string(); 13 | let params_witx = &func_witx.params; 14 | let mut params = vec![]; 15 | for param_witx in params_witx { 16 | let param_name = param_witx.name.as_str(); 17 | let param_type = ASType::from(¶m_witx.tref); 18 | params.push((param_name.to_string(), param_type)); 19 | } 20 | 21 | let results_witx = &func_witx.results; 22 | assert_eq!(results_witx.len(), 1); 23 | let result_witx = &results_witx[0]; 24 | let result = ASType::from(&result_witx.tref); 25 | let result = match result { 26 | ASType::Result(result) => result, 27 | _ => unreachable!(), 28 | }; 29 | 30 | let ok_type = result.ok_type.clone(); 31 | 32 | let mut results = vec![]; 33 | // A tuple in a result is expanded into additional parameters, transformed to 34 | // pointers 35 | if let ASType::Tuple(tuple_members) = ok_type.as_ref().leaf() { 36 | for (i, tuple_member) in tuple_members.iter().enumerate() { 37 | let name = format!("result{}_ptr", i); 38 | results.push((name, tuple_member.type_.clone())); 39 | } 40 | } else { 41 | let name = "result"; 42 | results.push((name.to_string(), ok_type)); 43 | } 44 | 45 | w.write_line(format!( 46 | "function {}(): {}", 47 | name.as_fn(), 48 | result.error_type.as_lang() 49 | ))?; 50 | if !params.is_empty() { 51 | let mut w = w.new_block(); 52 | w.write_line("- Input:")?; 53 | { 54 | let mut w = w.new_block(); 55 | for param in ¶ms { 56 | w.write_line(format!("- {}: {}", param.0.as_var(), param.1.as_lang()))?; 57 | } 58 | } 59 | } 60 | if !results.is_empty() { 61 | let mut w = w.new_block(); 62 | match results[0].1.as_ref() { 63 | ASType::Void if results.len() == 1 => { 64 | w.write_line("- No output")?; 65 | } 66 | _ => { 67 | w.write_line("- Output:")?; 68 | { 69 | let mut w = w.new_block(); 70 | for result in &results { 71 | let result_as_ptr = ASType::MutPtr(result.1.clone()); 72 | w.write_line(format!("- {}", result_as_ptr.as_lang()))?; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | w.eob()?; 79 | Ok(()) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/overview/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod function; 3 | mod r#struct; 4 | mod tuple; 5 | mod union; 6 | 7 | use std::io::Write; 8 | 9 | use common::*; 10 | 11 | use super::*; 12 | use crate::astype::*; 13 | use crate::error::*; 14 | use crate::pretty_writer::PrettyWriter; 15 | 16 | pub struct OverviewGenerator { 17 | module_name: Option, 18 | } 19 | 20 | impl OverviewGenerator { 21 | pub fn new(module_name: Option) -> Self { 22 | OverviewGenerator { module_name } 23 | } 24 | } 25 | 26 | impl Generator for OverviewGenerator { 27 | fn generate( 28 | &self, 29 | writer: &mut T, 30 | module_witx: witx::Module, 31 | options: &Options, 32 | ) -> Result<(), Error> { 33 | let mut w = PrettyWriter::new(writer, " "); 34 | let module_name = match &self.module_name { 35 | None => module_witx.name().as_str().to_string(), 36 | Some(module_name) => module_name.to_string(), 37 | }; 38 | let module_id = module_witx.module_id(); 39 | let skip_imports = options.skip_imports; 40 | 41 | if !options.skip_header { 42 | Self::header(&mut w)?; 43 | } 44 | 45 | let module_title_doc = format!( 46 | "---------------------- Module: [{}] ----------------------", 47 | module_name 48 | ); 49 | w.eob()?; 50 | w.write_line(module_title_doc)?; 51 | w.eob()?; 52 | 53 | for type_ in module_witx.typenames() { 54 | if skip_imports && &type_.module != module_id { 55 | continue; 56 | } 57 | let constants_for_type: Vec<_> = module_witx 58 | .constants() 59 | .filter_map(|x| { 60 | if x.ty == type_.name { 61 | Some(ASConstant { 62 | name: x.name.as_str().to_string(), 63 | value: x.value, 64 | }) 65 | } else { 66 | None 67 | } 68 | }) 69 | .collect(); 70 | Self::define_type(&mut w, type_.as_ref(), &constants_for_type)?; 71 | } 72 | 73 | for func in module_witx.funcs() { 74 | Self::define_func(&mut w, &module_name, func.as_ref())?; 75 | } 76 | 77 | Ok(()) 78 | } 79 | } 80 | 81 | impl OverviewGenerator { 82 | fn header(w: &mut PrettyWriter) -> Result<(), Error> { 83 | w.write_line("* API overview *")?; 84 | w.eob()?; 85 | Ok(()) 86 | } 87 | 88 | fn define_as_alias( 89 | w: &mut PrettyWriter, 90 | name: &str, 91 | other_type: &ASType, 92 | ) -> Result<(), Error> { 93 | w.write_line(format!( 94 | "alias {} = {}", 95 | name.as_type(), 96 | other_type.as_lang() 97 | ))?; 98 | Ok(()) 99 | } 100 | 101 | fn define_as_atom( 102 | w: &mut PrettyWriter, 103 | name: &str, 104 | type_: &ASType, 105 | ) -> Result<(), Error> { 106 | w.write_line(format!("alias {} = {}", name.as_type(), type_.as_lang()))?; 107 | Ok(()) 108 | } 109 | 110 | fn define_as_enum( 111 | w: &mut PrettyWriter, 112 | name: &str, 113 | enum_: &ASEnum, 114 | ) -> Result<(), Error> { 115 | let repr = enum_.repr.as_ref(); 116 | w.write_line(format!( 117 | "enum {}: (tag: {})", 118 | name.as_type(), 119 | repr.as_lang() 120 | ))?; 121 | { 122 | let mut w = w.new_block(); 123 | for choice in &enum_.choices { 124 | w.write_line(format!("- {}: {}", choice.name.as_const(), choice.value))?; 125 | } 126 | } 127 | Ok(()) 128 | } 129 | 130 | fn define_as_constants( 131 | w: &mut PrettyWriter, 132 | name: &str, 133 | constants: &ASConstants, 134 | ) -> Result<(), Error> { 135 | let repr = constants.repr.as_ref(); 136 | w.write_line(format!( 137 | "constants {}: (type: {})", 138 | name.as_type(), 139 | repr.as_lang() 140 | ))?; 141 | Self::define_constants_for_type(w, name, &constants.constants)?; 142 | Ok(()) 143 | } 144 | 145 | fn define_as_type( 146 | w: &mut PrettyWriter, 147 | name: &str, 148 | type_: &ASType, 149 | ) -> Result<(), Error> { 150 | match type_ { 151 | ASType::Alias(_) 152 | | ASType::Bool 153 | | ASType::Char8 154 | | ASType::Char32 155 | | ASType::F32 156 | | ASType::F64 157 | | ASType::U8 158 | | ASType::U16 159 | | ASType::U32 160 | | ASType::U64 161 | | ASType::S8 162 | | ASType::S16 163 | | ASType::S32 164 | | ASType::S64 165 | | ASType::USize 166 | | ASType::Handle(_) 167 | | ASType::Slice(_) 168 | | ASType::String(_) 169 | | ASType::ReadBuffer(_) 170 | | ASType::WriteBuffer(_) => Self::define_as_atom(w, name, type_)?, 171 | ASType::Enum(enum_) => Self::define_as_enum(w, name, enum_)?, 172 | ASType::Union(union_) => Self::define_as_union(w, name, union_)?, 173 | ASType::Constants(constants) => Self::define_as_constants(w, name, constants)?, 174 | ASType::Tuple(members) => Self::define_as_tuple(w, name, members)?, 175 | ASType::Struct(members) => Self::define_as_struct(w, name, members)?, 176 | _ => { 177 | dbg!(type_); 178 | unimplemented!(); 179 | } 180 | } 181 | Ok(()) 182 | } 183 | 184 | fn define_constants_for_type( 185 | w: &mut PrettyWriter, 186 | type_name: &str, 187 | constants: &[ASConstant], 188 | ) -> Result<(), Error> { 189 | if constants.is_empty() { 190 | return Ok(()); 191 | } 192 | w.write_line(format!("predefined constants for {}:", type_name.as_type()))?; 193 | { 194 | let mut w = w.new_block(); 195 | let mut hex = false; 196 | let mut single_bits: usize = 0; 197 | for constant in constants { 198 | if constant.value > 0xffff { 199 | hex = true; 200 | } 201 | if constant.value.count_ones() == 1 { 202 | single_bits += 1; 203 | } 204 | } 205 | if constants.len() > 2 && single_bits == constants.len() { 206 | hex = true; 207 | } 208 | for constant in constants { 209 | let value_s = if hex { 210 | format!("0x{:x}", constant.value) 211 | } else { 212 | format!("{}", constant.value) 213 | }; 214 | w.write_line(format!("- {} = {}", constant.name.as_const(), value_s))?; 215 | } 216 | } 217 | Ok(()) 218 | } 219 | 220 | fn define_type( 221 | w: &mut PrettyWriter, 222 | type_witx: &witx::NamedType, 223 | constants: &[ASConstant], 224 | ) -> Result<(), Error> { 225 | let type_name = type_witx.name.as_str(); 226 | let tref = &type_witx.tref; 227 | match tref { 228 | witx::TypeRef::Name(other_type) => { 229 | Self::define_as_alias(w, type_name, &ASType::from(&other_type.tref))? 230 | } 231 | witx::TypeRef::Value(type_witx) => { 232 | let t = ASType::from(type_witx.as_ref()); 233 | Self::define_as_type(w, type_name, &t)? 234 | } 235 | } 236 | Self::define_constants_for_type(w, type_name, constants)?; 237 | w.eob()?; 238 | Ok(()) 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/overview/struct.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl OverviewGenerator { 6 | pub fn define_as_struct( 7 | w: &mut PrettyWriter, 8 | name: &str, 9 | members: &[ASStructMember], 10 | ) -> Result<(), Error> { 11 | w.write_line(format!("struct {}:", name.as_type()))?; 12 | { 13 | let mut w = w.new_block(); 14 | for member in members { 15 | let member_type = member.type_.as_ref(); 16 | w.write_line(format!( 17 | "- {}: {}", 18 | member.name.as_var(), 19 | member_type.as_lang() 20 | ))?; 21 | } 22 | } 23 | Ok(()) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/overview/tuple.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl OverviewGenerator { 6 | pub fn define_as_tuple( 7 | w: &mut PrettyWriter, 8 | name: &str, 9 | members: &[ASTupleMember], 10 | ) -> Result<(), Error> { 11 | w.write_line(format!( 12 | "tuple {} = ({})", 13 | name.as_type(), 14 | members 15 | .iter() 16 | .map(|member| { member.type_.as_lang() }) 17 | .collect::>() 18 | .join(", ") 19 | ))?; 20 | Ok(()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/overview/union.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl OverviewGenerator { 6 | fn define_union_member( 7 | w: &mut PrettyWriter, 8 | _union_name: &str, 9 | _i: usize, 10 | member: &ASUnionMember, 11 | ) -> Result<(), Error> { 12 | let member_type = member.type_.as_ref(); 13 | w.write_line(format!( 14 | "- {}: {}", 15 | member.name.as_var(), 16 | member_type.as_lang(), 17 | ))?; 18 | Ok(()) 19 | } 20 | 21 | pub fn define_as_union( 22 | w: &mut PrettyWriter, 23 | name: &str, 24 | union_: &ASUnion, 25 | ) -> Result<(), Error> { 26 | let tag_repr = union_.tag_repr.as_ref(); 27 | w.write_line(format!( 28 | "union {}: (tag: {})", 29 | name.as_type(), 30 | tag_repr.as_lang() 31 | ))?; 32 | { 33 | let mut w = w.new_block(); 34 | for (i, member) in union_.members.iter().enumerate() { 35 | Self::define_union_member(&mut w, name, i, member)?; 36 | } 37 | } 38 | Ok(()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/pretty_writer.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::convert::Into; 3 | use std::io::prelude::*; 4 | use std::rc::Rc; 5 | 6 | use super::Error; 7 | 8 | pub struct PrettyWriter { 9 | writer: Rc>, 10 | indent: u32, 11 | indent_bytes: &'static str, 12 | continuation_bytes: &'static str, 13 | } 14 | 15 | impl Clone for PrettyWriter { 16 | fn clone(&self) -> Self { 17 | PrettyWriter { 18 | writer: self.writer.clone(), 19 | indent: self.indent, 20 | indent_bytes: self.indent_bytes, 21 | continuation_bytes: DEFAULT_CONTINUATION_BYTES, 22 | } 23 | } 24 | } 25 | 26 | const DEFAULT_CONTINUATION_BYTES: &str = " "; 27 | 28 | impl PrettyWriter { 29 | /// Create a new `PrettyWriter` with `indent` initial units of indentation 30 | pub fn new_with_indent(writer: W, indent: u32, indent_bytes: &'static str) -> Self { 31 | PrettyWriter { 32 | writer: Rc::new(RefCell::new(writer)), 33 | indent, 34 | indent_bytes, 35 | continuation_bytes: DEFAULT_CONTINUATION_BYTES, 36 | } 37 | } 38 | 39 | /// Create a new `PrettyWriter` with no initial indentation 40 | pub fn new(writer: W, indent_bytes: &'static str) -> Self { 41 | PrettyWriter::new_with_indent(writer, 0, indent_bytes) 42 | } 43 | 44 | /// Create a writer based on a existing writer, but with no indentation` 45 | #[allow(dead_code)] 46 | pub fn new_from_writer(&mut self) -> Self { 47 | PrettyWriter { 48 | writer: self.writer.clone(), 49 | indent: 0, 50 | indent_bytes: self.indent_bytes, 51 | continuation_bytes: DEFAULT_CONTINUATION_BYTES, 52 | } 53 | } 54 | 55 | /// Create an indented block within the current `PrettyWriter` 56 | pub fn new_block(&mut self) -> Self { 57 | PrettyWriter { 58 | writer: self.writer.clone(), 59 | indent: self.indent + 1, 60 | indent_bytes: self.indent_bytes, 61 | continuation_bytes: DEFAULT_CONTINUATION_BYTES, 62 | } 63 | } 64 | 65 | fn _write_all>(writer: &mut W, buf: T) -> Result<(), Error> { 66 | let buf = buf.as_ref(); 67 | writer.write_all(buf).map_err(Into::into) 68 | } 69 | 70 | /// Return the current indentation level 71 | #[allow(dead_code)] 72 | pub fn indent_level(&self) -> u32 { 73 | self.indent 74 | } 75 | 76 | /// Output an indentation string 77 | pub fn indent(&mut self) -> Result<&mut Self, Error> { 78 | let indent_bytes = &self.indent_bytes; 79 | { 80 | let mut writer = self.writer.borrow_mut(); 81 | for _ in 0..self.indent { 82 | Self::_write_all(&mut writer, indent_bytes)? 83 | } 84 | } 85 | Ok(self) 86 | } 87 | 88 | /// Output a space 89 | #[allow(dead_code)] 90 | pub fn space(&mut self) -> Result<&mut Self, Error> { 91 | Self::_write_all(&mut self.writer.borrow_mut(), b" ")?; 92 | Ok(self) 93 | } 94 | 95 | /// Output an end of line 96 | pub fn eol(&mut self) -> Result<&mut Self, Error> { 97 | Self::_write_all(&mut self.writer.borrow_mut(), b"\n")?; 98 | Ok(self) 99 | } 100 | 101 | /// Output a block separator 102 | pub fn eob(&mut self) -> Result<&mut Self, Error> { 103 | self.eol() 104 | } 105 | 106 | /// Continuation 107 | pub fn continuation(&mut self) -> Result<&mut Self, Error> { 108 | self.indent()?; 109 | let continuation_bytes = &self.continuation_bytes; 110 | Self::_write_all(&mut self.writer.borrow_mut(), continuation_bytes)?; 111 | Ok(self) 112 | } 113 | 114 | /// Write raw data 115 | pub fn write>(&mut self, buf: T) -> Result<&mut Self, Error> { 116 | let buf = buf.as_ref(); 117 | Self::_write_all(&mut self.writer.borrow_mut(), buf)?; 118 | Ok(self) 119 | } 120 | 121 | /// Indent, write raw data and terminate with an end of line 122 | pub fn write_line>(&mut self, buf: T) -> Result<&mut Self, Error> { 123 | let buf = buf.as_ref(); 124 | self.indent()?.write(buf)?.eol() 125 | } 126 | 127 | /// Write multiple indented lines 128 | pub fn write_lines>(&mut self, buf: T) -> Result<&mut Self, Error> { 129 | let buf = buf.as_ref(); 130 | for line in buf.lines().flatten() { 131 | self.write_line(line)?; 132 | } 133 | Ok(self) 134 | } 135 | 136 | /// Indent, write raw data after a continuation and terminate with an end of 137 | /// line 138 | pub fn write_line_continued>(&mut self, buf: T) -> Result<&mut Self, Error> { 139 | let buf = buf.as_ref(); 140 | self.continuation()?.write(buf)?.eol() 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/rust/common.rs: -------------------------------------------------------------------------------- 1 | use convert_case::{Case, Casing}; 2 | 3 | use super::tuple::Tuple; 4 | use crate::astype::*; 5 | 6 | pub trait IsNullable { 7 | fn is_nullable(&self) -> bool; 8 | } 9 | 10 | impl IsNullable for ASType { 11 | fn is_nullable(&self) -> bool { 12 | matches!( 13 | self, 14 | ASType::ConstPtr(_) 15 | | ASType::MutPtr(_) 16 | | ASType::ReadBuffer(_) 17 | | ASType::WriteBuffer(_) 18 | | ASType::Enum(_) 19 | | ASType::Struct(_) 20 | | ASType::Tuple(_) 21 | | ASType::Union(_) 22 | ) 23 | } 24 | } 25 | 26 | pub trait Normalize { 27 | fn as_str(&self) -> &str; 28 | 29 | fn as_type(&self) -> String { 30 | self.as_str().to_case(Case::Pascal) 31 | } 32 | 33 | fn as_fn(&self) -> String { 34 | escape_reserved_word(&self.as_str().to_case(Case::Snake)) 35 | } 36 | 37 | fn as_fn_suffix(&self) -> String { 38 | escape_reserved_word(&self.as_str().to_case(Case::Snake)) 39 | } 40 | 41 | fn as_var(&self) -> String { 42 | escape_reserved_word(&self.as_str().to_case(Case::Snake)) 43 | } 44 | 45 | fn as_const(&self) -> String { 46 | self.as_str().to_case(Case::UpperSnake) 47 | } 48 | 49 | fn as_namespace(&self) -> String { 50 | self.as_str().to_string().to_case(Case::UpperSnake) 51 | } 52 | } 53 | 54 | impl> Normalize for T { 55 | fn as_str(&self) -> &str { 56 | self.as_ref() 57 | } 58 | } 59 | 60 | pub trait ToLanguageRepresentation { 61 | fn as_astype(&self) -> &ASType; 62 | 63 | fn to_string(&self) -> String { 64 | self.as_lang() 65 | } 66 | 67 | fn as_lang(&self) -> String { 68 | match self.as_astype() { 69 | ASType::Alias(alias) => alias.name.as_type(), 70 | ASType::Bool => "bool".to_string(), 71 | ASType::Char32 => "Char32".to_string(), 72 | ASType::Char8 => "Char8".to_string(), 73 | ASType::F32 => "f32".to_string(), 74 | ASType::F64 => "f64".to_string(), 75 | ASType::Handle(_resource_name) => "WasiHandle".to_string(), 76 | ASType::ConstPtr(pointee) => format!("WasiPtr<{}>", pointee.to_string()), 77 | ASType::MutPtr(pointee) => format!("WasiMutPtr<{}>", pointee.to_string()), 78 | ASType::Option(_) => todo!(), 79 | ASType::Result(_) => todo!(), 80 | ASType::S8 => "i8".to_string(), 81 | ASType::S16 => "i16".to_string(), 82 | ASType::S32 => "i32".to_string(), 83 | ASType::S64 => "i64".to_string(), 84 | ASType::U8 => "u8".to_string(), 85 | ASType::U16 => "u16".to_string(), 86 | ASType::U32 => "u32".to_string(), 87 | ASType::U64 => "u64".to_string(), 88 | ASType::USize => "usize".to_string(), 89 | ASType::Void => "()".to_string(), 90 | ASType::Constants(_) => unimplemented!(), 91 | ASType::Enum(enum_) => { 92 | format!("{} /* Enum */", enum_.repr.as_ref().as_lang()) 93 | } 94 | ASType::Struct(_) => unimplemented!(), 95 | ASType::Tuple(tuple_members) => Tuple::name_for(tuple_members).as_type(), 96 | ASType::Union(_) => unimplemented!(), 97 | ASType::Slice(element_type) => format!("WasiMutSlice<{}>", element_type.as_lang()), 98 | ASType::String(_) => "WasiString".to_string(), 99 | ASType::ReadBuffer(element_type) => format!("WasiSlice<{}>", element_type.as_lang()), 100 | ASType::WriteBuffer(element_type) => { 101 | format!("WasiMutSlice<{}>", element_type.to_string()) 102 | } 103 | } 104 | } 105 | } 106 | 107 | impl ToLanguageRepresentation for ASType { 108 | fn as_astype(&self) -> &ASType { 109 | self 110 | } 111 | } 112 | 113 | /// Checks the given word against a list of reserved keywords. 114 | /// If the given word conflicts with a keyword, a trailing underscore will be 115 | /// appended. 116 | /// 117 | /// Adapted from [wiggle](https://docs.rs/wiggle/latest/wiggle/index.html) 118 | pub fn escape_reserved_word(word: &str) -> String { 119 | if STRICT.iter().chain(RESERVED).any(|k| *k == word) { 120 | // If the camel-cased string matched any strict or reserved keywords, then 121 | // append a trailing underscore to the identifier we generate. 122 | format!("{}_", word) 123 | } else { 124 | word.to_string() // Otherwise, use the string as is. 125 | } 126 | } 127 | 128 | /// Strict keywords. 129 | /// 130 | /// Source: [The Rust Reference][https://doc.rust-lang.org/reference/keywords.html#strict-keywords] 131 | const STRICT: &[&str] = &[ 132 | "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern", 133 | "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", 134 | "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", 135 | "unsafe", "use", "where", "while", 136 | ]; 137 | 138 | /// Reserved keywords. 139 | /// 140 | /// These keywords aren't used yet, but they are reserved for future use. They 141 | /// have the same restrictions as strict keywords. The reasoning behind this is 142 | /// to make current programs forward compatible with future versions of Rust by 143 | /// forbidding them to use these keywords. 144 | /// 145 | /// Source: [The Rust Reference](https://doc.rust-lang.org/reference/keywords.html#reserved-keywords) 146 | const RESERVED: &[&str] = &[ 147 | "abstract", "become", "box", "do", "final", "macro", "override", "priv", "try", "typeof", 148 | "unsized", "virtual", "yield", 149 | ]; 150 | -------------------------------------------------------------------------------- /src/rust/function.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl RustGenerator { 6 | pub fn define_func( 7 | w: &mut PrettyWriter, 8 | module_name: &str, 9 | func_witx: &witx::Function, 10 | ) -> Result<(), Error> { 11 | assert_eq!(func_witx.abi, witx::Abi::Preview1); 12 | let name = func_witx.name.as_str().to_string(); 13 | let params_witx = &func_witx.params; 14 | let mut params = vec![]; 15 | for param_witx in params_witx { 16 | let param_name = param_witx.name.as_str(); 17 | let param_type = ASType::from(¶m_witx.tref); 18 | params.push((param_name.to_string(), param_type)); 19 | } 20 | 21 | let results_witx = &func_witx.results; 22 | assert_eq!(results_witx.len(), 1); 23 | let result_witx = &results_witx[0]; 24 | let result = ASType::from(&result_witx.tref); 25 | let result = match result { 26 | ASType::Result(result) => result, 27 | _ => unreachable!(), 28 | }; 29 | 30 | let ok_type = result.ok_type.clone(); 31 | 32 | let docs = &func_witx.docs; 33 | if !docs.is_empty() { 34 | Self::write_docs(w, docs)?; 35 | } 36 | 37 | let mut params_decomposed = vec![]; 38 | 39 | for param in ¶ms { 40 | let mut decomposed = param.1.decompose(¶m.0, false); 41 | params_decomposed.append(&mut decomposed); 42 | } 43 | 44 | let mut results = vec![]; 45 | // A tuple in a result is expanded into additional parameters, transformed to 46 | // pointers 47 | if let ASType::Tuple(tuple_members) = ok_type.as_ref().leaf() { 48 | for (i, tuple_member) in tuple_members.iter().enumerate() { 49 | let name = format!("result{}_ptr", i); 50 | results.push((name, tuple_member.type_.clone())); 51 | } 52 | } else { 53 | let name = "result_ptr"; 54 | results.push((name.to_string(), ok_type)); 55 | } 56 | let mut results_decomposed = vec![]; 57 | for result in &results { 58 | let mut decomposed = result.1.decompose(&result.0, true); 59 | results_decomposed.append(&mut decomposed); 60 | } 61 | 62 | Self::define_func_raw( 63 | w, 64 | module_name, 65 | &name, 66 | ¶ms_decomposed, 67 | &results_decomposed, 68 | &result, 69 | )?; 70 | 71 | let signature_witx = func_witx.wasm_signature(witx::CallMode::DefinedImport); 72 | let params_count_witx = signature_witx.params.len() + signature_witx.results.len(); 73 | assert_eq!( 74 | params_count_witx, 75 | params_decomposed.len() + results_decomposed.len() + 1 76 | ); 77 | 78 | Ok(()) 79 | } 80 | 81 | fn define_func_raw( 82 | w: &mut PrettyWriter, 83 | module_name: &str, 84 | name: &str, 85 | params_decomposed: &[ASTypeDecomposed], 86 | results_decomposed: &[ASTypeDecomposed], 87 | result: &ASResult, 88 | ) -> Result<(), Error> { 89 | let results_decomposed_deref = results_decomposed 90 | .iter() 91 | .map(|result_ptr_type| match result_ptr_type.type_.as_ref() { 92 | ASType::MutPtr(result_type) => ASTypeDecomposed { 93 | name: result_ptr_type.name.clone(), 94 | type_: result_type.clone(), 95 | }, 96 | _ => panic!("Result type is not a pointer"), 97 | }) 98 | .collect::>(); 99 | let results_set = results_decomposed_deref 100 | .iter() 101 | .map(|result| result.type_.as_lang()) 102 | .collect::>(); 103 | let rust_fn_result_str = match results_set.len() { 104 | 0 => "()".to_string(), 105 | 1 => results_set[0].clone(), 106 | _ => format!("({})", results_set.join(", ")), 107 | }; 108 | w.indent()?.write(format!("pub fn {}(", name.as_fn()))?; 109 | if !params_decomposed.is_empty() || !results_decomposed.is_empty() { 110 | w.eol()?; 111 | } 112 | for param in params_decomposed { 113 | w.write_line_continued(format!( 114 | "{}: {},", 115 | param.name.as_var(), 116 | param.type_.as_lang(), 117 | ))?; 118 | } 119 | w.write_line(format!(") -> Result<{}, Error> {{", rust_fn_result_str))?; 120 | { 121 | let mut w = w.new_block(); 122 | 123 | // Inner (raw) definition 124 | { 125 | w.write_line(format!("#[link(wasm_import_module = \"{}\")]", module_name))?; 126 | w.write_line("extern \"C\" {")?; 127 | { 128 | let mut w = w.new_block(); 129 | w.indent()?.write(format!("fn {}(", name.as_fn()))?; 130 | if !params_decomposed.is_empty() { 131 | w.eol()?; 132 | } 133 | for param in params_decomposed.iter().chain(results_decomposed.iter()) { 134 | w.write_line_continued(format!( 135 | "{}: {},", 136 | param.name.as_var(), 137 | param.type_.as_lang(), 138 | ))?; 139 | } 140 | w.write_line(format!(") -> {};", result.error_type.as_lang()))?; 141 | } 142 | w.write_line("}")?; 143 | } 144 | 145 | // Wrapper 146 | for result in &results_decomposed_deref { 147 | w.write_line(format!( 148 | "let mut {} = std::mem::MaybeUninit::uninit();", 149 | result.name.as_var() 150 | ))?; 151 | } 152 | 153 | w.write_line(format!("let res = unsafe {{ {}(", name.as_fn()))?; 154 | for param in params_decomposed { 155 | w.write_line_continued(format!("{},", param.name.as_var()))?; 156 | } 157 | for result in results_decomposed_deref.iter() { 158 | w.write_line_continued(format!("{}.as_mut_ptr(),", result.name.as_var()))?; 159 | } 160 | w.write_line(")};")?; 161 | w.write_lines( 162 | "if res != 0 { 163 | return Err(Error::WasiError(res as _)); 164 | }", 165 | )?; 166 | let res_str = match results_decomposed.len() { 167 | 0 => "()".to_string(), 168 | 1 => format!( 169 | "unsafe {{ {}.assume_init() }}", 170 | results_decomposed_deref[0].name.as_var() 171 | ), 172 | _ => format!( 173 | "unsafe {{ ({}) }}", 174 | results_decomposed_deref 175 | .iter() 176 | .map(|result| format!("{}.assume_init()", result.name.as_var())) 177 | .collect::>() 178 | .join(", ") 179 | ), 180 | }; 181 | w.write_line(format!("Ok({})", res_str))?; 182 | }; 183 | w.write_line("}")?; 184 | w.eob()?; 185 | 186 | Ok(()) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/rust/header.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl RustGenerator { 6 | pub fn header(w: &mut PrettyWriter) -> Result<(), Error> { 7 | w.write_lines( 8 | " 9 | // 10 | // This file was automatically generated by witx-codegen - Do not edit manually. 11 | //", 12 | )?; 13 | w.write_lines( 14 | " 15 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 16 | pub enum Error { 17 | WasiError(i32), 18 | } 19 | impl std::error::Error for Error {} 20 | impl std::fmt::Display for Error { 21 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 22 | match self { 23 | Error::WasiError(e) => write!(f, \"Wasi error {}\", e), 24 | } 25 | } 26 | } 27 | 28 | pub type WasiHandle = i32; 29 | pub type Char8 = u8; 30 | pub type Char32 = u32; 31 | pub type WasiPtr = *const T; 32 | pub type WasiMutPtr = *mut T; 33 | pub type WasiStringBytesPtr = WasiPtr; 34 | 35 | #[repr(C)] 36 | #[derive(Copy, Clone, Debug)] 37 | pub struct WasiSlice { 38 | ptr: WasiPtr, 39 | len: usize, 40 | } 41 | 42 | #[repr(C)] 43 | #[derive(Copy, Clone, Debug)] 44 | pub struct WasiMutSlice { 45 | ptr: WasiMutPtr, 46 | len: usize, 47 | } 48 | 49 | impl WasiSlice { 50 | pub fn as_slice(&self) -> &[T] { 51 | unsafe { std::slice::from_raw_parts(self.ptr, self.len) } 52 | } 53 | 54 | pub fn from_slice(&self, slice: &[T]) -> Self { 55 | WasiSlice { 56 | ptr: slice.as_ptr() as _, 57 | len: slice.len(), 58 | } 59 | } 60 | } 61 | 62 | impl WasiMutSlice { 63 | pub fn as_slice(&self) -> &[T] { 64 | unsafe { std::slice::from_raw_parts(self.ptr, self.len) } 65 | } 66 | 67 | pub fn as_mut_slice(&self) -> &mut [T] { 68 | unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) } 69 | } 70 | 71 | pub fn from_slice(&self, slice: &[T]) -> Self { 72 | WasiMutSlice { 73 | ptr: slice.as_ptr() as _, 74 | len: slice.len(), 75 | } 76 | } 77 | 78 | pub fn from_mut_slice(&self, slice: &mut [T]) -> Self { 79 | WasiMutSlice { 80 | ptr: slice.as_mut_ptr(), 81 | len: slice.len(), 82 | } 83 | } 84 | } 85 | 86 | #[repr(C)] 87 | #[derive(Copy, Clone, Debug)] 88 | pub struct WasiString { 89 | ptr: WasiStringBytesPtr, 90 | len: usize, 91 | } 92 | 93 | impl> From for WasiString { 94 | fn from(s: T) -> Self { 95 | let s = s.as_ref(); 96 | WasiString { 97 | ptr: s.as_ptr() as _, 98 | len: s.len(), 99 | } 100 | } 101 | } 102 | 103 | impl WasiString { 104 | pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> { 105 | std::str::from_utf8(unsafe { std::slice::from_raw_parts(self.ptr, self.len) }) 106 | } 107 | 108 | pub fn as_slice(&self) -> &[u8] { 109 | unsafe { std::slice::from_raw_parts(self.ptr, self.len) } 110 | } 111 | 112 | pub fn from_slice(&self, slice: &[u8]) -> Self { 113 | WasiString { 114 | ptr: slice.as_ptr() as _, 115 | len: slice.len(), 116 | } 117 | } 118 | } 119 | ", 120 | )?; 121 | w.eob()?; 122 | Ok(()) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/rust/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod function; 3 | mod header; 4 | mod r#struct; 5 | mod tuple; 6 | mod union; 7 | 8 | use std::io::Write; 9 | 10 | use common::*; 11 | 12 | use super::*; 13 | use crate::astype::*; 14 | use crate::error::*; 15 | use crate::pretty_writer::PrettyWriter; 16 | 17 | pub struct RustGenerator { 18 | module_name: Option, 19 | } 20 | 21 | impl RustGenerator { 22 | pub fn new(module_name: Option) -> Self { 23 | RustGenerator { module_name } 24 | } 25 | } 26 | 27 | impl Generator for RustGenerator { 28 | fn generate( 29 | &self, 30 | writer: &mut T, 31 | module_witx: witx::Module, 32 | options: &Options, 33 | ) -> Result<(), Error> { 34 | let mut w = PrettyWriter::new(writer, " "); 35 | let module_name = match &self.module_name { 36 | None => module_witx.name().as_str().to_string(), 37 | Some(module_name) => module_name.to_string(), 38 | }; 39 | let module_id = module_witx.module_id(); 40 | let skip_imports = options.skip_imports; 41 | 42 | if !options.skip_header { 43 | Self::header(&mut w)?; 44 | } 45 | 46 | let module_title_comments = format!( 47 | "---------------------- Module: [{}] ----------------------", 48 | module_name 49 | ); 50 | Self::write_comments(&mut w, &module_title_comments)?; 51 | w.eob()?; 52 | 53 | for type_ in module_witx.typenames() { 54 | if skip_imports && &type_.module != module_id { 55 | continue; 56 | } 57 | let constants_for_type: Vec<_> = module_witx 58 | .constants() 59 | .filter_map(|x| { 60 | if x.ty == type_.name { 61 | Some(ASConstant { 62 | name: x.name.as_str().to_string(), 63 | value: x.value, 64 | }) 65 | } else { 66 | None 67 | } 68 | }) 69 | .collect(); 70 | Self::define_type(&mut w, type_.as_ref(), &constants_for_type)?; 71 | } 72 | 73 | for func in module_witx.funcs() { 74 | Self::define_func(&mut w, &module_name, func.as_ref())?; 75 | } 76 | 77 | Ok(()) 78 | } 79 | } 80 | 81 | impl RustGenerator { 82 | fn write_docs(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 83 | if docs.is_empty() { 84 | return Ok(()); 85 | } 86 | for docs_line in docs.lines() { 87 | w.write_line(format!("/// {}", docs_line))?; 88 | } 89 | Ok(()) 90 | } 91 | 92 | fn write_comments(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 93 | if docs.is_empty() { 94 | return Ok(()); 95 | } 96 | for docs_line in docs.lines() { 97 | w.write_line(format!("// {}", docs_line))?; 98 | } 99 | Ok(()) 100 | } 101 | 102 | fn define_as_alias( 103 | w: &mut PrettyWriter, 104 | name: &str, 105 | other_type: &ASType, 106 | ) -> Result<(), Error> { 107 | w.write_line(format!( 108 | "pub type {} = {};", 109 | name.as_type(), 110 | other_type.as_lang() 111 | ))?; 112 | Ok(()) 113 | } 114 | 115 | fn define_as_atom( 116 | w: &mut PrettyWriter, 117 | name: &str, 118 | type_: &ASType, 119 | ) -> Result<(), Error> { 120 | w.write_line(format!( 121 | "pub type {} = {};", 122 | name.as_type(), 123 | type_.as_lang() 124 | ))?; 125 | Ok(()) 126 | } 127 | 128 | fn define_as_enum( 129 | w: &mut PrettyWriter, 130 | name: &str, 131 | enum_: &ASEnum, 132 | ) -> Result<(), Error> { 133 | let repr = enum_.repr.as_ref(); 134 | w.write_line(format!("pub type {} = {};", name.as_type(), repr.as_lang()))?; 135 | w.eob()?; 136 | w.write_line("#[allow(non_snake_case)]")?; 137 | w.write_line(format!("pub mod {} {{", name.as_namespace()))?; 138 | { 139 | let mut w = w.new_block(); 140 | w.write_line(format!("use super::{};", name.as_type()))?; 141 | for choice in &enum_.choices { 142 | w.write_line(format!( 143 | "pub const {}: {} = {};", 144 | choice.name.as_const(), 145 | name.as_type(), 146 | choice.value 147 | ))?; 148 | } 149 | } 150 | w.write_line("}")?; 151 | Ok(()) 152 | } 153 | 154 | fn define_as_constants( 155 | w: &mut PrettyWriter, 156 | name: &str, 157 | constants: &ASConstants, 158 | ) -> Result<(), Error> { 159 | let repr = constants.repr.as_ref(); 160 | w.write_line(format!("pub type {} = {};", name.as_type(), repr.as_lang()))?; 161 | w.eob()?; 162 | Self::define_constants_for_type(w, name, &constants.constants)?; 163 | Ok(()) 164 | } 165 | 166 | fn define_as_type( 167 | w: &mut PrettyWriter, 168 | name: &str, 169 | type_: &ASType, 170 | ) -> Result<(), Error> { 171 | match type_ { 172 | ASType::Alias(_) 173 | | ASType::Bool 174 | | ASType::Char8 175 | | ASType::Char32 176 | | ASType::F32 177 | | ASType::F64 178 | | ASType::U8 179 | | ASType::U16 180 | | ASType::U32 181 | | ASType::U64 182 | | ASType::S8 183 | | ASType::S16 184 | | ASType::S32 185 | | ASType::S64 186 | | ASType::USize 187 | | ASType::Handle(_) 188 | | ASType::Slice(_) 189 | | ASType::String(_) 190 | | ASType::ReadBuffer(_) 191 | | ASType::WriteBuffer(_) => Self::define_as_atom(w, name, type_)?, 192 | ASType::Enum(enum_) => Self::define_as_enum(w, name, enum_)?, 193 | ASType::Union(union_) => Self::define_as_union(w, name, union_)?, 194 | ASType::Constants(constants) => Self::define_as_constants(w, name, constants)?, 195 | ASType::Tuple(members) => Self::define_as_tuple(w, name, members)?, 196 | ASType::Struct(members) => Self::define_as_struct(w, name, members)?, 197 | _ => { 198 | dbg!(type_); 199 | unimplemented!(); 200 | } 201 | } 202 | Ok(()) 203 | } 204 | 205 | fn define_constants_for_type( 206 | w: &mut PrettyWriter, 207 | type_name: &str, 208 | constants: &[ASConstant], 209 | ) -> Result<(), Error> { 210 | if constants.is_empty() { 211 | return Ok(()); 212 | } 213 | w.write_line("#[allow(non_snake_case)]")?; 214 | w.write_line(format!("pub mod {} {{", type_name.as_namespace()))?; 215 | { 216 | let mut w = w.new_block(); 217 | w.write_line(format!("use super::{};", type_name.as_type()))?; 218 | 219 | let mut hex = false; 220 | let mut single_bits: usize = 0; 221 | for constant in constants { 222 | if constant.value > 0xffff { 223 | hex = true; 224 | } 225 | if constant.value.count_ones() == 1 { 226 | single_bits += 1; 227 | } 228 | } 229 | if constants.len() > 2 && single_bits == constants.len() { 230 | hex = true; 231 | } 232 | for constant in constants { 233 | let value_s = if hex { 234 | format!("0x{:x}", constant.value) 235 | } else { 236 | format!("{}", constant.value) 237 | }; 238 | w.write_line(format!( 239 | "pub const {}: {} = {};", 240 | constant.name.as_const(), 241 | type_name.as_type(), 242 | value_s 243 | ))?; 244 | } 245 | } 246 | w.write_line("}")?; 247 | w.eob()?; 248 | Ok(()) 249 | } 250 | 251 | fn define_type( 252 | w: &mut PrettyWriter, 253 | type_witx: &witx::NamedType, 254 | constants: &[ASConstant], 255 | ) -> Result<(), Error> { 256 | let docs = &type_witx.docs; 257 | if !docs.is_empty() { 258 | Self::write_docs(w, docs)?; 259 | } 260 | let type_name = type_witx.name.as_str(); 261 | let tref = &type_witx.tref; 262 | match tref { 263 | witx::TypeRef::Name(other_type) => { 264 | Self::define_as_alias(w, type_name, &ASType::from(&other_type.tref))? 265 | } 266 | witx::TypeRef::Value(type_witx) => { 267 | let t = ASType::from(type_witx.as_ref()); 268 | Self::define_as_type(w, type_name, &t)? 269 | } 270 | } 271 | w.eob()?; 272 | Self::define_constants_for_type(w, type_name, constants)?; 273 | Ok(()) 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/rust/struct.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl RustGenerator { 6 | pub fn define_as_struct( 7 | w: &mut PrettyWriter, 8 | name: &str, 9 | members: &[ASStructMember], 10 | ) -> Result<(), Error> { 11 | w.write_line("#[repr(C, packed)]")? 12 | .write_line("#[derive(Copy,Clone)]")? 13 | .write_line(format!("pub struct {} {{", name.as_type()))?; 14 | { 15 | let mut w = w.new_block(); 16 | for member in members { 17 | let member_type = member.type_.as_ref(); 18 | w.write_line(format!( 19 | "pub {}: {},", 20 | member.name.as_var(), 21 | member_type.as_lang() 22 | ))?; 23 | 24 | let pad_len = member.padding; 25 | for i in 0..(pad_len & 1) { 26 | w.write_line(format!("__pad8_{}: u8,", i))?; 27 | } 28 | for i in 0..(pad_len & 3) / 2 { 29 | w.write_line(format!("__pad16_{}: u16,", i))?; 30 | } 31 | for i in 0..(pad_len & 7) / 4 { 32 | w.write_line(format!("__pad32_{}: u32,", i))?; 33 | } 34 | for i in 0..pad_len / 8 { 35 | w.write_line(format!("__pad64_{}: u64,", i))?; 36 | } 37 | } 38 | } 39 | w.write_line("}")?.eob()?; 40 | Ok(()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/rust/tuple.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | pub struct Tuple; 6 | 7 | impl Tuple { 8 | pub fn name_for(tuple_members: &[ASTupleMember]) -> String { 9 | format!( 10 | "WasiTuple{}{}", 11 | tuple_members.len(), 12 | tuple_members 13 | .iter() 14 | .map(|member| member.type_.to_string()) 15 | .collect::>() 16 | .join("_") 17 | ) 18 | } 19 | } 20 | 21 | impl RustGenerator { 22 | pub fn define_as_tuple( 23 | w: &mut PrettyWriter, 24 | name: &str, 25 | members: &[ASTupleMember], 26 | ) -> Result<(), Error> { 27 | w.write_line("#[repr(C, packed)]")? 28 | .write_line("#[derive(Copy, Clone, Debug)]")? 29 | .write_line(format!("pub struct {} {{ // -- Tuple", name.as_type()))?; 30 | { 31 | let mut w = w.new_block(); 32 | for (i, member) in members.iter().enumerate() { 33 | let member_type = member.type_.as_ref(); 34 | w.write_line(format!("pub v{}: {},", i, member_type.as_lang()))?; 35 | 36 | let pad_len = member.padding; 37 | for i in 0..(pad_len & 1) { 38 | w.write_line(format!("__pad8_{}: u8,", i))?; 39 | } 40 | for i in 0..(pad_len & 3) / 2 { 41 | w.write_line(format!("__pad16_{}: u16,", i))?; 42 | } 43 | for i in 0..(pad_len & 7) / 4 { 44 | w.write_line(format!("__pad32_{}: u32,", i))?; 45 | } 46 | for i in 0..pad_len / 8 { 47 | w.write_line(format!("__pad64_{}: u64,", i))?; 48 | } 49 | } 50 | } 51 | w.write_line("}")?.eob()?; 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/rust/union.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl RustGenerator { 6 | fn define_union_member_accessors( 7 | w: &mut PrettyWriter, 8 | _union_name: &str, 9 | i: usize, 10 | member: &ASUnionMember, 11 | inner_name: &str, 12 | ) -> Result<(), Error> { 13 | let name = &member.name; 14 | let member_is_void = matches!(member.type_.as_ref(), ASType::Void); 15 | 16 | if member_is_void { 17 | // new_* 18 | w.write_line(format!("pub fn new_{}() -> Self {{", name.as_fn_suffix(),))?; 19 | { 20 | let mut w = w.new_block(); 21 | w.write_line(format!("Self::new({})", i))?; 22 | } 23 | w.write_line("}")?.eob()?; 24 | } else { 25 | // !member_is_void 26 | // new_* 27 | w.write_line(format!( 28 | "pub fn new_{}(val: {}) -> Self {{", 29 | name.as_fn_suffix(), 30 | member.type_.as_lang() 31 | ))?; 32 | { 33 | let mut w = w.new_block(); 34 | w.write_line(format!("let mut tu = Self::new({});", i))?; 35 | w.write_line(format!( 36 | "tu.member = std::mem::MaybeUninit::new({} {{ {}: val }});", 37 | inner_name.as_type(), 38 | member.name.as_var() 39 | ))?; 40 | w.write_line("tu")?; 41 | } 42 | w.write_line("}")?.eob()?; 43 | 44 | // get_* 45 | w.write_line(format!( 46 | "pub fn into_{}(self) -> {} {{", 47 | name.as_fn_suffix(), 48 | member.type_.as_lang() 49 | ))?; 50 | { 51 | let mut w = w.new_block(); 52 | w.write_line(format!("assert_eq!(self.tag, {});", i))?; 53 | w.write_line(format!( 54 | "unsafe {{ self.member.assume_init().{} }}", 55 | member.name.as_var() 56 | ))?; 57 | } 58 | w.write_line("}")?.eob()?; 59 | 60 | // set_* 61 | w.write_line(format!( 62 | "pub fn set_{}(&mut self, val: {}) {{", 63 | name.as_fn_suffix(), 64 | member.type_.as_lang() 65 | ))?; 66 | { 67 | let mut w = w.new_block(); 68 | w.write_line(format!("assert_eq!(self.tag, {});", i))?; 69 | w.write_line(format!( 70 | "let uval = {} {{ {}: val }};", 71 | inner_name.as_type(), 72 | member.name.as_var() 73 | ))?; 74 | w.write_line("unsafe { *self.member.as_mut_ptr() = uval };")?; 75 | } 76 | w.write_line("}")?.eob()?; 77 | } 78 | 79 | // is_* 80 | w.write_line(format!( 81 | "pub fn is_{}(&self) -> bool {{", 82 | name.as_fn_suffix() 83 | ))?; 84 | { 85 | let mut w = w.new_block(); 86 | w.write_line(format!("self.tag == {}", i))?; 87 | } 88 | w.write_line("}")?.eob()?; 89 | 90 | Ok(()) 91 | } 92 | 93 | fn define_union_member( 94 | w: &mut PrettyWriter, 95 | union_name: &str, 96 | i: usize, 97 | member: &ASUnionMember, 98 | inner_name: &str, 99 | ) -> Result<(), Error> { 100 | let member_type = member.type_.as_ref(); 101 | match member_type { 102 | ASType::Void => { 103 | w.write_line(format!( 104 | "// --- {}: (no associated content) if tag={}", 105 | member.name.as_var(), 106 | i 107 | ))?; 108 | } 109 | _ => { 110 | w.write_line(format!( 111 | "// --- {}: {} if tag={}", 112 | member.name.as_var(), 113 | member_type.as_lang(), 114 | i 115 | ))?; 116 | } 117 | } 118 | w.eob()?; 119 | Self::define_union_member_accessors(w, union_name, i, member, inner_name)?; 120 | Ok(()) 121 | } 122 | 123 | pub fn define_as_union( 124 | w: &mut PrettyWriter, 125 | name: &str, 126 | union_: &ASUnion, 127 | ) -> Result<(), Error> { 128 | let tag_repr = union_.tag_repr.as_ref(); 129 | let inner_name = format!("{}_member", name); 130 | w.write_line("#[repr(C)]")? 131 | .write_line(format!("pub union {} {{", inner_name.as_type()))?; 132 | { 133 | let mut w = w.new_block(); 134 | for (i, member) in union_.members.iter().enumerate() { 135 | let member_is_void = matches!(member.type_.as_ref(), ASType::Void); 136 | if member_is_void { 137 | w.write_line(format!( 138 | "// {} with no associated value if tag={}", 139 | member.name.as_var(), 140 | i 141 | ))?; 142 | } else { 143 | w.write_line(format!( 144 | "{}: {}, // if tag={}", 145 | member.name.as_var(), 146 | member.type_.as_lang(), 147 | i 148 | ))?; 149 | } 150 | } 151 | } 152 | w.write_line("}")?; 153 | w.eob()?; 154 | 155 | w.write_line("#[repr(C, packed)]")? 156 | .write_line(format!("pub struct {} {{", name.as_type()))?; 157 | { 158 | let mut w = w.new_block(); 159 | w.write_line(format!("pub tag: {},", tag_repr.as_lang()))?; 160 | let pad_len = union_.padding_after_tag; 161 | for i in 0..(pad_len & 1) { 162 | w.write_line(format!("__pad8_{}: u8,", i))?; 163 | } 164 | for i in 0..(pad_len & 3) / 2 { 165 | w.write_line(format!("__pad16_{}: u16,", i))?; 166 | } 167 | for i in 0..(pad_len & 7) / 4 { 168 | w.write_line(format!("__pad32_{}: u32,", i))?; 169 | } 170 | for i in 0..pad_len / 8 { 171 | w.write_line(format!("__pad64_{}: u64,", i))?; 172 | } 173 | w.write_line(format!( 174 | "pub member: std::mem::MaybeUninit<{}>,", 175 | inner_name.as_type() 176 | ))?; 177 | } 178 | w.write_line("}")?; 179 | w.eob()?; 180 | 181 | w.write_line(format!("impl {} {{", name.as_type()))?; 182 | { 183 | let mut w = w.new_block(); 184 | w.write_line(format!("fn new(tag: {}) -> Self {{", tag_repr.as_lang()))?; 185 | { 186 | let mut w = w.new_block(); 187 | w.write_line("let mut tu = unsafe { std::mem::zeroed::() };")?; 188 | w.write_line("tu.tag = tag;")?; 189 | w.write_line("tu")?; 190 | } 191 | w.write_line("}")?.eob()?; 192 | 193 | for (i, member) in union_.members.iter().enumerate() { 194 | w.eob()?; 195 | Self::define_union_member(&mut w, name, i, member, &inner_name)?; 196 | } 197 | } 198 | w.write_line("}")?.eob()?; 199 | Ok(()) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/zig/common.rs: -------------------------------------------------------------------------------- 1 | use convert_case::{Case, Casing}; 2 | 3 | use super::tuple::Tuple; 4 | use crate::astype::*; 5 | 6 | pub trait IsNullable { 7 | fn is_nullable(&self) -> bool; 8 | } 9 | 10 | impl IsNullable for ASType { 11 | fn is_nullable(&self) -> bool { 12 | matches!( 13 | self, 14 | ASType::ConstPtr(_) 15 | | ASType::MutPtr(_) 16 | | ASType::ReadBuffer(_) 17 | | ASType::WriteBuffer(_) 18 | | ASType::Enum(_) 19 | | ASType::Struct(_) 20 | | ASType::Tuple(_) 21 | | ASType::Union(_) 22 | ) 23 | } 24 | } 25 | 26 | pub trait Normalize { 27 | fn as_str(&self) -> &str; 28 | 29 | fn as_type(&self) -> String { 30 | self.as_str().to_case(Case::Pascal) 31 | } 32 | 33 | fn as_fn(&self) -> String { 34 | self.as_str().to_case(Case::Camel) 35 | } 36 | 37 | fn as_fn_suffix(&self) -> String { 38 | self.as_str().to_case(Case::UpperCamel) 39 | } 40 | 41 | fn as_var(&self) -> String { 42 | escape_reserved_word(&self.as_str().to_case(Case::Snake)) 43 | } 44 | 45 | fn as_const(&self) -> String { 46 | self.as_str().to_case(Case::UpperSnake) 47 | } 48 | 49 | fn as_namespace(&self) -> String { 50 | self.as_str().to_string().to_case(Case::Pascal) 51 | } 52 | } 53 | 54 | impl> Normalize for T { 55 | fn as_str(&self) -> &str { 56 | self.as_ref() 57 | } 58 | } 59 | 60 | pub trait ToLanguageRepresentation { 61 | fn as_astype(&self) -> &ASType; 62 | 63 | fn to_string(&self) -> String { 64 | self.as_lang() 65 | } 66 | 67 | fn as_lang(&self) -> String { 68 | match self.as_astype() { 69 | ASType::Alias(alias) => alias.name.as_type(), 70 | ASType::Bool => "bool".to_string(), 71 | ASType::Char32 => "Char32".to_string(), 72 | ASType::Char8 => "Char8".to_string(), 73 | ASType::F32 => "f32".to_string(), 74 | ASType::F64 => "f64".to_string(), 75 | ASType::Handle(_resource_name) => "WasiHandle".to_string(), 76 | ASType::ConstPtr(pointee) => format!("WasiPtr({})", pointee.to_string()), 77 | ASType::MutPtr(pointee) => format!("WasiMutPtr({})", pointee.to_string()), 78 | ASType::Option(_) => todo!(), 79 | ASType::Result(_) => todo!(), 80 | ASType::S8 => "i8".to_string(), 81 | ASType::S16 => "i16".to_string(), 82 | ASType::S32 => "i32".to_string(), 83 | ASType::S64 => "i64".to_string(), 84 | ASType::U8 => "u8".to_string(), 85 | ASType::U16 => "u16".to_string(), 86 | ASType::U32 => "u32".to_string(), 87 | ASType::U64 => "u64".to_string(), 88 | ASType::USize => "usize".to_string(), 89 | ASType::Void => "()".to_string(), 90 | ASType::Constants(_) => unimplemented!(), 91 | ASType::Enum(enum_) => enum_.repr.as_ref().as_lang(), 92 | ASType::Struct(_) => unimplemented!(), 93 | ASType::Tuple(tuple_members) => Tuple::name_for(tuple_members).as_type(), 94 | ASType::Union(_) => unimplemented!(), 95 | ASType::Slice(element_type) => format!("WasiMutSlice({})", element_type.as_lang()), 96 | ASType::String(_) => "WasiString".to_string(), 97 | ASType::ReadBuffer(element_type) => format!("WasiSlice({})", element_type.as_lang()), 98 | ASType::WriteBuffer(element_type) => { 99 | format!("WasiMutSlice({})", element_type.to_string()) 100 | } 101 | } 102 | } 103 | } 104 | 105 | impl ToLanguageRepresentation for ASType { 106 | fn as_astype(&self) -> &ASType { 107 | self 108 | } 109 | } 110 | 111 | /// Checks the given word against a list of reserved keywords. 112 | /// If the given word conflicts with a keyword, a trailing underscore will be 113 | /// appended. 114 | pub fn escape_reserved_word(word: &str) -> String { 115 | if RESERVED.iter().any(|k| *k == word) { 116 | // If the camel-cased string matched any strict or reserved keywords, then 117 | // append a trailing underscore to the identifier we generate. 118 | format!("{}_", word) 119 | } else { 120 | word.to_string() // Otherwise, use the string as is. 121 | } 122 | } 123 | 124 | /// Reserved Keywords. 125 | /// 126 | /// Source: [Zig Language Reference](https://ziglang.org/documentation/master/#toc-Keyword-Reference) 127 | const RESERVED: &[&str] = &[ 128 | "align", 129 | "allowzero", 130 | "and", 131 | "anyframe", 132 | "anytype", 133 | "asm", 134 | "async", 135 | "await", 136 | "break", 137 | "catch", 138 | "comptime", 139 | "const", 140 | "continue", 141 | "defer", 142 | "else", 143 | "enum", 144 | "errdefer", 145 | "error", 146 | "export", 147 | "extern", 148 | "false", 149 | "fn", 150 | "for", 151 | "if", 152 | "inline", 153 | "noalias", 154 | "nosuspend", 155 | "null", 156 | "or", 157 | "orelse", 158 | "packed", 159 | "pub", 160 | "resume", 161 | "return", 162 | "linksection", 163 | "struct", 164 | "suspend", 165 | "switch", 166 | "test", 167 | "threadlocal", 168 | "true", 169 | "try", 170 | "undefined", 171 | "union", 172 | "unreachable", 173 | "usingnamespace", 174 | "var", 175 | "volatile", 176 | "while", 177 | ]; 178 | -------------------------------------------------------------------------------- /src/zig/function.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl ZigGenerator { 6 | pub fn define_func( 7 | w: &mut PrettyWriter, 8 | module_name: &str, 9 | func_witx: &witx::Function, 10 | ) -> Result<(), Error> { 11 | assert_eq!(func_witx.abi, witx::Abi::Preview1); 12 | let name = func_witx.name.as_str().to_string(); 13 | let params_witx = &func_witx.params; 14 | let mut params = vec![]; 15 | for param_witx in params_witx { 16 | let param_name = param_witx.name.as_str(); 17 | let param_type = ASType::from(¶m_witx.tref); 18 | params.push((param_name.to_string(), param_type)); 19 | } 20 | 21 | let results_witx = &func_witx.results; 22 | assert_eq!(results_witx.len(), 1); 23 | let result_witx = &results_witx[0]; 24 | let result = ASType::from(&result_witx.tref); 25 | let result = match result { 26 | ASType::Result(result) => result, 27 | _ => unreachable!(), 28 | }; 29 | 30 | let ok_type = result.ok_type.clone(); 31 | 32 | let docs = &func_witx.docs; 33 | if !docs.is_empty() { 34 | Self::write_docs(w, docs)?; 35 | } 36 | 37 | let mut params_decomposed = vec![]; 38 | 39 | for param in ¶ms { 40 | let mut decomposed = param.1.decompose(¶m.0, false); 41 | params_decomposed.append(&mut decomposed); 42 | } 43 | 44 | let mut results = vec![]; 45 | // A tuple in a result is expanded into additional parameters, transformed to 46 | // pointers 47 | if let ASType::Tuple(tuple_members) = ok_type.as_ref().leaf() { 48 | for (i, tuple_member) in tuple_members.iter().enumerate() { 49 | let name = format!("result{}_ptr", i); 50 | results.push((name, tuple_member.type_.clone())); 51 | } 52 | } else { 53 | let name = "result_ptr"; 54 | results.push((name.to_string(), ok_type)); 55 | } 56 | let mut results_decomposed = vec![]; 57 | for result in &results { 58 | let mut decomposed = result.1.decompose(&result.0, true); 59 | results_decomposed.append(&mut decomposed); 60 | } 61 | 62 | Self::define_func_raw( 63 | w, 64 | module_name, 65 | &name, 66 | ¶ms_decomposed, 67 | &results_decomposed, 68 | &result, 69 | )?; 70 | 71 | let signature_witx = func_witx.wasm_signature(witx::CallMode::DefinedImport); 72 | let params_count_witx = signature_witx.params.len() + signature_witx.results.len(); 73 | assert_eq!( 74 | params_count_witx, 75 | params_decomposed.len() + results_decomposed.len() + 1 76 | ); 77 | 78 | Ok(()) 79 | } 80 | 81 | fn define_func_raw( 82 | w: &mut PrettyWriter, 83 | module_name: &str, 84 | name: &str, 85 | params_decomposed: &[ASTypeDecomposed], 86 | results_decomposed: &[ASTypeDecomposed], 87 | result: &ASResult, 88 | ) -> Result<(), Error> { 89 | w.indent()? 90 | .write(format!("pub extern \"{}\" fn {}(", module_name, name))?; 91 | if !params_decomposed.is_empty() || !results_decomposed.is_empty() { 92 | w.eol()?; 93 | } 94 | for param in params_decomposed.iter().chain(results_decomposed.iter()) { 95 | w.write_line_continued(format!( 96 | "{}: {},", 97 | param.name.as_var(), 98 | param.type_.as_lang(), 99 | ))?; 100 | } 101 | w.write_line(format!(") callconv(.C) {};", result.error_type.as_lang()))?; 102 | w.eob()?; 103 | Ok(()) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/zig/header.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl ZigGenerator { 6 | pub fn header(w: &mut PrettyWriter) -> Result<(), Error> { 7 | w.write_lines( 8 | " 9 | // 10 | // This file was automatically generated by witx-codegen - Do not edit manually. 11 | // 12 | 13 | pub const WasiHandle = i32; 14 | pub const Char8 = u8; 15 | pub const Char32 = u32; 16 | pub fn WasiPtr(comptime T: type) type { 17 | return [*c]const T; 18 | } 19 | pub fn WasiMutPtr(comptime T: type) type { 20 | return [*c]T; 21 | } 22 | pub const WasiStringBytesPtr = WasiPtr(Char8); 23 | 24 | pub const WasiString = extern struct { 25 | ptr: WasiStringBytesPtr, 26 | len: usize, 27 | 28 | fn from_slice(slice: []const u8) WasiString { 29 | return WasiString{ .ptr = slice.ptr, .len = slice.len }; 30 | } 31 | 32 | fn as_slice(wasi_string: WasiString) []const u8 { 33 | return wasi_string.ptr[wasi_string.len]; 34 | } 35 | }; 36 | 37 | pub fn WasiSlice(comptime T: type) type { 38 | return extern struct { 39 | ptr: WasiPtr(T), 40 | len: usize, 41 | 42 | fn from_slice(slice: []const u8) WasiSlice { 43 | return WasiSlice{ .ptr = slice.ptr, .len = slice.len }; 44 | } 45 | 46 | fn as_slice(wasi_slice: WasiSlice) []const u8 { 47 | return wasi_slice.ptr[wasi_slice.len]; 48 | } 49 | }; 50 | } 51 | 52 | pub fn WasiMutSlice(comptime T: type) type { 53 | return extern struct { 54 | ptr: WasiMutPtr(T), 55 | len: usize, 56 | 57 | fn from_slice(slice: []u8) WasiMutSlice { 58 | return WasiMutSlice{ .ptr = slice.ptr, .len = slice.len }; 59 | } 60 | 61 | fn as_slice(wasi_slice: WasiMutSlice) []u8 { 62 | return wasi_slice.ptr[wasi_slice.len]; 63 | } 64 | }; 65 | } 66 | ", 67 | )?; 68 | w.eob()?; 69 | Ok(()) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/zig/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | mod function; 3 | mod header; 4 | mod r#struct; 5 | mod tuple; 6 | mod union; 7 | 8 | use std::io::Write; 9 | 10 | use common::*; 11 | 12 | use super::*; 13 | use crate::astype::*; 14 | use crate::error::*; 15 | use crate::pretty_writer::PrettyWriter; 16 | 17 | pub struct ZigGenerator { 18 | module_name: Option, 19 | } 20 | 21 | impl ZigGenerator { 22 | pub fn new(module_name: Option) -> Self { 23 | ZigGenerator { module_name } 24 | } 25 | } 26 | 27 | impl Generator for ZigGenerator { 28 | fn generate( 29 | &self, 30 | writer: &mut T, 31 | module_witx: witx::Module, 32 | options: &Options, 33 | ) -> Result<(), Error> { 34 | let mut w = PrettyWriter::new(writer, " "); 35 | let module_name = match &self.module_name { 36 | None => module_witx.name().as_str().to_string(), 37 | Some(module_name) => module_name.to_string(), 38 | }; 39 | let module_id = module_witx.module_id(); 40 | let skip_imports = options.skip_imports; 41 | 42 | if !options.skip_header { 43 | Self::header(&mut w)?; 44 | } 45 | 46 | let module_title_comments = format!( 47 | "---------------------- Module: [{}] ----------------------", 48 | module_name 49 | ); 50 | Self::write_comments(&mut w, &module_title_comments)?; 51 | w.eob()?; 52 | 53 | for type_ in module_witx.typenames() { 54 | if skip_imports && &type_.module != module_id { 55 | continue; 56 | } 57 | let constants_for_type: Vec<_> = module_witx 58 | .constants() 59 | .filter_map(|x| { 60 | if x.ty == type_.name { 61 | Some(ASConstant { 62 | name: x.name.as_str().to_string(), 63 | value: x.value, 64 | }) 65 | } else { 66 | None 67 | } 68 | }) 69 | .collect(); 70 | Self::define_type(&mut w, type_.as_ref(), &constants_for_type)?; 71 | } 72 | 73 | w.write_line(format!( 74 | "pub const {} = struct {{", 75 | module_name.as_namespace() 76 | ))?; 77 | { 78 | let mut w = w.new_block(); 79 | for func in module_witx.funcs() { 80 | Self::define_func(&mut w, &module_name, func.as_ref())?; 81 | } 82 | } 83 | w.write_line("};")?; 84 | w.eob()?; 85 | 86 | Ok(()) 87 | } 88 | } 89 | 90 | impl ZigGenerator { 91 | fn write_docs(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 92 | if docs.is_empty() { 93 | return Ok(()); 94 | } 95 | for docs_line in docs.lines() { 96 | w.write_line(format!("/// {}", docs_line))?; 97 | } 98 | Ok(()) 99 | } 100 | 101 | fn write_comments(w: &mut PrettyWriter, docs: &str) -> Result<(), Error> { 102 | if docs.is_empty() { 103 | return Ok(()); 104 | } 105 | for docs_line in docs.lines() { 106 | w.write_line(format!("// {}", docs_line))?; 107 | } 108 | Ok(()) 109 | } 110 | 111 | fn define_as_alias( 112 | w: &mut PrettyWriter, 113 | name: &str, 114 | other_type: &ASType, 115 | ) -> Result<(), Error> { 116 | w.write_line(format!( 117 | "pub const {} = {};", 118 | name.as_type(), 119 | other_type.as_lang() 120 | ))?; 121 | Ok(()) 122 | } 123 | 124 | fn define_as_atom( 125 | w: &mut PrettyWriter, 126 | name: &str, 127 | type_: &ASType, 128 | ) -> Result<(), Error> { 129 | w.write_line(format!( 130 | "pub const {} = {};", 131 | name.as_type(), 132 | type_.as_lang() 133 | ))?; 134 | Ok(()) 135 | } 136 | 137 | fn define_as_enum( 138 | w: &mut PrettyWriter, 139 | name: &str, 140 | enum_: &ASEnum, 141 | ) -> Result<(), Error> { 142 | let repr = enum_.repr.as_ref(); 143 | w.write_line(format!( 144 | "pub const {} = enum({}) {{", 145 | name.as_type(), 146 | repr.as_lang() 147 | ))?; 148 | { 149 | let mut w = w.new_block(); 150 | for choice in &enum_.choices { 151 | w.write_line(format!("{} = {},", choice.name.as_const(), choice.value))?; 152 | } 153 | } 154 | w.write_line("};")?; 155 | Ok(()) 156 | } 157 | 158 | fn define_as_constants( 159 | w: &mut PrettyWriter, 160 | name: &str, 161 | constants: &ASConstants, 162 | ) -> Result<(), Error> { 163 | let repr = constants.repr.as_ref(); 164 | w.write_line(format!( 165 | "pub const {} = {};", 166 | name.as_type(), 167 | repr.as_lang() 168 | ))?; 169 | Self::define_constants_for_type(w, name, &constants.constants)?; 170 | w.eob()?; 171 | Ok(()) 172 | } 173 | 174 | fn define_as_type( 175 | w: &mut PrettyWriter, 176 | name: &str, 177 | type_: &ASType, 178 | ) -> Result<(), Error> { 179 | match type_ { 180 | ASType::Alias(_) 181 | | ASType::Bool 182 | | ASType::Char8 183 | | ASType::Char32 184 | | ASType::F32 185 | | ASType::F64 186 | | ASType::U8 187 | | ASType::U16 188 | | ASType::U32 189 | | ASType::U64 190 | | ASType::S8 191 | | ASType::S16 192 | | ASType::S32 193 | | ASType::S64 194 | | ASType::USize 195 | | ASType::Handle(_) 196 | | ASType::Slice(_) 197 | | ASType::String(_) 198 | | ASType::ReadBuffer(_) 199 | | ASType::WriteBuffer(_) => Self::define_as_atom(w, name, type_)?, 200 | ASType::Enum(enum_) => Self::define_as_enum(w, name, enum_)?, 201 | ASType::Union(union_) => Self::define_as_union(w, name, union_)?, 202 | ASType::Constants(constants) => Self::define_as_constants(w, name, constants)?, 203 | ASType::Tuple(members) => Self::define_as_tuple(w, name, members)?, 204 | ASType::Struct(members) => Self::define_as_struct(w, name, members)?, 205 | _ => { 206 | dbg!(type_); 207 | unimplemented!(); 208 | } 209 | } 210 | Ok(()) 211 | } 212 | 213 | fn define_constants_for_type( 214 | w: &mut PrettyWriter, 215 | type_name: &str, 216 | constants: &[ASConstant], 217 | ) -> Result<(), Error> { 218 | if constants.is_empty() { 219 | return Ok(()); 220 | } 221 | 222 | let mut hex = false; 223 | let mut single_bits: usize = 0; 224 | for constant in constants { 225 | if constant.value > 0xffff { 226 | hex = true; 227 | } 228 | if constant.value.count_ones() == 1 { 229 | single_bits += 1; 230 | } 231 | } 232 | if constants.len() > 2 && single_bits == constants.len() { 233 | hex = true; 234 | } 235 | for constant in constants { 236 | let value_s = if hex { 237 | format!("0x{:x}", constant.value) 238 | } else { 239 | format!("{}", constant.value) 240 | }; 241 | w.write_line(format!( 242 | "pub const {}: {} = {};", 243 | format!("{}_{}", type_name, constant.name).as_const(), 244 | type_name.as_type(), 245 | value_s 246 | ))?; 247 | } 248 | w.eob()?; 249 | Ok(()) 250 | } 251 | 252 | fn define_type( 253 | w: &mut PrettyWriter, 254 | type_witx: &witx::NamedType, 255 | constants: &[ASConstant], 256 | ) -> Result<(), Error> { 257 | let docs = &type_witx.docs; 258 | if !docs.is_empty() { 259 | Self::write_docs(w, docs)?; 260 | } 261 | let type_name = type_witx.name.as_str(); 262 | let tref = &type_witx.tref; 263 | match tref { 264 | witx::TypeRef::Name(other_type) => { 265 | Self::define_as_alias(w, type_name, &ASType::from(&other_type.tref))? 266 | } 267 | witx::TypeRef::Value(type_witx) => { 268 | let t = ASType::from(type_witx.as_ref()); 269 | Self::define_as_type(w, type_name, &t)? 270 | } 271 | } 272 | w.eob()?; 273 | Self::define_constants_for_type(w, type_name, constants)?; 274 | Ok(()) 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/zig/struct.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl ZigGenerator { 6 | pub fn define_as_struct( 7 | w: &mut PrettyWriter, 8 | name: &str, 9 | members: &[ASStructMember], 10 | ) -> Result<(), Error> { 11 | w.write_line(format!("pub const {} = extern struct {{", name.as_type()))?; 12 | { 13 | let mut w = w.new_block(); 14 | for member in members { 15 | let member_type = member.type_.as_ref(); 16 | w.write_line(format!( 17 | "{}: {},", 18 | member.name.as_var(), 19 | member_type.as_lang() 20 | ))?; 21 | 22 | let pad_len = member.padding; 23 | for i in 0..(pad_len & 1) { 24 | w.write_line(format!("__pad8_{}: u8 = undefined,", i))?; 25 | } 26 | for i in 0..(pad_len & 3) / 2 { 27 | w.write_line(format!("__pad16_{}: u16 = undefined,", i))?; 28 | } 29 | for i in 0..(pad_len & 7) / 4 { 30 | w.write_line(format!("__pad32_{}: u32 = undefined,", i))?; 31 | } 32 | for i in 0..pad_len / 8 { 33 | w.write_line(format!("__pad64_{}: u64 = undefined,", i))?; 34 | } 35 | } 36 | } 37 | w.write_line("};")?.eob()?; 38 | Ok(()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/zig/tuple.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | pub struct Tuple; 6 | 7 | impl Tuple { 8 | pub fn name_for(tuple_members: &[ASTupleMember]) -> String { 9 | format!( 10 | "WasiTuple{}{}", 11 | tuple_members.len(), 12 | tuple_members 13 | .iter() 14 | .map(|member| member.type_.to_string()) 15 | .collect::>() 16 | .join("_") 17 | ) 18 | } 19 | } 20 | 21 | impl ZigGenerator { 22 | pub fn define_as_tuple( 23 | w: &mut PrettyWriter, 24 | name: &str, 25 | members: &[ASTupleMember], 26 | ) -> Result<(), Error> { 27 | w.write_line(format!( 28 | "pub const {} = extern struct {{ // -- Tuple", 29 | name.as_type() 30 | ))?; 31 | { 32 | let mut w = w.new_block(); 33 | for (i, member) in members.iter().enumerate() { 34 | let member_type = member.type_.as_ref(); 35 | w.write_line(format!("v{}: {},", i, member_type.as_lang()))?; 36 | 37 | let pad_len = member.padding; 38 | for i in 0..(pad_len & 1) { 39 | w.write_line(format!("__pad8_{}: u8 = undefined,", i))?; 40 | } 41 | for i in 0..(pad_len & 3) / 2 { 42 | w.write_line(format!("__pad16_{}: u16 = undefined,", i))?; 43 | } 44 | for i in 0..(pad_len & 7) / 4 { 45 | w.write_line(format!("__pad32_{}: u32 = undefined,", i))?; 46 | } 47 | for i in 0..pad_len / 8 { 48 | w.write_line(format!("__pad64_{}: u64 = undefined,", i))?; 49 | } 50 | } 51 | } 52 | w.write_line("};")?.eob()?; 53 | Ok(()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/zig/union.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::*; 4 | 5 | impl ZigGenerator { 6 | fn define_union_member_accessors( 7 | w: &mut PrettyWriter, 8 | union_name: &str, 9 | _i: usize, 10 | member: &ASUnionMember, 11 | ) -> Result<(), Error> { 12 | let name = &member.name; 13 | let member_is_void = matches!(member.type_.as_ref(), ASType::Void); 14 | 15 | if member_is_void { 16 | // new_* 17 | w.write_line(format!( 18 | "fn new{}() {} {{", 19 | name.as_fn_suffix(), 20 | union_name.as_type() 21 | ))?; 22 | { 23 | let mut w = w.new_block(); 24 | w.write_line(format!( 25 | "return {} {{ .tag = .{} }};", 26 | union_name.as_type(), 27 | name.as_var(), 28 | ))?; 29 | } 30 | w.write_line("}")?.eob()?; 31 | } else { 32 | // !member_is_void 33 | // new_* 34 | w.write_line(format!( 35 | "fn new{}(val: {}) {} {{", 36 | name.as_fn_suffix(), 37 | member.type_.as_lang(), 38 | union_name.as_type() 39 | ))?; 40 | { 41 | let mut w = w.new_block(); 42 | w.write_line(format!( 43 | "return {}{{ .tag = .{}, .member = .{{ .{} = val }} }};", 44 | union_name.as_type(), 45 | name.as_var(), 46 | name.as_var() 47 | ))?; 48 | } 49 | w.write_line("}")?.eob()?; 50 | 51 | // get_* 52 | w.write_line(format!( 53 | "pub fn {}(self: {}) {} {{", 54 | name.as_fn_suffix(), 55 | union_name.as_type(), 56 | member.type_.as_lang() 57 | ))?; 58 | { 59 | let mut w = w.new_block(); 60 | w.write_line(format!("std.debug.assert(self.tag == .{});", name.as_var()))?; 61 | w.write_line(format!("return self.member.{};", member.name.as_var()))?; 62 | } 63 | w.write_line("}")?.eob()?; 64 | 65 | // set_* 66 | w.write_line(format!( 67 | "pub fn set{}(self: *{}, val: {}) void {{", 68 | name.as_fn_suffix(), 69 | union_name.as_type(), 70 | member.type_.as_lang() 71 | ))?; 72 | { 73 | let mut w = w.new_block(); 74 | w.write_line(format!("std.debug.assert(self.tag == .{});", name.as_var()))?; 75 | w.write_line(format!("self.member.{} = val;", member.name.as_var()))?; 76 | } 77 | w.write_line("}")?.eob()?; 78 | } 79 | 80 | // is_* 81 | w.write_line(format!( 82 | "fn is{}(self: {}) bool {{", 83 | name.as_fn_suffix(), 84 | union_name.as_type(), 85 | ))?; 86 | { 87 | let mut w = w.new_block(); 88 | w.write_line(format!("return self.tag == .{};", name.as_var()))?; 89 | } 90 | w.write_line("}")?.eob()?; 91 | 92 | Ok(()) 93 | } 94 | 95 | fn define_union_member( 96 | w: &mut PrettyWriter, 97 | union_name: &str, 98 | i: usize, 99 | member: &ASUnionMember, 100 | ) -> Result<(), Error> { 101 | Self::define_union_member_accessors(w, union_name, i, member)?; 102 | Ok(()) 103 | } 104 | 105 | pub fn define_as_union( 106 | w: &mut PrettyWriter, 107 | name: &str, 108 | union_: &ASUnion, 109 | ) -> Result<(), Error> { 110 | let tag_repr = union_.tag_repr.as_ref(); 111 | w.write_line(format!("pub const {} = extern struct {{", name.as_type()))?; 112 | { 113 | let mut w = w.new_block(); 114 | w.write_line(format!("tag: enum({}) {{", tag_repr.as_lang()))?; 115 | { 116 | let mut w = w.new_block(); 117 | for (i, member) in union_.members.iter().enumerate() { 118 | w.write_line(format!("{} = {},", member.name.as_var(), i))?; 119 | } 120 | } 121 | w.write_line("},")?; 122 | let pad_len = union_.padding_after_tag; 123 | for i in 0..(pad_len & 1) { 124 | w.write_line(format!("__pad8_{}: u8 = undefined,", i))?; 125 | } 126 | for i in 0..(pad_len & 3) / 2 { 127 | w.write_line(format!("__pad16_{}: u16 = undefined,", i))?; 128 | } 129 | for i in 0..(pad_len & 7) / 4 { 130 | w.write_line(format!("__pad32_{}: u32 = undefined,", i))?; 131 | } 132 | for i in 0..pad_len / 8 { 133 | w.write_line(format!("__pad64_{}: u64 = undefined,", i))?; 134 | } 135 | w.write_line("member = extern union {")?; 136 | { 137 | let mut w = w.new_block(); 138 | for (_i, member) in union_.members.iter().enumerate() { 139 | let member_is_void = matches!(member.type_.as_ref(), ASType::Void); 140 | if !member_is_void { 141 | w.write_line(format!( 142 | "{}: {},", 143 | member.name.as_var(), 144 | member.type_.as_lang(), 145 | ))?; 146 | } 147 | } 148 | } 149 | w.write_line("},")?; 150 | } 151 | w.eob()?; 152 | 153 | for (i, member) in union_.members.iter().enumerate() { 154 | w.eob()?; 155 | Self::define_union_member(w, name, i, member)?; 156 | } 157 | w.write_line("};")?.eob()?; 158 | Ok(()) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /tests/integration.rs: -------------------------------------------------------------------------------- 1 | use witx_codegen::{generate, Config, OutputType}; 2 | 3 | const WITX_SOURCES: &[&str] = &[ 4 | "test_module.witx", 5 | "wasi_ephemeral_crypto_common.witx", 6 | "wasi_ephemeral_crypto_symmetric.witx", 7 | "wasi_experimental_http.witx", 8 | ]; 9 | 10 | const WITX_DIR: &str = env!("CARGO_MANIFEST_DIR"); 11 | 12 | #[test] 13 | fn witx_parse() { 14 | for s in WITX_SOURCES { 15 | let p = format!("{}/tests/{}", WITX_DIR, s); 16 | 17 | println!("Parsing {}", p); 18 | 19 | let _witx = witx::load(p).unwrap(); 20 | } 21 | } 22 | 23 | #[test] 24 | fn generate_rust() { 25 | let mut c = Config { 26 | output_type: OutputType::Rust, 27 | output_file: Some("/dev/null".to_string()), 28 | ..Default::default() 29 | }; 30 | 31 | for s in WITX_SOURCES { 32 | println!("Generate {}", s); 33 | 34 | let p = format!("{}/tests/{}", WITX_DIR, s); 35 | c.witx_files = vec![p]; 36 | 37 | generate(&c).unwrap(); 38 | } 39 | } 40 | 41 | #[test] 42 | fn generate_zig() { 43 | let mut c = Config { 44 | output_type: OutputType::Zig, 45 | output_file: Some("/dev/null".to_string()), 46 | ..Default::default() 47 | }; 48 | 49 | for s in WITX_SOURCES { 50 | println!("Generate {}", s); 51 | 52 | let p = format!("{}/tests/{}", WITX_DIR, s); 53 | c.witx_files = vec![p]; 54 | 55 | generate(&c).unwrap(); 56 | } 57 | } 58 | 59 | #[test] 60 | fn generate_doc() { 61 | let mut c = Config { 62 | output_type: OutputType::Doc, 63 | output_file: Some("/dev/null".to_string()), 64 | ..Default::default() 65 | }; 66 | 67 | for s in WITX_SOURCES { 68 | println!("Generate {}", s); 69 | 70 | let p = format!("{}/tests/{}", WITX_DIR, s); 71 | c.witx_files = vec![p]; 72 | 73 | generate(&c).unwrap(); 74 | } 75 | } 76 | 77 | #[test] 78 | fn generate_assemblyscript() { 79 | let mut c = Config { 80 | output_type: OutputType::AssemblyScript, 81 | output_file: Some("/dev/null".to_string()), 82 | ..Default::default() 83 | }; 84 | 85 | for s in WITX_SOURCES { 86 | println!("Generate {}", s); 87 | 88 | let p = format!("{}/tests/{}", WITX_DIR, s); 89 | c.witx_files = vec![p]; 90 | 91 | generate(&c).unwrap(); 92 | } 93 | } 94 | 95 | #[test] 96 | fn generate_cpp() { 97 | let mut c = Config { 98 | output_type: OutputType::Cpp, 99 | output_file: Some("/dev/null".to_string()), 100 | ..Default::default() 101 | }; 102 | 103 | for s in WITX_SOURCES { 104 | println!("Generate {}", s); 105 | 106 | let p = format!("{}/tests/{}", WITX_DIR, s); 107 | c.witx_files = vec![p]; 108 | 109 | generate(&c).unwrap(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/test_module.witx: -------------------------------------------------------------------------------- 1 | (module $test_module 2 | ;;; An enumeration 3 | (typename $test_errno 4 | (enum (@witx tag u16) 5 | ;;; Operation succeeded. 6 | $success 7 | 8 | ;;; A guest error occurred 9 | $guest_error 10 | 11 | ;;; Something else went wrong 12 | $some_other_error 13 | ) 14 | ) 15 | 16 | ;;; A boolean alias 17 | (typename $test_bool bool) 18 | 19 | ;;; A u32 alias 20 | (typename $test_medium_int u32) 21 | 22 | ;;; A u64 alias 23 | (typename $test_big_int u64) 24 | 25 | ;;; Some constants for a type 26 | (@witx const $test_big_int $zero 0) 27 | (@witx const $test_big_int $a_hundred 100) 28 | (@witx const $test_big_int $a_big_value 0xff00000000000000) 29 | (@witx const $test_big_int $a_bigger_value 0xffffffffffffffff) 30 | 31 | ;;; Some constants for another type, these are smaller and won't be encoded as hex 32 | (@witx const $test_medium_int $zero 0) 33 | (@witx const $test_medium_int $one 1) 34 | (@witx const $test_medium_int $two 2) 35 | (@witx const $test_medium_int $three 3) 36 | 37 | ;;; Flags 38 | (typename $test_big_flags (record (field $a bool) (field $b bool) (field $c bool) (field $d bool))) 39 | 40 | ;;; A structure 41 | (typename $test_struct (record (field $a_boolean bool) (field $a_byte u8) (field $a_string string))) 42 | 43 | ;;; A tuple 44 | (typename $test_tuple (tuple $test_bool $test_medium_int $test_big_int)) 45 | 46 | ;;; A string 47 | (typename $test_string string) 48 | 49 | ;;; An output buffer 50 | (typename $test_output_buffer (out-buffer u16)) 51 | 52 | ;;; An input buffer 53 | (typename $test_input_buffer (in-buffer u16)) 54 | 55 | ;;; A tagged union 56 | (typename $test_tagged_union (variant (@witx tag u16) (case $first_choice u8) (case $second_choice string) (case $third_choice f32) (case $empty_choice))) 57 | 58 | ;;; This function returns multiple values 59 | (@interface func (export "a_function_that_returns_multiple_values") 60 | (param $some_parameter u64) 61 | (param $some_other_parameter string) 62 | (result $error (expected (tuple $test_medium_int $test_big_int) (error $test_errno))) 63 | ) 64 | 65 | ;;; This function returns an actual tuple (expanded into multiple values) 66 | (@interface func (export "a_function_that_returns_an_actual_tuple") 67 | (param $some_parameter u64) 68 | (result $error (expected $test_tuple (error $test_errno))) 69 | ) 70 | 71 | ;;; This function returns nothing 72 | (@interface func (export "a_function_that_returns_nothing") 73 | (param $some_parameter u64) 74 | (result $error (expected (error $test_errno))) 75 | ) 76 | 77 | ;;; This function gets a string 78 | (@interface func (export "a_function_that_gets_a_string") 79 | (param $str string) 80 | (result $error (expected $test_bool (error $test_errno))) 81 | ) 82 | 83 | ;;; This function return a tagged union 84 | (@interface func (export "a_function_that_returns_a_tagged_union") 85 | (param $str $test_string) 86 | (result $error (expected $test_tagged_union (error $test_errno))) 87 | ) 88 | 89 | ;;; This function gets and returns a string 90 | (@interface func (export "a_function_that_gets_and_returns_a_string") 91 | (param $str $test_string) 92 | (result $error (expected $test_string (error $test_errno))) 93 | ) 94 | ) 95 | -------------------------------------------------------------------------------- /tests/wasi_experimental_http.witx: -------------------------------------------------------------------------------- 1 | (module $wasi_experimental_http 2 | (typename $http_error 3 | (enum (@witx tag u32) 4 | ;;; Success 5 | $success 6 | ;;; Invalid handle 7 | $invalid_handle 8 | ;;; Memory not found 9 | $memory_not_found 10 | ;;; Memory access error 11 | $memory_access_error 12 | ;;; Buffer too small 13 | $buffer_too_small 14 | ;;; Header not found 15 | $header_not_found 16 | ;;; UTF-8 error 17 | $utf8_error 18 | ;;; Destination not allowed 19 | $destination_not_allowed 20 | ;;; Invalid method 21 | $invalid_method 22 | ;;; Invalid encoding 23 | $invalid_encoding 24 | ;;; Invalid URL 25 | $invalid_url 26 | ;;; Request error 27 | $request_error 28 | ;;; Runtime error 29 | $runtime_error 30 | ;;; Too many sessions 31 | $too_many_sessions 32 | ) 33 | ) 34 | 35 | ;;; Handles for the HTTP extensions 36 | (resource $http_handle) 37 | 38 | ;;; HTTP status code 39 | (typename $status_code u16) 40 | 41 | ;;; An HTTP body being sent 42 | (typename $outgoing_body (in-buffer u8)) 43 | 44 | ;;; Buffer for an HTTP body being received 45 | (typename $incoming_body (out-buffer u8)) 46 | 47 | ;;; A response handle 48 | (typename $response_handle (handle $http_handle)) 49 | 50 | ;;; Buffer to store a header value 51 | (typename $header_value_buf (out-buffer u8)) 52 | 53 | ;;; Number of bytes having been written 54 | (typename $written_bytes (@witx usize)) 55 | 56 | ;;; Send a request 57 | (@interface func (export "req") 58 | (param $url string) 59 | (param $method string) 60 | (param $headers string) 61 | (param $body $outgoing_body) 62 | (result $error (expected (tuple $status_code $response_handle) (error $http_error))) 63 | ) 64 | 65 | ;;; Close a request handle 66 | (@interface func (export "close") 67 | (param $response_handle $response_handle) 68 | (result $error (expected (error $http_error))) 69 | ) 70 | 71 | ;;; Get the value associated with a header 72 | (@interface func (export "header_get") 73 | (param $response_handle $response_handle) 74 | (param $header_name string) 75 | (param $header_value_buf $header_value_buf) 76 | (result $error (expected $written_bytes (error $http_error))) 77 | ) 78 | 79 | ;;; Fill a buffer with the streamed content of a response body 80 | (@interface func (export "body_read") 81 | (param $response_handle $response_handle) 82 | (param $body_buf $incoming_body) 83 | (result $error (expected $written_bytes (error $http_error))) 84 | ) 85 | ) 86 | --------------------------------------------------------------------------------