├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── js-component ├── main.mjs └── wit │ ├── jsiface.wit │ └── world.wit ├── jsiface.mjs ├── ktfmt-0.47-jar-with-dependencies.jar ├── package-lock.json ├── package.json ├── run.mjs ├── run.sh ├── rust-component ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── src │ └── lib.rs └── wit │ ├── iface.wit │ └── world.wit ├── settings.gradle.kts ├── src └── wasmWasiMain │ └── kotlin │ ├── IfaceExports.kt │ └── bindings │ ├── ComponentSupport.kt │ ├── InternalW.kt │ └── W.kt ├── wasi_snapshot_preview1.reactor.wasm └── wit ├── deps ├── cli │ ├── command.wit │ ├── environment.wit │ ├── exit.wit │ ├── imports.wit │ ├── run.wit │ ├── stdio.wit │ └── terminal.wit ├── clocks │ ├── monotonic-clock.wit │ ├── wall-clock.wit │ └── world.wit ├── filesystem │ ├── preopens.wit │ ├── types.wit │ └── world.wit ├── io │ ├── error.wit │ ├── poll.wit │ ├── streams.wit │ └── world.wit ├── random │ ├── insecure-seed.wit │ ├── insecure.wit │ ├── random.wit │ └── world.wit └── sockets │ ├── instance-network.wit │ ├── ip-name-lookup.wit │ ├── network.wit │ ├── tcp-create-socket.wit │ ├── tcp.wit │ ├── udp-create-socket.wit │ ├── udp.wit │ └── world.wit ├── iface.wit ├── jsiface.wit └── world.wit /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .idea/ 3 | out/ 4 | .gradle/ 5 | node_modules/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Experiments with compiling Kotlin to Wasm Component, 3 | linking it with an example component written in Rust, transpiling it to JS + Core Wasm using [jco](https://github.com/bytecodealliance/jco), 4 | and running the result in Node.js. 5 | 6 | ## Install dependencies 7 | 8 | Checkout `skuzmich/cm-prototype` branch of [Kotlin](https://github.com/JetBrains/kotlin) somewhere and install it to mavenLocal: 9 | 10 | ``` 11 | cd path/to/kotlin 12 | ./gradlew install --parallel 13 | ``` 14 | 15 | Install recent Rust toolchain and add wasm32-unknown-unknown target: 16 | 17 | ```shell 18 | rustup target add wasm32-unknown-unknown 19 | ``` 20 | 21 | Install wasm-tools 22 | 23 | ```shell 24 | cargo install wasm-tools 25 | ``` 26 | 27 | Install Node.js v22+ with WasmGC support, for example via NVM: 28 | 29 | ```shell 30 | nvm install v22.2.0 31 | ``` 32 | 33 | Install jco and preview2-shim 34 | 35 | ```shell 36 | npm install 37 | ``` 38 | 39 | Install the latest experimental wit-bindgen CLI with Kotlin support via `cargo`: 40 | 41 | ```shell 42 | cargo install wit-bindgen-cli --git https://github.com/skuzmich/wit-bindgen --branch kotlin 43 | ``` 44 | 45 | ## Build and run 46 | 47 | ```shell 48 | ./run.sh 49 | ``` 50 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("multiplatform") version "2.0.255-SNAPSHOT" 3 | } 4 | 5 | repositories { 6 | mavenLocal() 7 | mavenCentral() 8 | } 9 | 10 | kotlin { 11 | wasmWasi { 12 | binaries.executable() 13 | nodejs() 14 | } 15 | } 16 | 17 | tasks.withType().configureEach { 18 | kotlinOptions.freeCompilerArgs += listOf( 19 | "-Xwasm-use-traps-instead-of-exceptions", 20 | "-language-version", "2.0", 21 | "-Xwasm-initialize-in-start-function", 22 | ) 23 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashor/kotlin-wasm-cm-experiments/d86bcf6e509a2ab8d174946484de83f818be2a4c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /js-component/main.mjs: -------------------------------------------------------------------------------- 1 | class X { 2 | constructor (a) { 3 | this.a = a; 4 | } 5 | getA () { 6 | return this.a + 200; 7 | } 8 | setA (a) { 9 | this.a = a; 10 | } 11 | static add (x, a) { 12 | x.setA(x.getA() + a); 13 | return x; 14 | } 15 | } 16 | 17 | export const jsiface = { X } -------------------------------------------------------------------------------- /js-component/wit/jsiface.wit: -------------------------------------------------------------------------------- 1 | package cm:example; 2 | 3 | interface jsiface { 4 | resource x { 5 | constructor(a: s32); 6 | get-a: func() -> s32; 7 | set-a: func(a: s32); 8 | add: static func(x: borrow, a: s32) -> x; 9 | } 10 | } -------------------------------------------------------------------------------- /js-component/wit/world.wit: -------------------------------------------------------------------------------- 1 | package cm:example; 2 | 3 | world w { 4 | export jsiface; 5 | } -------------------------------------------------------------------------------- /jsiface.mjs: -------------------------------------------------------------------------------- 1 | export class X { 2 | constructor (a) { 3 | this.a = a; 4 | } 5 | getA () { 6 | return this.a; 7 | } 8 | setA (a) { 9 | this.a = a; 10 | } 11 | static add (x, a) { 12 | x.setA(x.getA() + a); 13 | return x; 14 | } 15 | } -------------------------------------------------------------------------------- /ktfmt-0.47-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashor/kotlin-wasm-cm-experiments/d86bcf6e509a2ab8d174946484de83f818be2a4c/ktfmt-0.47-jar-with-dependencies.jar -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kotlin-wasm-cm-experiments", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@bytecodealliance/componentize-js": "^0.8.3", 9 | "@bytecodealliance/jco": "^1.2.2", 10 | "@bytecodealliance/preview2-shim": "^0.16.1" 11 | } 12 | }, 13 | "node_modules/@bytecodealliance/componentize-js": { 14 | "version": "0.8.3", 15 | "resolved": "https://registry.npmjs.org/@bytecodealliance/componentize-js/-/componentize-js-0.8.3.tgz", 16 | "integrity": "sha512-QyEHRtVg/Cg6RsA75AvRSbSOr0u+FLuXqB89X4Sys44K/VT5g/S9eMn8gqTotfuXVU3btS3Z4QAiyHSF2bja3w==", 17 | "dependencies": { 18 | "@bytecodealliance/jco": "1.1.1", 19 | "@bytecodealliance/wizer": "^3.0.1", 20 | "es-module-lexer": "^1.4.1" 21 | } 22 | }, 23 | "node_modules/@bytecodealliance/componentize-js/node_modules/@bytecodealliance/jco": { 24 | "version": "1.1.1", 25 | "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.1.1.tgz", 26 | "integrity": "sha512-s8Zz6GFPlo2g+dsGp1OMIWXSZnM4FyIloxNAc4grF5TZwFoD00Gj8b0xvpmFSeZj36X/bJPa7x3za3j7Cfeetw==", 27 | "workspaces": [ 28 | "packages/preview2-shim" 29 | ], 30 | "dependencies": { 31 | "@bytecodealliance/preview2-shim": "^0.16.1", 32 | "binaryen": "^116.0.0", 33 | "chalk-template": "^1", 34 | "commander": "^12", 35 | "mkdirp": "^3", 36 | "ora": "^8", 37 | "terser": "^5" 38 | }, 39 | "bin": { 40 | "jco": "src/jco.js" 41 | } 42 | }, 43 | "node_modules/@bytecodealliance/jco": { 44 | "version": "1.2.2", 45 | "resolved": "https://registry.npmjs.org/@bytecodealliance/jco/-/jco-1.2.2.tgz", 46 | "integrity": "sha512-lwbA4GXYRoq6ogyiM1wOUE6t4dm963B5YOwgWkJxYw8bBDzNEoaWn+MQoYqgvVY1F+SPvK17EBKBFgG1yOFu7Q==", 47 | "workspaces": [ 48 | "packages/preview2-shim" 49 | ], 50 | "dependencies": { 51 | "@bytecodealliance/preview2-shim": "^0.16.2", 52 | "binaryen": "^116.0.0", 53 | "chalk-template": "^1", 54 | "commander": "^12", 55 | "mkdirp": "^3", 56 | "ora": "^8", 57 | "terser": "^5" 58 | }, 59 | "bin": { 60 | "jco": "src/jco.js" 61 | } 62 | }, 63 | "node_modules/@bytecodealliance/preview2-shim": { 64 | "version": "0.16.2", 65 | "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.16.2.tgz", 66 | "integrity": "sha512-36MwesmbLSf3Y5/OHcS85iBaF0N92CQ4gpjtDVKSbrjxmrBKCWlWVfoQ03F/cqDg8k5K7pzVaVBH0XBIbTCfTQ==" 67 | }, 68 | "node_modules/@bytecodealliance/wizer": { 69 | "version": "3.0.1", 70 | "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer/-/wizer-3.0.1.tgz", 71 | "integrity": "sha512-f0NBiBHCNBkbFHTPRbA7aKf/t4KyNhi2KvSqw3QzCgi8wFF/uLZ0dhejj93rbiKO/iwWbmU7v9K3SVkW81mcjQ==", 72 | "bin": { 73 | "wizer": "wizer.js" 74 | }, 75 | "engines": { 76 | "node": ">=16" 77 | }, 78 | "optionalDependencies": { 79 | "@bytecodealliance/wizer-darwin-arm64": "3.0.1", 80 | "@bytecodealliance/wizer-darwin-x64": "3.0.1", 81 | "@bytecodealliance/wizer-linux-arm64": "3.0.1", 82 | "@bytecodealliance/wizer-linux-s390x": "3.0.1", 83 | "@bytecodealliance/wizer-linux-x64": "3.0.1", 84 | "@bytecodealliance/wizer-win32-x64": "3.0.1" 85 | } 86 | }, 87 | "node_modules/@bytecodealliance/wizer-darwin-arm64": { 88 | "version": "3.0.1", 89 | "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-arm64/-/wizer-darwin-arm64-3.0.1.tgz", 90 | "integrity": "sha512-/8KYSajyhO9koAE3qQhYfC6belZheJw9X3XqW7hrizTpj6n4z4OJFhhqwJmiYFUUsPtC7OxcXMFFPbTuSQPBcw==", 91 | "cpu": [ 92 | "arm64" 93 | ], 94 | "optional": true, 95 | "os": [ 96 | "darwin" 97 | ], 98 | "bin": { 99 | "wizer-darwin-arm64": "wizer" 100 | } 101 | }, 102 | "node_modules/@bytecodealliance/wizer-darwin-x64": { 103 | "version": "3.0.1", 104 | "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-darwin-x64/-/wizer-darwin-x64-3.0.1.tgz", 105 | "integrity": "sha512-bMReultN/r+W/BRXV0F+28U5dZwbQT/ZO0k4icZlhUhrv5/wpQJix7Z/ZvBnVQ+/JHb0QDUpFk2/zCtgkRXP6Q==", 106 | "cpu": [ 107 | "x64" 108 | ], 109 | "optional": true, 110 | "os": [ 111 | "darwin" 112 | ], 113 | "bin": { 114 | "wizer-darwin-x64": "wizer" 115 | } 116 | }, 117 | "node_modules/@bytecodealliance/wizer-linux-arm64": { 118 | "version": "3.0.1", 119 | "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-arm64/-/wizer-linux-arm64-3.0.1.tgz", 120 | "integrity": "sha512-35ZhAeYxWK3bTqqgwysbBWlGlrlMNKNng3ZITQV2PAtafpE7aCeqywl7VAS4lLRG5eTb7wxNgN7zf8d3wiIFTQ==", 121 | "cpu": [ 122 | "arm64" 123 | ], 124 | "optional": true, 125 | "os": [ 126 | "linux" 127 | ], 128 | "bin": { 129 | "wizer-linux-arm64": "wizer" 130 | } 131 | }, 132 | "node_modules/@bytecodealliance/wizer-linux-s390x": { 133 | "version": "3.0.1", 134 | "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-s390x/-/wizer-linux-s390x-3.0.1.tgz", 135 | "integrity": "sha512-Smvy9mguEMtX0lupDLTPshXUzAHeOhgscr1bhGNjeCCLD1sd8rIjBvWV19Wtra0BL1zTuU2EPOHjR/4k8WoyDg==", 136 | "cpu": [ 137 | "s390x" 138 | ], 139 | "optional": true, 140 | "os": [ 141 | "linux" 142 | ], 143 | "bin": { 144 | "wizer-linux-s390x": "wizer" 145 | } 146 | }, 147 | "node_modules/@bytecodealliance/wizer-linux-x64": { 148 | "version": "3.0.1", 149 | "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-linux-x64/-/wizer-linux-x64-3.0.1.tgz", 150 | "integrity": "sha512-uUue78xl7iwndsGgTsagHLTLyLBVHhwzuywiwHt1xw8y0X0O8REKRLBoB7+LdM+pttDPdFtKJgbTFL4UPAA7Yw==", 151 | "cpu": [ 152 | "x64" 153 | ], 154 | "optional": true, 155 | "os": [ 156 | "linux" 157 | ], 158 | "bin": { 159 | "wizer-linux-x64": "wizer" 160 | } 161 | }, 162 | "node_modules/@bytecodealliance/wizer-win32-x64": { 163 | "version": "3.0.1", 164 | "resolved": "https://registry.npmjs.org/@bytecodealliance/wizer-win32-x64/-/wizer-win32-x64-3.0.1.tgz", 165 | "integrity": "sha512-ycd38sx1UTZpHZwh8IfH/4N3n0OQUB8awxkUSLXf9PolEd088YbxoPB3noHy4E+L2oYN7KZMrg9517pX0z2RhQ==", 166 | "cpu": [ 167 | "x64" 168 | ], 169 | "optional": true, 170 | "os": [ 171 | "win32" 172 | ], 173 | "bin": { 174 | "wizer-win32-x64": "wizer" 175 | } 176 | }, 177 | "node_modules/@jridgewell/gen-mapping": { 178 | "version": "0.3.5", 179 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", 180 | "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", 181 | "dependencies": { 182 | "@jridgewell/set-array": "^1.2.1", 183 | "@jridgewell/sourcemap-codec": "^1.4.10", 184 | "@jridgewell/trace-mapping": "^0.3.24" 185 | }, 186 | "engines": { 187 | "node": ">=6.0.0" 188 | } 189 | }, 190 | "node_modules/@jridgewell/resolve-uri": { 191 | "version": "3.1.2", 192 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 193 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 194 | "engines": { 195 | "node": ">=6.0.0" 196 | } 197 | }, 198 | "node_modules/@jridgewell/set-array": { 199 | "version": "1.2.1", 200 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", 201 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", 202 | "engines": { 203 | "node": ">=6.0.0" 204 | } 205 | }, 206 | "node_modules/@jridgewell/source-map": { 207 | "version": "0.3.6", 208 | "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", 209 | "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", 210 | "dependencies": { 211 | "@jridgewell/gen-mapping": "^0.3.5", 212 | "@jridgewell/trace-mapping": "^0.3.25" 213 | } 214 | }, 215 | "node_modules/@jridgewell/sourcemap-codec": { 216 | "version": "1.4.15", 217 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 218 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" 219 | }, 220 | "node_modules/@jridgewell/trace-mapping": { 221 | "version": "0.3.25", 222 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", 223 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 224 | "dependencies": { 225 | "@jridgewell/resolve-uri": "^3.1.0", 226 | "@jridgewell/sourcemap-codec": "^1.4.14" 227 | } 228 | }, 229 | "node_modules/acorn": { 230 | "version": "8.11.3", 231 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", 232 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", 233 | "bin": { 234 | "acorn": "bin/acorn" 235 | }, 236 | "engines": { 237 | "node": ">=0.4.0" 238 | } 239 | }, 240 | "node_modules/ansi-regex": { 241 | "version": "6.0.1", 242 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", 243 | "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", 244 | "engines": { 245 | "node": ">=12" 246 | }, 247 | "funding": { 248 | "url": "https://github.com/chalk/ansi-regex?sponsor=1" 249 | } 250 | }, 251 | "node_modules/binaryen": { 252 | "version": "116.0.0", 253 | "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-116.0.0.tgz", 254 | "integrity": "sha512-Hp0dXC6Cb/rTwWEoUS2BRghObE7g/S9umKtxuTDt3f61G6fNTE/YVew/ezyy3IdHcLx3f17qfh6LwETgCfvWkQ==", 255 | "bin": { 256 | "wasm-opt": "bin/wasm-opt", 257 | "wasm2js": "bin/wasm2js" 258 | } 259 | }, 260 | "node_modules/buffer-from": { 261 | "version": "1.1.2", 262 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 263 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 264 | }, 265 | "node_modules/chalk": { 266 | "version": "5.3.0", 267 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", 268 | "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", 269 | "engines": { 270 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 271 | }, 272 | "funding": { 273 | "url": "https://github.com/chalk/chalk?sponsor=1" 274 | } 275 | }, 276 | "node_modules/chalk-template": { 277 | "version": "1.1.0", 278 | "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz", 279 | "integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==", 280 | "dependencies": { 281 | "chalk": "^5.2.0" 282 | }, 283 | "engines": { 284 | "node": ">=14.16" 285 | }, 286 | "funding": { 287 | "url": "https://github.com/chalk/chalk-template?sponsor=1" 288 | } 289 | }, 290 | "node_modules/cli-cursor": { 291 | "version": "4.0.0", 292 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", 293 | "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", 294 | "dependencies": { 295 | "restore-cursor": "^4.0.0" 296 | }, 297 | "engines": { 298 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 299 | }, 300 | "funding": { 301 | "url": "https://github.com/sponsors/sindresorhus" 302 | } 303 | }, 304 | "node_modules/cli-spinners": { 305 | "version": "2.9.2", 306 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", 307 | "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", 308 | "engines": { 309 | "node": ">=6" 310 | }, 311 | "funding": { 312 | "url": "https://github.com/sponsors/sindresorhus" 313 | } 314 | }, 315 | "node_modules/commander": { 316 | "version": "12.0.0", 317 | "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", 318 | "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", 319 | "engines": { 320 | "node": ">=18" 321 | } 322 | }, 323 | "node_modules/emoji-regex": { 324 | "version": "10.3.0", 325 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", 326 | "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" 327 | }, 328 | "node_modules/es-module-lexer": { 329 | "version": "1.5.0", 330 | "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz", 331 | "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==" 332 | }, 333 | "node_modules/get-east-asian-width": { 334 | "version": "1.2.0", 335 | "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", 336 | "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", 337 | "engines": { 338 | "node": ">=18" 339 | }, 340 | "funding": { 341 | "url": "https://github.com/sponsors/sindresorhus" 342 | } 343 | }, 344 | "node_modules/is-interactive": { 345 | "version": "2.0.0", 346 | "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", 347 | "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", 348 | "engines": { 349 | "node": ">=12" 350 | }, 351 | "funding": { 352 | "url": "https://github.com/sponsors/sindresorhus" 353 | } 354 | }, 355 | "node_modules/is-unicode-supported": { 356 | "version": "2.0.0", 357 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", 358 | "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", 359 | "engines": { 360 | "node": ">=18" 361 | }, 362 | "funding": { 363 | "url": "https://github.com/sponsors/sindresorhus" 364 | } 365 | }, 366 | "node_modules/log-symbols": { 367 | "version": "6.0.0", 368 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", 369 | "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", 370 | "dependencies": { 371 | "chalk": "^5.3.0", 372 | "is-unicode-supported": "^1.3.0" 373 | }, 374 | "engines": { 375 | "node": ">=18" 376 | }, 377 | "funding": { 378 | "url": "https://github.com/sponsors/sindresorhus" 379 | } 380 | }, 381 | "node_modules/log-symbols/node_modules/is-unicode-supported": { 382 | "version": "1.3.0", 383 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", 384 | "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", 385 | "engines": { 386 | "node": ">=12" 387 | }, 388 | "funding": { 389 | "url": "https://github.com/sponsors/sindresorhus" 390 | } 391 | }, 392 | "node_modules/mimic-fn": { 393 | "version": "2.1.0", 394 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 395 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 396 | "engines": { 397 | "node": ">=6" 398 | } 399 | }, 400 | "node_modules/mkdirp": { 401 | "version": "3.0.1", 402 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", 403 | "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", 404 | "bin": { 405 | "mkdirp": "dist/cjs/src/bin.js" 406 | }, 407 | "engines": { 408 | "node": ">=10" 409 | }, 410 | "funding": { 411 | "url": "https://github.com/sponsors/isaacs" 412 | } 413 | }, 414 | "node_modules/onetime": { 415 | "version": "5.1.2", 416 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", 417 | "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", 418 | "dependencies": { 419 | "mimic-fn": "^2.1.0" 420 | }, 421 | "engines": { 422 | "node": ">=6" 423 | }, 424 | "funding": { 425 | "url": "https://github.com/sponsors/sindresorhus" 426 | } 427 | }, 428 | "node_modules/ora": { 429 | "version": "8.0.1", 430 | "resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz", 431 | "integrity": "sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==", 432 | "dependencies": { 433 | "chalk": "^5.3.0", 434 | "cli-cursor": "^4.0.0", 435 | "cli-spinners": "^2.9.2", 436 | "is-interactive": "^2.0.0", 437 | "is-unicode-supported": "^2.0.0", 438 | "log-symbols": "^6.0.0", 439 | "stdin-discarder": "^0.2.1", 440 | "string-width": "^7.0.0", 441 | "strip-ansi": "^7.1.0" 442 | }, 443 | "engines": { 444 | "node": ">=18" 445 | }, 446 | "funding": { 447 | "url": "https://github.com/sponsors/sindresorhus" 448 | } 449 | }, 450 | "node_modules/restore-cursor": { 451 | "version": "4.0.0", 452 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", 453 | "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", 454 | "dependencies": { 455 | "onetime": "^5.1.0", 456 | "signal-exit": "^3.0.2" 457 | }, 458 | "engines": { 459 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 460 | }, 461 | "funding": { 462 | "url": "https://github.com/sponsors/sindresorhus" 463 | } 464 | }, 465 | "node_modules/signal-exit": { 466 | "version": "3.0.7", 467 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 468 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 469 | }, 470 | "node_modules/source-map": { 471 | "version": "0.6.1", 472 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 473 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 474 | "engines": { 475 | "node": ">=0.10.0" 476 | } 477 | }, 478 | "node_modules/source-map-support": { 479 | "version": "0.5.21", 480 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 481 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 482 | "dependencies": { 483 | "buffer-from": "^1.0.0", 484 | "source-map": "^0.6.0" 485 | } 486 | }, 487 | "node_modules/stdin-discarder": { 488 | "version": "0.2.2", 489 | "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", 490 | "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", 491 | "engines": { 492 | "node": ">=18" 493 | }, 494 | "funding": { 495 | "url": "https://github.com/sponsors/sindresorhus" 496 | } 497 | }, 498 | "node_modules/string-width": { 499 | "version": "7.1.0", 500 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", 501 | "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", 502 | "dependencies": { 503 | "emoji-regex": "^10.3.0", 504 | "get-east-asian-width": "^1.0.0", 505 | "strip-ansi": "^7.1.0" 506 | }, 507 | "engines": { 508 | "node": ">=18" 509 | }, 510 | "funding": { 511 | "url": "https://github.com/sponsors/sindresorhus" 512 | } 513 | }, 514 | "node_modules/strip-ansi": { 515 | "version": "7.1.0", 516 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 517 | "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 518 | "dependencies": { 519 | "ansi-regex": "^6.0.1" 520 | }, 521 | "engines": { 522 | "node": ">=12" 523 | }, 524 | "funding": { 525 | "url": "https://github.com/chalk/strip-ansi?sponsor=1" 526 | } 527 | }, 528 | "node_modules/terser": { 529 | "version": "5.30.3", 530 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz", 531 | "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==", 532 | "dependencies": { 533 | "@jridgewell/source-map": "^0.3.3", 534 | "acorn": "^8.8.2", 535 | "commander": "^2.20.0", 536 | "source-map-support": "~0.5.20" 537 | }, 538 | "bin": { 539 | "terser": "bin/terser" 540 | }, 541 | "engines": { 542 | "node": ">=10" 543 | } 544 | }, 545 | "node_modules/terser/node_modules/commander": { 546 | "version": "2.20.3", 547 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 548 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 549 | } 550 | } 551 | } 552 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@bytecodealliance/componentize-js": "^0.8.3", 4 | "@bytecodealliance/jco": "^1.2.2", 5 | "@bytecodealliance/preview2-shim": "^0.16.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /run.mjs: -------------------------------------------------------------------------------- 1 | import('./build/out/jco/kco-wasm-wasi.mjs').then(x => { 2 | console.log(x.iface.markdownToHtml('- Ha')); 3 | console.log(x.iface.test1( 4 | true, 5 | 1, 6 | 2, 7 | 3, 8 | 4, 9 | 5, 10 | 6, 11 | 7, 12 | 8, 13 | 'G' 14 | )); 15 | 16 | console.log(x.iface.testRecord( { a: "0", b: "11" })); 17 | console.log(x.iface.test2(["ab", "cd", null, "ef"])); 18 | 19 | 20 | console.log(x.iface.testResult(true)); 21 | 22 | try { 23 | console.log(x.iface.testResult(false)); 24 | } catch (e) { 25 | console.log("Caught error: ", e); 26 | } 27 | console.log(x.iface.testTuple([true], [false, "S1"], [true, "S2_JS", 50])); 28 | 29 | console.log(x.iface.testVariant({tag: 'a', val: 10})); 30 | console.log(x.iface.testVariant({tag: 'b', val: 20})); 31 | 32 | console.log(x.iface.testEnum("hot-pink")); 33 | console.log(x.iface.testEnum("lime-green")); 34 | 35 | console.log(x.iface.testFlags({a: true, b: true, c: true})); 36 | 37 | console.log("Testing..."); 38 | x.test.test(); 39 | console.log("Testing exported resources"); 40 | let { TestExportedResource } = x.test; 41 | { 42 | let test = new TestExportedResource(11); 43 | console.log(test.getA()); 44 | test.setA(22); 45 | console.log(test.getA()); 46 | let test2 = TestExportedResource.add(test, 33); 47 | console.log(test2.getA()); 48 | } 49 | console.log("Testing end..."); 50 | }); -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | COMPILED_WASM=./build/compileSync/wasmWasi/main/productionExecutable/kotlin 4 | MODULE_NAME=kco-wasm-wasi 5 | WASI_ADAPTER=./wasi_snapshot_preview1.reactor.wasm 6 | 7 | set -e 8 | 9 | rm -rf ./build/out 10 | mkdir -p ./build/out/dependencies 11 | 12 | # Build Rust component 13 | cp wit/iface.wit ./rust-component/wit/ 14 | cp wit/jsiface.wit ./js-component/wit/ 15 | 16 | (cd ./rust-component/ && \ 17 | cargo build --target wasm32-unknown-unknown --release && \ 18 | wasm-tools component new ./target/wasm32-unknown-unknown/release/iface.wasm -o ../build/out/dependencies/iface.wasm) 19 | 20 | # Generate WIT bindings for Kotlin 21 | wit-bindgen kotlin ./wit --out-dir src/wasmWasiMain/kotlin/bindings 22 | java -jar ./ktfmt-0.47-jar-with-dependencies.jar ./src/wasmWasiMain/kotlin/bindings 23 | 24 | # Compile Kotlin code 25 | ./gradlew :compileProductionExecutableKotlinWasmWasi 26 | 27 | # Embed wit interface to core wasm file (wasm-tools convention) 28 | mkdir -p ./build/out/wasm 29 | wasm-tools component embed wit/ $COMPILED_WASM/$MODULE_NAME.stub.wasm -o build/out/wasm/$MODULE_NAME.stube.embedded.wasm 30 | 31 | mkdir -p ./build/out/component 32 | 33 | # Create a component from core Wasm 34 | wasm-tools component new build/out/wasm/$MODULE_NAME.stube.embedded.wasm -o build/out/component/$MODULE_NAME.uncomposed.wasm --adapt $WASI_ADAPTER --realloc-via-memory-grow 35 | 36 | # Compose Kotlin component with Rust component into a single linked component 37 | wasm-tools compose build/out/component/$MODULE_NAME.uncomposed.wasm -o build/out/component/$MODULE_NAME.wasm --definitions ./build/out/dependencies/iface.wasm --search-path ./build/out/dependencies 38 | 39 | # Transpile component into JS + core Wasm 40 | npx jco transpile build/out/component/$MODULE_NAME.wasm -o build/out/jco --base64-cutoff 0 -q --map "cm:example/jsiface=./../../../jsiface.mjs" 41 | 42 | # Replace a stub module with a real Kotlin core module 43 | cp $COMPILED_WASM/$MODULE_NAME.wasm build/out/jco/$MODULE_NAME.core.wasm 44 | 45 | # Run in Node.js and call ini (main) function 46 | cp build/out/jco/$MODULE_NAME.js build/out/jco/$MODULE_NAME.mjs 47 | 48 | node run.mjs -------------------------------------------------------------------------------- /rust-component/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /rust-component/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.82" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "2.5.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 16 | 17 | [[package]] 18 | name = "equivalent" 19 | version = "1.0.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 22 | 23 | [[package]] 24 | name = "getopts" 25 | version = "0.2.21" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" 28 | dependencies = [ 29 | "unicode-width", 30 | ] 31 | 32 | [[package]] 33 | name = "hashbrown" 34 | version = "0.14.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 37 | 38 | [[package]] 39 | name = "heck" 40 | version = "0.4.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 43 | dependencies = [ 44 | "unicode-segmentation", 45 | ] 46 | 47 | [[package]] 48 | name = "id-arena" 49 | version = "2.2.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" 52 | 53 | [[package]] 54 | name = "iface" 55 | version = "0.1.0" 56 | dependencies = [ 57 | "pulldown-cmark", 58 | "wit-bindgen", 59 | ] 60 | 61 | [[package]] 62 | name = "indexmap" 63 | version = "2.2.6" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 66 | dependencies = [ 67 | "equivalent", 68 | "hashbrown", 69 | "serde", 70 | ] 71 | 72 | [[package]] 73 | name = "itoa" 74 | version = "1.0.11" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 77 | 78 | [[package]] 79 | name = "leb128" 80 | version = "0.2.5" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 83 | 84 | [[package]] 85 | name = "log" 86 | version = "0.4.21" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 89 | 90 | [[package]] 91 | name = "memchr" 92 | version = "2.7.2" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 95 | 96 | [[package]] 97 | name = "proc-macro2" 98 | version = "1.0.81" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 101 | dependencies = [ 102 | "unicode-ident", 103 | ] 104 | 105 | [[package]] 106 | name = "pulldown-cmark" 107 | version = "0.9.6" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" 110 | dependencies = [ 111 | "bitflags", 112 | "getopts", 113 | "memchr", 114 | "unicase", 115 | ] 116 | 117 | [[package]] 118 | name = "quote" 119 | version = "1.0.36" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 122 | dependencies = [ 123 | "proc-macro2", 124 | ] 125 | 126 | [[package]] 127 | name = "ryu" 128 | version = "1.0.17" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 131 | 132 | [[package]] 133 | name = "semver" 134 | version = "1.0.22" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" 137 | 138 | [[package]] 139 | name = "serde" 140 | version = "1.0.198" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" 143 | dependencies = [ 144 | "serde_derive", 145 | ] 146 | 147 | [[package]] 148 | name = "serde_derive" 149 | version = "1.0.198" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" 152 | dependencies = [ 153 | "proc-macro2", 154 | "quote", 155 | "syn", 156 | ] 157 | 158 | [[package]] 159 | name = "serde_json" 160 | version = "1.0.116" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" 163 | dependencies = [ 164 | "itoa", 165 | "ryu", 166 | "serde", 167 | ] 168 | 169 | [[package]] 170 | name = "smallvec" 171 | version = "1.13.2" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 174 | 175 | [[package]] 176 | name = "spdx" 177 | version = "0.10.4" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9" 180 | dependencies = [ 181 | "smallvec", 182 | ] 183 | 184 | [[package]] 185 | name = "syn" 186 | version = "2.0.60" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 189 | dependencies = [ 190 | "proc-macro2", 191 | "quote", 192 | "unicode-ident", 193 | ] 194 | 195 | [[package]] 196 | name = "unicase" 197 | version = "2.7.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 200 | dependencies = [ 201 | "version_check", 202 | ] 203 | 204 | [[package]] 205 | name = "unicode-ident" 206 | version = "1.0.12" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 209 | 210 | [[package]] 211 | name = "unicode-segmentation" 212 | version = "1.11.0" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 215 | 216 | [[package]] 217 | name = "unicode-width" 218 | version = "0.1.11" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 221 | 222 | [[package]] 223 | name = "unicode-xid" 224 | version = "0.2.4" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 227 | 228 | [[package]] 229 | name = "version_check" 230 | version = "0.9.4" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 233 | 234 | [[package]] 235 | name = "wasm-encoder" 236 | version = "0.38.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" 239 | dependencies = [ 240 | "leb128", 241 | ] 242 | 243 | [[package]] 244 | name = "wasm-encoder" 245 | version = "0.41.2" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" 248 | dependencies = [ 249 | "leb128", 250 | ] 251 | 252 | [[package]] 253 | name = "wasm-metadata" 254 | version = "0.10.20" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "18ebaa7bd0f9e7a5e5dd29b9a998acf21c4abed74265524dd7e85934597bfb10" 257 | dependencies = [ 258 | "anyhow", 259 | "indexmap", 260 | "serde", 261 | "serde_derive", 262 | "serde_json", 263 | "spdx", 264 | "wasm-encoder 0.41.2", 265 | "wasmparser 0.121.2", 266 | ] 267 | 268 | [[package]] 269 | name = "wasmparser" 270 | version = "0.118.2" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "77f1154f1ab868e2a01d9834a805faca7bf8b50d041b4ca714d005d0dab1c50c" 273 | dependencies = [ 274 | "indexmap", 275 | "semver", 276 | ] 277 | 278 | [[package]] 279 | name = "wasmparser" 280 | version = "0.121.2" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" 283 | dependencies = [ 284 | "bitflags", 285 | "indexmap", 286 | "semver", 287 | ] 288 | 289 | [[package]] 290 | name = "wit-bindgen" 291 | version = "0.14.0" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "0205c6e67438f9d657318e0d7ee407a8017cd7bc5f1636cd4a280d4ccbc8d4a0" 294 | dependencies = [ 295 | "bitflags", 296 | "wit-bindgen-rust-macro", 297 | ] 298 | 299 | [[package]] 300 | name = "wit-bindgen-core" 301 | version = "0.14.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "4dfc34e539edf78da2efed167549962a4d26000db04694408285f52d90703fee" 304 | dependencies = [ 305 | "anyhow", 306 | "wit-component", 307 | "wit-parser", 308 | ] 309 | 310 | [[package]] 311 | name = "wit-bindgen-rust" 312 | version = "0.14.0" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "e898ad170a2796e9ecc495ce52cc2518dc35364ec042a4e108dd3c454e2a24c6" 315 | dependencies = [ 316 | "anyhow", 317 | "heck", 318 | "wasm-metadata", 319 | "wit-bindgen-core", 320 | "wit-component", 321 | ] 322 | 323 | [[package]] 324 | name = "wit-bindgen-rust-macro" 325 | version = "0.14.0" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "cde5bc98cd9ebc817e7a0325120b5cf047643c2cc4583b917e66914215f94cff" 328 | dependencies = [ 329 | "anyhow", 330 | "proc-macro2", 331 | "quote", 332 | "syn", 333 | "wit-bindgen-core", 334 | "wit-bindgen-rust", 335 | "wit-component", 336 | ] 337 | 338 | [[package]] 339 | name = "wit-component" 340 | version = "0.18.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "5b8a35a2a9992898c9d27f1664001860595a4bc99d32dd3599d547412e17d7e2" 343 | dependencies = [ 344 | "anyhow", 345 | "bitflags", 346 | "indexmap", 347 | "log", 348 | "serde", 349 | "serde_derive", 350 | "serde_json", 351 | "wasm-encoder 0.38.1", 352 | "wasm-metadata", 353 | "wasmparser 0.118.2", 354 | "wit-parser", 355 | ] 356 | 357 | [[package]] 358 | name = "wit-parser" 359 | version = "0.13.2" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "316b36a9f0005f5aa4b03c39bc3728d045df136f8c13a73b7db4510dec725e08" 362 | dependencies = [ 363 | "anyhow", 364 | "id-arena", 365 | "indexmap", 366 | "log", 367 | "semver", 368 | "serde", 369 | "serde_derive", 370 | "serde_json", 371 | "unicode-xid", 372 | ] 373 | -------------------------------------------------------------------------------- /rust-component/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iface" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | wit-bindgen = "0.14" 8 | pulldown-cmark = "0.9" 9 | 10 | [lib] 11 | crate-type = ["cdylib"] 12 | -------------------------------------------------------------------------------- /rust-component/src/lib.rs: -------------------------------------------------------------------------------- 1 | use pulldown_cmark::{html, Parser}; 2 | 3 | wit_bindgen::generate!({ 4 | path: "wit", 5 | exports: { 6 | "cm:example/iface": Component 7 | }, 8 | }); 9 | 10 | struct Component; 11 | 12 | impl exports::cm::example::iface::Guest for Component { 13 | fn markdown_to_html(input: String) -> String { 14 | let parser = Parser::new(&input); 15 | let mut html_output = String::new(); 16 | html::push_html(&mut html_output, parser); 17 | html_output 18 | } 19 | 20 | fn test1(p1: bool, p2: i8, p3: i16, p4: i32, p5: i64, p6: u8, p7: u16, p8: u32, p9: u64, p10: char) -> String { 21 | return format!("{p1}, {p2}, {p3}, {p4}, {p5}, {p6}, {p7}, {p8}, {p9}, {p10}") 22 | } 23 | 24 | fn test2(x: Vec>) -> Vec { 25 | let mut result: Vec = Vec::new(); 26 | for s in x { 27 | match s { 28 | None => result.push("-".to_string()), 29 | Some(s) => result.push(s.repeat(2)) 30 | 31 | } 32 | } 33 | result.clone() 34 | } 35 | 36 | fn test3(x: Option>>) -> i32 { 37 | match x { 38 | None => 0, 39 | Some(y) => match y { 40 | None => 1, 41 | Some(z) => match z { 42 | None => 2, 43 | Some(z2) => z2 44 | } 45 | } 46 | } 47 | } 48 | 49 | fn test_record(x: exports::cm::example::iface::Ab) -> exports::cm::example::iface::Ab { 50 | return exports::cm::example::iface::Ab { 51 | a: x.a.repeat(2), 52 | b: x.b.repeat(2), 53 | } 54 | } 55 | 56 | fn test_result(x: bool) -> Result { 57 | if x { 58 | Ok(10) 59 | } else { 60 | Err(20) 61 | } 62 | } 63 | 64 | fn test_tuple( 65 | x1: (bool,), 66 | x2: (bool, String), 67 | x3: (bool, String, i32), 68 | ) -> ( 69 | bool, 70 | bool, 71 | String, 72 | bool, 73 | String, 74 | i32, 75 | ) { 76 | (x1.0, x2.0, x2.1, x3.0, x3.1, x3.2) 77 | } 78 | 79 | fn test_variant(x: exports::cm::example::iface::V) -> exports::cm::example::iface::V { 80 | match x { 81 | exports::cm::example::iface::V::A(x) => exports::cm::example::iface::V::A(x + 1), 82 | exports::cm::example::iface::V::B(x) => exports::cm::example::iface::V::B(x - 1.0), 83 | } 84 | } 85 | 86 | fn test_enum(x: exports::cm::example::iface::Color) -> i32 { 87 | match x { 88 | exports::cm::example::iface::Color::HotPink => 0, 89 | exports::cm::example::iface::Color::LimeGreen => 1, 90 | exports::cm::example::iface::Color::NavyBlue => 2, 91 | } 92 | } 93 | fn test_flags(x: exports::cm::example::iface::Fg) -> String { 94 | format!("{}", x.bits()) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /rust-component/wit/iface.wit: -------------------------------------------------------------------------------- 1 | package cm:example; 2 | 3 | interface iface { 4 | markdown-to-html: func(x: string) -> string; 5 | test1: func( 6 | p1: bool, 7 | p2: s8, 8 | p3: s16, 9 | p4: s32, 10 | p5: s64, 11 | p6: u8, 12 | p7: u16, 13 | p8: u32, 14 | p9: u64, 15 | p10: char, 16 | ) -> string; 17 | 18 | test2: func(x: list>) -> list; 19 | test3: func(x: option>>) -> s32; 20 | 21 | record ab { 22 | a: string, 23 | b: string, 24 | } 25 | test-record: func(x: ab) -> ab; 26 | test-result: func(x: bool) -> result; 27 | test-tuple: func(x1: tuple, x2: tuple, x3: tuple) -> 28 | tuple; 29 | 30 | variant v { 31 | a(s32), 32 | b(float32), 33 | } 34 | 35 | test-variant: func(x: v) -> v; 36 | 37 | enum color { 38 | hot-pink, 39 | lime-green, 40 | navy-blue, 41 | } 42 | 43 | test-enum: func(x: color) -> s32; 44 | 45 | flags fg { 46 | a, b, c, d, e, 47 | } 48 | 49 | test-flags: func(x: fg) -> string; 50 | } -------------------------------------------------------------------------------- /rust-component/wit/world.wit: -------------------------------------------------------------------------------- 1 | package cm:example; 2 | 3 | world w { 4 | export iface; 5 | } -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "kco" 2 | 3 | pluginManagement { 4 | resolutionStrategy { 5 | repositories { 6 | mavenLocal() 7 | gradlePluginPortal() 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/wasmWasiMain/kotlin/IfaceExports.kt: -------------------------------------------------------------------------------- 1 | object IfaceExportsImpl : IfaceExports { 2 | override fun markdownToHtml(x: String): String = Iface.markdownToHtml(x) 3 | override fun test1(p1: Boolean, p2: Byte, p3: Short, p4: Int, p5: Long, p6: UByte, p7: UShort, p8: UInt, p9: ULong, p10: Int): String = 4 | Iface.test1(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) 5 | override fun test2(x: List): List = 6 | Iface.test2(x) 7 | 8 | override fun test3(x: Option>): Int = Iface.test3(x) 9 | override fun testRecord(x: IfaceExports.Ab): IfaceExports.Ab = IfaceExports.Ab(x.a + "_kt", x.b + "_kt") 10 | override fun testResult(x: Boolean): Result = Iface.testResult(x) 11 | override fun testTuple(x1: Boolean, x2: Pair, x3: Triple): Tuple6 = 12 | Iface.testTuple(x1, x2, x3) 13 | override fun testVariant(x: IfaceExports.V): IfaceExports.V = x 14 | override fun testEnum(x: IfaceExports.Color): Int = x.ordinal 15 | override fun testFlags(x: IfaceExports.Fg): String = x.toString() 16 | } 17 | 18 | object TestExportsImpl : TestExports { 19 | override fun test() { 20 | println("\n-- Testing markdown to HTML") 21 | val html = Iface.markdownToHtml( 22 | """ 23 | - First item 24 | - Second item 25 | - Third item 26 | - Indented item 1 27 | - Indented item 2 28 | - Fourth item 29 | """.trimIndent() 30 | ) 31 | println(html) 32 | 33 | println("\n-- Testing primitives") 34 | println( 35 | Iface.test1( 36 | true, 37 | 1.toByte(), 38 | 2.toShort(), 39 | 3.toInt(), 40 | 4.toLong(), 41 | 5.toUByte(), 42 | 6.toUShort(), 43 | 7.toUInt(), 44 | 8UL, 45 | 'G'.code.toInt() 46 | ) 47 | ) 48 | 49 | println("\n-- Testing records") 50 | println(Iface.testRecord(Iface.Ab("0", "11"))) 51 | 52 | println("\n-- Testing lists") 53 | println(Iface.test2(listOf("ab", "cd", null, "ef"))) 54 | 55 | println("\n-- Testing options") 56 | println(Iface.test3(Option.None)) 57 | println(Iface.test3(Option.Some(Option.None))) 58 | println(Iface.test3(Option.Some(Option.Some(null)))) 59 | println(Iface.test3(Option.Some(Option.Some(1000)))) 60 | 61 | println("\n-- Testing result") 62 | println(Iface.testResult(true)) 63 | println(Iface.testResult(false)) 64 | 65 | println("\n-- Testing tuple") 66 | println(Iface.testTuple(true, Pair(false, "S1"), Triple(true, "S2", 50))) 67 | 68 | println("\n-- Testing variant") 69 | println(Iface.testVariant(Iface.V.A(10))) 70 | println(Iface.testVariant(Iface.V.B(10f))) 71 | 72 | println("\n-- Testing enum") 73 | println(Iface.testEnum(Iface.Color.HOT_PINK)) 74 | println(Iface.testEnum(Iface.Color.LIME_GREEN)) 75 | println(Iface.testEnum(Iface.Color.NAVY_BLUE)) 76 | 77 | println("\n-- Testing flags") 78 | println(Iface.testFlags(Iface.Fg(true, true, true, true))) 79 | println(Iface.testFlags(Iface.Fg(true, false, true, false))) 80 | println(Iface.testFlags(Iface.Fg(false, false, true, true))) 81 | 82 | println("\n-- Testing resources") 83 | Jsiface.X(10).use { x -> 84 | Jsiface.X.add(x, 20).use { x2 -> 85 | println(x2.getA()) 86 | x2.setA(300) 87 | println(x2.getA()) 88 | } 89 | } 90 | 91 | println("\n-- Testing WASI Preview 2") 92 | testWASI() 93 | println("\n-- iface exports testing done") 94 | println("--------------------------------\n\n") 95 | } 96 | 97 | class TestExportedResourceImpl(var a: Int) : TestExports.TestExportedResource() { 98 | override fun getA(): Int = a 99 | override fun setA(a: Int) { 100 | this.a = a 101 | } 102 | 103 | companion object : Statics { 104 | override fun add(x: TestExportedResourceImpl, a: Int): TestExportedResourceImpl = 105 | TestExportedResourceImpl(x.a + a) 106 | } 107 | } 108 | } 109 | 110 | fun testWASI() { 111 | println("Environment.getEnvironment: ${Environment.getEnvironment()}") 112 | println("Environment.getArguments: ${Environment.getArguments()}") 113 | println("Environment.initialCwd: ${Environment.initialCwd()}") 114 | 115 | println("Random.getRandomU64: ${Random.getRandomU64()}") 116 | println("Random.getRandomBytes: ${Random.getRandomBytes(10u)}") 117 | println("Insecure.getInsecureRandomU64: ${Insecure.getInsecureRandomU64()}") 118 | println("Insecure.getInsecureRandomBytes: ${Insecure.getInsecureRandomBytes(10u)}") 119 | println("InsecureSeed.insecureSeed: ${InsecureSeed.insecureSeed()}") 120 | 121 | println("MonotonicClock.now: ${MonotonicClock.now()}") 122 | println("MonotonicClock.resolution: ${MonotonicClock.resolution()}") 123 | // TODO: MonotonicClock.subscribeInstant() 124 | // TODO: MonotonicClock.subscribeDuration() 125 | 126 | println("WallClock.now: ${WallClock.now()}") 127 | println("WallClock.resolution: ${WallClock.resolution()}") 128 | 129 | WASI_020.println("Test writing to stdout via WASI 0.2 binding") 130 | WASI_020.printlnErr("Test writing to stderr via WASI 0.2 binding") 131 | 132 | println("Reading .gitignore file:\n ${WASI_020.readText(Environment.initialCwd() + "/.gitignore")}") 133 | } 134 | 135 | object WASI_020 { 136 | val stdout = Stdout.getStdout() 137 | val stderr = Stdout.getStdout() 138 | val rootDescriptor = Preopens.getDirectories().find { it.second == "/" }!!.first 139 | 140 | fun readText(absolutePath: String): String { 141 | rootDescriptor.openAt( 142 | pathFlags = Types.PathFlags(symlinkFollow = false), 143 | "./$absolutePath", 144 | flags = Types.DescriptorFlags(read = true, write = false), 145 | openFlags = Types.OpenFlags() 146 | ).getOrThrow().use { fd -> 147 | val stat = fd.stat().getOrThrow() 148 | val readResult = fd.read(stat.size, 0u).getOrThrow() 149 | return readResult.first.toUByteArray().toByteArray().decodeToString() 150 | } 151 | } 152 | 153 | fun println(x: Any) { 154 | stdout.blockingWriteAndFlush((x.toString() + "\n").encodeToByteArray().map { it.toUByte() }) 155 | } 156 | 157 | fun printlnErr(x: Any) { 158 | stderr.blockingWriteAndFlush((x.toString() + "\n").encodeToByteArray().map { it.toUByte() }) 159 | } 160 | } -------------------------------------------------------------------------------- /src/wasmWasiMain/kotlin/bindings/ComponentSupport.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(UnsafeWasmMemoryApi::class) 2 | 3 | import kotlin.wasm.unsafe.* 4 | 5 | class ComponentException(val value: Any?) : Throwable() 6 | 7 | sealed interface Option { 8 | data class Some(val value: T2) : Option 9 | 10 | data object None : Option 11 | } 12 | 13 | internal value class ResourceHandle(internal val value: Int) 14 | 15 | @WasmExport 16 | fun cabi_realloc(ptr: Int, oldSize: Int, align: Int, newSize: Int): Int = 17 | componentModelRealloc(ptr, oldSize, newSize) 18 | 19 | fun MemoryAllocator.STRING_TO_MEM(s: String): Int = 20 | writeToLinearMemory(s.encodeToByteArray()).address.toInt() 21 | 22 | fun STRING_FROM_MEM(addr: Int, len: Int): String = loadByteArray(addr.ptr, len).decodeToString() 23 | 24 | fun MALLOC(size: Int, align: Int): Int = TODO() 25 | 26 | val Int.ptr: Pointer 27 | get() = Pointer(this.toUInt()) 28 | 29 | fun Pointer.loadUByte(): UByte = loadByte().toUByte() 30 | 31 | fun Pointer.loadUShort(): UShort = loadShort().toUShort() 32 | 33 | fun Pointer.loadUInt(): UInt = loadInt().toUInt() 34 | 35 | fun Pointer.loadULong(): ULong = loadLong().toULong() 36 | 37 | internal fun MemoryAllocator.writeToLinearMemory(value: String): Pointer = 38 | writeToLinearMemory(value.encodeToByteArray()) 39 | 40 | internal fun loadString(addr: Pointer, size: Int): String = 41 | loadByteArray(addr, size).decodeToString() 42 | 43 | internal fun loadByteArray(addr: Pointer, size: Int): ByteArray = 44 | ByteArray(size) { i -> (addr + i).loadByte() } 45 | 46 | internal fun MemoryAllocator.writeToLinearMemory(array: ByteArray): Pointer { 47 | val pointer = allocate(array.size) 48 | var currentPointer = pointer 49 | array.forEach { 50 | currentPointer.storeByte(it) 51 | currentPointer += 1 52 | } 53 | return pointer 54 | } 55 | 56 | fun Pointer.loadFloat(): Float = Float.fromBits(loadInt()) 57 | 58 | fun Pointer.loadDouble(): Double = Double.fromBits(loadLong()) 59 | 60 | fun Pointer.storeFloat(value: Float) { 61 | storeInt(value.toRawBits()) 62 | } 63 | 64 | fun Pointer.storeDouble(value: Double) { 65 | storeLong(value.toRawBits()) 66 | } 67 | 68 | internal object RepTable { 69 | private val list = mutableListOf() 70 | 71 | private var firstVacant: Int? = null 72 | 73 | private data class Vacant(var next: Int?) 74 | 75 | fun add(v: Any): Int { 76 | val rep: Int 77 | if (firstVacant != null) { 78 | rep = firstVacant!! 79 | firstVacant = (list[rep] as Vacant).next 80 | list[rep] = v 81 | } else { 82 | rep = list.size 83 | list += v 84 | } 85 | return rep 86 | } 87 | 88 | fun get(rep: Int): Any { 89 | check(list[rep] !is Vacant) 90 | return list[rep] 91 | } 92 | 93 | fun remove(rep: Int): Any { 94 | val v = get(rep) 95 | list[rep] = Vacant(firstVacant) 96 | firstVacant = rep 97 | return v 98 | } 99 | 100 | override fun toString(): String { 101 | return "RepTable(firstVacant=${firstVacant}, list = $list)" 102 | } 103 | } 104 | 105 | data class Tuple2< 106 | T0, 107 | T1, 108 | >( 109 | val f0: T0, 110 | val f1: T1, 111 | ) 112 | 113 | data class Tuple3< 114 | T0, 115 | T1, 116 | T2, 117 | >( 118 | val f0: T0, 119 | val f1: T1, 120 | val f2: T2, 121 | ) 122 | 123 | data class Tuple4< 124 | T0, 125 | T1, 126 | T2, 127 | T3, 128 | >( 129 | val f0: T0, 130 | val f1: T1, 131 | val f2: T2, 132 | val f3: T3, 133 | ) 134 | 135 | data class Tuple6< 136 | T0, 137 | T1, 138 | T2, 139 | T3, 140 | T4, 141 | T5, 142 | >( 143 | val f0: T0, 144 | val f1: T1, 145 | val f2: T2, 146 | val f3: T3, 147 | val f4: T4, 148 | val f5: T5, 149 | ) 150 | 151 | data class Tuple8< 152 | T0, 153 | T1, 154 | T2, 155 | T3, 156 | T4, 157 | T5, 158 | T6, 159 | T7, 160 | >( 161 | val f0: T0, 162 | val f1: T1, 163 | val f2: T2, 164 | val f3: T3, 165 | val f4: T4, 166 | val f5: T5, 167 | val f6: T6, 168 | val f7: T7, 169 | ) 170 | -------------------------------------------------------------------------------- /wasi_snapshot_preview1.reactor.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bashor/kotlin-wasm-cm-experiments/d86bcf6e509a2ab8d174946484de83f818be2a4c/wasi_snapshot_preview1.reactor.wasm -------------------------------------------------------------------------------- /wit/deps/cli/command.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.0; 2 | 3 | world command { 4 | include imports; 5 | 6 | export run; 7 | } 8 | -------------------------------------------------------------------------------- /wit/deps/cli/environment.wit: -------------------------------------------------------------------------------- 1 | interface environment { 2 | /// Get the POSIX-style environment variables. 3 | /// 4 | /// Each environment variable is provided as a pair of string variable names 5 | /// and string value. 6 | /// 7 | /// Morally, these are a value import, but until value imports are available 8 | /// in the component model, this import function should return the same 9 | /// values each time it is called. 10 | get-environment: func() -> list>; 11 | 12 | /// Get the POSIX-style arguments to the program. 13 | get-arguments: func() -> list; 14 | 15 | /// Return a path that programs should use as their initial current working 16 | /// directory, interpreting `.` as shorthand for this. 17 | initial-cwd: func() -> option; 18 | } 19 | -------------------------------------------------------------------------------- /wit/deps/cli/exit.wit: -------------------------------------------------------------------------------- 1 | interface exit { 2 | /// Exit the current instance and any linked instances. 3 | exit: func(status: result); 4 | } 5 | -------------------------------------------------------------------------------- /wit/deps/cli/imports.wit: -------------------------------------------------------------------------------- 1 | package wasi:cli@0.2.0; 2 | 3 | world imports { 4 | include wasi:clocks/imports@0.2.0; 5 | include wasi:filesystem/imports@0.2.0; 6 | include wasi:sockets/imports@0.2.0; 7 | include wasi:random/imports@0.2.0; 8 | include wasi:io/imports@0.2.0; 9 | 10 | import environment; 11 | import exit; 12 | import stdin; 13 | import stdout; 14 | import stderr; 15 | import terminal-input; 16 | import terminal-output; 17 | import terminal-stdin; 18 | import terminal-stdout; 19 | import terminal-stderr; 20 | } 21 | -------------------------------------------------------------------------------- /wit/deps/cli/run.wit: -------------------------------------------------------------------------------- 1 | interface run { 2 | /// Run the program. 3 | run: func() -> result; 4 | } 5 | -------------------------------------------------------------------------------- /wit/deps/cli/stdio.wit: -------------------------------------------------------------------------------- 1 | interface stdin { 2 | use wasi:io/streams@0.2.0.{input-stream}; 3 | 4 | get-stdin: func() -> input-stream; 5 | } 6 | 7 | interface stdout { 8 | use wasi:io/streams@0.2.0.{output-stream}; 9 | 10 | get-stdout: func() -> output-stream; 11 | } 12 | 13 | interface stderr { 14 | use wasi:io/streams@0.2.0.{output-stream}; 15 | 16 | get-stderr: func() -> output-stream; 17 | } 18 | -------------------------------------------------------------------------------- /wit/deps/cli/terminal.wit: -------------------------------------------------------------------------------- 1 | /// Terminal input. 2 | /// 3 | /// In the future, this may include functions for disabling echoing, 4 | /// disabling input buffering so that keyboard events are sent through 5 | /// immediately, querying supported features, and so on. 6 | interface terminal-input { 7 | /// The input side of a terminal. 8 | resource terminal-input; 9 | } 10 | 11 | /// Terminal output. 12 | /// 13 | /// In the future, this may include functions for querying the terminal 14 | /// size, being notified of terminal size changes, querying supported 15 | /// features, and so on. 16 | interface terminal-output { 17 | /// The output side of a terminal. 18 | resource terminal-output; 19 | } 20 | 21 | /// An interface providing an optional `terminal-input` for stdin as a 22 | /// link-time authority. 23 | interface terminal-stdin { 24 | use terminal-input.{terminal-input}; 25 | 26 | /// If stdin is connected to a terminal, return a `terminal-input` handle 27 | /// allowing further interaction with it. 28 | get-terminal-stdin: func() -> option; 29 | } 30 | 31 | /// An interface providing an optional `terminal-output` for stdout as a 32 | /// link-time authority. 33 | interface terminal-stdout { 34 | use terminal-output.{terminal-output}; 35 | 36 | /// If stdout is connected to a terminal, return a `terminal-output` handle 37 | /// allowing further interaction with it. 38 | get-terminal-stdout: func() -> option; 39 | } 40 | 41 | /// An interface providing an optional `terminal-output` for stderr as a 42 | /// link-time authority. 43 | interface terminal-stderr { 44 | use terminal-output.{terminal-output}; 45 | 46 | /// If stderr is connected to a terminal, return a `terminal-output` handle 47 | /// allowing further interaction with it. 48 | get-terminal-stderr: func() -> option; 49 | } 50 | -------------------------------------------------------------------------------- /wit/deps/clocks/monotonic-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.0; 2 | /// WASI Monotonic Clock is a clock API intended to let users measure elapsed 3 | /// time. 4 | /// 5 | /// It is intended to be portable at least between Unix-family platforms and 6 | /// Windows. 7 | /// 8 | /// A monotonic clock is a clock which has an unspecified initial value, and 9 | /// successive reads of the clock will produce non-decreasing values. 10 | /// 11 | /// It is intended for measuring elapsed time. 12 | interface monotonic-clock { 13 | use wasi:io/poll@0.2.0.{pollable}; 14 | 15 | /// An instant in time, in nanoseconds. An instant is relative to an 16 | /// unspecified initial value, and can only be compared to instances from 17 | /// the same monotonic-clock. 18 | type instant = u64; 19 | 20 | /// A duration of time, in nanoseconds. 21 | type duration = u64; 22 | 23 | /// Read the current value of the clock. 24 | /// 25 | /// The clock is monotonic, therefore calling this function repeatedly will 26 | /// produce a sequence of non-decreasing values. 27 | now: func() -> instant; 28 | 29 | /// Query the resolution of the clock. Returns the duration of time 30 | /// corresponding to a clock tick. 31 | resolution: func() -> duration; 32 | 33 | /// Create a `pollable` which will resolve once the specified instant 34 | /// occured. 35 | subscribe-instant: func( 36 | when: instant, 37 | ) -> pollable; 38 | 39 | /// Create a `pollable` which will resolve once the given duration has 40 | /// elapsed, starting at the time at which this function was called. 41 | /// occured. 42 | subscribe-duration: func( 43 | when: duration, 44 | ) -> pollable; 45 | } 46 | -------------------------------------------------------------------------------- /wit/deps/clocks/wall-clock.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.0; 2 | /// WASI Wall Clock is a clock API intended to let users query the current 3 | /// time. The name "wall" makes an analogy to a "clock on the wall", which 4 | /// is not necessarily monotonic as it may be reset. 5 | /// 6 | /// It is intended to be portable at least between Unix-family platforms and 7 | /// Windows. 8 | /// 9 | /// A wall clock is a clock which measures the date and time according to 10 | /// some external reference. 11 | /// 12 | /// External references may be reset, so this clock is not necessarily 13 | /// monotonic, making it unsuitable for measuring elapsed time. 14 | /// 15 | /// It is intended for reporting the current date and time for humans. 16 | interface wall-clock { 17 | /// A time and date in seconds plus nanoseconds. 18 | record datetime { 19 | seconds: u64, 20 | nanoseconds: u32, 21 | } 22 | 23 | /// Read the current value of the clock. 24 | /// 25 | /// This clock is not monotonic, therefore calling this function repeatedly 26 | /// will not necessarily produce a sequence of non-decreasing values. 27 | /// 28 | /// The returned timestamps represent the number of seconds since 29 | /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], 30 | /// also known as [Unix Time]. 31 | /// 32 | /// The nanoseconds field of the output is always less than 1000000000. 33 | /// 34 | /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 35 | /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time 36 | now: func() -> datetime; 37 | 38 | /// Query the resolution of the clock. 39 | /// 40 | /// The nanoseconds field of the output is always less than 1000000000. 41 | resolution: func() -> datetime; 42 | } 43 | -------------------------------------------------------------------------------- /wit/deps/clocks/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:clocks@0.2.0; 2 | 3 | world imports { 4 | import monotonic-clock; 5 | import wall-clock; 6 | } 7 | -------------------------------------------------------------------------------- /wit/deps/filesystem/preopens.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.0; 2 | 3 | interface preopens { 4 | use types.{descriptor}; 5 | 6 | /// Return the set of preopened directories, and their path. 7 | get-directories: func() -> list>; 8 | } 9 | -------------------------------------------------------------------------------- /wit/deps/filesystem/types.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.0; 2 | /// WASI filesystem is a filesystem API primarily intended to let users run WASI 3 | /// programs that access their files on their existing filesystems, without 4 | /// significant overhead. 5 | /// 6 | /// It is intended to be roughly portable between Unix-family platforms and 7 | /// Windows, though it does not hide many of the major differences. 8 | /// 9 | /// Paths are passed as interface-type `string`s, meaning they must consist of 10 | /// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain 11 | /// paths which are not accessible by this API. 12 | /// 13 | /// The directory separator in WASI is always the forward-slash (`/`). 14 | /// 15 | /// All paths in WASI are relative paths, and are interpreted relative to a 16 | /// `descriptor` referring to a base directory. If a `path` argument to any WASI 17 | /// function starts with `/`, or if any step of resolving a `path`, including 18 | /// `..` and symbolic link steps, reaches a directory outside of the base 19 | /// directory, or reaches a symlink to an absolute or rooted path in the 20 | /// underlying filesystem, the function fails with `error-code::not-permitted`. 21 | /// 22 | /// For more information about WASI path resolution and sandboxing, see 23 | /// [WASI filesystem path resolution]. 24 | /// 25 | /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md 26 | interface types { 27 | use wasi:io/streams@0.2.0.{input-stream, output-stream, error}; 28 | use wasi:clocks/wall-clock@0.2.0.{datetime}; 29 | 30 | /// File size or length of a region within a file. 31 | type filesize = u64; 32 | 33 | /// The type of a filesystem object referenced by a descriptor. 34 | /// 35 | /// Note: This was called `filetype` in earlier versions of WASI. 36 | enum descriptor-type { 37 | /// The type of the descriptor or file is unknown or is different from 38 | /// any of the other types specified. 39 | unknown, 40 | /// The descriptor refers to a block device inode. 41 | block-device, 42 | /// The descriptor refers to a character device inode. 43 | character-device, 44 | /// The descriptor refers to a directory inode. 45 | directory, 46 | /// The descriptor refers to a named pipe. 47 | fifo, 48 | /// The file refers to a symbolic link inode. 49 | symbolic-link, 50 | /// The descriptor refers to a regular file inode. 51 | regular-file, 52 | /// The descriptor refers to a socket. 53 | socket, 54 | } 55 | 56 | /// Descriptor flags. 57 | /// 58 | /// Note: This was called `fdflags` in earlier versions of WASI. 59 | flags descriptor-flags { 60 | /// Read mode: Data can be read. 61 | read, 62 | /// Write mode: Data can be written to. 63 | write, 64 | /// Request that writes be performed according to synchronized I/O file 65 | /// integrity completion. The data stored in the file and the file's 66 | /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. 67 | /// 68 | /// The precise semantics of this operation have not yet been defined for 69 | /// WASI. At this time, it should be interpreted as a request, and not a 70 | /// requirement. 71 | file-integrity-sync, 72 | /// Request that writes be performed according to synchronized I/O data 73 | /// integrity completion. Only the data stored in the file is 74 | /// synchronized. This is similar to `O_DSYNC` in POSIX. 75 | /// 76 | /// The precise semantics of this operation have not yet been defined for 77 | /// WASI. At this time, it should be interpreted as a request, and not a 78 | /// requirement. 79 | data-integrity-sync, 80 | /// Requests that reads be performed at the same level of integrety 81 | /// requested for writes. This is similar to `O_RSYNC` in POSIX. 82 | /// 83 | /// The precise semantics of this operation have not yet been defined for 84 | /// WASI. At this time, it should be interpreted as a request, and not a 85 | /// requirement. 86 | requested-write-sync, 87 | /// Mutating directories mode: Directory contents may be mutated. 88 | /// 89 | /// When this flag is unset on a descriptor, operations using the 90 | /// descriptor which would create, rename, delete, modify the data or 91 | /// metadata of filesystem objects, or obtain another handle which 92 | /// would permit any of those, shall fail with `error-code::read-only` if 93 | /// they would otherwise succeed. 94 | /// 95 | /// This may only be set on directories. 96 | mutate-directory, 97 | } 98 | 99 | /// File attributes. 100 | /// 101 | /// Note: This was called `filestat` in earlier versions of WASI. 102 | record descriptor-stat { 103 | /// File type. 104 | %type: descriptor-type, 105 | /// Number of hard links to the file. 106 | link-count: link-count, 107 | /// For regular files, the file size in bytes. For symbolic links, the 108 | /// length in bytes of the pathname contained in the symbolic link. 109 | size: filesize, 110 | /// Last data access timestamp. 111 | /// 112 | /// If the `option` is none, the platform doesn't maintain an access 113 | /// timestamp for this file. 114 | data-access-timestamp: option, 115 | /// Last data modification timestamp. 116 | /// 117 | /// If the `option` is none, the platform doesn't maintain a 118 | /// modification timestamp for this file. 119 | data-modification-timestamp: option, 120 | /// Last file status-change timestamp. 121 | /// 122 | /// If the `option` is none, the platform doesn't maintain a 123 | /// status-change timestamp for this file. 124 | status-change-timestamp: option, 125 | } 126 | 127 | /// Flags determining the method of how paths are resolved. 128 | flags path-flags { 129 | /// As long as the resolved path corresponds to a symbolic link, it is 130 | /// expanded. 131 | symlink-follow, 132 | } 133 | 134 | /// Open flags used by `open-at`. 135 | flags open-flags { 136 | /// Create file if it does not exist, similar to `O_CREAT` in POSIX. 137 | create, 138 | /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. 139 | directory, 140 | /// Fail if file already exists, similar to `O_EXCL` in POSIX. 141 | exclusive, 142 | /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. 143 | truncate, 144 | } 145 | 146 | /// Number of hard links to an inode. 147 | type link-count = u64; 148 | 149 | /// When setting a timestamp, this gives the value to set it to. 150 | variant new-timestamp { 151 | /// Leave the timestamp set to its previous value. 152 | no-change, 153 | /// Set the timestamp to the current time of the system clock associated 154 | /// with the filesystem. 155 | now, 156 | /// Set the timestamp to the given value. 157 | timestamp(datetime), 158 | } 159 | 160 | /// A directory entry. 161 | record directory-entry { 162 | /// The type of the file referred to by this directory entry. 163 | %type: descriptor-type, 164 | 165 | /// The name of the object. 166 | name: string, 167 | } 168 | 169 | /// Error codes returned by functions, similar to `errno` in POSIX. 170 | /// Not all of these error codes are returned by the functions provided by this 171 | /// API; some are used in higher-level library layers, and others are provided 172 | /// merely for alignment with POSIX. 173 | enum error-code { 174 | /// Permission denied, similar to `EACCES` in POSIX. 175 | access, 176 | /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. 177 | would-block, 178 | /// Connection already in progress, similar to `EALREADY` in POSIX. 179 | already, 180 | /// Bad descriptor, similar to `EBADF` in POSIX. 181 | bad-descriptor, 182 | /// Device or resource busy, similar to `EBUSY` in POSIX. 183 | busy, 184 | /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. 185 | deadlock, 186 | /// Storage quota exceeded, similar to `EDQUOT` in POSIX. 187 | quota, 188 | /// File exists, similar to `EEXIST` in POSIX. 189 | exist, 190 | /// File too large, similar to `EFBIG` in POSIX. 191 | file-too-large, 192 | /// Illegal byte sequence, similar to `EILSEQ` in POSIX. 193 | illegal-byte-sequence, 194 | /// Operation in progress, similar to `EINPROGRESS` in POSIX. 195 | in-progress, 196 | /// Interrupted function, similar to `EINTR` in POSIX. 197 | interrupted, 198 | /// Invalid argument, similar to `EINVAL` in POSIX. 199 | invalid, 200 | /// I/O error, similar to `EIO` in POSIX. 201 | io, 202 | /// Is a directory, similar to `EISDIR` in POSIX. 203 | is-directory, 204 | /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. 205 | loop, 206 | /// Too many links, similar to `EMLINK` in POSIX. 207 | too-many-links, 208 | /// Message too large, similar to `EMSGSIZE` in POSIX. 209 | message-size, 210 | /// Filename too long, similar to `ENAMETOOLONG` in POSIX. 211 | name-too-long, 212 | /// No such device, similar to `ENODEV` in POSIX. 213 | no-device, 214 | /// No such file or directory, similar to `ENOENT` in POSIX. 215 | no-entry, 216 | /// No locks available, similar to `ENOLCK` in POSIX. 217 | no-lock, 218 | /// Not enough space, similar to `ENOMEM` in POSIX. 219 | insufficient-memory, 220 | /// No space left on device, similar to `ENOSPC` in POSIX. 221 | insufficient-space, 222 | /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. 223 | not-directory, 224 | /// Directory not empty, similar to `ENOTEMPTY` in POSIX. 225 | not-empty, 226 | /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. 227 | not-recoverable, 228 | /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. 229 | unsupported, 230 | /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. 231 | no-tty, 232 | /// No such device or address, similar to `ENXIO` in POSIX. 233 | no-such-device, 234 | /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. 235 | overflow, 236 | /// Operation not permitted, similar to `EPERM` in POSIX. 237 | not-permitted, 238 | /// Broken pipe, similar to `EPIPE` in POSIX. 239 | pipe, 240 | /// Read-only file system, similar to `EROFS` in POSIX. 241 | read-only, 242 | /// Invalid seek, similar to `ESPIPE` in POSIX. 243 | invalid-seek, 244 | /// Text file busy, similar to `ETXTBSY` in POSIX. 245 | text-file-busy, 246 | /// Cross-device link, similar to `EXDEV` in POSIX. 247 | cross-device, 248 | } 249 | 250 | /// File or memory access pattern advisory information. 251 | enum advice { 252 | /// The application has no advice to give on its behavior with respect 253 | /// to the specified data. 254 | normal, 255 | /// The application expects to access the specified data sequentially 256 | /// from lower offsets to higher offsets. 257 | sequential, 258 | /// The application expects to access the specified data in a random 259 | /// order. 260 | random, 261 | /// The application expects to access the specified data in the near 262 | /// future. 263 | will-need, 264 | /// The application expects that it will not access the specified data 265 | /// in the near future. 266 | dont-need, 267 | /// The application expects to access the specified data once and then 268 | /// not reuse it thereafter. 269 | no-reuse, 270 | } 271 | 272 | /// A 128-bit hash value, split into parts because wasm doesn't have a 273 | /// 128-bit integer type. 274 | record metadata-hash-value { 275 | /// 64 bits of a 128-bit hash value. 276 | lower: u64, 277 | /// Another 64 bits of a 128-bit hash value. 278 | upper: u64, 279 | } 280 | 281 | /// A descriptor is a reference to a filesystem object, which may be a file, 282 | /// directory, named pipe, special file, or other object on which filesystem 283 | /// calls may be made. 284 | resource descriptor { 285 | /// Return a stream for reading from a file, if available. 286 | /// 287 | /// May fail with an error-code describing why the file cannot be read. 288 | /// 289 | /// Multiple read, write, and append streams may be active on the same open 290 | /// file and they do not interfere with each other. 291 | /// 292 | /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. 293 | read-via-stream: func( 294 | /// The offset within the file at which to start reading. 295 | offset: filesize, 296 | ) -> result; 297 | 298 | /// Return a stream for writing to a file, if available. 299 | /// 300 | /// May fail with an error-code describing why the file cannot be written. 301 | /// 302 | /// Note: This allows using `write-stream`, which is similar to `write` in 303 | /// POSIX. 304 | write-via-stream: func( 305 | /// The offset within the file at which to start writing. 306 | offset: filesize, 307 | ) -> result; 308 | 309 | /// Return a stream for appending to a file, if available. 310 | /// 311 | /// May fail with an error-code describing why the file cannot be appended. 312 | /// 313 | /// Note: This allows using `write-stream`, which is similar to `write` with 314 | /// `O_APPEND` in in POSIX. 315 | append-via-stream: func() -> result; 316 | 317 | /// Provide file advisory information on a descriptor. 318 | /// 319 | /// This is similar to `posix_fadvise` in POSIX. 320 | advise: func( 321 | /// The offset within the file to which the advisory applies. 322 | offset: filesize, 323 | /// The length of the region to which the advisory applies. 324 | length: filesize, 325 | /// The advice. 326 | advice: advice 327 | ) -> result<_, error-code>; 328 | 329 | /// Synchronize the data of a file to disk. 330 | /// 331 | /// This function succeeds with no effect if the file descriptor is not 332 | /// opened for writing. 333 | /// 334 | /// Note: This is similar to `fdatasync` in POSIX. 335 | sync-data: func() -> result<_, error-code>; 336 | 337 | /// Get flags associated with a descriptor. 338 | /// 339 | /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. 340 | /// 341 | /// Note: This returns the value that was the `fs_flags` value returned 342 | /// from `fdstat_get` in earlier versions of WASI. 343 | get-flags: func() -> result; 344 | 345 | /// Get the dynamic type of a descriptor. 346 | /// 347 | /// Note: This returns the same value as the `type` field of the `fd-stat` 348 | /// returned by `stat`, `stat-at` and similar. 349 | /// 350 | /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided 351 | /// by `fstat` in POSIX. 352 | /// 353 | /// Note: This returns the value that was the `fs_filetype` value returned 354 | /// from `fdstat_get` in earlier versions of WASI. 355 | get-type: func() -> result; 356 | 357 | /// Adjust the size of an open file. If this increases the file's size, the 358 | /// extra bytes are filled with zeros. 359 | /// 360 | /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. 361 | set-size: func(size: filesize) -> result<_, error-code>; 362 | 363 | /// Adjust the timestamps of an open file or directory. 364 | /// 365 | /// Note: This is similar to `futimens` in POSIX. 366 | /// 367 | /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. 368 | set-times: func( 369 | /// The desired values of the data access timestamp. 370 | data-access-timestamp: new-timestamp, 371 | /// The desired values of the data modification timestamp. 372 | data-modification-timestamp: new-timestamp, 373 | ) -> result<_, error-code>; 374 | 375 | /// Read from a descriptor, without using and updating the descriptor's offset. 376 | /// 377 | /// This function returns a list of bytes containing the data that was 378 | /// read, along with a bool which, when true, indicates that the end of the 379 | /// file was reached. The returned list will contain up to `length` bytes; it 380 | /// may return fewer than requested, if the end of the file is reached or 381 | /// if the I/O operation is interrupted. 382 | /// 383 | /// In the future, this may change to return a `stream`. 384 | /// 385 | /// Note: This is similar to `pread` in POSIX. 386 | read: func( 387 | /// The maximum number of bytes to read. 388 | length: filesize, 389 | /// The offset within the file at which to read. 390 | offset: filesize, 391 | ) -> result, bool>, error-code>; 392 | 393 | /// Write to a descriptor, without using and updating the descriptor's offset. 394 | /// 395 | /// It is valid to write past the end of a file; the file is extended to the 396 | /// extent of the write, with bytes between the previous end and the start of 397 | /// the write set to zero. 398 | /// 399 | /// In the future, this may change to take a `stream`. 400 | /// 401 | /// Note: This is similar to `pwrite` in POSIX. 402 | write: func( 403 | /// Data to write 404 | buffer: list, 405 | /// The offset within the file at which to write. 406 | offset: filesize, 407 | ) -> result; 408 | 409 | /// Read directory entries from a directory. 410 | /// 411 | /// On filesystems where directories contain entries referring to themselves 412 | /// and their parents, often named `.` and `..` respectively, these entries 413 | /// are omitted. 414 | /// 415 | /// This always returns a new stream which starts at the beginning of the 416 | /// directory. Multiple streams may be active on the same directory, and they 417 | /// do not interfere with each other. 418 | read-directory: func() -> result; 419 | 420 | /// Synchronize the data and metadata of a file to disk. 421 | /// 422 | /// This function succeeds with no effect if the file descriptor is not 423 | /// opened for writing. 424 | /// 425 | /// Note: This is similar to `fsync` in POSIX. 426 | sync: func() -> result<_, error-code>; 427 | 428 | /// Create a directory. 429 | /// 430 | /// Note: This is similar to `mkdirat` in POSIX. 431 | create-directory-at: func( 432 | /// The relative path at which to create the directory. 433 | path: string, 434 | ) -> result<_, error-code>; 435 | 436 | /// Return the attributes of an open file or directory. 437 | /// 438 | /// Note: This is similar to `fstat` in POSIX, except that it does not return 439 | /// device and inode information. For testing whether two descriptors refer to 440 | /// the same underlying filesystem object, use `is-same-object`. To obtain 441 | /// additional data that can be used do determine whether a file has been 442 | /// modified, use `metadata-hash`. 443 | /// 444 | /// Note: This was called `fd_filestat_get` in earlier versions of WASI. 445 | stat: func() -> result; 446 | 447 | /// Return the attributes of a file or directory. 448 | /// 449 | /// Note: This is similar to `fstatat` in POSIX, except that it does not 450 | /// return device and inode information. See the `stat` description for a 451 | /// discussion of alternatives. 452 | /// 453 | /// Note: This was called `path_filestat_get` in earlier versions of WASI. 454 | stat-at: func( 455 | /// Flags determining the method of how the path is resolved. 456 | path-flags: path-flags, 457 | /// The relative path of the file or directory to inspect. 458 | path: string, 459 | ) -> result; 460 | 461 | /// Adjust the timestamps of a file or directory. 462 | /// 463 | /// Note: This is similar to `utimensat` in POSIX. 464 | /// 465 | /// Note: This was called `path_filestat_set_times` in earlier versions of 466 | /// WASI. 467 | set-times-at: func( 468 | /// Flags determining the method of how the path is resolved. 469 | path-flags: path-flags, 470 | /// The relative path of the file or directory to operate on. 471 | path: string, 472 | /// The desired values of the data access timestamp. 473 | data-access-timestamp: new-timestamp, 474 | /// The desired values of the data modification timestamp. 475 | data-modification-timestamp: new-timestamp, 476 | ) -> result<_, error-code>; 477 | 478 | /// Create a hard link. 479 | /// 480 | /// Note: This is similar to `linkat` in POSIX. 481 | link-at: func( 482 | /// Flags determining the method of how the path is resolved. 483 | old-path-flags: path-flags, 484 | /// The relative source path from which to link. 485 | old-path: string, 486 | /// The base directory for `new-path`. 487 | new-descriptor: borrow, 488 | /// The relative destination path at which to create the hard link. 489 | new-path: string, 490 | ) -> result<_, error-code>; 491 | 492 | /// Open a file or directory. 493 | /// 494 | /// The returned descriptor is not guaranteed to be the lowest-numbered 495 | /// descriptor not currently open/ it is randomized to prevent applications 496 | /// from depending on making assumptions about indexes, since this is 497 | /// error-prone in multi-threaded contexts. The returned descriptor is 498 | /// guaranteed to be less than 2**31. 499 | /// 500 | /// If `flags` contains `descriptor-flags::mutate-directory`, and the base 501 | /// descriptor doesn't have `descriptor-flags::mutate-directory` set, 502 | /// `open-at` fails with `error-code::read-only`. 503 | /// 504 | /// If `flags` contains `write` or `mutate-directory`, or `open-flags` 505 | /// contains `truncate` or `create`, and the base descriptor doesn't have 506 | /// `descriptor-flags::mutate-directory` set, `open-at` fails with 507 | /// `error-code::read-only`. 508 | /// 509 | /// Note: This is similar to `openat` in POSIX. 510 | open-at: func( 511 | /// Flags determining the method of how the path is resolved. 512 | path-flags: path-flags, 513 | /// The relative path of the object to open. 514 | path: string, 515 | /// The method by which to open the file. 516 | open-flags: open-flags, 517 | /// Flags to use for the resulting descriptor. 518 | %flags: descriptor-flags, 519 | ) -> result; 520 | 521 | /// Read the contents of a symbolic link. 522 | /// 523 | /// If the contents contain an absolute or rooted path in the underlying 524 | /// filesystem, this function fails with `error-code::not-permitted`. 525 | /// 526 | /// Note: This is similar to `readlinkat` in POSIX. 527 | readlink-at: func( 528 | /// The relative path of the symbolic link from which to read. 529 | path: string, 530 | ) -> result; 531 | 532 | /// Remove a directory. 533 | /// 534 | /// Return `error-code::not-empty` if the directory is not empty. 535 | /// 536 | /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. 537 | remove-directory-at: func( 538 | /// The relative path to a directory to remove. 539 | path: string, 540 | ) -> result<_, error-code>; 541 | 542 | /// Rename a filesystem object. 543 | /// 544 | /// Note: This is similar to `renameat` in POSIX. 545 | rename-at: func( 546 | /// The relative source path of the file or directory to rename. 547 | old-path: string, 548 | /// The base directory for `new-path`. 549 | new-descriptor: borrow, 550 | /// The relative destination path to which to rename the file or directory. 551 | new-path: string, 552 | ) -> result<_, error-code>; 553 | 554 | /// Create a symbolic link (also known as a "symlink"). 555 | /// 556 | /// If `old-path` starts with `/`, the function fails with 557 | /// `error-code::not-permitted`. 558 | /// 559 | /// Note: This is similar to `symlinkat` in POSIX. 560 | symlink-at: func( 561 | /// The contents of the symbolic link. 562 | old-path: string, 563 | /// The relative destination path at which to create the symbolic link. 564 | new-path: string, 565 | ) -> result<_, error-code>; 566 | 567 | /// Unlink a filesystem object that is not a directory. 568 | /// 569 | /// Return `error-code::is-directory` if the path refers to a directory. 570 | /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. 571 | unlink-file-at: func( 572 | /// The relative path to a file to unlink. 573 | path: string, 574 | ) -> result<_, error-code>; 575 | 576 | /// Test whether two descriptors refer to the same filesystem object. 577 | /// 578 | /// In POSIX, this corresponds to testing whether the two descriptors have the 579 | /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. 580 | /// wasi-filesystem does not expose device and inode numbers, so this function 581 | /// may be used instead. 582 | is-same-object: func(other: borrow) -> bool; 583 | 584 | /// Return a hash of the metadata associated with a filesystem object referred 585 | /// to by a descriptor. 586 | /// 587 | /// This returns a hash of the last-modification timestamp and file size, and 588 | /// may also include the inode number, device number, birth timestamp, and 589 | /// other metadata fields that may change when the file is modified or 590 | /// replaced. It may also include a secret value chosen by the 591 | /// implementation and not otherwise exposed. 592 | /// 593 | /// Implementations are encourated to provide the following properties: 594 | /// 595 | /// - If the file is not modified or replaced, the computed hash value should 596 | /// usually not change. 597 | /// - If the object is modified or replaced, the computed hash value should 598 | /// usually change. 599 | /// - The inputs to the hash should not be easily computable from the 600 | /// computed hash. 601 | /// 602 | /// However, none of these is required. 603 | metadata-hash: func() -> result; 604 | 605 | /// Return a hash of the metadata associated with a filesystem object referred 606 | /// to by a directory descriptor and a relative path. 607 | /// 608 | /// This performs the same hash computation as `metadata-hash`. 609 | metadata-hash-at: func( 610 | /// Flags determining the method of how the path is resolved. 611 | path-flags: path-flags, 612 | /// The relative path of the file or directory to inspect. 613 | path: string, 614 | ) -> result; 615 | } 616 | 617 | /// A stream of directory entries. 618 | resource directory-entry-stream { 619 | /// Read a single directory entry from a `directory-entry-stream`. 620 | read-directory-entry: func() -> result, error-code>; 621 | } 622 | 623 | /// Attempts to extract a filesystem-related `error-code` from the stream 624 | /// `error` provided. 625 | /// 626 | /// Stream operations which return `stream-error::last-operation-failed` 627 | /// have a payload with more information about the operation that failed. 628 | /// This payload can be passed through to this function to see if there's 629 | /// filesystem-related information about the error to return. 630 | /// 631 | /// Note that this function is fallible because not all stream-related 632 | /// errors are filesystem-related errors. 633 | filesystem-error-code: func(err: borrow) -> option; 634 | } 635 | -------------------------------------------------------------------------------- /wit/deps/filesystem/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:filesystem@0.2.0; 2 | 3 | world imports { 4 | import types; 5 | import preopens; 6 | } 7 | -------------------------------------------------------------------------------- /wit/deps/io/error.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.0; 2 | 3 | 4 | interface error { 5 | /// A resource which represents some error information. 6 | /// 7 | /// The only method provided by this resource is `to-debug-string`, 8 | /// which provides some human-readable information about the error. 9 | /// 10 | /// In the `wasi:io` package, this resource is returned through the 11 | /// `wasi:io/streams/stream-error` type. 12 | /// 13 | /// To provide more specific error information, other interfaces may 14 | /// provide functions to further "downcast" this error into more specific 15 | /// error information. For example, `error`s returned in streams derived 16 | /// from filesystem types to be described using the filesystem's own 17 | /// error-code type, using the function 18 | /// `wasi:filesystem/types/filesystem-error-code`, which takes a parameter 19 | /// `borrow` and returns 20 | /// `option`. 21 | /// 22 | /// The set of functions which can "downcast" an `error` into a more 23 | /// concrete type is open. 24 | resource error { 25 | /// Returns a string that is suitable to assist humans in debugging 26 | /// this error. 27 | /// 28 | /// WARNING: The returned string should not be consumed mechanically! 29 | /// It may change across platforms, hosts, or other implementation 30 | /// details. Parsing this string is a major platform-compatibility 31 | /// hazard. 32 | to-debug-string: func() -> string; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /wit/deps/io/poll.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.0; 2 | 3 | /// A poll API intended to let users wait for I/O events on multiple handles 4 | /// at once. 5 | interface poll { 6 | /// `pollable` represents a single I/O event which may be ready, or not. 7 | resource pollable { 8 | 9 | /// Return the readiness of a pollable. This function never blocks. 10 | /// 11 | /// Returns `true` when the pollable is ready, and `false` otherwise. 12 | ready: func() -> bool; 13 | 14 | /// `block` returns immediately if the pollable is ready, and otherwise 15 | /// blocks until ready. 16 | /// 17 | /// This function is equivalent to calling `poll.poll` on a list 18 | /// containing only this pollable. 19 | block: func(); 20 | } 21 | 22 | /// Poll for completion on a set of pollables. 23 | /// 24 | /// This function takes a list of pollables, which identify I/O sources of 25 | /// interest, and waits until one or more of the events is ready for I/O. 26 | /// 27 | /// The result `list` contains one or more indices of handles in the 28 | /// argument list that is ready for I/O. 29 | /// 30 | /// If the list contains more elements than can be indexed with a `u32` 31 | /// value, this function traps. 32 | /// 33 | /// A timeout can be implemented by adding a pollable from the 34 | /// wasi-clocks API to the list. 35 | /// 36 | /// This function does not return a `result`; polling in itself does not 37 | /// do any I/O so it doesn't fail. If any of the I/O sources identified by 38 | /// the pollables has an error, it is indicated by marking the source as 39 | /// being reaedy for I/O. 40 | poll: func(in: list>) -> list; 41 | } 42 | -------------------------------------------------------------------------------- /wit/deps/io/streams.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.0; 2 | 3 | /// WASI I/O is an I/O abstraction API which is currently focused on providing 4 | /// stream types. 5 | /// 6 | /// In the future, the component model is expected to add built-in stream types; 7 | /// when it does, they are expected to subsume this API. 8 | interface streams { 9 | use error.{error}; 10 | use poll.{pollable}; 11 | 12 | /// An error for input-stream and output-stream operations. 13 | variant stream-error { 14 | /// The last operation (a write or flush) failed before completion. 15 | /// 16 | /// More information is available in the `error` payload. 17 | last-operation-failed(error), 18 | /// The stream is closed: no more input will be accepted by the 19 | /// stream. A closed output-stream will return this error on all 20 | /// future operations. 21 | closed 22 | } 23 | 24 | /// An input bytestream. 25 | /// 26 | /// `input-stream`s are *non-blocking* to the extent practical on underlying 27 | /// platforms. I/O operations always return promptly; if fewer bytes are 28 | /// promptly available than requested, they return the number of bytes promptly 29 | /// available, which could even be zero. To wait for data to be available, 30 | /// use the `subscribe` function to obtain a `pollable` which can be polled 31 | /// for using `wasi:io/poll`. 32 | resource input-stream { 33 | /// Perform a non-blocking read from the stream. 34 | /// 35 | /// When the source of a `read` is binary data, the bytes from the source 36 | /// are returned verbatim. When the source of a `read` is known to the 37 | /// implementation to be text, bytes containing the UTF-8 encoding of the 38 | /// text are returned. 39 | /// 40 | /// This function returns a list of bytes containing the read data, 41 | /// when successful. The returned list will contain up to `len` bytes; 42 | /// it may return fewer than requested, but not more. The list is 43 | /// empty when no bytes are available for reading at this time. The 44 | /// pollable given by `subscribe` will be ready when more bytes are 45 | /// available. 46 | /// 47 | /// This function fails with a `stream-error` when the operation 48 | /// encounters an error, giving `last-operation-failed`, or when the 49 | /// stream is closed, giving `closed`. 50 | /// 51 | /// When the caller gives a `len` of 0, it represents a request to 52 | /// read 0 bytes. If the stream is still open, this call should 53 | /// succeed and return an empty list, or otherwise fail with `closed`. 54 | /// 55 | /// The `len` parameter is a `u64`, which could represent a list of u8 which 56 | /// is not possible to allocate in wasm32, or not desirable to allocate as 57 | /// as a return value by the callee. The callee may return a list of bytes 58 | /// less than `len` in size while more bytes are available for reading. 59 | read: func( 60 | /// The maximum number of bytes to read 61 | len: u64 62 | ) -> result, stream-error>; 63 | 64 | /// Read bytes from a stream, after blocking until at least one byte can 65 | /// be read. Except for blocking, behavior is identical to `read`. 66 | blocking-read: func( 67 | /// The maximum number of bytes to read 68 | len: u64 69 | ) -> result, stream-error>; 70 | 71 | /// Skip bytes from a stream. Returns number of bytes skipped. 72 | /// 73 | /// Behaves identical to `read`, except instead of returning a list 74 | /// of bytes, returns the number of bytes consumed from the stream. 75 | skip: func( 76 | /// The maximum number of bytes to skip. 77 | len: u64, 78 | ) -> result; 79 | 80 | /// Skip bytes from a stream, after blocking until at least one byte 81 | /// can be skipped. Except for blocking behavior, identical to `skip`. 82 | blocking-skip: func( 83 | /// The maximum number of bytes to skip. 84 | len: u64, 85 | ) -> result; 86 | 87 | /// Create a `pollable` which will resolve once either the specified stream 88 | /// has bytes available to read or the other end of the stream has been 89 | /// closed. 90 | /// The created `pollable` is a child resource of the `input-stream`. 91 | /// Implementations may trap if the `input-stream` is dropped before 92 | /// all derived `pollable`s created with this function are dropped. 93 | subscribe: func() -> pollable; 94 | } 95 | 96 | 97 | /// An output bytestream. 98 | /// 99 | /// `output-stream`s are *non-blocking* to the extent practical on 100 | /// underlying platforms. Except where specified otherwise, I/O operations also 101 | /// always return promptly, after the number of bytes that can be written 102 | /// promptly, which could even be zero. To wait for the stream to be ready to 103 | /// accept data, the `subscribe` function to obtain a `pollable` which can be 104 | /// polled for using `wasi:io/poll`. 105 | resource output-stream { 106 | /// Check readiness for writing. This function never blocks. 107 | /// 108 | /// Returns the number of bytes permitted for the next call to `write`, 109 | /// or an error. Calling `write` with more bytes than this function has 110 | /// permitted will trap. 111 | /// 112 | /// When this function returns 0 bytes, the `subscribe` pollable will 113 | /// become ready when this function will report at least 1 byte, or an 114 | /// error. 115 | check-write: func() -> result; 116 | 117 | /// Perform a write. This function never blocks. 118 | /// 119 | /// When the destination of a `write` is binary data, the bytes from 120 | /// `contents` are written verbatim. When the destination of a `write` is 121 | /// known to the implementation to be text, the bytes of `contents` are 122 | /// transcoded from UTF-8 into the encoding of the destination and then 123 | /// written. 124 | /// 125 | /// Precondition: check-write gave permit of Ok(n) and contents has a 126 | /// length of less than or equal to n. Otherwise, this function will trap. 127 | /// 128 | /// returns Err(closed) without writing if the stream has closed since 129 | /// the last call to check-write provided a permit. 130 | write: func( 131 | contents: list 132 | ) -> result<_, stream-error>; 133 | 134 | /// Perform a write of up to 4096 bytes, and then flush the stream. Block 135 | /// until all of these operations are complete, or an error occurs. 136 | /// 137 | /// This is a convenience wrapper around the use of `check-write`, 138 | /// `subscribe`, `write`, and `flush`, and is implemented with the 139 | /// following pseudo-code: 140 | /// 141 | /// ```text 142 | /// let pollable = this.subscribe(); 143 | /// while !contents.is_empty() { 144 | /// // Wait for the stream to become writable 145 | /// pollable.block(); 146 | /// let Ok(n) = this.check-write(); // eliding error handling 147 | /// let len = min(n, contents.len()); 148 | /// let (chunk, rest) = contents.split_at(len); 149 | /// this.write(chunk ); // eliding error handling 150 | /// contents = rest; 151 | /// } 152 | /// this.flush(); 153 | /// // Wait for completion of `flush` 154 | /// pollable.block(); 155 | /// // Check for any errors that arose during `flush` 156 | /// let _ = this.check-write(); // eliding error handling 157 | /// ``` 158 | blocking-write-and-flush: func( 159 | contents: list 160 | ) -> result<_, stream-error>; 161 | 162 | /// Request to flush buffered output. This function never blocks. 163 | /// 164 | /// This tells the output-stream that the caller intends any buffered 165 | /// output to be flushed. the output which is expected to be flushed 166 | /// is all that has been passed to `write` prior to this call. 167 | /// 168 | /// Upon calling this function, the `output-stream` will not accept any 169 | /// writes (`check-write` will return `ok(0)`) until the flush has 170 | /// completed. The `subscribe` pollable will become ready when the 171 | /// flush has completed and the stream can accept more writes. 172 | flush: func() -> result<_, stream-error>; 173 | 174 | /// Request to flush buffered output, and block until flush completes 175 | /// and stream is ready for writing again. 176 | blocking-flush: func() -> result<_, stream-error>; 177 | 178 | /// Create a `pollable` which will resolve once the output-stream 179 | /// is ready for more writing, or an error has occured. When this 180 | /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an 181 | /// error. 182 | /// 183 | /// If the stream is closed, this pollable is always ready immediately. 184 | /// 185 | /// The created `pollable` is a child resource of the `output-stream`. 186 | /// Implementations may trap if the `output-stream` is dropped before 187 | /// all derived `pollable`s created with this function are dropped. 188 | subscribe: func() -> pollable; 189 | 190 | /// Write zeroes to a stream. 191 | /// 192 | /// This should be used precisely like `write` with the exact same 193 | /// preconditions (must use check-write first), but instead of 194 | /// passing a list of bytes, you simply pass the number of zero-bytes 195 | /// that should be written. 196 | write-zeroes: func( 197 | /// The number of zero-bytes to write 198 | len: u64 199 | ) -> result<_, stream-error>; 200 | 201 | /// Perform a write of up to 4096 zeroes, and then flush the stream. 202 | /// Block until all of these operations are complete, or an error 203 | /// occurs. 204 | /// 205 | /// This is a convenience wrapper around the use of `check-write`, 206 | /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with 207 | /// the following pseudo-code: 208 | /// 209 | /// ```text 210 | /// let pollable = this.subscribe(); 211 | /// while num_zeroes != 0 { 212 | /// // Wait for the stream to become writable 213 | /// pollable.block(); 214 | /// let Ok(n) = this.check-write(); // eliding error handling 215 | /// let len = min(n, num_zeroes); 216 | /// this.write-zeroes(len); // eliding error handling 217 | /// num_zeroes -= len; 218 | /// } 219 | /// this.flush(); 220 | /// // Wait for completion of `flush` 221 | /// pollable.block(); 222 | /// // Check for any errors that arose during `flush` 223 | /// let _ = this.check-write(); // eliding error handling 224 | /// ``` 225 | blocking-write-zeroes-and-flush: func( 226 | /// The number of zero-bytes to write 227 | len: u64 228 | ) -> result<_, stream-error>; 229 | 230 | /// Read from one stream and write to another. 231 | /// 232 | /// The behavior of splice is equivelant to: 233 | /// 1. calling `check-write` on the `output-stream` 234 | /// 2. calling `read` on the `input-stream` with the smaller of the 235 | /// `check-write` permitted length and the `len` provided to `splice` 236 | /// 3. calling `write` on the `output-stream` with that read data. 237 | /// 238 | /// Any error reported by the call to `check-write`, `read`, or 239 | /// `write` ends the splice and reports that error. 240 | /// 241 | /// This function returns the number of bytes transferred; it may be less 242 | /// than `len`. 243 | splice: func( 244 | /// The stream to read from 245 | src: borrow, 246 | /// The number of bytes to splice 247 | len: u64, 248 | ) -> result; 249 | 250 | /// Read from one stream and write to another, with blocking. 251 | /// 252 | /// This is similar to `splice`, except that it blocks until the 253 | /// `output-stream` is ready for writing, and the `input-stream` 254 | /// is ready for reading, before performing the `splice`. 255 | blocking-splice: func( 256 | /// The stream to read from 257 | src: borrow, 258 | /// The number of bytes to splice 259 | len: u64, 260 | ) -> result; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /wit/deps/io/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:io@0.2.0; 2 | 3 | world imports { 4 | import streams; 5 | import poll; 6 | } 7 | -------------------------------------------------------------------------------- /wit/deps/random/insecure-seed.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | /// The insecure-seed interface for seeding hash-map DoS resistance. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | interface insecure-seed { 7 | /// Return a 128-bit value that may contain a pseudo-random value. 8 | /// 9 | /// The returned value is not required to be computed from a CSPRNG, and may 10 | /// even be entirely deterministic. Host implementations are encouraged to 11 | /// provide pseudo-random values to any program exposed to 12 | /// attacker-controlled content, to enable DoS protection built into many 13 | /// languages' hash-map implementations. 14 | /// 15 | /// This function is intended to only be called once, by a source language 16 | /// to initialize Denial Of Service (DoS) protection in its hash-map 17 | /// implementation. 18 | /// 19 | /// # Expected future evolution 20 | /// 21 | /// This will likely be changed to a value import, to prevent it from being 22 | /// called multiple times and potentially used for purposes other than DoS 23 | /// protection. 24 | insecure-seed: func() -> tuple; 25 | } 26 | -------------------------------------------------------------------------------- /wit/deps/random/insecure.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | /// The insecure interface for insecure pseudo-random numbers. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | interface insecure { 7 | /// Return `len` insecure pseudo-random bytes. 8 | /// 9 | /// This function is not cryptographically secure. Do not use it for 10 | /// anything related to security. 11 | /// 12 | /// There are no requirements on the values of the returned bytes, however 13 | /// implementations are encouraged to return evenly distributed values with 14 | /// a long period. 15 | get-insecure-random-bytes: func(len: u64) -> list; 16 | 17 | /// Return an insecure pseudo-random `u64` value. 18 | /// 19 | /// This function returns the same type of pseudo-random data as 20 | /// `get-insecure-random-bytes`, represented as a `u64`. 21 | get-insecure-random-u64: func() -> u64; 22 | } 23 | -------------------------------------------------------------------------------- /wit/deps/random/random.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | /// WASI Random is a random data API. 3 | /// 4 | /// It is intended to be portable at least between Unix-family platforms and 5 | /// Windows. 6 | interface random { 7 | /// Return `len` cryptographically-secure random or pseudo-random bytes. 8 | /// 9 | /// This function must produce data at least as cryptographically secure and 10 | /// fast as an adequately seeded cryptographically-secure pseudo-random 11 | /// number generator (CSPRNG). It must not block, from the perspective of 12 | /// the calling program, under any circumstances, including on the first 13 | /// request and on requests for numbers of bytes. The returned data must 14 | /// always be unpredictable. 15 | /// 16 | /// This function must always return fresh data. Deterministic environments 17 | /// must omit this function, rather than implementing it with deterministic 18 | /// data. 19 | get-random-bytes: func(len: u64) -> list; 20 | 21 | /// Return a cryptographically-secure random or pseudo-random `u64` value. 22 | /// 23 | /// This function returns the same type of data as `get-random-bytes`, 24 | /// represented as a `u64`. 25 | get-random-u64: func() -> u64; 26 | } 27 | -------------------------------------------------------------------------------- /wit/deps/random/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:random@0.2.0; 2 | 3 | world imports { 4 | import random; 5 | import insecure; 6 | import insecure-seed; 7 | } 8 | -------------------------------------------------------------------------------- /wit/deps/sockets/instance-network.wit: -------------------------------------------------------------------------------- 1 | 2 | /// This interface provides a value-export of the default network handle.. 3 | interface instance-network { 4 | use network.{network}; 5 | 6 | /// Get a handle to the default network. 7 | instance-network: func() -> network; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /wit/deps/sockets/ip-name-lookup.wit: -------------------------------------------------------------------------------- 1 | 2 | interface ip-name-lookup { 3 | use wasi:io/poll@0.2.0.{pollable}; 4 | use network.{network, error-code, ip-address}; 5 | 6 | 7 | /// Resolve an internet host name to a list of IP addresses. 8 | /// 9 | /// Unicode domain names are automatically converted to ASCII using IDNA encoding. 10 | /// If the input is an IP address string, the address is parsed and returned 11 | /// as-is without making any external requests. 12 | /// 13 | /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. 14 | /// 15 | /// This function never blocks. It either immediately fails or immediately 16 | /// returns successfully with a `resolve-address-stream` that can be used 17 | /// to (asynchronously) fetch the results. 18 | /// 19 | /// # Typical errors 20 | /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. 21 | /// 22 | /// # References: 23 | /// - 24 | /// - 25 | /// - 26 | /// - 27 | resolve-addresses: func(network: borrow, name: string) -> result; 28 | 29 | resource resolve-address-stream { 30 | /// Returns the next address from the resolver. 31 | /// 32 | /// This function should be called multiple times. On each call, it will 33 | /// return the next address in connection order preference. If all 34 | /// addresses have been exhausted, this function returns `none`. 35 | /// 36 | /// This function never returns IPv4-mapped IPv6 addresses. 37 | /// 38 | /// # Typical errors 39 | /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) 40 | /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) 41 | /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) 42 | /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) 43 | resolve-next-address: func() -> result, error-code>; 44 | 45 | /// Create a `pollable` which will resolve once the stream is ready for I/O. 46 | /// 47 | /// Note: this function is here for WASI Preview2 only. 48 | /// It's planned to be removed when `future` is natively supported in Preview3. 49 | subscribe: func() -> pollable; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /wit/deps/sockets/network.wit: -------------------------------------------------------------------------------- 1 | 2 | interface network { 3 | /// An opaque resource that represents access to (a subset of) the network. 4 | /// This enables context-based security for networking. 5 | /// There is no need for this to map 1:1 to a physical network interface. 6 | resource network; 7 | 8 | /// Error codes. 9 | /// 10 | /// In theory, every API can return any error code. 11 | /// In practice, API's typically only return the errors documented per API 12 | /// combined with a couple of errors that are always possible: 13 | /// - `unknown` 14 | /// - `access-denied` 15 | /// - `not-supported` 16 | /// - `out-of-memory` 17 | /// - `concurrency-conflict` 18 | /// 19 | /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. 20 | enum error-code { 21 | /// Unknown error 22 | unknown, 23 | 24 | /// Access denied. 25 | /// 26 | /// POSIX equivalent: EACCES, EPERM 27 | access-denied, 28 | 29 | /// The operation is not supported. 30 | /// 31 | /// POSIX equivalent: EOPNOTSUPP 32 | not-supported, 33 | 34 | /// One of the arguments is invalid. 35 | /// 36 | /// POSIX equivalent: EINVAL 37 | invalid-argument, 38 | 39 | /// Not enough memory to complete the operation. 40 | /// 41 | /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY 42 | out-of-memory, 43 | 44 | /// The operation timed out before it could finish completely. 45 | timeout, 46 | 47 | /// This operation is incompatible with another asynchronous operation that is already in progress. 48 | /// 49 | /// POSIX equivalent: EALREADY 50 | concurrency-conflict, 51 | 52 | /// Trying to finish an asynchronous operation that: 53 | /// - has not been started yet, or: 54 | /// - was already finished by a previous `finish-*` call. 55 | /// 56 | /// Note: this is scheduled to be removed when `future`s are natively supported. 57 | not-in-progress, 58 | 59 | /// The operation has been aborted because it could not be completed immediately. 60 | /// 61 | /// Note: this is scheduled to be removed when `future`s are natively supported. 62 | would-block, 63 | 64 | 65 | /// The operation is not valid in the socket's current state. 66 | invalid-state, 67 | 68 | /// A new socket resource could not be created because of a system limit. 69 | new-socket-limit, 70 | 71 | /// A bind operation failed because the provided address is not an address that the `network` can bind to. 72 | address-not-bindable, 73 | 74 | /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. 75 | address-in-use, 76 | 77 | /// The remote address is not reachable 78 | remote-unreachable, 79 | 80 | 81 | /// The TCP connection was forcefully rejected 82 | connection-refused, 83 | 84 | /// The TCP connection was reset. 85 | connection-reset, 86 | 87 | /// A TCP connection was aborted. 88 | connection-aborted, 89 | 90 | 91 | /// The size of a datagram sent to a UDP socket exceeded the maximum 92 | /// supported size. 93 | datagram-too-large, 94 | 95 | 96 | /// Name does not exist or has no suitable associated IP addresses. 97 | name-unresolvable, 98 | 99 | /// A temporary failure in name resolution occurred. 100 | temporary-resolver-failure, 101 | 102 | /// A permanent failure in name resolution occurred. 103 | permanent-resolver-failure, 104 | } 105 | 106 | enum ip-address-family { 107 | /// Similar to `AF_INET` in POSIX. 108 | ipv4, 109 | 110 | /// Similar to `AF_INET6` in POSIX. 111 | ipv6, 112 | } 113 | 114 | type ipv4-address = tuple; 115 | type ipv6-address = tuple; 116 | 117 | variant ip-address { 118 | ipv4(ipv4-address), 119 | ipv6(ipv6-address), 120 | } 121 | 122 | record ipv4-socket-address { 123 | /// sin_port 124 | port: u16, 125 | /// sin_addr 126 | address: ipv4-address, 127 | } 128 | 129 | record ipv6-socket-address { 130 | /// sin6_port 131 | port: u16, 132 | /// sin6_flowinfo 133 | flow-info: u32, 134 | /// sin6_addr 135 | address: ipv6-address, 136 | /// sin6_scope_id 137 | scope-id: u32, 138 | } 139 | 140 | variant ip-socket-address { 141 | ipv4(ipv4-socket-address), 142 | ipv6(ipv6-socket-address), 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /wit/deps/sockets/tcp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | interface tcp-create-socket { 3 | use network.{network, error-code, ip-address-family}; 4 | use tcp.{tcp-socket}; 5 | 6 | /// Create a new TCP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. 9 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 10 | /// 11 | /// This function does not require a network capability handle. This is considered to be safe because 12 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` 13 | /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 14 | /// 15 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 16 | /// 17 | /// # Typical errors 18 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 19 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 20 | /// 21 | /// # References 22 | /// - 23 | /// - 24 | /// - 25 | /// - 26 | create-tcp-socket: func(address-family: ip-address-family) -> result; 27 | } 28 | -------------------------------------------------------------------------------- /wit/deps/sockets/tcp.wit: -------------------------------------------------------------------------------- 1 | 2 | interface tcp { 3 | use wasi:io/streams@0.2.0.{input-stream, output-stream}; 4 | use wasi:io/poll@0.2.0.{pollable}; 5 | use wasi:clocks/monotonic-clock@0.2.0.{duration}; 6 | use network.{network, error-code, ip-socket-address, ip-address-family}; 7 | 8 | enum shutdown-type { 9 | /// Similar to `SHUT_RD` in POSIX. 10 | receive, 11 | 12 | /// Similar to `SHUT_WR` in POSIX. 13 | send, 14 | 15 | /// Similar to `SHUT_RDWR` in POSIX. 16 | both, 17 | } 18 | 19 | /// A TCP socket resource. 20 | /// 21 | /// The socket can be in one of the following states: 22 | /// - `unbound` 23 | /// - `bind-in-progress` 24 | /// - `bound` (See note below) 25 | /// - `listen-in-progress` 26 | /// - `listening` 27 | /// - `connect-in-progress` 28 | /// - `connected` 29 | /// - `closed` 30 | /// See 31 | /// for a more information. 32 | /// 33 | /// Note: Except where explicitly mentioned, whenever this documentation uses 34 | /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. 35 | /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) 36 | /// 37 | /// In addition to the general error codes documented on the 38 | /// `network::error-code` type, TCP socket methods may always return 39 | /// `error(invalid-state)` when in the `closed` state. 40 | resource tcp-socket { 41 | /// Bind the socket to a specific network on the provided IP address and port. 42 | /// 43 | /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which 44 | /// network interface(s) to bind to. 45 | /// If the TCP/UDP port is zero, the socket will be bound to a random free port. 46 | /// 47 | /// Bind can be attempted multiple times on the same socket, even with 48 | /// different arguments on each iteration. But never concurrently and 49 | /// only as long as the previous bind failed. Once a bind succeeds, the 50 | /// binding can't be changed anymore. 51 | /// 52 | /// # Typical errors 53 | /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) 54 | /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) 55 | /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) 56 | /// - `invalid-state`: The socket is already bound. (EINVAL) 57 | /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) 58 | /// - `address-in-use`: Address is already in use. (EADDRINUSE) 59 | /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) 60 | /// - `not-in-progress`: A `bind` operation is not in progress. 61 | /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 62 | /// 63 | /// # Implementors note 64 | /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT 65 | /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR 66 | /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior 67 | /// and SO_REUSEADDR performs something different entirely. 68 | /// 69 | /// Unlike in POSIX, in WASI the bind operation is async. This enables 70 | /// interactive WASI hosts to inject permission prompts. Runtimes that 71 | /// don't want to make use of this ability can simply call the native 72 | /// `bind` as part of either `start-bind` or `finish-bind`. 73 | /// 74 | /// # References 75 | /// - 76 | /// - 77 | /// - 78 | /// - 79 | start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; 80 | finish-bind: func() -> result<_, error-code>; 81 | 82 | /// Connect to a remote endpoint. 83 | /// 84 | /// On success: 85 | /// - the socket is transitioned into the `connection` state. 86 | /// - a pair of streams is returned that can be used to read & write to the connection 87 | /// 88 | /// After a failed connection attempt, the socket will be in the `closed` 89 | /// state and the only valid action left is to `drop` the socket. A single 90 | /// socket can not be used to connect more than once. 91 | /// 92 | /// # Typical errors 93 | /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) 94 | /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) 95 | /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) 96 | /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) 97 | /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) 98 | /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. 99 | /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) 100 | /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) 101 | /// - `timeout`: Connection timed out. (ETIMEDOUT) 102 | /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) 103 | /// - `connection-reset`: The connection was reset. (ECONNRESET) 104 | /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) 105 | /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 106 | /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) 107 | /// - `not-in-progress`: A connect operation is not in progress. 108 | /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 109 | /// 110 | /// # Implementors note 111 | /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. 112 | /// Because all WASI sockets are non-blocking this is expected to return 113 | /// EINPROGRESS, which should be translated to `ok()` in WASI. 114 | /// 115 | /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` 116 | /// with a timeout of 0 on the socket descriptor. Followed by a check for 117 | /// the `SO_ERROR` socket option, in case the poll signaled readiness. 118 | /// 119 | /// # References 120 | /// - 121 | /// - 122 | /// - 123 | /// - 124 | start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; 125 | finish-connect: func() -> result, error-code>; 126 | 127 | /// Start listening for new connections. 128 | /// 129 | /// Transitions the socket into the `listening` state. 130 | /// 131 | /// Unlike POSIX, the socket must already be explicitly bound. 132 | /// 133 | /// # Typical errors 134 | /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) 135 | /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) 136 | /// - `invalid-state`: The socket is already in the `listening` state. 137 | /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) 138 | /// - `not-in-progress`: A listen operation is not in progress. 139 | /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 140 | /// 141 | /// # Implementors note 142 | /// Unlike in POSIX, in WASI the listen operation is async. This enables 143 | /// interactive WASI hosts to inject permission prompts. Runtimes that 144 | /// don't want to make use of this ability can simply call the native 145 | /// `listen` as part of either `start-listen` or `finish-listen`. 146 | /// 147 | /// # References 148 | /// - 149 | /// - 150 | /// - 151 | /// - 152 | start-listen: func() -> result<_, error-code>; 153 | finish-listen: func() -> result<_, error-code>; 154 | 155 | /// Accept a new client socket. 156 | /// 157 | /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: 158 | /// - `address-family` 159 | /// - `keep-alive-enabled` 160 | /// - `keep-alive-idle-time` 161 | /// - `keep-alive-interval` 162 | /// - `keep-alive-count` 163 | /// - `hop-limit` 164 | /// - `receive-buffer-size` 165 | /// - `send-buffer-size` 166 | /// 167 | /// On success, this function returns the newly accepted client socket along with 168 | /// a pair of streams that can be used to read & write to the connection. 169 | /// 170 | /// # Typical errors 171 | /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) 172 | /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) 173 | /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) 174 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 175 | /// 176 | /// # References 177 | /// - 178 | /// - 179 | /// - 180 | /// - 181 | accept: func() -> result, error-code>; 182 | 183 | /// Get the bound local address. 184 | /// 185 | /// POSIX mentions: 186 | /// > If the socket has not been bound to a local name, the value 187 | /// > stored in the object pointed to by `address` is unspecified. 188 | /// 189 | /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. 190 | /// 191 | /// # Typical errors 192 | /// - `invalid-state`: The socket is not bound to any local address. 193 | /// 194 | /// # References 195 | /// - 196 | /// - 197 | /// - 198 | /// - 199 | local-address: func() -> result; 200 | 201 | /// Get the remote address. 202 | /// 203 | /// # Typical errors 204 | /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) 205 | /// 206 | /// # References 207 | /// - 208 | /// - 209 | /// - 210 | /// - 211 | remote-address: func() -> result; 212 | 213 | /// Whether the socket is in the `listening` state. 214 | /// 215 | /// Equivalent to the SO_ACCEPTCONN socket option. 216 | is-listening: func() -> bool; 217 | 218 | /// Whether this is a IPv4 or IPv6 socket. 219 | /// 220 | /// Equivalent to the SO_DOMAIN socket option. 221 | address-family: func() -> ip-address-family; 222 | 223 | /// Hints the desired listen queue size. Implementations are free to ignore this. 224 | /// 225 | /// If the provided value is 0, an `invalid-argument` error is returned. 226 | /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 227 | /// 228 | /// # Typical errors 229 | /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. 230 | /// - `invalid-argument`: (set) The provided value was 0. 231 | /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. 232 | set-listen-backlog-size: func(value: u64) -> result<_, error-code>; 233 | 234 | /// Enables or disables keepalive. 235 | /// 236 | /// The keepalive behavior can be adjusted using: 237 | /// - `keep-alive-idle-time` 238 | /// - `keep-alive-interval` 239 | /// - `keep-alive-count` 240 | /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. 241 | /// 242 | /// Equivalent to the SO_KEEPALIVE socket option. 243 | keep-alive-enabled: func() -> result; 244 | set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; 245 | 246 | /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. 247 | /// 248 | /// If the provided value is 0, an `invalid-argument` error is returned. 249 | /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 250 | /// I.e. after setting a value, reading the same setting back may return a different value. 251 | /// 252 | /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) 253 | /// 254 | /// # Typical errors 255 | /// - `invalid-argument`: (set) The provided value was 0. 256 | keep-alive-idle-time: func() -> result; 257 | set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; 258 | 259 | /// The time between keepalive packets. 260 | /// 261 | /// If the provided value is 0, an `invalid-argument` error is returned. 262 | /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 263 | /// I.e. after setting a value, reading the same setting back may return a different value. 264 | /// 265 | /// Equivalent to the TCP_KEEPINTVL socket option. 266 | /// 267 | /// # Typical errors 268 | /// - `invalid-argument`: (set) The provided value was 0. 269 | keep-alive-interval: func() -> result; 270 | set-keep-alive-interval: func(value: duration) -> result<_, error-code>; 271 | 272 | /// The maximum amount of keepalive packets TCP should send before aborting the connection. 273 | /// 274 | /// If the provided value is 0, an `invalid-argument` error is returned. 275 | /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 276 | /// I.e. after setting a value, reading the same setting back may return a different value. 277 | /// 278 | /// Equivalent to the TCP_KEEPCNT socket option. 279 | /// 280 | /// # Typical errors 281 | /// - `invalid-argument`: (set) The provided value was 0. 282 | keep-alive-count: func() -> result; 283 | set-keep-alive-count: func(value: u32) -> result<_, error-code>; 284 | 285 | /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. 286 | /// 287 | /// If the provided value is 0, an `invalid-argument` error is returned. 288 | /// 289 | /// # Typical errors 290 | /// - `invalid-argument`: (set) The TTL value must be 1 or higher. 291 | hop-limit: func() -> result; 292 | set-hop-limit: func(value: u8) -> result<_, error-code>; 293 | 294 | /// The kernel buffer space reserved for sends/receives on this socket. 295 | /// 296 | /// If the provided value is 0, an `invalid-argument` error is returned. 297 | /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 298 | /// I.e. after setting a value, reading the same setting back may return a different value. 299 | /// 300 | /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. 301 | /// 302 | /// # Typical errors 303 | /// - `invalid-argument`: (set) The provided value was 0. 304 | receive-buffer-size: func() -> result; 305 | set-receive-buffer-size: func(value: u64) -> result<_, error-code>; 306 | send-buffer-size: func() -> result; 307 | set-send-buffer-size: func(value: u64) -> result<_, error-code>; 308 | 309 | /// Create a `pollable` which can be used to poll for, or block on, 310 | /// completion of any of the asynchronous operations of this socket. 311 | /// 312 | /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` 313 | /// return `error(would-block)`, this pollable can be used to wait for 314 | /// their success or failure, after which the method can be retried. 315 | /// 316 | /// The pollable is not limited to the async operation that happens to be 317 | /// in progress at the time of calling `subscribe` (if any). Theoretically, 318 | /// `subscribe` only has to be called once per socket and can then be 319 | /// (re)used for the remainder of the socket's lifetime. 320 | /// 321 | /// See 322 | /// for a more information. 323 | /// 324 | /// Note: this function is here for WASI Preview2 only. 325 | /// It's planned to be removed when `future` is natively supported in Preview3. 326 | subscribe: func() -> pollable; 327 | 328 | /// Initiate a graceful shutdown. 329 | /// 330 | /// - `receive`: The socket is not expecting to receive any data from 331 | /// the peer. The `input-stream` associated with this socket will be 332 | /// closed. Any data still in the receive queue at time of calling 333 | /// this method will be discarded. 334 | /// - `send`: The socket has no more data to send to the peer. The `output-stream` 335 | /// associated with this socket will be closed and a FIN packet will be sent. 336 | /// - `both`: Same effect as `receive` & `send` combined. 337 | /// 338 | /// This function is idempotent. Shutting a down a direction more than once 339 | /// has no effect and returns `ok`. 340 | /// 341 | /// The shutdown function does not close (drop) the socket. 342 | /// 343 | /// # Typical errors 344 | /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) 345 | /// 346 | /// # References 347 | /// - 348 | /// - 349 | /// - 350 | /// - 351 | shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /wit/deps/sockets/udp-create-socket.wit: -------------------------------------------------------------------------------- 1 | 2 | interface udp-create-socket { 3 | use network.{network, error-code, ip-address-family}; 4 | use udp.{udp-socket}; 5 | 6 | /// Create a new UDP socket. 7 | /// 8 | /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. 9 | /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. 10 | /// 11 | /// This function does not require a network capability handle. This is considered to be safe because 12 | /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, 13 | /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. 14 | /// 15 | /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. 16 | /// 17 | /// # Typical errors 18 | /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) 19 | /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) 20 | /// 21 | /// # References: 22 | /// - 23 | /// - 24 | /// - 25 | /// - 26 | create-udp-socket: func(address-family: ip-address-family) -> result; 27 | } 28 | -------------------------------------------------------------------------------- /wit/deps/sockets/udp.wit: -------------------------------------------------------------------------------- 1 | 2 | interface udp { 3 | use wasi:io/poll@0.2.0.{pollable}; 4 | use network.{network, error-code, ip-socket-address, ip-address-family}; 5 | 6 | /// A received datagram. 7 | record incoming-datagram { 8 | /// The payload. 9 | /// 10 | /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. 11 | data: list, 12 | 13 | /// The source address. 14 | /// 15 | /// This field is guaranteed to match the remote address the stream was initialized with, if any. 16 | /// 17 | /// Equivalent to the `src_addr` out parameter of `recvfrom`. 18 | remote-address: ip-socket-address, 19 | } 20 | 21 | /// A datagram to be sent out. 22 | record outgoing-datagram { 23 | /// The payload. 24 | data: list, 25 | 26 | /// The destination address. 27 | /// 28 | /// The requirements on this field depend on how the stream was initialized: 29 | /// - with a remote address: this field must be None or match the stream's remote address exactly. 30 | /// - without a remote address: this field is required. 31 | /// 32 | /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. 33 | remote-address: option, 34 | } 35 | 36 | 37 | 38 | /// A UDP socket handle. 39 | resource udp-socket { 40 | /// Bind the socket to a specific network on the provided IP address and port. 41 | /// 42 | /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which 43 | /// network interface(s) to bind to. 44 | /// If the port is zero, the socket will be bound to a random free port. 45 | /// 46 | /// # Typical errors 47 | /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) 48 | /// - `invalid-state`: The socket is already bound. (EINVAL) 49 | /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) 50 | /// - `address-in-use`: Address is already in use. (EADDRINUSE) 51 | /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) 52 | /// - `not-in-progress`: A `bind` operation is not in progress. 53 | /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) 54 | /// 55 | /// # Implementors note 56 | /// Unlike in POSIX, in WASI the bind operation is async. This enables 57 | /// interactive WASI hosts to inject permission prompts. Runtimes that 58 | /// don't want to make use of this ability can simply call the native 59 | /// `bind` as part of either `start-bind` or `finish-bind`. 60 | /// 61 | /// # References 62 | /// - 63 | /// - 64 | /// - 65 | /// - 66 | start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; 67 | finish-bind: func() -> result<_, error-code>; 68 | 69 | /// Set up inbound & outbound communication channels, optionally to a specific peer. 70 | /// 71 | /// This function only changes the local socket configuration and does not generate any network traffic. 72 | /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, 73 | /// based on the best network path to `remote-address`. 74 | /// 75 | /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: 76 | /// - `send` can only be used to send to this destination. 77 | /// - `receive` will only return datagrams sent from the provided `remote-address`. 78 | /// 79 | /// This method may be called multiple times on the same socket to change its association, but 80 | /// only the most recently returned pair of streams will be operational. Implementations may trap if 81 | /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. 82 | /// 83 | /// The POSIX equivalent in pseudo-code is: 84 | /// ```text 85 | /// if (was previously connected) { 86 | /// connect(s, AF_UNSPEC) 87 | /// } 88 | /// if (remote_address is Some) { 89 | /// connect(s, remote_address) 90 | /// } 91 | /// ``` 92 | /// 93 | /// Unlike in POSIX, the socket must already be explicitly bound. 94 | /// 95 | /// # Typical errors 96 | /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) 97 | /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) 98 | /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) 99 | /// - `invalid-state`: The socket is not bound. 100 | /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) 101 | /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 102 | /// - `connection-refused`: The connection was refused. (ECONNREFUSED) 103 | /// 104 | /// # References 105 | /// - 106 | /// - 107 | /// - 108 | /// - 109 | %stream: func(remote-address: option) -> result, error-code>; 110 | 111 | /// Get the current bound address. 112 | /// 113 | /// POSIX mentions: 114 | /// > If the socket has not been bound to a local name, the value 115 | /// > stored in the object pointed to by `address` is unspecified. 116 | /// 117 | /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. 118 | /// 119 | /// # Typical errors 120 | /// - `invalid-state`: The socket is not bound to any local address. 121 | /// 122 | /// # References 123 | /// - 124 | /// - 125 | /// - 126 | /// - 127 | local-address: func() -> result; 128 | 129 | /// Get the address the socket is currently streaming to. 130 | /// 131 | /// # Typical errors 132 | /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) 133 | /// 134 | /// # References 135 | /// - 136 | /// - 137 | /// - 138 | /// - 139 | remote-address: func() -> result; 140 | 141 | /// Whether this is a IPv4 or IPv6 socket. 142 | /// 143 | /// Equivalent to the SO_DOMAIN socket option. 144 | address-family: func() -> ip-address-family; 145 | 146 | /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. 147 | /// 148 | /// If the provided value is 0, an `invalid-argument` error is returned. 149 | /// 150 | /// # Typical errors 151 | /// - `invalid-argument`: (set) The TTL value must be 1 or higher. 152 | unicast-hop-limit: func() -> result; 153 | set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; 154 | 155 | /// The kernel buffer space reserved for sends/receives on this socket. 156 | /// 157 | /// If the provided value is 0, an `invalid-argument` error is returned. 158 | /// Any other value will never cause an error, but it might be silently clamped and/or rounded. 159 | /// I.e. after setting a value, reading the same setting back may return a different value. 160 | /// 161 | /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. 162 | /// 163 | /// # Typical errors 164 | /// - `invalid-argument`: (set) The provided value was 0. 165 | receive-buffer-size: func() -> result; 166 | set-receive-buffer-size: func(value: u64) -> result<_, error-code>; 167 | send-buffer-size: func() -> result; 168 | set-send-buffer-size: func(value: u64) -> result<_, error-code>; 169 | 170 | /// Create a `pollable` which will resolve once the socket is ready for I/O. 171 | /// 172 | /// Note: this function is here for WASI Preview2 only. 173 | /// It's planned to be removed when `future` is natively supported in Preview3. 174 | subscribe: func() -> pollable; 175 | } 176 | 177 | resource incoming-datagram-stream { 178 | /// Receive messages on the socket. 179 | /// 180 | /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. 181 | /// The returned list may contain fewer elements than requested, but never more. 182 | /// 183 | /// This function returns successfully with an empty list when either: 184 | /// - `max-results` is 0, or: 185 | /// - `max-results` is greater than 0, but no results are immediately available. 186 | /// This function never returns `error(would-block)`. 187 | /// 188 | /// # Typical errors 189 | /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 190 | /// - `connection-refused`: The connection was refused. (ECONNREFUSED) 191 | /// 192 | /// # References 193 | /// - 194 | /// - 195 | /// - 196 | /// - 197 | /// - 198 | /// - 199 | /// - 200 | /// - 201 | receive: func(max-results: u64) -> result, error-code>; 202 | 203 | /// Create a `pollable` which will resolve once the stream is ready to receive again. 204 | /// 205 | /// Note: this function is here for WASI Preview2 only. 206 | /// It's planned to be removed when `future` is natively supported in Preview3. 207 | subscribe: func() -> pollable; 208 | } 209 | 210 | resource outgoing-datagram-stream { 211 | /// Check readiness for sending. This function never blocks. 212 | /// 213 | /// Returns the number of datagrams permitted for the next call to `send`, 214 | /// or an error. Calling `send` with more datagrams than this function has 215 | /// permitted will trap. 216 | /// 217 | /// When this function returns ok(0), the `subscribe` pollable will 218 | /// become ready when this function will report at least ok(1), or an 219 | /// error. 220 | /// 221 | /// Never returns `would-block`. 222 | check-send: func() -> result; 223 | 224 | /// Send messages on the socket. 225 | /// 226 | /// This function attempts to send all provided `datagrams` on the socket without blocking and 227 | /// returns how many messages were actually sent (or queued for sending). This function never 228 | /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. 229 | /// 230 | /// This function semantically behaves the same as iterating the `datagrams` list and sequentially 231 | /// sending each individual datagram until either the end of the list has been reached or the first error occurred. 232 | /// If at least one datagram has been sent successfully, this function never returns an error. 233 | /// 234 | /// If the input list is empty, the function returns `ok(0)`. 235 | /// 236 | /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if 237 | /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. 238 | /// 239 | /// # Typical errors 240 | /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) 241 | /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) 242 | /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) 243 | /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) 244 | /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) 245 | /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) 246 | /// - `connection-refused`: The connection was refused. (ECONNREFUSED) 247 | /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) 248 | /// 249 | /// # References 250 | /// - 251 | /// - 252 | /// - 253 | /// - 254 | /// - 255 | /// - 256 | /// - 257 | /// - 258 | send: func(datagrams: list) -> result; 259 | 260 | /// Create a `pollable` which will resolve once the stream is ready to send again. 261 | /// 262 | /// Note: this function is here for WASI Preview2 only. 263 | /// It's planned to be removed when `future` is natively supported in Preview3. 264 | subscribe: func() -> pollable; 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /wit/deps/sockets/world.wit: -------------------------------------------------------------------------------- 1 | package wasi:sockets@0.2.0; 2 | 3 | world imports { 4 | import instance-network; 5 | import network; 6 | import udp; 7 | import udp-create-socket; 8 | import tcp; 9 | import tcp-create-socket; 10 | import ip-name-lookup; 11 | } 12 | -------------------------------------------------------------------------------- /wit/iface.wit: -------------------------------------------------------------------------------- 1 | package cm:example; 2 | 3 | interface iface { 4 | markdown-to-html: func(x: string) -> string; 5 | test1: func( 6 | p1: bool, 7 | p2: s8, 8 | p3: s16, 9 | p4: s32, 10 | p5: s64, 11 | p6: u8, 12 | p7: u16, 13 | p8: u32, 14 | p9: u64, 15 | p10: char, 16 | ) -> string; 17 | 18 | test2: func(x: list>) -> list; 19 | test3: func(x: option>>) -> s32; 20 | 21 | record ab { 22 | a: string, 23 | b: string, 24 | } 25 | test-record: func(x: ab) -> ab; 26 | test-result: func(x: bool) -> result; 27 | test-tuple: func(x1: tuple, x2: tuple, x3: tuple) -> 28 | tuple; 29 | 30 | variant v { 31 | a(s32), 32 | b(float32), 33 | } 34 | 35 | test-variant: func(x: v) -> v; 36 | 37 | enum color { 38 | hot-pink, 39 | lime-green, 40 | navy-blue, 41 | } 42 | 43 | test-enum: func(x: color) -> s32; 44 | 45 | flags fg { 46 | a, b, c, d, e, 47 | } 48 | 49 | test-flags: func(x: fg) -> string; 50 | } -------------------------------------------------------------------------------- /wit/jsiface.wit: -------------------------------------------------------------------------------- 1 | package cm:example; 2 | 3 | interface jsiface { 4 | resource x { 5 | constructor(a: s32); 6 | get-a: func() -> s32; 7 | set-a: func(a: s32); 8 | add: static func(x: borrow, a: s32) -> x; 9 | } 10 | } -------------------------------------------------------------------------------- /wit/world.wit: -------------------------------------------------------------------------------- 1 | interface test { 2 | resource test-exported-resource { 3 | constructor(a: s32); 4 | get-a: func() -> s32; 5 | set-a: func(a: s32); 6 | add: static func(x: borrow, a: s32) -> test-exported-resource; 7 | } 8 | test: func(); 9 | } 10 | 11 | world w { 12 | include wasi:cli/imports@0.2.0; 13 | import jsiface; 14 | import iface; 15 | export iface; 16 | export test; 17 | } --------------------------------------------------------------------------------