aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDamian Gryski <[email protected]>2024-07-02 07:02:03 -0700
committerGitHub <[email protected]>2024-07-02 07:02:03 -0700
commit9cb263479c4b98f2d28889ca1acc297454e0d875 (patch)
treea30bcf8ef9086c5b8edfcee3b42bef352f396878
parentf18c6e342f834988caf43fd652b2baae9c3093f0 (diff)
downloadtinygo-9cb263479c4b98f2d28889ca1acc297454e0d875.tar.gz
tinygo-9cb263479c4b98f2d28889ca1acc297454e0d875.zip
wasi preview 2 support (#4027)
* all: wasip2 support Co-authored-by: Randy Reddig <[email protected]>
-rw-r--r--.github/workflows/linux.yml24
-rw-r--r--.github/workflows/windows.yml4
-rw-r--r--.gitignore5
-rw-r--r--.gitmodules6
-rw-r--r--GNUmakefile38
-rw-r--r--builder/build.go62
-rw-r--r--builder/builder_test.go2
-rw-r--r--compileopts/options.go2
-rw-r--r--compileopts/target.go2
-rw-r--r--compiler/symbol.go48
-rw-r--r--compiler/testdata/errors.go60
-rw-r--r--goenv/goenv.go5
m---------lib/wasi-cli0
-rw-r--r--loader/goroot.go3
-rw-r--r--main.go70
-rw-r--r--main_test.go12
-rw-r--r--src/crypto/rand/rand_arc4random.go2
-rw-r--r--src/crypto/rand/rand_urandom.go2
-rw-r--r--src/internal/wasi/cli/v0.2.0/command/command.wit.go6
-rw-r--r--src/internal/wasi/cli/v0.2.0/environment/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/environment/environment.wit.go66
-rw-r--r--src/internal/wasi/cli/v0.2.0/exit/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/exit/exit.wit.go27
-rw-r--r--src/internal/wasi/cli/v0.2.0/run/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/run/run.exports.go19
-rw-r--r--src/internal/wasi/cli/v0.2.0/run/run.wit.go18
-rw-r--r--src/internal/wasi/cli/v0.2.0/stderr/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/stderr/stderr.wit.go26
-rw-r--r--src/internal/wasi/cli/v0.2.0/stdin/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/stdin/stdin.wit.go26
-rw-r--r--src/internal/wasi/cli/v0.2.0/stdout/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/stdout/stdout.wit.go26
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-input/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-input/terminal-input.wit.go38
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-output/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-output/terminal-output.wit.go38
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-stderr/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-stderr/terminal-stderr.wit.go31
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-stdin/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-stdin/terminal-stdin.wit.go31
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-stdout/empty.s3
-rw-r--r--src/internal/wasi/cli/v0.2.0/terminal-stdout/terminal-stdout.wit.go31
-rw-r--r--src/internal/wasi/clocks/v0.2.0/monotonic-clock/empty.s3
-rw-r--r--src/internal/wasi/clocks/v0.2.0/monotonic-clock/monotonic-clock.wit.go115
-rw-r--r--src/internal/wasi/clocks/v0.2.0/wall-clock/empty.s3
-rw-r--r--src/internal/wasi/clocks/v0.2.0/wall-clock/wall-clock.wit.go80
-rw-r--r--src/internal/wasi/filesystem/v0.2.0/preopens/empty.s3
-rw-r--r--src/internal/wasi/filesystem/v0.2.0/preopens/preopens.wit.go27
-rw-r--r--src/internal/wasi/filesystem/v0.2.0/types/abi.go27
-rw-r--r--src/internal/wasi/filesystem/v0.2.0/types/empty.s3
-rw-r--r--src/internal/wasi/filesystem/v0.2.0/types/types.wit.go1284
-rw-r--r--src/internal/wasi/io/v0.2.0/error/empty.s3
-rw-r--r--src/internal/wasi/io/v0.2.0/error/error.wit.go73
-rw-r--r--src/internal/wasi/io/v0.2.0/poll/empty.s3
-rw-r--r--src/internal/wasi/io/v0.2.0/poll/poll.wit.go110
-rw-r--r--src/internal/wasi/io/v0.2.0/streams/empty.s3
-rw-r--r--src/internal/wasi/io/v0.2.0/streams/streams.wit.go521
-rw-r--r--src/internal/wasi/random/v0.2.0/insecure-seed/empty.s3
-rw-r--r--src/internal/wasi/random/v0.2.0/insecure-seed/insecure-seed.wit.go43
-rw-r--r--src/internal/wasi/random/v0.2.0/insecure/empty.s3
-rw-r--r--src/internal/wasi/random/v0.2.0/insecure/insecure.wit.go59
-rw-r--r--src/internal/wasi/random/v0.2.0/random/empty.s3
-rw-r--r--src/internal/wasi/random/v0.2.0/random/random.wit.go63
-rw-r--r--src/internal/wasi/sockets/v0.2.0/instance-network/empty.s3
-rw-r--r--src/internal/wasi/sockets/v0.2.0/instance-network/instance-network.wit.go30
-rw-r--r--src/internal/wasi/sockets/v0.2.0/ip-name-lookup/empty.s3
-rw-r--r--src/internal/wasi/sockets/v0.2.0/ip-name-lookup/ip-name-lookup.wit.go123
-rw-r--r--src/internal/wasi/sockets/v0.2.0/network/empty.s3
-rw-r--r--src/internal/wasi/sockets/v0.2.0/network/network.wit.go278
-rw-r--r--src/internal/wasi/sockets/v0.2.0/tcp-create-socket/empty.s3
-rw-r--r--src/internal/wasi/sockets/v0.2.0/tcp-create-socket/tcp-create-socket.wit.go54
-rw-r--r--src/internal/wasi/sockets/v0.2.0/tcp/abi.go71
-rw-r--r--src/internal/wasi/sockets/v0.2.0/tcp/empty.s3
-rw-r--r--src/internal/wasi/sockets/v0.2.0/tcp/tcp.wit.go839
-rw-r--r--src/internal/wasi/sockets/v0.2.0/udp-create-socket/empty.s3
-rw-r--r--src/internal/wasi/sockets/v0.2.0/udp-create-socket/udp-create-socket.wit.go54
-rw-r--r--src/internal/wasi/sockets/v0.2.0/udp/abi.go92
-rw-r--r--src/internal/wasi/sockets/v0.2.0/udp/empty.s3
-rw-r--r--src/internal/wasi/sockets/v0.2.0/udp/udp.wit.go643
-rw-r--r--src/os/dir_test.go2
-rw-r--r--src/os/dir_unix.go2
-rw-r--r--src/os/dir_wasi.go (renamed from src/os/dir_wasip1.go)2
-rw-r--r--src/os/dirent_linux.go2
-rw-r--r--src/os/env_unix_test.go2
-rw-r--r--src/os/exec_posix.go2
-rw-r--r--src/os/file_other.go2
-rw-r--r--src/os/file_unix.go2
-rw-r--r--src/os/getpagesize_test.go2
-rw-r--r--src/os/os_anyos_test.go6
-rw-r--r--src/os/os_chmod_test.go2
-rw-r--r--src/os/os_hardlink_test.go2
-rw-r--r--src/os/os_symlink_test.go2
-rw-r--r--src/os/pipe_test.go2
-rw-r--r--src/os/read_test.go2
-rw-r--r--src/os/removeall_noat.go2
-rw-r--r--src/os/removeall_other.go2
-rw-r--r--src/os/removeall_test.go2
-rw-r--r--src/os/seek_unix_bad.go2
-rw-r--r--src/os/stat_linuxlike.go2
-rw-r--r--src/os/stat_other.go2
-rw-r--r--src/os/stat_unix.go2
-rw-r--r--src/os/tempfile_test.go2
-rw-r--r--src/os/types_unix.go2
-rw-r--r--src/runtime/os_linux.go2
-rw-r--r--src/runtime/os_other.go2
-rw-r--r--src/runtime/os_wasip2.go5
-rw-r--r--src/runtime/runtime_tinygowasm.go2
-rw-r--r--src/runtime/runtime_tinygowasmp2.go76
-rw-r--r--src/runtime/runtime_unix.go2
-rw-r--r--src/runtime/runtime_wasm_js_scheduler.go2
-rw-r--r--src/runtime/runtime_wasm_wasip2.go61
-rw-r--r--src/syscall/env_libc.go59
-rw-r--r--src/syscall/env_wasip2.go11
-rw-r--r--src/syscall/errno_other.go2
-rw-r--r--src/syscall/errno_wasip1.go8
-rw-r--r--src/syscall/errno_wasip2.go7
-rw-r--r--src/syscall/file_emulated.go2
-rw-r--r--src/syscall/file_hosted.go2
-rw-r--r--src/syscall/libc_wasip2.go1356
-rw-r--r--src/syscall/syscall_libc.go54
-rw-r--r--src/syscall/syscall_libc_wasi.go (renamed from src/syscall/syscall_libc_wasip1.go)26
-rw-r--r--src/testing/testing_test.go4
m---------src/vendor/github.com/ydnar/wasm-tools-go0
-rw-r--r--targets/wasip2.json28
-rwxr-xr-xtools/tgtestjson.sh22
125 files changed, 7032 insertions, 153 deletions
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 38f0d92f5..8bb92e083 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -134,17 +134,19 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
+ with:
+ submodules: true
- name: Install Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
- name: Install wasmtime
- run: |
- mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
- curl https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
- tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
- echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
+ uses: bytecodealliance/actions/wasmtime/setup@v1
+ with:
+ version: "19.0.1"
+ - name: Install wasm-tools
+ uses: bytecodealliance/actions/wasm-tools/setup@v1
- name: Download release artifact
uses: actions/download-artifact@v4
with:
@@ -154,8 +156,8 @@ jobs:
mkdir -p ~/lib
tar -C ~/lib -xf tinygo.linux-amd64.tar.gz
ln -s ~/lib/tinygo/bin/tinygo ~/go/bin/tinygo
- - run: make tinygo-test-wasi-fast
- run: make tinygo-test-wasip1-fast
+ - run: make tinygo-test-wasip2-fast
- run: make smoketest
assert-test-linux:
# Run all tests that can run on Linux, with LLVM assertions enabled to catch
@@ -187,11 +189,11 @@ jobs:
with:
node-version: '18'
- name: Install wasmtime
- run: |
- mkdir -p $HOME/.wasmtime $HOME/.wasmtime/bin
- curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v14.0.4/wasmtime-v14.0.4-x86_64-linux.tar.xz -o wasmtime-v14.0.4-x86_64-linux.tar.xz -SfL
- tar -C $HOME/.wasmtime/bin --wildcards -xf wasmtime-v14.0.4-x86_64-linux.tar.xz --strip-components=1 wasmtime-v14.0.4-x86_64-linux/*
- echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
+ uses: bytecodealliance/actions/wasmtime/setup@v1
+ with:
+ version: "19.0.1"
+ - name: Setup `wasm-tools`
+ uses: bytecodealliance/actions/wasm-tools/setup@v1
- name: Restore LLVM source cache
uses: actions/cache/restore@v4
id: cache-llvm-source
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 5cfba9217..2b26a9b1c 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -220,5 +220,5 @@ jobs:
shell: bash
working-directory: build
run: 7z x release.zip -r
- - name: Test stdlib packages on wasi
- run: make tinygo-test-wasi-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
+ - name: Test stdlib packages on wasip1
+ run: make tinygo-test-wasip1-fast TINYGO=$(PWD)/build/tinygo/bin/tinygo
diff --git a/.gitignore b/.gitignore
index 9d4f702f8..9994ee3dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+go.work
+go.work.sum
+
docs/_build
src/device/avr/*.go
src/device/avr/*.ld
@@ -17,7 +20,7 @@ src/device/kendryte/*.go
src/device/kendryte/*.s
src/device/rp/*.go
src/device/rp/*.s
-vendor
+./vendor
llvm-build
llvm-project
build/*
diff --git a/.gitmodules b/.gitmodules
index c3e7e47bb..edd7b215c 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -39,3 +39,9 @@
path = src/net
url = https://github.com/tinygo-org/net.git
branch = dev
+[submodule "lib/wasi-cli"]
+ path = lib/wasi-cli
+ url = https://github.com/WebAssembly/wasi-cli
+[submodule "src/vendor/github.com/ydnar/wasm-tools-go"]
+ path = src/vendor/github.com/ydnar/wasm-tools-go
+ url = https://github.com/ydnar/wasm-tools-go.git
diff --git a/GNUmakefile b/GNUmakefile
index 2cc70df7f..ca8718ca5 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -268,6 +268,11 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a:
@if [ ! -e lib/wasi-libc/Makefile ]; then echo "Submodules have not been downloaded. Please download them using:\n git submodule update --init"; exit 1; fi
cd lib/wasi-libc && $(MAKE) -j4 EXTRA_CFLAGS="-O2 -g -DNDEBUG -mnontrapping-fptoint -msign-ext" MALLOC_IMPL=none CC="$(CLANG)" AR=$(LLVM_AR) NM=$(LLVM_NM)
+# Generate WASI syscall bindings
+.PHONY: wasi-syscall
+wasi-syscall:
+ wit-bindgen-go generate -o ./src/internal -p internal --versioned ./lib/wasi-cli/wit
+
# Check for Node.js used during WASM tests.
NODEJS_VERSION := $(word 1,$(subst ., ,$(shell node -v | cut -c 2-)))
MIN_NODEJS_VERSION=18
@@ -430,15 +435,36 @@ tinygo-test-wasi:
$(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
tinygo-test-wasip1:
GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW) ./tests/runtime_wasi
-tinygo-test-wasi-fast:
- $(TINYGO) test -target wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
tinygo-test-wasip1-fast:
- GOOS=wasip1 GOARCH=wasm $(TINYGO) test $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
-tinygo-bench-wasi:
+ $(TINYGO) test -target=wasip1 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
+
+tinygo-test-wasip2-slow:
+ $(TINYGO) test -target=wasip2 $(TEST_PACKAGES_SLOW)
+tinygo-test-wasip2-fast:
+ $(TINYGO) test -target=wasip2 $(TEST_PACKAGES_FAST) ./tests/runtime_wasi
+
+tinygo-test-wasip2-sum-slow:
+ TINYGO=$(TINYGO) \
+ TARGET=wasip2 \
+ TESTOPTS="-x -work" \
+ PACKAGES="$(TEST_PACKAGES_SLOW)" \
+ gotestsum --raw-command -- ./tools/tgtestjson.sh
+tinygo-test-wasip2-sum-fast:
+ TINYGO=$(TINYGO) \
+ TARGET=wasip2 \
+ TESTOPTS="-x -work" \
+ PACKAGES="$(TEST_PACKAGES_FAST)" \
+ gotestsum --raw-command -- ./tools/tgtestjson.sh
+tinygo-bench-wasip1:
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
-tinygo-bench-wasi-fast:
+tinygo-bench-wasip1-fast:
$(TINYGO) test -target wasip1 -bench . $(TEST_PACKAGES_FAST)
+tinygo-bench-wasip2:
+ $(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_SLOW)
+tinygo-bench-wasip2-fast:
+ $(TINYGO) test -target wasip2 -bench . $(TEST_PACKAGES_FAST)
+
# Test external packages in a large corpus.
test-corpus:
CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=1h -buildmode exe -tags byollvm -run TestCorpus . -corpus=testdata/corpus.yaml
@@ -832,6 +858,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-bottom-half/headers
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/arch
@mkdir -p build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
+ @mkdir -p build/release/tinygo/lib/wasi-cli/
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0
@mkdir -p build/release/tinygo/pkg/thumbv6m-unknown-unknown-eabi-cortex-m0plus
@mkdir -p build/release/tinygo/pkg/thumbv7em-unknown-unknown-eabi-cortex-m4
@@ -891,6 +918,7 @@ endif
@cp -rp lib/wasi-libc/libc-top-half/musl/src/string build/release/tinygo/lib/wasi-libc/libc-top-half/musl/src
@cp -rp lib/wasi-libc/libc-top-half/musl/include build/release/tinygo/lib/wasi-libc/libc-top-half/musl
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
+ @cp -rp lib/wasi-cli/wit build/release/tinygo/lib/wasi-cli/wit
@cp -rp llvm-project/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt-builtins
@cp -rp llvm-project/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt-builtins
@cp -rp src build/release/tinygo/src
diff --git a/builder/build.go b/builder/build.go
index c569bb1d2..d99d640af 100644
--- a/builder/build.go
+++ b/builder/build.go
@@ -840,11 +840,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
"--output", result.Executable,
)
+ wasmopt := goenv.Get("WASMOPT")
if config.Options.PrintCommands != nil {
- config.Options.PrintCommands(goenv.Get("WASMOPT"), args...)
+ config.Options.PrintCommands(wasmopt, args...)
}
-
- cmd := exec.Command(goenv.Get("WASMOPT"), args...)
+ cmd := exec.Command(wasmopt, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -854,6 +854,62 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
}
+ // Run wasm-tools for component-model binaries
+ witPackage := strings.ReplaceAll(config.Target.WITPackage, "{root}", goenv.Get("TINYGOROOT"))
+ if config.Options.WITPackage != "" {
+ witPackage = config.Options.WITPackage
+ }
+ witWorld := config.Target.WITWorld
+ if config.Options.WITWorld != "" {
+ witWorld = config.Options.WITWorld
+ }
+ if witPackage != "" && witWorld != "" {
+
+ // wasm-tools component embed -w wasi:cli/command
+ // $$(tinygo env TINYGOROOT)/lib/wasi-cli/wit/ main.wasm -o embedded.wasm
+ args := []string{
+ "component",
+ "embed",
+ "-w", witWorld,
+ witPackage,
+ result.Executable,
+ "-o", result.Executable,
+ }
+
+ wasmtools := goenv.Get("WASMTOOLS")
+ if config.Options.PrintCommands != nil {
+ config.Options.PrintCommands(wasmtools, args...)
+ }
+ cmd := exec.Command(wasmtools, args...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+
+ err := cmd.Run()
+ if err != nil {
+ return fmt.Errorf("wasm-tools failed: %w", err)
+ }
+
+ // wasm-tools component new embedded.wasm -o component.wasm
+ args = []string{
+ "component",
+ "new",
+ result.Executable,
+ "-o", result.Executable,
+ }
+
+ if config.Options.PrintCommands != nil {
+ config.Options.PrintCommands(wasmtools, args...)
+ }
+ cmd = exec.Command(wasmtools, args...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+
+ err = cmd.Run()
+ if err != nil {
+ return fmt.Errorf("wasm-tools failed: %w", err)
+ }
+ }
+
// Print code size if requested.
if config.Options.PrintSizes == "short" || config.Options.PrintSizes == "full" {
packagePathMap := make(map[string]string, len(lprogram.Packages))
diff --git a/builder/builder_test.go b/builder/builder_test.go
index 3bb3e9841..c7b863873 100644
--- a/builder/builder_test.go
+++ b/builder/builder_test.go
@@ -34,6 +34,7 @@ func TestClangAttributes(t *testing.T) {
"nintendoswitch",
"riscv-qemu",
"wasip1",
+ "wasip2",
"wasm",
"wasm-unknown",
}
@@ -61,6 +62,7 @@ func TestClangAttributes(t *testing.T) {
{GOOS: "windows", GOARCH: "amd64"},
{GOOS: "windows", GOARCH: "arm64"},
{GOOS: "wasip1", GOARCH: "wasm"},
+ {GOOS: "wasip2", GOARCH: "wasm"},
} {
name := "GOOS=" + options.GOOS + ",GOARCH=" + options.GOARCH
if options.GOARCH == "arm" {
diff --git a/compileopts/options.go b/compileopts/options.go
index debdaf08c..8b0e2266d 100644
--- a/compileopts/options.go
+++ b/compileopts/options.go
@@ -53,6 +53,8 @@ type Options struct {
Monitor bool
BaudRate int
Timeout time.Duration
+ WITPackage string // pass through to wasm-tools component embed invocation
+ WITWorld string // pass through to wasm-tools component embed -w option
}
// Verify performs a validation on the given options, raising an error if options are not valid.
diff --git a/compileopts/target.go b/compileopts/target.go
index 9a7d550fa..646029e44 100644
--- a/compileopts/target.go
+++ b/compileopts/target.go
@@ -62,6 +62,8 @@ type TargetSpec struct {
JLinkDevice string `json:"jlink-device,omitempty"`
CodeModel string `json:"code-model,omitempty"`
RelocationModel string `json:"relocation-model,omitempty"`
+ WITPackage string `json:"wit-package,omitempty"`
+ WITWorld string `json:"wit-world,omitempty"`
}
// overrideProperties overrides all properties that are set in child into itself using reflection.
diff --git a/compiler/symbol.go b/compiler/symbol.go
index bf5ac5f1b..5ebdee147 100644
--- a/compiler/symbol.go
+++ b/compiler/symbol.go
@@ -346,7 +346,7 @@ func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) {
// The list of allowed types is based on this proposal:
// https://github.com/golang/go/issues/59149
func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
- if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" {
+ if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" || c.pkg.Path() == "syscall" {
// The runtime is a special case. Allow all kinds of parameters
// (importantly, including pointers).
return
@@ -360,38 +360,68 @@ func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) {
c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma))
} else if f.Signature.Results().Len() == 1 {
result := f.Signature.Results().At(0)
- if !isValidWasmType(result.Type(), true) {
+ if !isValidWasmType(result.Type(), siteResult) {
c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String()))
}
}
for _, param := range f.Params {
// Check whether the type is allowed.
// Only a very limited number of types can be mapped to WebAssembly.
- if !isValidWasmType(param.Type(), false) {
+ if !isValidWasmType(param.Type(), siteParam) {
c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String()))
}
}
}
-// Check whether the type maps directly to a WebAssembly type, according to:
+// Check whether the type maps directly to a WebAssembly type.
+//
+// This reflects the relaxed type restrictions proposed here (except for structs.HostLayout):
+// https://github.com/golang/go/issues/66984
+//
+// This previously reflected the additional restrictions documented here:
// https://github.com/golang/go/issues/59149
-func isValidWasmType(typ types.Type, isReturn bool) bool {
+func isValidWasmType(typ types.Type, site wasmSite) bool {
switch typ := typ.Underlying().(type) {
case *types.Basic:
switch typ.Kind() {
- case types.Int32, types.Uint32, types.Int64, types.Uint64:
+ case types.Bool:
+ return true
+ case types.Int, types.Uint, types.Int8, types.Uint8, types.Int16, types.Uint16, types.Int32, types.Uint32, types.Int64, types.Uint64:
return true
case types.Float32, types.Float64:
return true
- case types.UnsafePointer:
- if !isReturn {
- return true
+ case types.Uintptr, types.UnsafePointer:
+ return true
+ case types.String:
+ // string flattens to two values, so disallowed as a result
+ return site == siteParam || site == siteIndirect
+ }
+ case *types.Array:
+ return site == siteIndirect && isValidWasmType(typ.Elem(), siteIndirect)
+ case *types.Struct:
+ if site != siteIndirect {
+ return false
+ }
+ for i := 0; i < typ.NumFields(); i++ {
+ if !isValidWasmType(typ.Field(i).Type(), siteIndirect) {
+ return false
}
}
+ return true
+ case *types.Pointer:
+ return isValidWasmType(typ.Elem(), siteIndirect)
}
return false
}
+type wasmSite int
+
+const (
+ siteParam wasmSite = iota
+ siteResult
+ siteIndirect // pointer or field
+)
+
// getParams returns the function parameters, including the receiver at the
// start. This is an alternative to the Params member of *ssa.Function, which is
// not yet populated when the package has not yet been built.
diff --git a/compiler/testdata/errors.go b/compiler/testdata/errors.go
index 5778a931e..81e8a76a1 100644
--- a/compiler/testdata/errors.go
+++ b/compiler/testdata/errors.go
@@ -13,31 +13,65 @@ func implementation() {
type Uint uint32
+type S struct {
+ a [4]uint32
+ b uintptr
+ c int
+ d float32
+ e float64
+}
+
//go:wasmimport modulename validparam
-func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint)
+func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint, f uintptr, g string, h *int32, i *S)
-// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type int
-// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type string
+// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type [4]uint32
// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type []byte
-// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32
+// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type struct{a int}
+// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type chan struct{}
+// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type func()
//
//go:wasmimport modulename invalidparam
-func invalidparam(a int, b string, c []byte, d *int32)
+func invalidparam(a [4]uint32, b []byte, c struct{ a int }, d chan struct{}, e func())
+
+//go:wasmimport modulename validreturn_int32
+func validreturn_int32() int32
+
+//go:wasmimport modulename validreturn_int
+func validreturn_int() int
+
+//go:wasmimport modulename validreturn_ptr_int32
+func validreturn_ptr_int32() *int32
+
+//go:wasmimport modulename validreturn_ptr_string
+func validreturn_ptr_string() *string
-//go:wasmimport modulename validreturn
-func validreturn() int32
+//go:wasmimport modulename validreturn_ptr_struct
+func validreturn_ptr_struct() *S
+
+//go:wasmimport modulename validreturn_unsafe_pointer
+func validreturn_unsafe_pointer() unsafe.Pointer
// ERROR: //go:wasmimport modulename manyreturns: too many return values
//
//go:wasmimport modulename manyreturns
func manyreturns() (int32, int32)
-// ERROR: //go:wasmimport modulename invalidreturn: unsupported result type int
+// ERROR: //go:wasmimport modulename invalidreturn_func: unsupported result type func()
+//
+//go:wasmimport modulename invalidreturn_func
+func invalidreturn_func() func()
+
+// ERROR: //go:wasmimport modulename invalidreturn_slice_byte: unsupported result type []byte
+//
+//go:wasmimport modulename invalidreturn_slice_byte
+func invalidreturn_slice_byte() []byte
+
+// ERROR: //go:wasmimport modulename invalidreturn_chan_int: unsupported result type chan int
//
-//go:wasmimport modulename invalidreturn
-func invalidreturn() int
+//go:wasmimport modulename invalidreturn_chan_int
+func invalidreturn_chan_int() chan int
-// ERROR: //go:wasmimport modulename invalidUnsafePointerReturn: unsupported result type unsafe.Pointer
+// ERROR: //go:wasmimport modulename invalidreturn_string: unsupported result type string
//
-//go:wasmimport modulename invalidUnsafePointerReturn
-func invalidUnsafePointerReturn() unsafe.Pointer
+//go:wasmimport modulename invalidreturn_string
+func invalidreturn_string() string
diff --git a/goenv/goenv.go b/goenv/goenv.go
index be1c631ca..187bcc6df 100644
--- a/goenv/goenv.go
+++ b/goenv/goenv.go
@@ -159,6 +159,11 @@ func Get(name string) string {
}
return findWasmOpt()
+ case "WASMTOOLS":
+ if path := os.Getenv("WASMTOOLS"); path != "" {
+ return path
+ }
+ return "wasm-tools"
default:
return ""
}
diff --git a/lib/wasi-cli b/lib/wasi-cli
new file mode 160000
+Subproject 6ae82617096e83e6606047736e84ac397b78863
diff --git a/loader/goroot.go b/loader/goroot.go
index 7c8d97fa4..72b2f3335 100644
--- a/loader/goroot.go
+++ b/loader/goroot.go
@@ -241,6 +241,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"internal/fuzz/": false,
"internal/reflectlite/": false,
"internal/task/": false,
+ "internal/wasi/": false,
"machine/": false,
"net/": true,
"net/http/": false,
@@ -250,6 +251,8 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"runtime/": false,
"sync/": true,
"testing/": true,
+ "vendor/": true,
+ "vendor/github.com/": false,
}
if goMinor >= 19 {
diff --git a/main.go b/main.go
index 6b3aeb3b0..cd2e43ace 100644
--- a/main.go
+++ b/main.go
@@ -306,16 +306,25 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
// reads any files.
//
// Ex. run --dir=.. --dir=../.. --dir=../../..
- dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot)
+ var dirs []string
+ switch config.Options.Target {
+ case "wasip1":
+ dirs = dirsToModuleRootRel(result.MainDir, result.ModuleRoot)
+ case "wasip2":
+ dirs = dirsToModuleRootAbs(result.MainDir, result.ModuleRoot)
+ default:
+ return fmt.Errorf("unknown GOOS target: %v", config.Options.Target)
+ }
+
args := []string{"run"}
- for _, d := range dirs[1:] {
+ for _, d := range dirs {
args = append(args, "--dir="+d)
}
- // The below re-organizes the arguments so that the current
- // directory is added last.
+ args = append(args, "--env=PWD="+cmd.Dir)
+
args = append(args, cmd.Args[1:]...)
- cmd.Args = append(cmd.Args[:1:1], args...)
+ cmd.Args = args
}
// Run the test.
@@ -356,8 +365,8 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options
return passed, err
}
-func dirsToModuleRoot(maindir, modroot string) []string {
- var dirs = []string{"."}
+func dirsToModuleRootRel(maindir, modroot string) []string {
+ var dirs []string
last := ".."
// strip off path elements until we hit the module root
// adding `..`, `../..`, `../../..` until we're done
@@ -366,6 +375,20 @@ func dirsToModuleRoot(maindir, modroot string) []string {
last = filepath.Join(last, "..")
maindir = filepath.Dir(maindir)
}
+ dirs = append(dirs, ".")
+ return dirs
+}
+
+func dirsToModuleRootAbs(maindir, modroot string) []string {
+ var dirs = []string{maindir}
+ last := filepath.Join(maindir, "..")
+ // strip off path elements until we hit the module root
+ // adding `..`, `../..`, `../../..` until we're done
+ for maindir != modroot {
+ dirs = append(dirs, last)
+ last = filepath.Join(last, "..")
+ maindir = filepath.Dir(maindir)
+ }
return dirs
}
@@ -785,6 +808,9 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error {
// passes command line arguments and evironment variables in a way appropriate
// for the given emulator.
func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) (builder.BuildResult, error) {
+
+ isSingleFile := strings.HasSuffix(pkgName, ".go")
+
// Determine whether we're on a system that supports environment variables
// and command line parameters (operating systems, WASI) or not (baremetal,
// WebAssembly in the browser). If we're on a system without an environment,
@@ -818,9 +844,6 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
}
}
} else if config.EmulatorName() == "wasmtime" {
- // Wasmtime needs some special flags to pass environment variables
- // and allow reading from the current directory.
- emuArgs = append(emuArgs, "--dir=.")
for _, v := range environmentVars {
emuArgs = append(emuArgs, "--env", v)
}
@@ -876,7 +899,26 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c
if err != nil {
return result, err
}
+
name = emulator[0]
+
+ if name == "wasmtime" {
+ // Wasmtime needs some special flags to pass environment variables
+ // and allow reading from the current directory.
+ switch config.Options.Target {
+ case "wasip1":
+ emuArgs = append(emuArgs, "--dir=.")
+ case "wasip2":
+ dir := result.MainDir
+ if isSingleFile {
+ cwd, _ := os.Getwd()
+ dir = cwd
+ }
+ emuArgs = append(emuArgs, "--dir="+dir)
+ emuArgs = append(emuArgs, "--env=PWD="+dir)
+ }
+ }
+
emuArgs = append(emuArgs, emulator[1:]...)
args = append(emuArgs, args...)
}
@@ -1465,6 +1507,12 @@ func main() {
flag.StringVar(&outpath, "o", "", "output filename")
}
+ var witPackage, witWorld string
+ if command == "help" || command == "build" || command == "test" || command == "run" {
+ flag.StringVar(&witPackage, "wit-package", "", "wit package for wasm component embedding")
+ flag.StringVar(&witWorld, "wit-world", "", "wit world for wasm component embedding")
+ }
+
var testConfig compileopts.TestConfig
if command == "help" || command == "test" {
flag.BoolVar(&testConfig.CompileOnly, "c", false, "compile the test binary but do not run it")
@@ -1544,6 +1592,8 @@ func main() {
Monitor: *monitor,
BaudRate: *baudrate,
Timeout: *timeout,
+ WITPackage: witPackage,
+ WITWorld: witWorld,
}
if *printCommands {
options.PrintCommands = printCommand
diff --git a/main_test.go b/main_test.go
index 1ea16cd14..a4ec3c810 100644
--- a/main_test.go
+++ b/main_test.go
@@ -181,6 +181,10 @@ func TestBuild(t *testing.T) {
t.Parallel()
runPlatTests(optionsFromTarget("wasip1", sema), tests, t)
})
+ t.Run("WASIp2", func(t *testing.T) {
+ t.Parallel()
+ runPlatTests(optionsFromTarget("wasip2", sema), tests, t)
+ })
}
}
@@ -235,6 +239,14 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
default:
}
}
+ if options.Target == "wasip2" {
+ switch name {
+ case "cgo/":
+ // waisp2 use our own libc; cgo tests fail
+ continue
+ }
+ }
+
name := name // redefine to avoid race condition
t.Run(name, func(t *testing.T) {
t.Parallel()
diff --git a/src/crypto/rand/rand_arc4random.go b/src/crypto/rand/rand_arc4random.go
index 1b1796b4d..64a6218bd 100644
--- a/src/crypto/rand/rand_arc4random.go
+++ b/src/crypto/rand/rand_arc4random.go
@@ -1,4 +1,4 @@
-//go:build darwin || tinygo.wasm
+//go:build darwin || wasip1 || wasip2 || wasm
// This implementation of crypto/rand uses the arc4random_buf function
// (available on both MacOS and WASI) to generate random numbers.
diff --git a/src/crypto/rand/rand_urandom.go b/src/crypto/rand/rand_urandom.go
index e9a8d485e..53554529b 100644
--- a/src/crypto/rand/rand_urandom.go
+++ b/src/crypto/rand/rand_urandom.go
@@ -1,4 +1,4 @@
-//go:build linux && !baremetal && !wasip1
+//go:build linux && !baremetal && !wasip1 && !wasip2
// This implementation of crypto/rand uses the /dev/urandom pseudo-file to
// generate random numbers.
diff --git a/src/internal/wasi/cli/v0.2.0/command/command.wit.go b/src/internal/wasi/cli/v0.2.0/command/command.wit.go
new file mode 100644
index 000000000..be6dd3045
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/command/command.wit.go
@@ -0,0 +1,6 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package command represents the world "wasi:cli/[email protected]".
+package command
diff --git a/src/internal/wasi/cli/v0.2.0/environment/empty.s b/src/internal/wasi/cli/v0.2.0/environment/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/environment/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/environment/environment.wit.go b/src/internal/wasi/cli/v0.2.0/environment/environment.wit.go
new file mode 100644
index 000000000..b75e74b18
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/environment/environment.wit.go
@@ -0,0 +1,66 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package environment represents the imported interface "wasi:cli/[email protected]".
+package environment
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// GetEnvironment represents the imported function "get-environment".
+//
+// Get the POSIX-style environment variables.
+//
+// Each environment variable is provided as a pair of string variable names
+// and string value.
+//
+// Morally, these are a value import, but until value imports are available
+// in the component model, this import function should return the same
+// values each time it is called.
+//
+// get-environment: func() -> list<tuple<string, string>>
+//
+//go:nosplit
+func GetEnvironment() (result cm.List[[2]string]) {
+ wasmimport_GetEnvironment(&result)
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-environment
+//go:noescape
+func wasmimport_GetEnvironment(result *cm.List[[2]string])
+
+// GetArguments represents the imported function "get-arguments".
+//
+// Get the POSIX-style arguments to the program.
+//
+// get-arguments: func() -> list<string>
+//
+//go:nosplit
+func GetArguments() (result cm.List[string]) {
+ wasmimport_GetArguments(&result)
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-arguments
+//go:noescape
+func wasmimport_GetArguments(result *cm.List[string])
+
+// InitialCWD represents the imported function "initial-cwd".
+//
+// Return a path that programs should use as their initial current working
+// directory, interpreting `.` as shorthand for this.
+//
+// initial-cwd: func() -> option<string>
+//
+//go:nosplit
+func InitialCWD() (result cm.Option[string]) {
+ wasmimport_InitialCWD(&result)
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] initial-cwd
+//go:noescape
+func wasmimport_InitialCWD(result *cm.Option[string])
diff --git a/src/internal/wasi/cli/v0.2.0/exit/empty.s b/src/internal/wasi/cli/v0.2.0/exit/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/exit/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/exit/exit.wit.go b/src/internal/wasi/cli/v0.2.0/exit/exit.wit.go
new file mode 100644
index 000000000..36af67d24
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/exit/exit.wit.go
@@ -0,0 +1,27 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package exit represents the imported interface "wasi:cli/[email protected]".
+package exit
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// Exit represents the imported function "exit".
+//
+// Exit the current instance and any linked instances.
+//
+// exit: func(status: result)
+//
+//go:nosplit
+func Exit(status cm.Result) {
+ status0 := cm.LowerResult(status)
+ wasmimport_Exit((uint32)(status0))
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] exit
+//go:noescape
+func wasmimport_Exit(status0 uint32)
diff --git a/src/internal/wasi/cli/v0.2.0/run/empty.s b/src/internal/wasi/cli/v0.2.0/run/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/run/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/run/run.exports.go b/src/internal/wasi/cli/v0.2.0/run/run.exports.go
new file mode 100644
index 000000000..267e75a08
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/run/run.exports.go
@@ -0,0 +1,19 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+package run
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// Exports represents the caller-defined exports from "wasi:cli/[email protected]".
+var Exports struct {
+ // Run represents the caller-defined, exported function "run".
+ //
+ // Run the program.
+ //
+ // run: func() -> result
+ Run func() (result cm.Result)
+}
diff --git a/src/internal/wasi/cli/v0.2.0/run/run.wit.go b/src/internal/wasi/cli/v0.2.0/run/run.wit.go
new file mode 100644
index 000000000..728d0292a
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/run/run.wit.go
@@ -0,0 +1,18 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package run represents the exported interface "wasi:cli/[email protected]".
+package run
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+//go:wasmexport wasi:cli/[email protected]#run
+//export wasi:cli/[email protected]#run
+func wasmexport_Run() (result0 uint32) {
+ result := Exports.Run()
+ result0 = cm.LowerResult(result)
+ return
+}
diff --git a/src/internal/wasi/cli/v0.2.0/stderr/empty.s b/src/internal/wasi/cli/v0.2.0/stderr/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/stderr/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/stderr/stderr.wit.go b/src/internal/wasi/cli/v0.2.0/stderr/stderr.wit.go
new file mode 100644
index 000000000..fd2b7517c
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/stderr/stderr.wit.go
@@ -0,0 +1,26 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package stderr represents the imported interface "wasi:cli/[email protected]".
+package stderr
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/io/v0.2.0/streams"
+)
+
+// GetStderr represents the imported function "get-stderr".
+//
+// get-stderr: func() -> output-stream
+//
+//go:nosplit
+func GetStderr() (result streams.OutputStream) {
+ result0 := wasmimport_GetStderr()
+ result = cm.Reinterpret[streams.OutputStream]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-stderr
+//go:noescape
+func wasmimport_GetStderr() (result0 uint32)
diff --git a/src/internal/wasi/cli/v0.2.0/stdin/empty.s b/src/internal/wasi/cli/v0.2.0/stdin/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/stdin/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/stdin/stdin.wit.go b/src/internal/wasi/cli/v0.2.0/stdin/stdin.wit.go
new file mode 100644
index 000000000..abe35cbbb
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/stdin/stdin.wit.go
@@ -0,0 +1,26 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package stdin represents the imported interface "wasi:cli/[email protected]".
+package stdin
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/io/v0.2.0/streams"
+)
+
+// GetStdin represents the imported function "get-stdin".
+//
+// get-stdin: func() -> input-stream
+//
+//go:nosplit
+func GetStdin() (result streams.InputStream) {
+ result0 := wasmimport_GetStdin()
+ result = cm.Reinterpret[streams.InputStream]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-stdin
+//go:noescape
+func wasmimport_GetStdin() (result0 uint32)
diff --git a/src/internal/wasi/cli/v0.2.0/stdout/empty.s b/src/internal/wasi/cli/v0.2.0/stdout/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/stdout/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/stdout/stdout.wit.go b/src/internal/wasi/cli/v0.2.0/stdout/stdout.wit.go
new file mode 100644
index 000000000..2f56b19ec
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/stdout/stdout.wit.go
@@ -0,0 +1,26 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package stdout represents the imported interface "wasi:cli/[email protected]".
+package stdout
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/io/v0.2.0/streams"
+)
+
+// GetStdout represents the imported function "get-stdout".
+//
+// get-stdout: func() -> output-stream
+//
+//go:nosplit
+func GetStdout() (result streams.OutputStream) {
+ result0 := wasmimport_GetStdout()
+ result = cm.Reinterpret[streams.OutputStream]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-stdout
+//go:noescape
+func wasmimport_GetStdout() (result0 uint32)
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-input/empty.s b/src/internal/wasi/cli/v0.2.0/terminal-input/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-input/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-input/terminal-input.wit.go b/src/internal/wasi/cli/v0.2.0/terminal-input/terminal-input.wit.go
new file mode 100644
index 000000000..478864428
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-input/terminal-input.wit.go
@@ -0,0 +1,38 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package terminalinput represents the imported interface "wasi:cli/[email protected]".
+//
+// Terminal input.
+//
+// In the future, this may include functions for disabling echoing,
+// disabling input buffering so that keyboard events are sent through
+// immediately, querying supported features, and so on.
+package terminalinput
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// TerminalInput represents the imported resource "wasi:cli/[email protected]#terminal-input".
+//
+// The input side of a terminal.
+//
+// resource terminal-input
+type TerminalInput cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "terminal-input".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self TerminalInput) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TerminalInputResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] [resource-drop]terminal-input
+//go:noescape
+func wasmimport_TerminalInputResourceDrop(self0 uint32)
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-output/empty.s b/src/internal/wasi/cli/v0.2.0/terminal-output/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-output/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-output/terminal-output.wit.go b/src/internal/wasi/cli/v0.2.0/terminal-output/terminal-output.wit.go
new file mode 100644
index 000000000..759348b85
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-output/terminal-output.wit.go
@@ -0,0 +1,38 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package terminaloutput represents the imported interface "wasi:cli/[email protected]".
+//
+// Terminal output.
+//
+// In the future, this may include functions for querying the terminal
+// size, being notified of terminal size changes, querying supported
+// features, and so on.
+package terminaloutput
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// TerminalOutput represents the imported resource "wasi:cli/[email protected]#terminal-output".
+//
+// The output side of a terminal.
+//
+// resource terminal-output
+type TerminalOutput cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "terminal-output".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self TerminalOutput) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TerminalOutputResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] [resource-drop]terminal-output
+//go:noescape
+func wasmimport_TerminalOutputResourceDrop(self0 uint32)
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-stderr/empty.s b/src/internal/wasi/cli/v0.2.0/terminal-stderr/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-stderr/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-stderr/terminal-stderr.wit.go b/src/internal/wasi/cli/v0.2.0/terminal-stderr/terminal-stderr.wit.go
new file mode 100644
index 000000000..db720e151
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-stderr/terminal-stderr.wit.go
@@ -0,0 +1,31 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package terminalstderr represents the imported interface "wasi:cli/[email protected]".
+//
+// An interface providing an optional `terminal-output` for stderr as a
+// link-time authority.
+package terminalstderr
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
+)
+
+// GetTerminalStderr represents the imported function "get-terminal-stderr".
+//
+// If stderr is connected to a terminal, return a `terminal-output` handle
+// allowing further interaction with it.
+//
+// get-terminal-stderr: func() -> option<terminal-output>
+//
+//go:nosplit
+func GetTerminalStderr() (result cm.Option[terminaloutput.TerminalOutput]) {
+ wasmimport_GetTerminalStderr(&result)
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-terminal-stderr
+//go:noescape
+func wasmimport_GetTerminalStderr(result *cm.Option[terminaloutput.TerminalOutput])
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-stdin/empty.s b/src/internal/wasi/cli/v0.2.0/terminal-stdin/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-stdin/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-stdin/terminal-stdin.wit.go b/src/internal/wasi/cli/v0.2.0/terminal-stdin/terminal-stdin.wit.go
new file mode 100644
index 000000000..091d8e8c5
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-stdin/terminal-stdin.wit.go
@@ -0,0 +1,31 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package terminalstdin represents the imported interface "wasi:cli/[email protected]".
+//
+// An interface providing an optional `terminal-input` for stdin as a
+// link-time authority.
+package terminalstdin
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ terminalinput "internal/wasi/cli/v0.2.0/terminal-input"
+)
+
+// GetTerminalStdin represents the imported function "get-terminal-stdin".
+//
+// If stdin is connected to a terminal, return a `terminal-input` handle
+// allowing further interaction with it.
+//
+// get-terminal-stdin: func() -> option<terminal-input>
+//
+//go:nosplit
+func GetTerminalStdin() (result cm.Option[terminalinput.TerminalInput]) {
+ wasmimport_GetTerminalStdin(&result)
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-terminal-stdin
+//go:noescape
+func wasmimport_GetTerminalStdin(result *cm.Option[terminalinput.TerminalInput])
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-stdout/empty.s b/src/internal/wasi/cli/v0.2.0/terminal-stdout/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-stdout/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/cli/v0.2.0/terminal-stdout/terminal-stdout.wit.go b/src/internal/wasi/cli/v0.2.0/terminal-stdout/terminal-stdout.wit.go
new file mode 100644
index 000000000..d0d9bfe48
--- /dev/null
+++ b/src/internal/wasi/cli/v0.2.0/terminal-stdout/terminal-stdout.wit.go
@@ -0,0 +1,31 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package terminalstdout represents the imported interface "wasi:cli/[email protected]".
+//
+// An interface providing an optional `terminal-output` for stdout as a
+// link-time authority.
+package terminalstdout
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ terminaloutput "internal/wasi/cli/v0.2.0/terminal-output"
+)
+
+// GetTerminalStdout represents the imported function "get-terminal-stdout".
+//
+// If stdout is connected to a terminal, return a `terminal-output` handle
+// allowing further interaction with it.
+//
+// get-terminal-stdout: func() -> option<terminal-output>
+//
+//go:nosplit
+func GetTerminalStdout() (result cm.Option[terminaloutput.TerminalOutput]) {
+ wasmimport_GetTerminalStdout(&result)
+ return
+}
+
+//go:wasmimport wasi:cli/[email protected] get-terminal-stdout
+//go:noescape
+func wasmimport_GetTerminalStdout(result *cm.Option[terminaloutput.TerminalOutput])
diff --git a/src/internal/wasi/clocks/v0.2.0/monotonic-clock/empty.s b/src/internal/wasi/clocks/v0.2.0/monotonic-clock/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/clocks/v0.2.0/monotonic-clock/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/clocks/v0.2.0/monotonic-clock/monotonic-clock.wit.go b/src/internal/wasi/clocks/v0.2.0/monotonic-clock/monotonic-clock.wit.go
new file mode 100644
index 000000000..4c6803365
--- /dev/null
+++ b/src/internal/wasi/clocks/v0.2.0/monotonic-clock/monotonic-clock.wit.go
@@ -0,0 +1,115 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package monotonicclock represents the imported interface "wasi:clocks/[email protected]".
+//
+// WASI Monotonic Clock is a clock API intended to let users measure elapsed
+// time.
+//
+// It is intended to be portable at least between Unix-family platforms and
+// Windows.
+//
+// A monotonic clock is a clock which has an unspecified initial value, and
+// successive reads of the clock will produce non-decreasing values.
+//
+// It is intended for measuring elapsed time.
+package monotonicclock
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/io/v0.2.0/poll"
+)
+
+// Instant represents the u64 "wasi:clocks/[email protected]#instant".
+//
+// An instant in time, in nanoseconds. An instant is relative to an
+// unspecified initial value, and can only be compared to instances from
+// the same monotonic-clock.
+//
+// type instant = u64
+type Instant uint64
+
+// Duration represents the u64 "wasi:clocks/[email protected]#duration".
+//
+// A duration of time, in nanoseconds.
+//
+// type duration = u64
+type Duration uint64
+
+// Now represents the imported function "now".
+//
+// Read the current value of the clock.
+//
+// The clock is monotonic, therefore calling this function repeatedly will
+// produce a sequence of non-decreasing values.
+//
+// now: func() -> instant
+//
+//go:nosplit
+func Now() (result Instant) {
+ result0 := wasmimport_Now()
+ result = (Instant)((uint64)(result0))
+ return
+}
+
+//go:wasmimport wasi:clocks/[email protected] now
+//go:noescape
+func wasmimport_Now() (result0 uint64)
+
+// Resolution represents the imported function "resolution".
+//
+// Query the resolution of the clock. Returns the duration of time
+// corresponding to a clock tick.
+//
+// resolution: func() -> duration
+//
+//go:nosplit
+func Resolution() (result Duration) {
+ result0 := wasmimport_Resolution()
+ result = (Duration)((uint64)(result0))
+ return
+}
+
+//go:wasmimport wasi:clocks/[email protected] resolution
+//go:noescape
+func wasmimport_Resolution() (result0 uint64)
+
+// SubscribeInstant represents the imported function "subscribe-instant".
+//
+// Create a `pollable` which will resolve once the specified instant
+// occured.
+//
+// subscribe-instant: func(when: instant) -> pollable
+//
+//go:nosplit
+func SubscribeInstant(when Instant) (result poll.Pollable) {
+ when0 := (uint64)(when)
+ result0 := wasmimport_SubscribeInstant((uint64)(when0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:clocks/[email protected] subscribe-instant
+//go:noescape
+func wasmimport_SubscribeInstant(when0 uint64) (result0 uint32)
+
+// SubscribeDuration represents the imported function "subscribe-duration".
+//
+// Create a `pollable` which will resolve once the given duration has
+// elapsed, starting at the time at which this function was called.
+// occured.
+//
+// subscribe-duration: func(when: duration) -> pollable
+//
+//go:nosplit
+func SubscribeDuration(when Duration) (result poll.Pollable) {
+ when0 := (uint64)(when)
+ result0 := wasmimport_SubscribeDuration((uint64)(when0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:clocks/[email protected] subscribe-duration
+//go:noescape
+func wasmimport_SubscribeDuration(when0 uint64) (result0 uint32)
diff --git a/src/internal/wasi/clocks/v0.2.0/wall-clock/empty.s b/src/internal/wasi/clocks/v0.2.0/wall-clock/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/clocks/v0.2.0/wall-clock/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/clocks/v0.2.0/wall-clock/wall-clock.wit.go b/src/internal/wasi/clocks/v0.2.0/wall-clock/wall-clock.wit.go
new file mode 100644
index 000000000..a270f78d6
--- /dev/null
+++ b/src/internal/wasi/clocks/v0.2.0/wall-clock/wall-clock.wit.go
@@ -0,0 +1,80 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package wallclock represents the imported interface "wasi:clocks/[email protected]".
+//
+// WASI Wall Clock is a clock API intended to let users query the current
+// time. The name "wall" makes an analogy to a "clock on the wall", which
+// is not necessarily monotonic as it may be reset.
+//
+// It is intended to be portable at least between Unix-family platforms and
+// Windows.
+//
+// A wall clock is a clock which measures the date and time according to
+// some external reference.
+//
+// External references may be reset, so this clock is not necessarily
+// monotonic, making it unsuitable for measuring elapsed time.
+//
+// It is intended for reporting the current date and time for humans.
+package wallclock
+
+// DateTime represents the record "wasi:clocks/[email protected]#datetime".
+//
+// A time and date in seconds plus nanoseconds.
+//
+// record datetime {
+// seconds: u64,
+// nanoseconds: u32,
+// }
+type DateTime struct {
+ Seconds uint64
+ Nanoseconds uint32
+}
+
+// Now represents the imported function "now".
+//
+// Read the current value of the clock.
+//
+// This clock is not monotonic, therefore calling this function repeatedly
+// will not necessarily produce a sequence of non-decreasing values.
+//
+// The returned timestamps represent the number of seconds since
+// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch],
+// also known as [Unix Time].
+//
+// The nanoseconds field of the output is always less than 1000000000.
+//
+// now: func() -> datetime
+//
+// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16
+// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time
+//
+//go:nosplit
+func Now() (result DateTime) {
+ wasmimport_Now(&result)
+ return
+}
+
+//go:wasmimport wasi:clocks/[email protected] now
+//go:noescape
+func wasmimport_Now(result *DateTime)
+
+// Resolution represents the imported function "resolution".
+//
+// Query the resolution of the clock.
+//
+// The nanoseconds field of the output is always less than 1000000000.
+//
+// resolution: func() -> datetime
+//
+//go:nosplit
+func Resolution() (result DateTime) {
+ wasmimport_Resolution(&result)
+ return
+}
+
+//go:wasmimport wasi:clocks/[email protected] resolution
+//go:noescape
+func wasmimport_Resolution(result *DateTime)
diff --git a/src/internal/wasi/filesystem/v0.2.0/preopens/empty.s b/src/internal/wasi/filesystem/v0.2.0/preopens/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/filesystem/v0.2.0/preopens/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/filesystem/v0.2.0/preopens/preopens.wit.go b/src/internal/wasi/filesystem/v0.2.0/preopens/preopens.wit.go
new file mode 100644
index 000000000..d0b71bf11
--- /dev/null
+++ b/src/internal/wasi/filesystem/v0.2.0/preopens/preopens.wit.go
@@ -0,0 +1,27 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package preopens represents the imported interface "wasi:filesystem/[email protected]".
+package preopens
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/filesystem/v0.2.0/types"
+)
+
+// GetDirectories represents the imported function "get-directories".
+//
+// Return the set of preopened directories, and their path.
+//
+// get-directories: func() -> list<tuple<descriptor, string>>
+//
+//go:nosplit
+func GetDirectories() (result cm.List[cm.Tuple[types.Descriptor, string]]) {
+ wasmimport_GetDirectories(&result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] get-directories
+//go:noescape
+func wasmimport_GetDirectories(result *cm.List[cm.Tuple[types.Descriptor, string]])
diff --git a/src/internal/wasi/filesystem/v0.2.0/types/abi.go b/src/internal/wasi/filesystem/v0.2.0/types/abi.go
new file mode 100644
index 000000000..288ef2e22
--- /dev/null
+++ b/src/internal/wasi/filesystem/v0.2.0/types/abi.go
@@ -0,0 +1,27 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+package types
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
+)
+
+func lower_DateTime(v wallclock.DateTime) (f0 uint64, f1 uint32) {
+ f0 = (uint64)(v.Seconds)
+ f1 = (uint32)(v.Nanoseconds)
+ return
+}
+
+func lower_NewTimestamp(v NewTimestamp) (f0 uint32, f1 uint64, f2 uint32) {
+ f0 = (uint32)(cm.Tag(&v))
+ switch f0 {
+ case 2: // timestamp
+ v1, v2 := lower_DateTime(*v.Timestamp())
+ f1 = (uint64)(v1)
+ f2 = (uint32)(v2)
+ }
+ return
+}
diff --git a/src/internal/wasi/filesystem/v0.2.0/types/empty.s b/src/internal/wasi/filesystem/v0.2.0/types/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/filesystem/v0.2.0/types/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/filesystem/v0.2.0/types/types.wit.go b/src/internal/wasi/filesystem/v0.2.0/types/types.wit.go
new file mode 100644
index 000000000..387426d1b
--- /dev/null
+++ b/src/internal/wasi/filesystem/v0.2.0/types/types.wit.go
@@ -0,0 +1,1284 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package types represents the imported interface "wasi:filesystem/[email protected]".
+//
+// WASI filesystem is a filesystem API primarily intended to let users run WASI
+// programs that access their files on their existing filesystems, without
+// significant overhead.
+//
+// It is intended to be roughly portable between Unix-family platforms and
+// Windows, though it does not hide many of the major differences.
+//
+// Paths are passed as interface-type `string`s, meaning they must consist of
+// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain
+// paths which are not accessible by this API.
+//
+// The directory separator in WASI is always the forward-slash (`/`).
+//
+// All paths in WASI are relative paths, and are interpreted relative to a
+// `descriptor` referring to a base directory. If a `path` argument to any WASI
+// function starts with `/`, or if any step of resolving a `path`, including
+// `..` and symbolic link steps, reaches a directory outside of the base
+// directory, or reaches a symlink to an absolute or rooted path in the
+// underlying filesystem, the function fails with `error-code::not-permitted`.
+//
+// For more information about WASI path resolution and sandboxing, see
+// [WASI filesystem path resolution].
+//
+// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md
+package types
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
+ ioerror "internal/wasi/io/v0.2.0/error"
+ "internal/wasi/io/v0.2.0/streams"
+)
+
+// FileSize represents the u64 "wasi:filesystem/[email protected]#filesize".
+//
+// File size or length of a region within a file.
+//
+// type filesize = u64
+type FileSize uint64
+
+// DescriptorType represents the enum "wasi:filesystem/[email protected]#descriptor-type".
+//
+// The type of a filesystem object referenced by a descriptor.
+//
+// Note: This was called `filetype` in earlier versions of WASI.
+//
+// enum descriptor-type {
+// unknown,
+// block-device,
+// character-device,
+// directory,
+// fifo,
+// symbolic-link,
+// regular-file,
+// socket
+// }
+type DescriptorType uint8
+
+const (
+ // The type of the descriptor or file is unknown or is different from
+ // any of the other types specified.
+ DescriptorTypeUnknown DescriptorType = iota
+
+ // The descriptor refers to a block device inode.
+ DescriptorTypeBlockDevice
+
+ // The descriptor refers to a character device inode.
+ DescriptorTypeCharacterDevice
+
+ // The descriptor refers to a directory inode.
+ DescriptorTypeDirectory
+
+ // The descriptor refers to a named pipe.
+ DescriptorTypeFIFO
+
+ // The file refers to a symbolic link inode.
+ DescriptorTypeSymbolicLink
+
+ // The descriptor refers to a regular file inode.
+ DescriptorTypeRegularFile
+
+ // The descriptor refers to a socket.
+ DescriptorTypeSocket
+)
+
+// DescriptorFlags represents the flags "wasi:filesystem/[email protected]#descriptor-flags".
+//
+// Descriptor flags.
+//
+// Note: This was called `fdflags` in earlier versions of WASI.
+//
+// flags descriptor-flags {
+// read,
+// write,
+// file-integrity-sync,
+// data-integrity-sync,
+// requested-write-sync,
+// mutate-directory,
+// }
+type DescriptorFlags uint8
+
+const (
+ // Read mode: Data can be read.
+ DescriptorFlagsRead DescriptorFlags = 1 << iota
+
+ // Write mode: Data can be written to.
+ DescriptorFlagsWrite
+
+ // Request that writes be performed according to synchronized I/O file
+ // integrity completion. The data stored in the file and the file's
+ // metadata are synchronized. This is similar to `O_SYNC` in POSIX.
+ //
+ // The precise semantics of this operation have not yet been defined for
+ // WASI. At this time, it should be interpreted as a request, and not a
+ // requirement.
+ DescriptorFlagsFileIntegritySync
+
+ // Request that writes be performed according to synchronized I/O data
+ // integrity completion. Only the data stored in the file is
+ // synchronized. This is similar to `O_DSYNC` in POSIX.
+ //
+ // The precise semantics of this operation have not yet been defined for
+ // WASI. At this time, it should be interpreted as a request, and not a
+ // requirement.
+ DescriptorFlagsDataIntegritySync
+
+ // Requests that reads be performed at the same level of integrety
+ // requested for writes. This is similar to `O_RSYNC` in POSIX.
+ //
+ // The precise semantics of this operation have not yet been defined for
+ // WASI. At this time, it should be interpreted as a request, and not a
+ // requirement.
+ DescriptorFlagsRequestedWriteSync
+
+ // Mutating directories mode: Directory contents may be mutated.
+ //
+ // When this flag is unset on a descriptor, operations using the
+ // descriptor which would create, rename, delete, modify the data or
+ // metadata of filesystem objects, or obtain another handle which
+ // would permit any of those, shall fail with `error-code::read-only` if
+ // they would otherwise succeed.
+ //
+ // This may only be set on directories.
+ DescriptorFlagsMutateDirectory
+)
+
+// PathFlags represents the flags "wasi:filesystem/[email protected]#path-flags".
+//
+// Flags determining the method of how paths are resolved.
+//
+// flags path-flags {
+// symlink-follow,
+// }
+type PathFlags uint8
+
+const (
+ // As long as the resolved path corresponds to a symbolic link, it is
+ // expanded.
+ PathFlagsSymlinkFollow PathFlags = 1 << iota
+)
+
+// OpenFlags represents the flags "wasi:filesystem/[email protected]#open-flags".
+//
+// Open flags used by `open-at`.
+//
+// flags open-flags {
+// create,
+// directory,
+// exclusive,
+// truncate,
+// }
+type OpenFlags uint8
+
+const (
+ // Create file if it does not exist, similar to `O_CREAT` in POSIX.
+ OpenFlagsCreate OpenFlags = 1 << iota
+
+ // Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
+ OpenFlagsDirectory
+
+ // Fail if file already exists, similar to `O_EXCL` in POSIX.
+ OpenFlagsExclusive
+
+ // Truncate file to size 0, similar to `O_TRUNC` in POSIX.
+ OpenFlagsTruncate
+)
+
+// LinkCount represents the u64 "wasi:filesystem/[email protected]#link-count".
+//
+// Number of hard links to an inode.
+//
+// type link-count = u64
+type LinkCount uint64
+
+// DescriptorStat represents the record "wasi:filesystem/[email protected]#descriptor-stat".
+//
+// File attributes.
+//
+// Note: This was called `filestat` in earlier versions of WASI.
+//
+// record descriptor-stat {
+// %type: descriptor-type,
+// link-count: link-count,
+// size: filesize,
+// data-access-timestamp: option<datetime>,
+// data-modification-timestamp: option<datetime>,
+// status-change-timestamp: option<datetime>,
+// }
+type DescriptorStat struct {
+ // File type.
+ Type DescriptorType
+
+ // Number of hard links to the file.
+ LinkCount LinkCount
+
+ // For regular files, the file size in bytes. For symbolic links, the
+ // length in bytes of the pathname contained in the symbolic link.
+ Size FileSize
+
+ // Last data access timestamp.
+ //
+ // If the `option` is none, the platform doesn't maintain an access
+ // timestamp for this file.
+ DataAccessTimestamp cm.Option[wallclock.DateTime]
+
+ // Last data modification timestamp.
+ //
+ // If the `option` is none, the platform doesn't maintain a
+ // modification timestamp for this file.
+ DataModificationTimestamp cm.Option[wallclock.DateTime]
+
+ // Last file status-change timestamp.
+ //
+ // If the `option` is none, the platform doesn't maintain a
+ // status-change timestamp for this file.
+ StatusChangeTimestamp cm.Option[wallclock.DateTime]
+}
+
+// NewTimestamp represents the variant "wasi:filesystem/[email protected]#new-timestamp".
+//
+// When setting a timestamp, this gives the value to set it to.
+//
+// variant new-timestamp {
+// no-change,
+// now,
+// timestamp(datetime),
+// }
+type NewTimestamp cm.Variant[uint8, wallclock.DateTime, wallclock.DateTime]
+
+// NewTimestampNoChange returns a [NewTimestamp] of case "no-change".
+//
+// Leave the timestamp set to its previous value.
+func NewTimestampNoChange() NewTimestamp {
+ var data struct{}
+ return cm.New[NewTimestamp](0, data)
+}
+
+// NoChange returns true if [NewTimestamp] represents the variant case "no-change".
+func (self *NewTimestamp) NoChange() bool {
+ return cm.Tag(self) == 0
+}
+
+// NewTimestampNow returns a [NewTimestamp] of case "now".
+//
+// Set the timestamp to the current time of the system clock associated
+// with the filesystem.
+func NewTimestampNow() NewTimestamp {
+ var data struct{}
+ return cm.New[NewTimestamp](1, data)
+}
+
+// Now returns true if [NewTimestamp] represents the variant case "now".
+func (self *NewTimestamp) Now() bool {
+ return cm.Tag(self) == 1
+}
+
+// NewTimestampTimestamp returns a [NewTimestamp] of case "timestamp".
+//
+// Set the timestamp to the given value.
+func NewTimestampTimestamp(data wallclock.DateTime) NewTimestamp {
+ return cm.New[NewTimestamp](2, data)
+}
+
+// Timestamp returns a non-nil *[wallclock.DateTime] if [NewTimestamp] represents the variant case "timestamp".
+func (self *NewTimestamp) Timestamp() *wallclock.DateTime {
+ return cm.Case[wallclock.DateTime](self, 2)
+}
+
+// DirectoryEntry represents the record "wasi:filesystem/[email protected]#directory-entry".
+//
+// A directory entry.
+//
+// record directory-entry {
+// %type: descriptor-type,
+// name: string,
+// }
+type DirectoryEntry struct {
+ // The type of the file referred to by this directory entry.
+ Type DescriptorType
+
+ // The name of the object.
+ Name string
+}
+
+// ErrorCode represents the enum "wasi:filesystem/[email protected]#error-code".
+//
+// Error codes returned by functions, similar to `errno` in POSIX.
+// Not all of these error codes are returned by the functions provided by this
+// API; some are used in higher-level library layers, and others are provided
+// merely for alignment with POSIX.
+//
+// enum error-code {
+// access,
+// would-block,
+// already,
+// bad-descriptor,
+// busy,
+// deadlock,
+// quota,
+// exist,
+// file-too-large,
+// illegal-byte-sequence,
+// in-progress,
+// interrupted,
+// invalid,
+// io,
+// is-directory,
+// loop,
+// too-many-links,
+// message-size,
+// name-too-long,
+// no-device,
+// no-entry,
+// no-lock,
+// insufficient-memory,
+// insufficient-space,
+// not-directory,
+// not-empty,
+// not-recoverable,
+// unsupported,
+// no-tty,
+// no-such-device,
+// overflow,
+// not-permitted,
+// pipe,
+// read-only,
+// invalid-seek,
+// text-file-busy,
+// cross-device
+// }
+type ErrorCode uint8
+
+const (
+ // Permission denied, similar to `EACCES` in POSIX.
+ ErrorCodeAccess ErrorCode = iota
+
+ // Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK`
+ // in POSIX.
+ ErrorCodeWouldBlock
+
+ // Connection already in progress, similar to `EALREADY` in POSIX.
+ ErrorCodeAlready
+
+ // Bad descriptor, similar to `EBADF` in POSIX.
+ ErrorCodeBadDescriptor
+
+ // Device or resource busy, similar to `EBUSY` in POSIX.
+ ErrorCodeBusy
+
+ // Resource deadlock would occur, similar to `EDEADLK` in POSIX.
+ ErrorCodeDeadlock
+
+ // Storage quota exceeded, similar to `EDQUOT` in POSIX.
+ ErrorCodeQuota
+
+ // File exists, similar to `EEXIST` in POSIX.
+ ErrorCodeExist
+
+ // File too large, similar to `EFBIG` in POSIX.
+ ErrorCodeFileTooLarge
+
+ // Illegal byte sequence, similar to `EILSEQ` in POSIX.
+ ErrorCodeIllegalByteSequence
+
+ // Operation in progress, similar to `EINPROGRESS` in POSIX.
+ ErrorCodeInProgress
+
+ // Interrupted function, similar to `EINTR` in POSIX.
+ ErrorCodeInterrupted
+
+ // Invalid argument, similar to `EINVAL` in POSIX.
+ ErrorCodeInvalid
+
+ // I/O error, similar to `EIO` in POSIX.
+ ErrorCodeIO
+
+ // Is a directory, similar to `EISDIR` in POSIX.
+ ErrorCodeIsDirectory
+
+ // Too many levels of symbolic links, similar to `ELOOP` in POSIX.
+ ErrorCodeLoop
+
+ // Too many links, similar to `EMLINK` in POSIX.
+ ErrorCodeTooManyLinks
+
+ // Message too large, similar to `EMSGSIZE` in POSIX.
+ ErrorCodeMessageSize
+
+ // Filename too long, similar to `ENAMETOOLONG` in POSIX.
+ ErrorCodeNameTooLong
+
+ // No such device, similar to `ENODEV` in POSIX.
+ ErrorCodeNoDevice
+
+ // No such file or directory, similar to `ENOENT` in POSIX.
+ ErrorCodeNoEntry
+
+ // No locks available, similar to `ENOLCK` in POSIX.
+ ErrorCodeNoLock
+
+ // Not enough space, similar to `ENOMEM` in POSIX.
+ ErrorCodeInsufficientMemory
+
+ // No space left on device, similar to `ENOSPC` in POSIX.
+ ErrorCodeInsufficientSpace
+
+ // Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX.
+ ErrorCodeNotDirectory
+
+ // Directory not empty, similar to `ENOTEMPTY` in POSIX.
+ ErrorCodeNotEmpty
+
+ // State not recoverable, similar to `ENOTRECOVERABLE` in POSIX.
+ ErrorCodeNotRecoverable
+
+ // Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX.
+ ErrorCodeUnsupported
+
+ // Inappropriate I/O control operation, similar to `ENOTTY` in POSIX.
+ ErrorCodeNoTTY
+
+ // No such device or address, similar to `ENXIO` in POSIX.
+ ErrorCodeNoSuchDevice
+
+ // Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX.
+ ErrorCodeOverflow
+
+ // Operation not permitted, similar to `EPERM` in POSIX.
+ ErrorCodeNotPermitted
+
+ // Broken pipe, similar to `EPIPE` in POSIX.
+ ErrorCodePipe
+
+ // Read-only file system, similar to `EROFS` in POSIX.
+ ErrorCodeReadOnly
+
+ // Invalid seek, similar to `ESPIPE` in POSIX.
+ ErrorCodeInvalidSeek
+
+ // Text file busy, similar to `ETXTBSY` in POSIX.
+ ErrorCodeTextFileBusy
+
+ // Cross-device link, similar to `EXDEV` in POSIX.
+ ErrorCodeCrossDevice
+)
+
+// Advice represents the enum "wasi:filesystem/[email protected]#advice".
+//
+// File or memory access pattern advisory information.
+//
+// enum advice {
+// normal,
+// sequential,
+// random,
+// will-need,
+// dont-need,
+// no-reuse
+// }
+type Advice uint8
+
+const (
+ // The application has no advice to give on its behavior with respect
+ // to the specified data.
+ AdviceNormal Advice = iota
+
+ // The application expects to access the specified data sequentially
+ // from lower offsets to higher offsets.
+ AdviceSequential
+
+ // The application expects to access the specified data in a random
+ // order.
+ AdviceRandom
+
+ // The application expects to access the specified data in the near
+ // future.
+ AdviceWillNeed
+
+ // The application expects that it will not access the specified data
+ // in the near future.
+ AdviceDontNeed
+
+ // The application expects to access the specified data once and then
+ // not reuse it thereafter.
+ AdviceNoReuse
+)
+
+// MetadataHashValue represents the record "wasi:filesystem/[email protected]#metadata-hash-value".
+//
+// A 128-bit hash value, split into parts because wasm doesn't have a
+// 128-bit integer type.
+//
+// record metadata-hash-value {
+// lower: u64,
+// upper: u64,
+// }
+type MetadataHashValue struct {
+ // 64 bits of a 128-bit hash value.
+ Lower uint64
+
+ // Another 64 bits of a 128-bit hash value.
+ Upper uint64
+}
+
+// Descriptor represents the imported resource "wasi:filesystem/[email protected]#descriptor".
+//
+// A descriptor is a reference to a filesystem object, which may be a file,
+// directory, named pipe, special file, or other object on which filesystem
+// calls may be made.
+//
+// resource descriptor
+type Descriptor cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "descriptor".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self Descriptor) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [resource-drop]descriptor
+//go:noescape
+func wasmimport_DescriptorResourceDrop(self0 uint32)
+
+// Advise represents the imported method "advise".
+//
+// Provide file advisory information on a descriptor.
+//
+// This is similar to `posix_fadvise` in POSIX.
+//
+// advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) Advise(offset FileSize, length FileSize, advice Advice) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ offset0 := (uint64)(offset)
+ length0 := (uint64)(length)
+ advice0 := (uint32)(advice)
+ wasmimport_DescriptorAdvise((uint32)(self0), (uint64)(offset0), (uint64)(length0), (uint32)(advice0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.advise
+//go:noescape
+func wasmimport_DescriptorAdvise(self0 uint32, offset0 uint64, length0 uint64, advice0 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// AppendViaStream represents the imported method "append-via-stream".
+//
+// Return a stream for appending to a file, if available.
+//
+// May fail with an error-code describing why the file cannot be appended.
+//
+// Note: This allows using `write-stream`, which is similar to `write` with
+// `O_APPEND` in in POSIX.
+//
+// append-via-stream: func() -> result<output-stream, error-code>
+//
+//go:nosplit
+func (self Descriptor) AppendViaStream() (result cm.OKResult[streams.OutputStream, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorAppendViaStream((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.append-via-stream
+//go:noescape
+func wasmimport_DescriptorAppendViaStream(self0 uint32, result *cm.OKResult[streams.OutputStream, ErrorCode])
+
+// CreateDirectoryAt represents the imported method "create-directory-at".
+//
+// Create a directory.
+//
+// Note: This is similar to `mkdirat` in POSIX.
+//
+// create-directory-at: func(path: string) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) CreateDirectoryAt(path string) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ path0, path1 := cm.LowerString(path)
+ wasmimport_DescriptorCreateDirectoryAt((uint32)(self0), (*uint8)(path0), (uint32)(path1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.create-directory-at
+//go:noescape
+func wasmimport_DescriptorCreateDirectoryAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// GetFlags represents the imported method "get-flags".
+//
+// Get flags associated with a descriptor.
+//
+// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX.
+//
+// Note: This returns the value that was the `fs_flags` value returned
+// from `fdstat_get` in earlier versions of WASI.
+//
+// get-flags: func() -> result<descriptor-flags, error-code>
+//
+//go:nosplit
+func (self Descriptor) GetFlags() (result cm.OKResult[DescriptorFlags, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorGetFlags((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.get-flags
+//go:noescape
+func wasmimport_DescriptorGetFlags(self0 uint32, result *cm.OKResult[DescriptorFlags, ErrorCode])
+
+// GetType represents the imported method "get-type".
+//
+// Get the dynamic type of a descriptor.
+//
+// Note: This returns the same value as the `type` field of the `fd-stat`
+// returned by `stat`, `stat-at` and similar.
+//
+// Note: This returns similar flags to the `st_mode & S_IFMT` value provided
+// by `fstat` in POSIX.
+//
+// Note: This returns the value that was the `fs_filetype` value returned
+// from `fdstat_get` in earlier versions of WASI.
+//
+// get-type: func() -> result<descriptor-type, error-code>
+//
+//go:nosplit
+func (self Descriptor) GetType() (result cm.OKResult[DescriptorType, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorGetType((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.get-type
+//go:noescape
+func wasmimport_DescriptorGetType(self0 uint32, result *cm.OKResult[DescriptorType, ErrorCode])
+
+// IsSameObject represents the imported method "is-same-object".
+//
+// Test whether two descriptors refer to the same filesystem object.
+//
+// In POSIX, this corresponds to testing whether the two descriptors have the
+// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers.
+// wasi-filesystem does not expose device and inode numbers, so this function
+// may be used instead.
+//
+// is-same-object: func(other: borrow<descriptor>) -> bool
+//
+//go:nosplit
+func (self Descriptor) IsSameObject(other Descriptor) (result bool) {
+ self0 := cm.Reinterpret[uint32](self)
+ other0 := cm.Reinterpret[uint32](other)
+ result0 := wasmimport_DescriptorIsSameObject((uint32)(self0), (uint32)(other0))
+ result = cm.U32ToBool((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.is-same-object
+//go:noescape
+func wasmimport_DescriptorIsSameObject(self0 uint32, other0 uint32) (result0 uint32)
+
+// LinkAt represents the imported method "link-at".
+//
+// Create a hard link.
+//
+// Note: This is similar to `linkat` in POSIX.
+//
+// link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow<descriptor>,
+// new-path: string) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) LinkAt(oldPathFlags PathFlags, oldPath string, newDescriptor Descriptor, newPath string) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ oldPathFlags0 := (uint32)(oldPathFlags)
+ oldPath0, oldPath1 := cm.LowerString(oldPath)
+ newDescriptor0 := cm.Reinterpret[uint32](newDescriptor)
+ newPath0, newPath1 := cm.LowerString(newPath)
+ wasmimport_DescriptorLinkAt((uint32)(self0), (uint32)(oldPathFlags0), (*uint8)(oldPath0), (uint32)(oldPath1), (uint32)(newDescriptor0), (*uint8)(newPath0), (uint32)(newPath1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.link-at
+//go:noescape
+func wasmimport_DescriptorLinkAt(self0 uint32, oldPathFlags0 uint32, oldPath0 *uint8, oldPath1 uint32, newDescriptor0 uint32, newPath0 *uint8, newPath1 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// MetadataHash represents the imported method "metadata-hash".
+//
+// Return a hash of the metadata associated with a filesystem object referred
+// to by a descriptor.
+//
+// This returns a hash of the last-modification timestamp and file size, and
+// may also include the inode number, device number, birth timestamp, and
+// other metadata fields that may change when the file is modified or
+// replaced. It may also include a secret value chosen by the
+// implementation and not otherwise exposed.
+//
+// Implementations are encourated to provide the following properties:
+//
+// - If the file is not modified or replaced, the computed hash value should
+// usually not change.
+// - If the object is modified or replaced, the computed hash value should
+// usually change.
+// - The inputs to the hash should not be easily computable from the
+// computed hash.
+//
+// However, none of these is required.
+//
+// metadata-hash: func() -> result<metadata-hash-value, error-code>
+//
+//go:nosplit
+func (self Descriptor) MetadataHash() (result cm.OKResult[MetadataHashValue, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorMetadataHash((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.metadata-hash
+//go:noescape
+func wasmimport_DescriptorMetadataHash(self0 uint32, result *cm.OKResult[MetadataHashValue, ErrorCode])
+
+// MetadataHashAt represents the imported method "metadata-hash-at".
+//
+// Return a hash of the metadata associated with a filesystem object referred
+// to by a directory descriptor and a relative path.
+//
+// This performs the same hash computation as `metadata-hash`.
+//
+// metadata-hash-at: func(path-flags: path-flags, path: string) -> result<metadata-hash-value,
+// error-code>
+//
+//go:nosplit
+func (self Descriptor) MetadataHashAt(pathFlags PathFlags, path string) (result cm.OKResult[MetadataHashValue, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ pathFlags0 := (uint32)(pathFlags)
+ path0, path1 := cm.LowerString(path)
+ wasmimport_DescriptorMetadataHashAt((uint32)(self0), (uint32)(pathFlags0), (*uint8)(path0), (uint32)(path1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.metadata-hash-at
+//go:noescape
+func wasmimport_DescriptorMetadataHashAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, result *cm.OKResult[MetadataHashValue, ErrorCode])
+
+// OpenAt represents the imported method "open-at".
+//
+// Open a file or directory.
+//
+// The returned descriptor is not guaranteed to be the lowest-numbered
+// descriptor not currently open/ it is randomized to prevent applications
+// from depending on making assumptions about indexes, since this is
+// error-prone in multi-threaded contexts. The returned descriptor is
+// guaranteed to be less than 2**31.
+//
+// If `flags` contains `descriptor-flags::mutate-directory`, and the base
+// descriptor doesn't have `descriptor-flags::mutate-directory` set,
+// `open-at` fails with `error-code::read-only`.
+//
+// If `flags` contains `write` or `mutate-directory`, or `open-flags`
+// contains `truncate` or `create`, and the base descriptor doesn't have
+// `descriptor-flags::mutate-directory` set, `open-at` fails with
+// `error-code::read-only`.
+//
+// Note: This is similar to `openat` in POSIX.
+//
+// open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags:
+// descriptor-flags) -> result<descriptor, error-code>
+//
+//go:nosplit
+func (self Descriptor) OpenAt(pathFlags PathFlags, path string, openFlags OpenFlags, flags DescriptorFlags) (result cm.OKResult[Descriptor, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ pathFlags0 := (uint32)(pathFlags)
+ path0, path1 := cm.LowerString(path)
+ openFlags0 := (uint32)(openFlags)
+ flags0 := (uint32)(flags)
+ wasmimport_DescriptorOpenAt((uint32)(self0), (uint32)(pathFlags0), (*uint8)(path0), (uint32)(path1), (uint32)(openFlags0), (uint32)(flags0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.open-at
+//go:noescape
+func wasmimport_DescriptorOpenAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, openFlags0 uint32, flags0 uint32, result *cm.OKResult[Descriptor, ErrorCode])
+
+// Read represents the imported method "read".
+//
+// Read from a descriptor, without using and updating the descriptor's offset.
+//
+// This function returns a list of bytes containing the data that was
+// read, along with a bool which, when true, indicates that the end of the
+// file was reached. The returned list will contain up to `length` bytes; it
+// may return fewer than requested, if the end of the file is reached or
+// if the I/O operation is interrupted.
+//
+// In the future, this may change to return a `stream<u8, error-code>`.
+//
+// Note: This is similar to `pread` in POSIX.
+//
+// read: func(length: filesize, offset: filesize) -> result<tuple<list<u8>, bool>,
+// error-code>
+//
+//go:nosplit
+func (self Descriptor) Read(length FileSize, offset FileSize) (result cm.OKResult[cm.Tuple[cm.List[uint8], bool], ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ length0 := (uint64)(length)
+ offset0 := (uint64)(offset)
+ wasmimport_DescriptorRead((uint32)(self0), (uint64)(length0), (uint64)(offset0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.read
+//go:noescape
+func wasmimport_DescriptorRead(self0 uint32, length0 uint64, offset0 uint64, result *cm.OKResult[cm.Tuple[cm.List[uint8], bool], ErrorCode])
+
+// ReadDirectory represents the imported method "read-directory".
+//
+// Read directory entries from a directory.
+//
+// On filesystems where directories contain entries referring to themselves
+// and their parents, often named `.` and `..` respectively, these entries
+// are omitted.
+//
+// This always returns a new stream which starts at the beginning of the
+// directory. Multiple streams may be active on the same directory, and they
+// do not interfere with each other.
+//
+// read-directory: func() -> result<directory-entry-stream, error-code>
+//
+//go:nosplit
+func (self Descriptor) ReadDirectory() (result cm.OKResult[DirectoryEntryStream, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorReadDirectory((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.read-directory
+//go:noescape
+func wasmimport_DescriptorReadDirectory(self0 uint32, result *cm.OKResult[DirectoryEntryStream, ErrorCode])
+
+// ReadViaStream represents the imported method "read-via-stream".
+//
+// Return a stream for reading from a file, if available.
+//
+// May fail with an error-code describing why the file cannot be read.
+//
+// Multiple read, write, and append streams may be active on the same open
+// file and they do not interfere with each other.
+//
+// Note: This allows using `read-stream`, which is similar to `read` in POSIX.
+//
+// read-via-stream: func(offset: filesize) -> result<input-stream, error-code>
+//
+//go:nosplit
+func (self Descriptor) ReadViaStream(offset FileSize) (result cm.OKResult[streams.InputStream, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ offset0 := (uint64)(offset)
+ wasmimport_DescriptorReadViaStream((uint32)(self0), (uint64)(offset0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.read-via-stream
+//go:noescape
+func wasmimport_DescriptorReadViaStream(self0 uint32, offset0 uint64, result *cm.OKResult[streams.InputStream, ErrorCode])
+
+// ReadLinkAt represents the imported method "readlink-at".
+//
+// Read the contents of a symbolic link.
+//
+// If the contents contain an absolute or rooted path in the underlying
+// filesystem, this function fails with `error-code::not-permitted`.
+//
+// Note: This is similar to `readlinkat` in POSIX.
+//
+// readlink-at: func(path: string) -> result<string, error-code>
+//
+//go:nosplit
+func (self Descriptor) ReadLinkAt(path string) (result cm.OKResult[string, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ path0, path1 := cm.LowerString(path)
+ wasmimport_DescriptorReadLinkAt((uint32)(self0), (*uint8)(path0), (uint32)(path1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.readlink-at
+//go:noescape
+func wasmimport_DescriptorReadLinkAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.OKResult[string, ErrorCode])
+
+// RemoveDirectoryAt represents the imported method "remove-directory-at".
+//
+// Remove a directory.
+//
+// Return `error-code::not-empty` if the directory is not empty.
+//
+// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
+//
+// remove-directory-at: func(path: string) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) RemoveDirectoryAt(path string) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ path0, path1 := cm.LowerString(path)
+ wasmimport_DescriptorRemoveDirectoryAt((uint32)(self0), (*uint8)(path0), (uint32)(path1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.remove-directory-at
+//go:noescape
+func wasmimport_DescriptorRemoveDirectoryAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// RenameAt represents the imported method "rename-at".
+//
+// Rename a filesystem object.
+//
+// Note: This is similar to `renameat` in POSIX.
+//
+// rename-at: func(old-path: string, new-descriptor: borrow<descriptor>, new-path:
+// string) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) RenameAt(oldPath string, newDescriptor Descriptor, newPath string) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ oldPath0, oldPath1 := cm.LowerString(oldPath)
+ newDescriptor0 := cm.Reinterpret[uint32](newDescriptor)
+ newPath0, newPath1 := cm.LowerString(newPath)
+ wasmimport_DescriptorRenameAt((uint32)(self0), (*uint8)(oldPath0), (uint32)(oldPath1), (uint32)(newDescriptor0), (*uint8)(newPath0), (uint32)(newPath1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.rename-at
+//go:noescape
+func wasmimport_DescriptorRenameAt(self0 uint32, oldPath0 *uint8, oldPath1 uint32, newDescriptor0 uint32, newPath0 *uint8, newPath1 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// SetSize represents the imported method "set-size".
+//
+// Adjust the size of an open file. If this increases the file's size, the
+// extra bytes are filled with zeros.
+//
+// Note: This was called `fd_filestat_set_size` in earlier versions of WASI.
+//
+// set-size: func(size: filesize) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) SetSize(size FileSize) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ size0 := (uint64)(size)
+ wasmimport_DescriptorSetSize((uint32)(self0), (uint64)(size0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.set-size
+//go:noescape
+func wasmimport_DescriptorSetSize(self0 uint32, size0 uint64, result *cm.ErrResult[struct{}, ErrorCode])
+
+// SetTimes represents the imported method "set-times".
+//
+// Adjust the timestamps of an open file or directory.
+//
+// Note: This is similar to `futimens` in POSIX.
+//
+// Note: This was called `fd_filestat_set_times` in earlier versions of WASI.
+//
+// set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp:
+// new-timestamp) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) SetTimes(dataAccessTimestamp NewTimestamp, dataModificationTimestamp NewTimestamp) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ dataAccessTimestamp0, dataAccessTimestamp1, dataAccessTimestamp2 := lower_NewTimestamp(dataAccessTimestamp)
+ dataModificationTimestamp0, dataModificationTimestamp1, dataModificationTimestamp2 := lower_NewTimestamp(dataModificationTimestamp)
+ wasmimport_DescriptorSetTimes((uint32)(self0), (uint32)(dataAccessTimestamp0), (uint64)(dataAccessTimestamp1), (uint32)(dataAccessTimestamp2), (uint32)(dataModificationTimestamp0), (uint64)(dataModificationTimestamp1), (uint32)(dataModificationTimestamp2), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.set-times
+//go:noescape
+func wasmimport_DescriptorSetTimes(self0 uint32, dataAccessTimestamp0 uint32, dataAccessTimestamp1 uint64, dataAccessTimestamp2 uint32, dataModificationTimestamp0 uint32, dataModificationTimestamp1 uint64, dataModificationTimestamp2 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// SetTimesAt represents the imported method "set-times-at".
+//
+// Adjust the timestamps of a file or directory.
+//
+// Note: This is similar to `utimensat` in POSIX.
+//
+// Note: This was called `path_filestat_set_times` in earlier versions of
+// WASI.
+//
+// set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp:
+// new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) SetTimesAt(pathFlags PathFlags, path string, dataAccessTimestamp NewTimestamp, dataModificationTimestamp NewTimestamp) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ pathFlags0 := (uint32)(pathFlags)
+ path0, path1 := cm.LowerString(path)
+ dataAccessTimestamp0, dataAccessTimestamp1, dataAccessTimestamp2 := lower_NewTimestamp(dataAccessTimestamp)
+ dataModificationTimestamp0, dataModificationTimestamp1, dataModificationTimestamp2 := lower_NewTimestamp(dataModificationTimestamp)
+ wasmimport_DescriptorSetTimesAt((uint32)(self0), (uint32)(pathFlags0), (*uint8)(path0), (uint32)(path1), (uint32)(dataAccessTimestamp0), (uint64)(dataAccessTimestamp1), (uint32)(dataAccessTimestamp2), (uint32)(dataModificationTimestamp0), (uint64)(dataModificationTimestamp1), (uint32)(dataModificationTimestamp2), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.set-times-at
+//go:noescape
+func wasmimport_DescriptorSetTimesAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, dataAccessTimestamp0 uint32, dataAccessTimestamp1 uint64, dataAccessTimestamp2 uint32, dataModificationTimestamp0 uint32, dataModificationTimestamp1 uint64, dataModificationTimestamp2 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// Stat represents the imported method "stat".
+//
+// Return the attributes of an open file or directory.
+//
+// Note: This is similar to `fstat` in POSIX, except that it does not return
+// device and inode information. For testing whether two descriptors refer to
+// the same underlying filesystem object, use `is-same-object`. To obtain
+// additional data that can be used do determine whether a file has been
+// modified, use `metadata-hash`.
+//
+// Note: This was called `fd_filestat_get` in earlier versions of WASI.
+//
+// stat: func() -> result<descriptor-stat, error-code>
+//
+//go:nosplit
+func (self Descriptor) Stat() (result cm.OKResult[DescriptorStat, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorStat((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.stat
+//go:noescape
+func wasmimport_DescriptorStat(self0 uint32, result *cm.OKResult[DescriptorStat, ErrorCode])
+
+// StatAt represents the imported method "stat-at".
+//
+// Return the attributes of a file or directory.
+//
+// Note: This is similar to `fstatat` in POSIX, except that it does not
+// return device and inode information. See the `stat` description for a
+// discussion of alternatives.
+//
+// Note: This was called `path_filestat_get` in earlier versions of WASI.
+//
+// stat-at: func(path-flags: path-flags, path: string) -> result<descriptor-stat,
+// error-code>
+//
+//go:nosplit
+func (self Descriptor) StatAt(pathFlags PathFlags, path string) (result cm.OKResult[DescriptorStat, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ pathFlags0 := (uint32)(pathFlags)
+ path0, path1 := cm.LowerString(path)
+ wasmimport_DescriptorStatAt((uint32)(self0), (uint32)(pathFlags0), (*uint8)(path0), (uint32)(path1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.stat-at
+//go:noescape
+func wasmimport_DescriptorStatAt(self0 uint32, pathFlags0 uint32, path0 *uint8, path1 uint32, result *cm.OKResult[DescriptorStat, ErrorCode])
+
+// SymlinkAt represents the imported method "symlink-at".
+//
+// Create a symbolic link (also known as a "symlink").
+//
+// If `old-path` starts with `/`, the function fails with
+// `error-code::not-permitted`.
+//
+// Note: This is similar to `symlinkat` in POSIX.
+//
+// symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) SymlinkAt(oldPath string, newPath string) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ oldPath0, oldPath1 := cm.LowerString(oldPath)
+ newPath0, newPath1 := cm.LowerString(newPath)
+ wasmimport_DescriptorSymlinkAt((uint32)(self0), (*uint8)(oldPath0), (uint32)(oldPath1), (*uint8)(newPath0), (uint32)(newPath1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.symlink-at
+//go:noescape
+func wasmimport_DescriptorSymlinkAt(self0 uint32, oldPath0 *uint8, oldPath1 uint32, newPath0 *uint8, newPath1 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// Sync represents the imported method "sync".
+//
+// Synchronize the data and metadata of a file to disk.
+//
+// This function succeeds with no effect if the file descriptor is not
+// opened for writing.
+//
+// Note: This is similar to `fsync` in POSIX.
+//
+// sync: func() -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) Sync() (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorSync((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.sync
+//go:noescape
+func wasmimport_DescriptorSync(self0 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// SyncData represents the imported method "sync-data".
+//
+// Synchronize the data of a file to disk.
+//
+// This function succeeds with no effect if the file descriptor is not
+// opened for writing.
+//
+// Note: This is similar to `fdatasync` in POSIX.
+//
+// sync-data: func() -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) SyncData() (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DescriptorSyncData((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.sync-data
+//go:noescape
+func wasmimport_DescriptorSyncData(self0 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// UnlinkFileAt represents the imported method "unlink-file-at".
+//
+// Unlink a filesystem object that is not a directory.
+//
+// Return `error-code::is-directory` if the path refers to a directory.
+// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
+//
+// unlink-file-at: func(path: string) -> result<_, error-code>
+//
+//go:nosplit
+func (self Descriptor) UnlinkFileAt(path string) (result cm.ErrResult[struct{}, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ path0, path1 := cm.LowerString(path)
+ wasmimport_DescriptorUnlinkFileAt((uint32)(self0), (*uint8)(path0), (uint32)(path1), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.unlink-file-at
+//go:noescape
+func wasmimport_DescriptorUnlinkFileAt(self0 uint32, path0 *uint8, path1 uint32, result *cm.ErrResult[struct{}, ErrorCode])
+
+// Write represents the imported method "write".
+//
+// Write to a descriptor, without using and updating the descriptor's offset.
+//
+// It is valid to write past the end of a file; the file is extended to the
+// extent of the write, with bytes between the previous end and the start of
+// the write set to zero.
+//
+// In the future, this may change to take a `stream<u8, error-code>`.
+//
+// Note: This is similar to `pwrite` in POSIX.
+//
+// write: func(buffer: list<u8>, offset: filesize) -> result<filesize, error-code>
+//
+//go:nosplit
+func (self Descriptor) Write(buffer cm.List[uint8], offset FileSize) (result cm.OKResult[FileSize, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ buffer0, buffer1 := cm.LowerList(buffer)
+ offset0 := (uint64)(offset)
+ wasmimport_DescriptorWrite((uint32)(self0), (*uint8)(buffer0), (uint32)(buffer1), (uint64)(offset0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.write
+//go:noescape
+func wasmimport_DescriptorWrite(self0 uint32, buffer0 *uint8, buffer1 uint32, offset0 uint64, result *cm.OKResult[FileSize, ErrorCode])
+
+// WriteViaStream represents the imported method "write-via-stream".
+//
+// Return a stream for writing to a file, if available.
+//
+// May fail with an error-code describing why the file cannot be written.
+//
+// Note: This allows using `write-stream`, which is similar to `write` in
+// POSIX.
+//
+// write-via-stream: func(offset: filesize) -> result<output-stream, error-code>
+//
+//go:nosplit
+func (self Descriptor) WriteViaStream(offset FileSize) (result cm.OKResult[streams.OutputStream, ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ offset0 := (uint64)(offset)
+ wasmimport_DescriptorWriteViaStream((uint32)(self0), (uint64)(offset0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]descriptor.write-via-stream
+//go:noescape
+func wasmimport_DescriptorWriteViaStream(self0 uint32, offset0 uint64, result *cm.OKResult[streams.OutputStream, ErrorCode])
+
+// DirectoryEntryStream represents the imported resource "wasi:filesystem/[email protected]#directory-entry-stream".
+//
+// A stream of directory entries.
+//
+// resource directory-entry-stream
+type DirectoryEntryStream cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "directory-entry-stream".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self DirectoryEntryStream) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DirectoryEntryStreamResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [resource-drop]directory-entry-stream
+//go:noescape
+func wasmimport_DirectoryEntryStreamResourceDrop(self0 uint32)
+
+// ReadDirectoryEntry represents the imported method "read-directory-entry".
+//
+// Read a single directory entry from a `directory-entry-stream`.
+//
+// read-directory-entry: func() -> result<option<directory-entry>, error-code>
+//
+//go:nosplit
+func (self DirectoryEntryStream) ReadDirectoryEntry() (result cm.OKResult[cm.Option[DirectoryEntry], ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_DirectoryEntryStreamReadDirectoryEntry((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] [method]directory-entry-stream.read-directory-entry
+//go:noescape
+func wasmimport_DirectoryEntryStreamReadDirectoryEntry(self0 uint32, result *cm.OKResult[cm.Option[DirectoryEntry], ErrorCode])
+
+// FilesystemErrorCode represents the imported function "filesystem-error-code".
+//
+// Attempts to extract a filesystem-related `error-code` from the stream
+// `error` provided.
+//
+// Stream operations which return `stream-error::last-operation-failed`
+// have a payload with more information about the operation that failed.
+// This payload can be passed through to this function to see if there's
+// filesystem-related information about the error to return.
+//
+// Note that this function is fallible because not all stream-related
+// errors are filesystem-related errors.
+//
+// filesystem-error-code: func(err: borrow<error>) -> option<error-code>
+//
+//go:nosplit
+func FilesystemErrorCode(err ioerror.Error) (result cm.Option[ErrorCode]) {
+ err0 := cm.Reinterpret[uint32](err)
+ wasmimport_FilesystemErrorCode((uint32)(err0), &result)
+ return
+}
+
+//go:wasmimport wasi:filesystem/[email protected] filesystem-error-code
+//go:noescape
+func wasmimport_FilesystemErrorCode(err0 uint32, result *cm.Option[ErrorCode])
diff --git a/src/internal/wasi/io/v0.2.0/error/empty.s b/src/internal/wasi/io/v0.2.0/error/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/io/v0.2.0/error/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/io/v0.2.0/error/error.wit.go b/src/internal/wasi/io/v0.2.0/error/error.wit.go
new file mode 100644
index 000000000..f962f22ea
--- /dev/null
+++ b/src/internal/wasi/io/v0.2.0/error/error.wit.go
@@ -0,0 +1,73 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package ioerror represents the imported interface "wasi:io/[email protected]".
+package ioerror
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// Error represents the imported resource "wasi:io/[email protected]#error".
+//
+// A resource which represents some error information.
+//
+// The only method provided by this resource is `to-debug-string`,
+// which provides some human-readable information about the error.
+//
+// In the `wasi:io` package, this resource is returned through the
+// `wasi:io/streams/stream-error` type.
+//
+// To provide more specific error information, other interfaces may
+// provide functions to further "downcast" this error into more specific
+// error information. For example, `error`s returned in streams derived
+// from filesystem types to be described using the filesystem's own
+// error-code type, using the function
+// `wasi:filesystem/types/filesystem-error-code`, which takes a parameter
+// `borrow<error>` and returns
+// `option<wasi:filesystem/types/error-code>`.
+//
+// The set of functions which can "downcast" an `error` into a more
+// concrete type is open.
+//
+// resource error
+type Error cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "error".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self Error) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_ErrorResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [resource-drop]error
+//go:noescape
+func wasmimport_ErrorResourceDrop(self0 uint32)
+
+// ToDebugString represents the imported method "to-debug-string".
+//
+// Returns a string that is suitable to assist humans in debugging
+// this error.
+//
+// WARNING: The returned string should not be consumed mechanically!
+// It may change across platforms, hosts, or other implementation
+// details. Parsing this string is a major platform-compatibility
+// hazard.
+//
+// to-debug-string: func() -> string
+//
+//go:nosplit
+func (self Error) ToDebugString() (result string) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_ErrorToDebugString((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]error.to-debug-string
+//go:noescape
+func wasmimport_ErrorToDebugString(self0 uint32, result *string)
diff --git a/src/internal/wasi/io/v0.2.0/poll/empty.s b/src/internal/wasi/io/v0.2.0/poll/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/io/v0.2.0/poll/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/io/v0.2.0/poll/poll.wit.go b/src/internal/wasi/io/v0.2.0/poll/poll.wit.go
new file mode 100644
index 000000000..0c362c079
--- /dev/null
+++ b/src/internal/wasi/io/v0.2.0/poll/poll.wit.go
@@ -0,0 +1,110 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package poll represents the imported interface "wasi:io/[email protected]".
+//
+// A poll API intended to let users wait for I/O events on multiple handles
+// at once.
+package poll
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// Pollable represents the imported resource "wasi:io/[email protected]#pollable".
+//
+// `pollable` represents a single I/O event which may be ready, or not.
+//
+// resource pollable
+type Pollable cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "pollable".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self Pollable) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_PollableResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [resource-drop]pollable
+//go:noescape
+func wasmimport_PollableResourceDrop(self0 uint32)
+
+// Block represents the imported method "block".
+//
+// `block` returns immediately if the pollable is ready, and otherwise
+// blocks until ready.
+//
+// This function is equivalent to calling `poll.poll` on a list
+// containing only this pollable.
+//
+// block: func()
+//
+//go:nosplit
+func (self Pollable) Block() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_PollableBlock((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]pollable.block
+//go:noescape
+func wasmimport_PollableBlock(self0 uint32)
+
+// Ready represents the imported method "ready".
+//
+// Return the readiness of a pollable. This function never blocks.
+//
+// Returns `true` when the pollable is ready, and `false` otherwise.
+//
+// ready: func() -> bool
+//
+//go:nosplit
+func (self Pollable) Ready() (result bool) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_PollableReady((uint32)(self0))
+ result = cm.U32ToBool((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]pollable.ready
+//go:noescape
+func wasmimport_PollableReady(self0 uint32) (result0 uint32)
+
+// Poll represents the imported function "poll".
+//
+// Poll for completion on a set of pollables.
+//
+// This function takes a list of pollables, which identify I/O sources of
+// interest, and waits until one or more of the events is ready for I/O.
+//
+// The result `list<u32>` contains one or more indices of handles in the
+// argument list that is ready for I/O.
+//
+// If the list contains more elements than can be indexed with a `u32`
+// value, this function traps.
+//
+// A timeout can be implemented by adding a pollable from the
+// wasi-clocks API to the list.
+//
+// This function does not return a `result`; polling in itself does not
+// do any I/O so it doesn't fail. If any of the I/O sources identified by
+// the pollables has an error, it is indicated by marking the source as
+// being reaedy for I/O.
+//
+// poll: func(in: list<borrow<pollable>>) -> list<u32>
+//
+//go:nosplit
+func Poll(in cm.List[Pollable]) (result cm.List[uint32]) {
+ in0, in1 := cm.LowerList(in)
+ wasmimport_Poll((*Pollable)(in0), (uint32)(in1), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] poll
+//go:noescape
+func wasmimport_Poll(in0 *Pollable, in1 uint32, result *cm.List[uint32])
diff --git a/src/internal/wasi/io/v0.2.0/streams/empty.s b/src/internal/wasi/io/v0.2.0/streams/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/io/v0.2.0/streams/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/io/v0.2.0/streams/streams.wit.go b/src/internal/wasi/io/v0.2.0/streams/streams.wit.go
new file mode 100644
index 000000000..8983d5ac0
--- /dev/null
+++ b/src/internal/wasi/io/v0.2.0/streams/streams.wit.go
@@ -0,0 +1,521 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package streams represents the imported interface "wasi:io/[email protected]".
+//
+// WASI I/O is an I/O abstraction API which is currently focused on providing
+// stream types.
+//
+// In the future, the component model is expected to add built-in stream types;
+// when it does, they are expected to subsume this API.
+package streams
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ ioerror "internal/wasi/io/v0.2.0/error"
+ "internal/wasi/io/v0.2.0/poll"
+)
+
+// StreamError represents the imported variant "wasi:io/[email protected]#stream-error".
+//
+// An error for input-stream and output-stream operations.
+//
+// variant stream-error {
+// last-operation-failed(error),
+// closed,
+// }
+type StreamError cm.Variant[uint8, ioerror.Error, ioerror.Error]
+
+// StreamErrorLastOperationFailed returns a [StreamError] of case "last-operation-failed".
+//
+// The last operation (a write or flush) failed before completion.
+//
+// More information is available in the `error` payload.
+func StreamErrorLastOperationFailed(data ioerror.Error) StreamError {
+ return cm.New[StreamError](0, data)
+}
+
+// LastOperationFailed returns a non-nil *[ioerror.Error] if [StreamError] represents the variant case "last-operation-failed".
+func (self *StreamError) LastOperationFailed() *ioerror.Error {
+ return cm.Case[ioerror.Error](self, 0)
+}
+
+// StreamErrorClosed returns a [StreamError] of case "closed".
+//
+// The stream is closed: no more input will be accepted by the
+// stream. A closed output-stream will return this error on all
+// future operations.
+func StreamErrorClosed() StreamError {
+ var data struct{}
+ return cm.New[StreamError](1, data)
+}
+
+// Closed returns true if [StreamError] represents the variant case "closed".
+func (self *StreamError) Closed() bool {
+ return cm.Tag(self) == 1
+}
+
+// InputStream represents the imported resource "wasi:io/[email protected]#input-stream".
+//
+// An input bytestream.
+//
+// `input-stream`s are *non-blocking* to the extent practical on underlying
+// platforms. I/O operations always return promptly; if fewer bytes are
+// promptly available than requested, they return the number of bytes promptly
+// available, which could even be zero. To wait for data to be available,
+// use the `subscribe` function to obtain a `pollable` which can be polled
+// for using `wasi:io/poll`.
+//
+// resource input-stream
+type InputStream cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "input-stream".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self InputStream) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_InputStreamResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [resource-drop]input-stream
+//go:noescape
+func wasmimport_InputStreamResourceDrop(self0 uint32)
+
+// BlockingRead represents the imported method "blocking-read".
+//
+// Read bytes from a stream, after blocking until at least one byte can
+// be read. Except for blocking, behavior is identical to `read`.
+//
+// blocking-read: func(len: u64) -> result<list<u8>, stream-error>
+//
+//go:nosplit
+func (self InputStream) BlockingRead(len_ uint64) (result cm.OKResult[cm.List[uint8], StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ len0 := (uint64)(len_)
+ wasmimport_InputStreamBlockingRead((uint32)(self0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]input-stream.blocking-read
+//go:noescape
+func wasmimport_InputStreamBlockingRead(self0 uint32, len0 uint64, result *cm.OKResult[cm.List[uint8], StreamError])
+
+// BlockingSkip represents the imported method "blocking-skip".
+//
+// Skip bytes from a stream, after blocking until at least one byte
+// can be skipped. Except for blocking behavior, identical to `skip`.
+//
+// blocking-skip: func(len: u64) -> result<u64, stream-error>
+//
+//go:nosplit
+func (self InputStream) BlockingSkip(len_ uint64) (result cm.OKResult[uint64, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ len0 := (uint64)(len_)
+ wasmimport_InputStreamBlockingSkip((uint32)(self0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]input-stream.blocking-skip
+//go:noescape
+func wasmimport_InputStreamBlockingSkip(self0 uint32, len0 uint64, result *cm.OKResult[uint64, StreamError])
+
+// Read represents the imported method "read".
+//
+// Perform a non-blocking read from the stream.
+//
+// When the source of a `read` is binary data, the bytes from the source
+// are returned verbatim. When the source of a `read` is known to the
+// implementation to be text, bytes containing the UTF-8 encoding of the
+// text are returned.
+//
+// This function returns a list of bytes containing the read data,
+// when successful. The returned list will contain up to `len` bytes;
+// it may return fewer than requested, but not more. The list is
+// empty when no bytes are available for reading at this time. The
+// pollable given by `subscribe` will be ready when more bytes are
+// available.
+//
+// This function fails with a `stream-error` when the operation
+// encounters an error, giving `last-operation-failed`, or when the
+// stream is closed, giving `closed`.
+//
+// When the caller gives a `len` of 0, it represents a request to
+// read 0 bytes. If the stream is still open, this call should
+// succeed and return an empty list, or otherwise fail with `closed`.
+//
+// The `len` parameter is a `u64`, which could represent a list of u8 which
+// is not possible to allocate in wasm32, or not desirable to allocate as
+// as a return value by the callee. The callee may return a list of bytes
+// less than `len` in size while more bytes are available for reading.
+//
+// read: func(len: u64) -> result<list<u8>, stream-error>
+//
+//go:nosplit
+func (self InputStream) Read(len_ uint64) (result cm.OKResult[cm.List[uint8], StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ len0 := (uint64)(len_)
+ wasmimport_InputStreamRead((uint32)(self0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]input-stream.read
+//go:noescape
+func wasmimport_InputStreamRead(self0 uint32, len0 uint64, result *cm.OKResult[cm.List[uint8], StreamError])
+
+// Skip represents the imported method "skip".
+//
+// Skip bytes from a stream. Returns number of bytes skipped.
+//
+// Behaves identical to `read`, except instead of returning a list
+// of bytes, returns the number of bytes consumed from the stream.
+//
+// skip: func(len: u64) -> result<u64, stream-error>
+//
+//go:nosplit
+func (self InputStream) Skip(len_ uint64) (result cm.OKResult[uint64, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ len0 := (uint64)(len_)
+ wasmimport_InputStreamSkip((uint32)(self0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]input-stream.skip
+//go:noescape
+func wasmimport_InputStreamSkip(self0 uint32, len0 uint64, result *cm.OKResult[uint64, StreamError])
+
+// Subscribe represents the imported method "subscribe".
+//
+// Create a `pollable` which will resolve once either the specified stream
+// has bytes available to read or the other end of the stream has been
+// closed.
+// The created `pollable` is a child resource of the `input-stream`.
+// Implementations may trap if the `input-stream` is dropped before
+// all derived `pollable`s created with this function are dropped.
+//
+// subscribe: func() -> pollable
+//
+//go:nosplit
+func (self InputStream) Subscribe() (result poll.Pollable) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_InputStreamSubscribe((uint32)(self0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]input-stream.subscribe
+//go:noescape
+func wasmimport_InputStreamSubscribe(self0 uint32) (result0 uint32)
+
+// OutputStream represents the imported resource "wasi:io/[email protected]#output-stream".
+//
+// An output bytestream.
+//
+// `output-stream`s are *non-blocking* to the extent practical on
+// underlying platforms. Except where specified otherwise, I/O operations also
+// always return promptly, after the number of bytes that can be written
+// promptly, which could even be zero. To wait for the stream to be ready to
+// accept data, the `subscribe` function to obtain a `pollable` which can be
+// polled for using `wasi:io/poll`.
+//
+// resource output-stream
+type OutputStream cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "output-stream".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self OutputStream) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_OutputStreamResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [resource-drop]output-stream
+//go:noescape
+func wasmimport_OutputStreamResourceDrop(self0 uint32)
+
+// BlockingFlush represents the imported method "blocking-flush".
+//
+// Request to flush buffered output, and block until flush completes
+// and stream is ready for writing again.
+//
+// blocking-flush: func() -> result<_, stream-error>
+//
+//go:nosplit
+func (self OutputStream) BlockingFlush() (result cm.ErrResult[struct{}, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_OutputStreamBlockingFlush((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.blocking-flush
+//go:noescape
+func wasmimport_OutputStreamBlockingFlush(self0 uint32, result *cm.ErrResult[struct{}, StreamError])
+
+// BlockingSplice represents the imported method "blocking-splice".
+//
+// Read from one stream and write to another, with blocking.
+//
+// This is similar to `splice`, except that it blocks until the
+// `output-stream` is ready for writing, and the `input-stream`
+// is ready for reading, before performing the `splice`.
+//
+// blocking-splice: func(src: borrow<input-stream>, len: u64) -> result<u64, stream-error>
+//
+//go:nosplit
+func (self OutputStream) BlockingSplice(src InputStream, len_ uint64) (result cm.OKResult[uint64, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ src0 := cm.Reinterpret[uint32](src)
+ len0 := (uint64)(len_)
+ wasmimport_OutputStreamBlockingSplice((uint32)(self0), (uint32)(src0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.blocking-splice
+//go:noescape
+func wasmimport_OutputStreamBlockingSplice(self0 uint32, src0 uint32, len0 uint64, result *cm.OKResult[uint64, StreamError])
+
+// BlockingWriteAndFlush represents the imported method "blocking-write-and-flush".
+//
+// Perform a write of up to 4096 bytes, and then flush the stream. Block
+// until all of these operations are complete, or an error occurs.
+//
+// This is a convenience wrapper around the use of `check-write`,
+// `subscribe`, `write`, and `flush`, and is implemented with the
+// following pseudo-code:
+//
+// let pollable = this.subscribe();
+// while !contents.is_empty() {
+// // Wait for the stream to become writable
+// pollable.block();
+// let Ok(n) = this.check-write(); // eliding error handling
+// let len = min(n, contents.len());
+// let (chunk, rest) = contents.split_at(len);
+// this.write(chunk ); // eliding error handling
+// contents = rest;
+// }
+// this.flush();
+// // Wait for completion of `flush`
+// pollable.block();
+// // Check for any errors that arose during `flush`
+// let _ = this.check-write(); // eliding error handling
+//
+// blocking-write-and-flush: func(contents: list<u8>) -> result<_, stream-error>
+//
+//go:nosplit
+func (self OutputStream) BlockingWriteAndFlush(contents cm.List[uint8]) (result cm.ErrResult[struct{}, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ contents0, contents1 := cm.LowerList(contents)
+ wasmimport_OutputStreamBlockingWriteAndFlush((uint32)(self0), (*uint8)(contents0), (uint32)(contents1), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.blocking-write-and-flush
+//go:noescape
+func wasmimport_OutputStreamBlockingWriteAndFlush(self0 uint32, contents0 *uint8, contents1 uint32, result *cm.ErrResult[struct{}, StreamError])
+
+// BlockingWriteZeroesAndFlush represents the imported method "blocking-write-zeroes-and-flush".
+//
+// Perform a write of up to 4096 zeroes, and then flush the stream.
+// Block until all of these operations are complete, or an error
+// occurs.
+//
+// This is a convenience wrapper around the use of `check-write`,
+// `subscribe`, `write-zeroes`, and `flush`, and is implemented with
+// the following pseudo-code:
+//
+// let pollable = this.subscribe();
+// while num_zeroes != 0 {
+// // Wait for the stream to become writable
+// pollable.block();
+// let Ok(n) = this.check-write(); // eliding error handling
+// let len = min(n, num_zeroes);
+// this.write-zeroes(len); // eliding error handling
+// num_zeroes -= len;
+// }
+// this.flush();
+// // Wait for completion of `flush`
+// pollable.block();
+// // Check for any errors that arose during `flush`
+// let _ = this.check-write(); // eliding error handling
+//
+// blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>
+//
+//go:nosplit
+func (self OutputStream) BlockingWriteZeroesAndFlush(len_ uint64) (result cm.ErrResult[struct{}, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ len0 := (uint64)(len_)
+ wasmimport_OutputStreamBlockingWriteZeroesAndFlush((uint32)(self0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.blocking-write-zeroes-and-flush
+//go:noescape
+func wasmimport_OutputStreamBlockingWriteZeroesAndFlush(self0 uint32, len0 uint64, result *cm.ErrResult[struct{}, StreamError])
+
+// CheckWrite represents the imported method "check-write".
+//
+// Check readiness for writing. This function never blocks.
+//
+// Returns the number of bytes permitted for the next call to `write`,
+// or an error. Calling `write` with more bytes than this function has
+// permitted will trap.
+//
+// When this function returns 0 bytes, the `subscribe` pollable will
+// become ready when this function will report at least 1 byte, or an
+// error.
+//
+// check-write: func() -> result<u64, stream-error>
+//
+//go:nosplit
+func (self OutputStream) CheckWrite() (result cm.OKResult[uint64, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_OutputStreamCheckWrite((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.check-write
+//go:noescape
+func wasmimport_OutputStreamCheckWrite(self0 uint32, result *cm.OKResult[uint64, StreamError])
+
+// Flush represents the imported method "flush".
+//
+// Request to flush buffered output. This function never blocks.
+//
+// This tells the output-stream that the caller intends any buffered
+// output to be flushed. the output which is expected to be flushed
+// is all that has been passed to `write` prior to this call.
+//
+// Upon calling this function, the `output-stream` will not accept any
+// writes (`check-write` will return `ok(0)`) until the flush has
+// completed. The `subscribe` pollable will become ready when the
+// flush has completed and the stream can accept more writes.
+//
+// flush: func() -> result<_, stream-error>
+//
+//go:nosplit
+func (self OutputStream) Flush() (result cm.ErrResult[struct{}, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_OutputStreamFlush((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.flush
+//go:noescape
+func wasmimport_OutputStreamFlush(self0 uint32, result *cm.ErrResult[struct{}, StreamError])
+
+// Splice represents the imported method "splice".
+//
+// Read from one stream and write to another.
+//
+// The behavior of splice is equivelant to:
+// 1. calling `check-write` on the `output-stream`
+// 2. calling `read` on the `input-stream` with the smaller of the
+// `check-write` permitted length and the `len` provided to `splice`
+// 3. calling `write` on the `output-stream` with that read data.
+//
+// Any error reported by the call to `check-write`, `read`, or
+// `write` ends the splice and reports that error.
+//
+// This function returns the number of bytes transferred; it may be less
+// than `len`.
+//
+// splice: func(src: borrow<input-stream>, len: u64) -> result<u64, stream-error>
+//
+//go:nosplit
+func (self OutputStream) Splice(src InputStream, len_ uint64) (result cm.OKResult[uint64, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ src0 := cm.Reinterpret[uint32](src)
+ len0 := (uint64)(len_)
+ wasmimport_OutputStreamSplice((uint32)(self0), (uint32)(src0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.splice
+//go:noescape
+func wasmimport_OutputStreamSplice(self0 uint32, src0 uint32, len0 uint64, result *cm.OKResult[uint64, StreamError])
+
+// Subscribe represents the imported method "subscribe".
+//
+// Create a `pollable` which will resolve once the output-stream
+// is ready for more writing, or an error has occured. When this
+// pollable is ready, `check-write` will return `ok(n)` with n>0, or an
+// error.
+//
+// If the stream is closed, this pollable is always ready immediately.
+//
+// The created `pollable` is a child resource of the `output-stream`.
+// Implementations may trap if the `output-stream` is dropped before
+// all derived `pollable`s created with this function are dropped.
+//
+// subscribe: func() -> pollable
+//
+//go:nosplit
+func (self OutputStream) Subscribe() (result poll.Pollable) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_OutputStreamSubscribe((uint32)(self0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.subscribe
+//go:noescape
+func wasmimport_OutputStreamSubscribe(self0 uint32) (result0 uint32)
+
+// Write represents the imported method "write".
+//
+// Perform a write. This function never blocks.
+//
+// When the destination of a `write` is binary data, the bytes from
+// `contents` are written verbatim. When the destination of a `write` is
+// known to the implementation to be text, the bytes of `contents` are
+// transcoded from UTF-8 into the encoding of the destination and then
+// written.
+//
+// Precondition: check-write gave permit of Ok(n) and contents has a
+// length of less than or equal to n. Otherwise, this function will trap.
+//
+// returns Err(closed) without writing if the stream has closed since
+// the last call to check-write provided a permit.
+//
+// write: func(contents: list<u8>) -> result<_, stream-error>
+//
+//go:nosplit
+func (self OutputStream) Write(contents cm.List[uint8]) (result cm.ErrResult[struct{}, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ contents0, contents1 := cm.LowerList(contents)
+ wasmimport_OutputStreamWrite((uint32)(self0), (*uint8)(contents0), (uint32)(contents1), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.write
+//go:noescape
+func wasmimport_OutputStreamWrite(self0 uint32, contents0 *uint8, contents1 uint32, result *cm.ErrResult[struct{}, StreamError])
+
+// WriteZeroes represents the imported method "write-zeroes".
+//
+// Write zeroes to a stream.
+//
+// This should be used precisely like `write` with the exact same
+// preconditions (must use check-write first), but instead of
+// passing a list of bytes, you simply pass the number of zero-bytes
+// that should be written.
+//
+// write-zeroes: func(len: u64) -> result<_, stream-error>
+//
+//go:nosplit
+func (self OutputStream) WriteZeroes(len_ uint64) (result cm.ErrResult[struct{}, StreamError]) {
+ self0 := cm.Reinterpret[uint32](self)
+ len0 := (uint64)(len_)
+ wasmimport_OutputStreamWriteZeroes((uint32)(self0), (uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:io/[email protected] [method]output-stream.write-zeroes
+//go:noescape
+func wasmimport_OutputStreamWriteZeroes(self0 uint32, len0 uint64, result *cm.ErrResult[struct{}, StreamError])
diff --git a/src/internal/wasi/random/v0.2.0/insecure-seed/empty.s b/src/internal/wasi/random/v0.2.0/insecure-seed/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/random/v0.2.0/insecure-seed/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/random/v0.2.0/insecure-seed/insecure-seed.wit.go b/src/internal/wasi/random/v0.2.0/insecure-seed/insecure-seed.wit.go
new file mode 100644
index 000000000..e9ac78082
--- /dev/null
+++ b/src/internal/wasi/random/v0.2.0/insecure-seed/insecure-seed.wit.go
@@ -0,0 +1,43 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package insecureseed represents the imported interface "wasi:random/[email protected]".
+//
+// The insecure-seed interface for seeding hash-map DoS resistance.
+//
+// It is intended to be portable at least between Unix-family platforms and
+// Windows.
+package insecureseed
+
+// InsecureSeed represents the imported function "insecure-seed".
+//
+// Return a 128-bit value that may contain a pseudo-random value.
+//
+// The returned value is not required to be computed from a CSPRNG, and may
+// even be entirely deterministic. Host implementations are encouraged to
+// provide pseudo-random values to any program exposed to
+// attacker-controlled content, to enable DoS protection built into many
+// languages' hash-map implementations.
+//
+// This function is intended to only be called once, by a source language
+// to initialize Denial Of Service (DoS) protection in its hash-map
+// implementation.
+//
+// # Expected future evolution
+//
+// This will likely be changed to a value import, to prevent it from being
+// called multiple times and potentially used for purposes other than DoS
+// protection.
+//
+// insecure-seed: func() -> tuple<u64, u64>
+//
+//go:nosplit
+func InsecureSeed() (result [2]uint64) {
+ wasmimport_InsecureSeed(&result)
+ return
+}
+
+//go:wasmimport wasi:random/[email protected] insecure-seed
+//go:noescape
+func wasmimport_InsecureSeed(result *[2]uint64)
diff --git a/src/internal/wasi/random/v0.2.0/insecure/empty.s b/src/internal/wasi/random/v0.2.0/insecure/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/random/v0.2.0/insecure/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/random/v0.2.0/insecure/insecure.wit.go b/src/internal/wasi/random/v0.2.0/insecure/insecure.wit.go
new file mode 100644
index 000000000..fbea789b5
--- /dev/null
+++ b/src/internal/wasi/random/v0.2.0/insecure/insecure.wit.go
@@ -0,0 +1,59 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package insecure represents the imported interface "wasi:random/[email protected]".
+//
+// The insecure interface for insecure pseudo-random numbers.
+//
+// It is intended to be portable at least between Unix-family platforms and
+// Windows.
+package insecure
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// GetInsecureRandomBytes represents the imported function "get-insecure-random-bytes".
+//
+// Return `len` insecure pseudo-random bytes.
+//
+// This function is not cryptographically secure. Do not use it for
+// anything related to security.
+//
+// There are no requirements on the values of the returned bytes, however
+// implementations are encouraged to return evenly distributed values with
+// a long period.
+//
+// get-insecure-random-bytes: func(len: u64) -> list<u8>
+//
+//go:nosplit
+func GetInsecureRandomBytes(len_ uint64) (result cm.List[uint8]) {
+ len0 := (uint64)(len_)
+ wasmimport_GetInsecureRandomBytes((uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:random/[email protected] get-insecure-random-bytes
+//go:noescape
+func wasmimport_GetInsecureRandomBytes(len0 uint64, result *cm.List[uint8])
+
+// GetInsecureRandomU64 represents the imported function "get-insecure-random-u64".
+//
+// Return an insecure pseudo-random `u64` value.
+//
+// This function returns the same type of pseudo-random data as
+// `get-insecure-random-bytes`, represented as a `u64`.
+//
+// get-insecure-random-u64: func() -> u64
+//
+//go:nosplit
+func GetInsecureRandomU64() (result uint64) {
+ result0 := wasmimport_GetInsecureRandomU64()
+ result = (uint64)((uint64)(result0))
+ return
+}
+
+//go:wasmimport wasi:random/[email protected] get-insecure-random-u64
+//go:noescape
+func wasmimport_GetInsecureRandomU64() (result0 uint64)
diff --git a/src/internal/wasi/random/v0.2.0/random/empty.s b/src/internal/wasi/random/v0.2.0/random/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/random/v0.2.0/random/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/random/v0.2.0/random/random.wit.go b/src/internal/wasi/random/v0.2.0/random/random.wit.go
new file mode 100644
index 000000000..f60d468e1
--- /dev/null
+++ b/src/internal/wasi/random/v0.2.0/random/random.wit.go
@@ -0,0 +1,63 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package random represents the imported interface "wasi:random/[email protected]".
+//
+// WASI Random is a random data API.
+//
+// It is intended to be portable at least between Unix-family platforms and
+// Windows.
+package random
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// GetRandomBytes represents the imported function "get-random-bytes".
+//
+// Return `len` cryptographically-secure random or pseudo-random bytes.
+//
+// This function must produce data at least as cryptographically secure and
+// fast as an adequately seeded cryptographically-secure pseudo-random
+// number generator (CSPRNG). It must not block, from the perspective of
+// the calling program, under any circumstances, including on the first
+// request and on requests for numbers of bytes. The returned data must
+// always be unpredictable.
+//
+// This function must always return fresh data. Deterministic environments
+// must omit this function, rather than implementing it with deterministic
+// data.
+//
+// get-random-bytes: func(len: u64) -> list<u8>
+//
+//go:nosplit
+func GetRandomBytes(len_ uint64) (result cm.List[uint8]) {
+ len0 := (uint64)(len_)
+ wasmimport_GetRandomBytes((uint64)(len0), &result)
+ return
+}
+
+//go:wasmimport wasi:random/[email protected] get-random-bytes
+//go:noescape
+func wasmimport_GetRandomBytes(len0 uint64, result *cm.List[uint8])
+
+// GetRandomU64 represents the imported function "get-random-u64".
+//
+// Return a cryptographically-secure random or pseudo-random `u64` value.
+//
+// This function returns the same type of data as `get-random-bytes`,
+// represented as a `u64`.
+//
+// get-random-u64: func() -> u64
+//
+//go:nosplit
+func GetRandomU64() (result uint64) {
+ result0 := wasmimport_GetRandomU64()
+ result = (uint64)((uint64)(result0))
+ return
+}
+
+//go:wasmimport wasi:random/[email protected] get-random-u64
+//go:noescape
+func wasmimport_GetRandomU64() (result0 uint64)
diff --git a/src/internal/wasi/sockets/v0.2.0/instance-network/empty.s b/src/internal/wasi/sockets/v0.2.0/instance-network/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/instance-network/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/sockets/v0.2.0/instance-network/instance-network.wit.go b/src/internal/wasi/sockets/v0.2.0/instance-network/instance-network.wit.go
new file mode 100644
index 000000000..fbd9dc8ad
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/instance-network/instance-network.wit.go
@@ -0,0 +1,30 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package instancenetwork represents the imported interface "wasi:sockets/[email protected]".
+//
+// This interface provides a value-export of the default network handle..
+package instancenetwork
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/sockets/v0.2.0/network"
+)
+
+// InstanceNetwork represents the imported function "instance-network".
+//
+// Get a handle to the default network.
+//
+// instance-network: func() -> network
+//
+//go:nosplit
+func InstanceNetwork() (result network.Network) {
+ result0 := wasmimport_InstanceNetwork()
+ result = cm.Reinterpret[network.Network]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] instance-network
+//go:noescape
+func wasmimport_InstanceNetwork() (result0 uint32)
diff --git a/src/internal/wasi/sockets/v0.2.0/ip-name-lookup/empty.s b/src/internal/wasi/sockets/v0.2.0/ip-name-lookup/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/ip-name-lookup/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/sockets/v0.2.0/ip-name-lookup/ip-name-lookup.wit.go b/src/internal/wasi/sockets/v0.2.0/ip-name-lookup/ip-name-lookup.wit.go
new file mode 100644
index 000000000..fc81b36ac
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/ip-name-lookup/ip-name-lookup.wit.go
@@ -0,0 +1,123 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package ipnamelookup represents the imported interface "wasi:sockets/[email protected]".
+package ipnamelookup
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/io/v0.2.0/poll"
+ "internal/wasi/sockets/v0.2.0/network"
+)
+
+// ResolveAddressStream represents the imported resource "wasi:sockets/[email protected]#resolve-address-stream".
+//
+// resource resolve-address-stream
+type ResolveAddressStream cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "resolve-address-stream".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self ResolveAddressStream) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_ResolveAddressStreamResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [resource-drop]resolve-address-stream
+//go:noescape
+func wasmimport_ResolveAddressStreamResourceDrop(self0 uint32)
+
+// ResolveNextAddress represents the imported method "resolve-next-address".
+//
+// Returns the next address from the resolver.
+//
+// This function should be called multiple times. On each call, it will
+// return the next address in connection order preference. If all
+// addresses have been exhausted, this function returns `none`.
+//
+// This function never returns IPv4-mapped IPv6 addresses.
+//
+// # Typical errors
+// - `name-unresolvable`: Name does not exist or has no suitable associated
+// IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
+// - `temporary-resolver-failure`: A temporary failure in name resolution occurred.
+// (EAI_AGAIN)
+// - `permanent-resolver-failure`: A permanent failure in name resolution occurred.
+// (EAI_FAIL)
+// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN)
+//
+// resolve-next-address: func() -> result<option<ip-address>, error-code>
+//
+//go:nosplit
+func (self ResolveAddressStream) ResolveNextAddress() (result cm.OKResult[cm.Option[network.IPAddress], network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_ResolveAddressStreamResolveNextAddress((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]resolve-address-stream.resolve-next-address
+//go:noescape
+func wasmimport_ResolveAddressStreamResolveNextAddress(self0 uint32, result *cm.OKResult[cm.Option[network.IPAddress], network.ErrorCode])
+
+// Subscribe represents the imported method "subscribe".
+//
+// Create a `pollable` which will resolve once the stream is ready for I/O.
+//
+// Note: this function is here for WASI Preview2 only.
+// It's planned to be removed when `future` is natively supported in Preview3.
+//
+// subscribe: func() -> pollable
+//
+//go:nosplit
+func (self ResolveAddressStream) Subscribe() (result poll.Pollable) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_ResolveAddressStreamSubscribe((uint32)(self0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]resolve-address-stream.subscribe
+//go:noescape
+func wasmimport_ResolveAddressStreamSubscribe(self0 uint32) (result0 uint32)
+
+// ResolveAddresses represents the imported function "resolve-addresses".
+//
+// Resolve an internet host name to a list of IP addresses.
+//
+// Unicode domain names are automatically converted to ASCII using IDNA encoding.
+// If the input is an IP address string, the address is parsed and returned
+// as-is without making any external requests.
+//
+// See the wasi-socket proposal README.md for a comparison with getaddrinfo.
+//
+// This function never blocks. It either immediately fails or immediately
+// returns successfully with a `resolve-address-stream` that can be used
+// to (asynchronously) fetch the results.
+//
+// # Typical errors
+// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address.
+//
+// # References:
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html>
+// - <https://man7.org/linux/man-pages/man3/getaddrinfo.3.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo>
+// - <https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&sektion=3>
+//
+// resolve-addresses: func(network: borrow<network>, name: string) -> result<resolve-address-stream,
+// error-code>
+//
+//go:nosplit
+func ResolveAddresses(network_ network.Network, name string) (result cm.OKResult[ResolveAddressStream, network.ErrorCode]) {
+ network0 := cm.Reinterpret[uint32](network_)
+ name0, name1 := cm.LowerString(name)
+ wasmimport_ResolveAddresses((uint32)(network0), (*uint8)(name0), (uint32)(name1), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] resolve-addresses
+//go:noescape
+func wasmimport_ResolveAddresses(network0 uint32, name0 *uint8, name1 uint32, result *cm.OKResult[ResolveAddressStream, network.ErrorCode])
diff --git a/src/internal/wasi/sockets/v0.2.0/network/empty.s b/src/internal/wasi/sockets/v0.2.0/network/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/network/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/sockets/v0.2.0/network/network.wit.go b/src/internal/wasi/sockets/v0.2.0/network/network.wit.go
new file mode 100644
index 000000000..4cca93aca
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/network/network.wit.go
@@ -0,0 +1,278 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package network represents the imported interface "wasi:sockets/[email protected]".
+package network
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+// Network represents the imported resource "wasi:sockets/[email protected]#network".
+//
+// An opaque resource that represents access to (a subset of) the network.
+// This enables context-based security for networking.
+// There is no need for this to map 1:1 to a physical network interface.
+//
+// resource network
+type Network cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "network".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self Network) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_NetworkResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [resource-drop]network
+//go:noescape
+func wasmimport_NetworkResourceDrop(self0 uint32)
+
+// ErrorCode represents the enum "wasi:sockets/[email protected]#error-code".
+//
+// Error codes.
+//
+// In theory, every API can return any error code.
+// In practice, API's typically only return the errors documented per API
+// combined with a couple of errors that are always possible:
+// - `unknown`
+// - `access-denied`
+// - `not-supported`
+// - `out-of-memory`
+// - `concurrency-conflict`
+//
+// See each individual API for what the POSIX equivalents are. They sometimes differ
+// per API.
+//
+// enum error-code {
+// unknown,
+// access-denied,
+// not-supported,
+// invalid-argument,
+// out-of-memory,
+// timeout,
+// concurrency-conflict,
+// not-in-progress,
+// would-block,
+// invalid-state,
+// new-socket-limit,
+// address-not-bindable,
+// address-in-use,
+// remote-unreachable,
+// connection-refused,
+// connection-reset,
+// connection-aborted,
+// datagram-too-large,
+// name-unresolvable,
+// temporary-resolver-failure,
+// permanent-resolver-failure
+// }
+type ErrorCode uint8
+
+const (
+ // Unknown error
+ ErrorCodeUnknown ErrorCode = iota
+
+ // Access denied.
+ //
+ // POSIX equivalent: EACCES, EPERM
+ ErrorCodeAccessDenied
+
+ // The operation is not supported.
+ //
+ // POSIX equivalent: EOPNOTSUPP
+ ErrorCodeNotSupported
+
+ // One of the arguments is invalid.
+ //
+ // POSIX equivalent: EINVAL
+ ErrorCodeInvalidArgument
+
+ // Not enough memory to complete the operation.
+ //
+ // POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
+ ErrorCodeOutOfMemory
+
+ // The operation timed out before it could finish completely.
+ ErrorCodeTimeout
+
+ // This operation is incompatible with another asynchronous operation that is already
+ // in progress.
+ //
+ // POSIX equivalent: EALREADY
+ ErrorCodeConcurrencyConflict
+
+ // Trying to finish an asynchronous operation that:
+ // - has not been started yet, or:
+ // - was already finished by a previous `finish-*` call.
+ //
+ // Note: this is scheduled to be removed when `future`s are natively supported.
+ ErrorCodeNotInProgress
+
+ // The operation has been aborted because it could not be completed immediately.
+ //
+ // Note: this is scheduled to be removed when `future`s are natively supported.
+ ErrorCodeWouldBlock
+
+ // The operation is not valid in the socket's current state.
+ ErrorCodeInvalidState
+
+ // A new socket resource could not be created because of a system limit.
+ ErrorCodeNewSocketLimit
+
+ // A bind operation failed because the provided address is not an address that the
+ // `network` can bind to.
+ ErrorCodeAddressNotBindable
+
+ // A bind operation failed because the provided address is already in use or because
+ // there are no ephemeral ports available.
+ ErrorCodeAddressInUse
+
+ // The remote address is not reachable
+ ErrorCodeRemoteUnreachable
+
+ // The TCP connection was forcefully rejected
+ ErrorCodeConnectionRefused
+
+ // The TCP connection was reset.
+ ErrorCodeConnectionReset
+
+ // A TCP connection was aborted.
+ ErrorCodeConnectionAborted
+
+ // The size of a datagram sent to a UDP socket exceeded the maximum
+ // supported size.
+ ErrorCodeDatagramTooLarge
+
+ // Name does not exist or has no suitable associated IP addresses.
+ ErrorCodeNameUnresolvable
+
+ // A temporary failure in name resolution occurred.
+ ErrorCodeTemporaryResolverFailure
+
+ // A permanent failure in name resolution occurred.
+ ErrorCodePermanentResolverFailure
+)
+
+// IPAddressFamily represents the enum "wasi:sockets/[email protected]#ip-address-family".
+//
+// enum ip-address-family {
+// ipv4,
+// ipv6
+// }
+type IPAddressFamily uint8
+
+const (
+ // Similar to `AF_INET` in POSIX.
+ IPAddressFamilyIPv4 IPAddressFamily = iota
+
+ // Similar to `AF_INET6` in POSIX.
+ IPAddressFamilyIPv6
+)
+
+// IPv4Address represents the tuple "wasi:sockets/[email protected]#ipv4-address".
+//
+// type ipv4-address = tuple<u8, u8, u8, u8>
+type IPv4Address [4]uint8
+
+// IPv6Address represents the tuple "wasi:sockets/[email protected]#ipv6-address".
+//
+// type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>
+type IPv6Address [8]uint16
+
+// IPAddress represents the variant "wasi:sockets/[email protected]#ip-address".
+//
+// variant ip-address {
+// ipv4(ipv4-address),
+// ipv6(ipv6-address),
+// }
+type IPAddress cm.Variant[uint8, IPv6Address, IPv6Address]
+
+// IPAddressIPv4 returns a [IPAddress] of case "ipv4".
+func IPAddressIPv4(data IPv4Address) IPAddress {
+ return cm.New[IPAddress](0, data)
+}
+
+// IPv4 returns a non-nil *[IPv4Address] if [IPAddress] represents the variant case "ipv4".
+func (self *IPAddress) IPv4() *IPv4Address {
+ return cm.Case[IPv4Address](self, 0)
+}
+
+// IPAddressIPv6 returns a [IPAddress] of case "ipv6".
+func IPAddressIPv6(data IPv6Address) IPAddress {
+ return cm.New[IPAddress](1, data)
+}
+
+// IPv6 returns a non-nil *[IPv6Address] if [IPAddress] represents the variant case "ipv6".
+func (self *IPAddress) IPv6() *IPv6Address {
+ return cm.Case[IPv6Address](self, 1)
+}
+
+// IPv4SocketAddress represents the record "wasi:sockets/[email protected]#ipv4-socket-address".
+//
+// record ipv4-socket-address {
+// port: u16,
+// address: ipv4-address,
+// }
+type IPv4SocketAddress struct {
+ // sin_port
+ Port uint16
+
+ // sin_addr
+ Address IPv4Address
+}
+
+// IPv6SocketAddress represents the record "wasi:sockets/[email protected]#ipv6-socket-address".
+//
+// record ipv6-socket-address {
+// port: u16,
+// flow-info: u32,
+// address: ipv6-address,
+// scope-id: u32,
+// }
+type IPv6SocketAddress struct {
+ // sin6_port
+ Port uint16
+
+ // sin6_flowinfo
+ FlowInfo uint32
+
+ // sin6_addr
+ Address IPv6Address
+
+ // sin6_scope_id
+ ScopeID uint32
+}
+
+// IPSocketAddress represents the variant "wasi:sockets/[email protected]#ip-socket-address".
+//
+// variant ip-socket-address {
+// ipv4(ipv4-socket-address),
+// ipv6(ipv6-socket-address),
+// }
+type IPSocketAddress cm.Variant[uint8, IPv6SocketAddress, IPv6SocketAddress]
+
+// IPSocketAddressIPv4 returns a [IPSocketAddress] of case "ipv4".
+func IPSocketAddressIPv4(data IPv4SocketAddress) IPSocketAddress {
+ return cm.New[IPSocketAddress](0, data)
+}
+
+// IPv4 returns a non-nil *[IPv4SocketAddress] if [IPSocketAddress] represents the variant case "ipv4".
+func (self *IPSocketAddress) IPv4() *IPv4SocketAddress {
+ return cm.Case[IPv4SocketAddress](self, 0)
+}
+
+// IPSocketAddressIPv6 returns a [IPSocketAddress] of case "ipv6".
+func IPSocketAddressIPv6(data IPv6SocketAddress) IPSocketAddress {
+ return cm.New[IPSocketAddress](1, data)
+}
+
+// IPv6 returns a non-nil *[IPv6SocketAddress] if [IPSocketAddress] represents the variant case "ipv6".
+func (self *IPSocketAddress) IPv6() *IPv6SocketAddress {
+ return cm.Case[IPv6SocketAddress](self, 1)
+}
diff --git a/src/internal/wasi/sockets/v0.2.0/tcp-create-socket/empty.s b/src/internal/wasi/sockets/v0.2.0/tcp-create-socket/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/tcp-create-socket/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/sockets/v0.2.0/tcp-create-socket/tcp-create-socket.wit.go b/src/internal/wasi/sockets/v0.2.0/tcp-create-socket/tcp-create-socket.wit.go
new file mode 100644
index 000000000..906128abe
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/tcp-create-socket/tcp-create-socket.wit.go
@@ -0,0 +1,54 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package tcpcreatesocket represents the imported interface "wasi:sockets/[email protected]".
+package tcpcreatesocket
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/sockets/v0.2.0/network"
+ "internal/wasi/sockets/v0.2.0/tcp"
+)
+
+// CreateTCPSocket represents the imported function "create-tcp-socket".
+//
+// Create a new TCP socket.
+//
+// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
+// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
+//
+// This function does not require a network capability handle. This is considered
+// to be safe because
+// at time of creation, the socket is not bound to any `network` yet. Up to the moment
+// `bind`/`connect`
+// is called, the socket is effectively an in-memory configuration object, unable
+// to communicate with the outside world.
+//
+// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous
+// operations.
+//
+// # Typical errors
+// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT)
+// - `new-socket-limit`: The new socket resource could not be created because of
+// a system limit. (EMFILE, ENFILE)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+//
+// create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket,
+// error-code>
+//
+//go:nosplit
+func CreateTCPSocket(addressFamily network.IPAddressFamily) (result cm.OKResult[tcp.TCPSocket, network.ErrorCode]) {
+ addressFamily0 := (uint32)(addressFamily)
+ wasmimport_CreateTCPSocket((uint32)(addressFamily0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] create-tcp-socket
+//go:noescape
+func wasmimport_CreateTCPSocket(addressFamily0 uint32, result *cm.OKResult[tcp.TCPSocket, network.ErrorCode])
diff --git a/src/internal/wasi/sockets/v0.2.0/tcp/abi.go b/src/internal/wasi/sockets/v0.2.0/tcp/abi.go
new file mode 100644
index 000000000..fecab79fd
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/tcp/abi.go
@@ -0,0 +1,71 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+package tcp
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/sockets/v0.2.0/network"
+)
+
+func lower_IPv4Address(v network.IPv4Address) (f0 uint32, f1 uint32, f2 uint32, f3 uint32) {
+ f0 = (uint32)(v[0])
+ f1 = (uint32)(v[1])
+ f2 = (uint32)(v[2])
+ f3 = (uint32)(v[3])
+ return
+}
+
+func lower_IPv4SocketAddress(v network.IPv4SocketAddress) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32) {
+ f0 = (uint32)(v.Port)
+ f1, f2, f3, f4 = lower_IPv4Address(v.Address)
+ return
+}
+
+func lower_IPv6Address(v network.IPv6Address) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32, f5 uint32, f6 uint32, f7 uint32) {
+ f0 = (uint32)(v[0])
+ f1 = (uint32)(v[1])
+ f2 = (uint32)(v[2])
+ f3 = (uint32)(v[3])
+ f4 = (uint32)(v[4])
+ f5 = (uint32)(v[5])
+ f6 = (uint32)(v[6])
+ f7 = (uint32)(v[7])
+ return
+}
+
+func lower_IPv6SocketAddress(v network.IPv6SocketAddress) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32, f5 uint32, f6 uint32, f7 uint32, f8 uint32, f9 uint32, f10 uint32) {
+ f0 = (uint32)(v.Port)
+ f1 = (uint32)(v.FlowInfo)
+ f2, f3, f4, f5, f6, f7, f8, f9 = lower_IPv6Address(v.Address)
+ f10 = (uint32)(v.ScopeID)
+ return
+}
+
+func lower_IPSocketAddress(v network.IPSocketAddress) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32, f5 uint32, f6 uint32, f7 uint32, f8 uint32, f9 uint32, f10 uint32, f11 uint32) {
+ f0 = (uint32)(cm.Tag(&v))
+ switch f0 {
+ case 0: // ipv4
+ v1, v2, v3, v4, v5 := lower_IPv4SocketAddress(*v.IPv4())
+ f1 = (uint32)(v1)
+ f2 = (uint32)(v2)
+ f3 = (uint32)(v3)
+ f4 = (uint32)(v4)
+ f5 = (uint32)(v5)
+ case 1: // ipv6
+ v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 := lower_IPv6SocketAddress(*v.IPv6())
+ f1 = (uint32)(v1)
+ f2 = (uint32)(v2)
+ f3 = (uint32)(v3)
+ f4 = (uint32)(v4)
+ f5 = (uint32)(v5)
+ f6 = (uint32)(v6)
+ f7 = (uint32)(v7)
+ f8 = (uint32)(v8)
+ f9 = (uint32)(v9)
+ f10 = (uint32)(v10)
+ f11 = (uint32)(v11)
+ }
+ return
+}
diff --git a/src/internal/wasi/sockets/v0.2.0/tcp/empty.s b/src/internal/wasi/sockets/v0.2.0/tcp/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/tcp/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/sockets/v0.2.0/tcp/tcp.wit.go b/src/internal/wasi/sockets/v0.2.0/tcp/tcp.wit.go
new file mode 100644
index 000000000..f390849cb
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/tcp/tcp.wit.go
@@ -0,0 +1,839 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package tcp represents the imported interface "wasi:sockets/[email protected]".
+package tcp
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ monotonicclock "internal/wasi/clocks/v0.2.0/monotonic-clock"
+ "internal/wasi/io/v0.2.0/poll"
+ "internal/wasi/io/v0.2.0/streams"
+ "internal/wasi/sockets/v0.2.0/network"
+)
+
+// ShutdownType represents the enum "wasi:sockets/[email protected]#shutdown-type".
+//
+// enum shutdown-type {
+// receive,
+// send,
+// both
+// }
+type ShutdownType uint8
+
+const (
+ // Similar to `SHUT_RD` in POSIX.
+ ShutdownTypeReceive ShutdownType = iota
+
+ // Similar to `SHUT_WR` in POSIX.
+ ShutdownTypeSend
+
+ // Similar to `SHUT_RDWR` in POSIX.
+ ShutdownTypeBoth
+)
+
+// TCPSocket represents the imported resource "wasi:sockets/[email protected]#tcp-socket".
+//
+// A TCP socket resource.
+//
+// The socket can be in one of the following states:
+// - `unbound`
+// - `bind-in-progress`
+// - `bound` (See note below)
+// - `listen-in-progress`
+// - `listening`
+// - `connect-in-progress`
+// - `connected`
+// - `closed`
+// See <https://github.com/WebAssembly/wasi-sockets/TcpSocketOperationalSemantics.md>
+// for a more information.
+//
+// Note: Except where explicitly mentioned, whenever this documentation uses
+// the term "bound" without backticks it actually means: in the `bound` state *or
+// higher*.
+// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`)
+//
+// In addition to the general error codes documented on the
+// `network::error-code` type, TCP socket methods may always return
+// `error(invalid-state)` when in the `closed` state.
+//
+// resource tcp-socket
+type TCPSocket cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "tcp-socket".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self TCPSocket) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [resource-drop]tcp-socket
+//go:noescape
+func wasmimport_TCPSocketResourceDrop(self0 uint32)
+
+// Accept represents the imported method "accept".
+//
+// Accept a new client socket.
+//
+// The returned socket is bound and in the `connected` state. The following properties
+// are inherited from the listener socket:
+// - `address-family`
+// - `keep-alive-enabled`
+// - `keep-alive-idle-time`
+// - `keep-alive-interval`
+// - `keep-alive-count`
+// - `hop-limit`
+// - `receive-buffer-size`
+// - `send-buffer-size`
+//
+// On success, this function returns the newly accepted client socket along with
+// a pair of streams that can be used to read & write to the connection.
+//
+// # Typical errors
+// - `invalid-state`: Socket is not in the `listening` state. (EINVAL)
+// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
+// - `connection-aborted`: An incoming connection was pending, but was terminated
+// by the client before this listener could accept it. (ECONNABORTED)
+// - `new-socket-limit`: The new socket resource could not be created because of
+// a system limit. (EMFILE, ENFILE)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html>
+// - <https://man7.org/linux/man-pages/man2/accept.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept>
+// - <https://man.freebsd.org/cgi/man.cgi?query=accept&sektion=2>
+//
+// accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>
+//
+//go:nosplit
+func (self TCPSocket) Accept() (result cm.OKResult[cm.Tuple3[TCPSocket, streams.InputStream, streams.OutputStream], network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketAccept((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.accept
+//go:noescape
+func wasmimport_TCPSocketAccept(self0 uint32, result *cm.OKResult[cm.Tuple3[TCPSocket, streams.InputStream, streams.OutputStream], network.ErrorCode])
+
+// AddressFamily represents the imported method "address-family".
+//
+// Whether this is a IPv4 or IPv6 socket.
+//
+// Equivalent to the SO_DOMAIN socket option.
+//
+// address-family: func() -> ip-address-family
+//
+//go:nosplit
+func (self TCPSocket) AddressFamily() (result network.IPAddressFamily) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_TCPSocketAddressFamily((uint32)(self0))
+ result = (network.IPAddressFamily)((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.address-family
+//go:noescape
+func wasmimport_TCPSocketAddressFamily(self0 uint32) (result0 uint32)
+
+// FinishBind represents the imported method "finish-bind".
+//
+// finish-bind: func() -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) FinishBind() (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketFinishBind((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.finish-bind
+//go:noescape
+func wasmimport_TCPSocketFinishBind(self0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// FinishConnect represents the imported method "finish-connect".
+//
+// finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>
+//
+//go:nosplit
+func (self TCPSocket) FinishConnect() (result cm.OKResult[cm.Tuple[streams.InputStream, streams.OutputStream], network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketFinishConnect((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.finish-connect
+//go:noescape
+func wasmimport_TCPSocketFinishConnect(self0 uint32, result *cm.OKResult[cm.Tuple[streams.InputStream, streams.OutputStream], network.ErrorCode])
+
+// FinishListen represents the imported method "finish-listen".
+//
+// finish-listen: func() -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) FinishListen() (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketFinishListen((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.finish-listen
+//go:noescape
+func wasmimport_TCPSocketFinishListen(self0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// HopLimit represents the imported method "hop-limit".
+//
+// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+//
+// # Typical errors
+// - `invalid-argument`: (set) The TTL value must be 1 or higher.
+//
+// hop-limit: func() -> result<u8, error-code>
+//
+//go:nosplit
+func (self TCPSocket) HopLimit() (result cm.OKResult[uint8, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketHopLimit((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.hop-limit
+//go:noescape
+func wasmimport_TCPSocketHopLimit(self0 uint32, result *cm.OKResult[uint8, network.ErrorCode])
+
+// IsListening represents the imported method "is-listening".
+//
+// Whether the socket is in the `listening` state.
+//
+// Equivalent to the SO_ACCEPTCONN socket option.
+//
+// is-listening: func() -> bool
+//
+//go:nosplit
+func (self TCPSocket) IsListening() (result bool) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_TCPSocketIsListening((uint32)(self0))
+ result = cm.U32ToBool((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.is-listening
+//go:noescape
+func wasmimport_TCPSocketIsListening(self0 uint32) (result0 uint32)
+
+// KeepAliveCount represents the imported method "keep-alive-count".
+//
+// The maximum amount of keepalive packets TCP should send before aborting the connection.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+// Any other value will never cause an error, but it might be silently clamped and/or
+// rounded.
+// I.e. after setting a value, reading the same setting back may return a different
+// value.
+//
+// Equivalent to the TCP_KEEPCNT socket option.
+//
+// # Typical errors
+// - `invalid-argument`: (set) The provided value was 0.
+//
+// keep-alive-count: func() -> result<u32, error-code>
+//
+//go:nosplit
+func (self TCPSocket) KeepAliveCount() (result cm.OKResult[uint32, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketKeepAliveCount((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.keep-alive-count
+//go:noescape
+func wasmimport_TCPSocketKeepAliveCount(self0 uint32, result *cm.OKResult[uint32, network.ErrorCode])
+
+// KeepAliveEnabled represents the imported method "keep-alive-enabled".
+//
+// Enables or disables keepalive.
+//
+// The keepalive behavior can be adjusted using:
+// - `keep-alive-idle-time`
+// - `keep-alive-interval`
+// - `keep-alive-count`
+// These properties can be configured while `keep-alive-enabled` is false, but only
+// come into effect when `keep-alive-enabled` is true.
+//
+// Equivalent to the SO_KEEPALIVE socket option.
+//
+// keep-alive-enabled: func() -> result<bool, error-code>
+//
+//go:nosplit
+func (self TCPSocket) KeepAliveEnabled() (result cm.OKResult[bool, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketKeepAliveEnabled((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.keep-alive-enabled
+//go:noescape
+func wasmimport_TCPSocketKeepAliveEnabled(self0 uint32, result *cm.OKResult[bool, network.ErrorCode])
+
+// KeepAliveIdleTime represents the imported method "keep-alive-idle-time".
+//
+// Amount of time the connection has to be idle before TCP starts sending keepalive
+// packets.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+// Any other value will never cause an error, but it might be silently clamped and/or
+// rounded.
+// I.e. after setting a value, reading the same setting back may return a different
+// value.
+//
+// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS)
+//
+// # Typical errors
+// - `invalid-argument`: (set) The provided value was 0.
+//
+// keep-alive-idle-time: func() -> result<duration, error-code>
+//
+//go:nosplit
+func (self TCPSocket) KeepAliveIdleTime() (result cm.OKResult[monotonicclock.Duration, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketKeepAliveIdleTime((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.keep-alive-idle-time
+//go:noescape
+func wasmimport_TCPSocketKeepAliveIdleTime(self0 uint32, result *cm.OKResult[monotonicclock.Duration, network.ErrorCode])
+
+// KeepAliveInterval represents the imported method "keep-alive-interval".
+//
+// The time between keepalive packets.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+// Any other value will never cause an error, but it might be silently clamped and/or
+// rounded.
+// I.e. after setting a value, reading the same setting back may return a different
+// value.
+//
+// Equivalent to the TCP_KEEPINTVL socket option.
+//
+// # Typical errors
+// - `invalid-argument`: (set) The provided value was 0.
+//
+// keep-alive-interval: func() -> result<duration, error-code>
+//
+//go:nosplit
+func (self TCPSocket) KeepAliveInterval() (result cm.OKResult[monotonicclock.Duration, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketKeepAliveInterval((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.keep-alive-interval
+//go:noescape
+func wasmimport_TCPSocketKeepAliveInterval(self0 uint32, result *cm.OKResult[monotonicclock.Duration, network.ErrorCode])
+
+// LocalAddress represents the imported method "local-address".
+//
+// Get the bound local address.
+//
+// POSIX mentions:
+// > If the socket has not been bound to a local name, the value
+// > stored in the object pointed to by `address` is unspecified.
+//
+// WASI is stricter and requires `local-address` to return `invalid-state` when the
+// socket hasn't been bound yet.
+//
+// # Typical errors
+// - `invalid-state`: The socket is not bound to any local address.
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+//
+// local-address: func() -> result<ip-socket-address, error-code>
+//
+//go:nosplit
+func (self TCPSocket) LocalAddress() (result cm.OKResult[network.IPSocketAddress, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketLocalAddress((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.local-address
+//go:noescape
+func wasmimport_TCPSocketLocalAddress(self0 uint32, result *cm.OKResult[network.IPSocketAddress, network.ErrorCode])
+
+// ReceiveBufferSize represents the imported method "receive-buffer-size".
+//
+// The kernel buffer space reserved for sends/receives on this socket.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+// Any other value will never cause an error, but it might be silently clamped and/or
+// rounded.
+// I.e. after setting a value, reading the same setting back may return a different
+// value.
+//
+// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+//
+// # Typical errors
+// - `invalid-argument`: (set) The provided value was 0.
+//
+// receive-buffer-size: func() -> result<u64, error-code>
+//
+//go:nosplit
+func (self TCPSocket) ReceiveBufferSize() (result cm.OKResult[uint64, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketReceiveBufferSize((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.receive-buffer-size
+//go:noescape
+func wasmimport_TCPSocketReceiveBufferSize(self0 uint32, result *cm.OKResult[uint64, network.ErrorCode])
+
+// RemoteAddress represents the imported method "remote-address".
+//
+// Get the remote address.
+//
+// # Typical errors
+// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+//
+// remote-address: func() -> result<ip-socket-address, error-code>
+//
+//go:nosplit
+func (self TCPSocket) RemoteAddress() (result cm.OKResult[network.IPSocketAddress, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketRemoteAddress((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.remote-address
+//go:noescape
+func wasmimport_TCPSocketRemoteAddress(self0 uint32, result *cm.OKResult[network.IPSocketAddress, network.ErrorCode])
+
+// SendBufferSize represents the imported method "send-buffer-size".
+//
+// send-buffer-size: func() -> result<u64, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SendBufferSize() (result cm.OKResult[uint64, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketSendBufferSize((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.send-buffer-size
+//go:noescape
+func wasmimport_TCPSocketSendBufferSize(self0 uint32, result *cm.OKResult[uint64, network.ErrorCode])
+
+// SetHopLimit represents the imported method "set-hop-limit".
+//
+// set-hop-limit: func(value: u8) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetHopLimit(value uint8) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint32)(value)
+ wasmimport_TCPSocketSetHopLimit((uint32)(self0), (uint32)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-hop-limit
+//go:noescape
+func wasmimport_TCPSocketSetHopLimit(self0 uint32, value0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetKeepAliveCount represents the imported method "set-keep-alive-count".
+//
+// set-keep-alive-count: func(value: u32) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetKeepAliveCount(value uint32) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint32)(value)
+ wasmimport_TCPSocketSetKeepAliveCount((uint32)(self0), (uint32)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-keep-alive-count
+//go:noescape
+func wasmimport_TCPSocketSetKeepAliveCount(self0 uint32, value0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetKeepAliveEnabled represents the imported method "set-keep-alive-enabled".
+//
+// set-keep-alive-enabled: func(value: bool) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetKeepAliveEnabled(value bool) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := cm.BoolToU32(value)
+ wasmimport_TCPSocketSetKeepAliveEnabled((uint32)(self0), (uint32)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-keep-alive-enabled
+//go:noescape
+func wasmimport_TCPSocketSetKeepAliveEnabled(self0 uint32, value0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetKeepAliveIdleTime represents the imported method "set-keep-alive-idle-time".
+//
+// set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetKeepAliveIdleTime(value monotonicclock.Duration) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint64)(value)
+ wasmimport_TCPSocketSetKeepAliveIdleTime((uint32)(self0), (uint64)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-keep-alive-idle-time
+//go:noescape
+func wasmimport_TCPSocketSetKeepAliveIdleTime(self0 uint32, value0 uint64, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetKeepAliveInterval represents the imported method "set-keep-alive-interval".
+//
+// set-keep-alive-interval: func(value: duration) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetKeepAliveInterval(value monotonicclock.Duration) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint64)(value)
+ wasmimport_TCPSocketSetKeepAliveInterval((uint32)(self0), (uint64)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-keep-alive-interval
+//go:noescape
+func wasmimport_TCPSocketSetKeepAliveInterval(self0 uint32, value0 uint64, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetListenBacklogSize represents the imported method "set-listen-backlog-size".
+//
+// Hints the desired listen queue size. Implementations are free to ignore this.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+// Any other value will never cause an error, but it might be silently clamped and/or
+// rounded.
+//
+// # Typical errors
+// - `not-supported`: (set) The platform does not support changing the backlog
+// size after the initial listen.
+// - `invalid-argument`: (set) The provided value was 0.
+// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected`
+// state.
+//
+// set-listen-backlog-size: func(value: u64) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetListenBacklogSize(value uint64) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint64)(value)
+ wasmimport_TCPSocketSetListenBacklogSize((uint32)(self0), (uint64)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-listen-backlog-size
+//go:noescape
+func wasmimport_TCPSocketSetListenBacklogSize(self0 uint32, value0 uint64, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetReceiveBufferSize represents the imported method "set-receive-buffer-size".
+//
+// set-receive-buffer-size: func(value: u64) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetReceiveBufferSize(value uint64) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint64)(value)
+ wasmimport_TCPSocketSetReceiveBufferSize((uint32)(self0), (uint64)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-receive-buffer-size
+//go:noescape
+func wasmimport_TCPSocketSetReceiveBufferSize(self0 uint32, value0 uint64, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetSendBufferSize represents the imported method "set-send-buffer-size".
+//
+// set-send-buffer-size: func(value: u64) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) SetSendBufferSize(value uint64) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint64)(value)
+ wasmimport_TCPSocketSetSendBufferSize((uint32)(self0), (uint64)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.set-send-buffer-size
+//go:noescape
+func wasmimport_TCPSocketSetSendBufferSize(self0 uint32, value0 uint64, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// Shutdown represents the imported method "shutdown".
+//
+// Initiate a graceful shutdown.
+//
+// - `receive`: The socket is not expecting to receive any data from
+// the peer. The `input-stream` associated with this socket will be
+// closed. Any data still in the receive queue at time of calling
+// this method will be discarded.
+// - `send`: The socket has no more data to send to the peer. The `output-stream`
+// associated with this socket will be closed and a FIN packet will be sent.
+// - `both`: Same effect as `receive` & `send` combined.
+//
+// This function is idempotent. Shutting a down a direction more than once
+// has no effect and returns `ok`.
+//
+// The shutdown function does not close (drop) the socket.
+//
+// # Typical errors
+// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html>
+// - <https://man7.org/linux/man-pages/man2/shutdown.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown>
+// - <https://man.freebsd.org/cgi/man.cgi?query=shutdown&sektion=2>
+//
+// shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) Shutdown(shutdownType ShutdownType) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ shutdownType0 := (uint32)(shutdownType)
+ wasmimport_TCPSocketShutdown((uint32)(self0), (uint32)(shutdownType0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.shutdown
+//go:noescape
+func wasmimport_TCPSocketShutdown(self0 uint32, shutdownType0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// StartBind represents the imported method "start-bind".
+//
+// Bind the socket to a specific network on the provided IP address and port.
+//
+// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the
+// implementation to decide which
+// network interface(s) to bind to.
+// If the TCP/UDP port is zero, the socket will be bound to a random free port.
+//
+// Bind can be attempted multiple times on the same socket, even with
+// different arguments on each iteration. But never concurrently and
+// only as long as the previous bind failed. Once a bind succeeds, the
+// binding can't be changed anymore.
+//
+// # Typical errors
+// - `invalid-argument`: The `local-address` has the wrong address family.
+// (EAFNOSUPPORT, EFAULT on Windows)
+// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL)
+// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address.
+// (EINVAL)
+// - `invalid-state`: The socket is already bound. (EINVAL)
+// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS
+// on Windows)
+// - `address-in-use`: Address is already in use. (EADDRINUSE)
+// - `address-not-bindable`: `local-address` is not an address that the `network`
+// can bind to. (EADDRNOTAVAIL)
+// - `not-in-progress`: A `bind` operation is not in progress.
+// - `would-block`: Can't finish the operation, it is still in progress.
+// (EWOULDBLOCK, EAGAIN)
+//
+// # Implementors note
+// When binding to a non-zero port, this bind operation shouldn't be affected by the
+// TIME_WAIT
+// state of a recently closed socket on the same local address. In practice this means
+// that the SO_REUSEADDR
+// socket option should be set implicitly on all platforms, except on Windows where
+// this is the default behavior
+// and SO_REUSEADDR performs something different entirely.
+//
+// Unlike in POSIX, in WASI the bind operation is async. This enables
+// interactive WASI hosts to inject permission prompts. Runtimes that
+// don't want to make use of this ability can simply call the native
+// `bind` as part of either `start-bind` or `finish-bind`.
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+//
+// start-bind: func(network: borrow<network>, local-address: ip-socket-address) ->
+// result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) StartBind(network_ network.Network, localAddress network.IPSocketAddress) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ network0 := cm.Reinterpret[uint32](network_)
+ localAddress0, localAddress1, localAddress2, localAddress3, localAddress4, localAddress5, localAddress6, localAddress7, localAddress8, localAddress9, localAddress10, localAddress11 := lower_IPSocketAddress(localAddress)
+ wasmimport_TCPSocketStartBind((uint32)(self0), (uint32)(network0), (uint32)(localAddress0), (uint32)(localAddress1), (uint32)(localAddress2), (uint32)(localAddress3), (uint32)(localAddress4), (uint32)(localAddress5), (uint32)(localAddress6), (uint32)(localAddress7), (uint32)(localAddress8), (uint32)(localAddress9), (uint32)(localAddress10), (uint32)(localAddress11), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.start-bind
+//go:noescape
+func wasmimport_TCPSocketStartBind(self0 uint32, network0 uint32, localAddress0 uint32, localAddress1 uint32, localAddress2 uint32, localAddress3 uint32, localAddress4 uint32, localAddress5 uint32, localAddress6 uint32, localAddress7 uint32, localAddress8 uint32, localAddress9 uint32, localAddress10 uint32, localAddress11 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// StartConnect represents the imported method "start-connect".
+//
+// Connect to a remote endpoint.
+//
+// On success:
+// - the socket is transitioned into the `connection` state.
+// - a pair of streams is returned that can be used to read & write to the connection
+//
+// After a failed connection attempt, the socket will be in the `closed`
+// state and the only valid action left is to `drop` the socket. A single
+// socket can not be used to connect more than once.
+//
+// # Typical errors
+// - `invalid-argument`: The `remote-address` has the wrong address family.
+// (EAFNOSUPPORT)
+// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL,
+// ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
+// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address.
+// (EINVAL, EADDRNOTAVAIL on Illumos)
+// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY
+// (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
+// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL
+// on Windows)
+// - `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`.
+// - `invalid-state`: The socket is already in the `connected` state.
+// (EISCONN)
+// - `invalid-state`: The socket is already in the `listening` state.
+// (EOPNOTSUPP, EINVAL on Windows)
+// - `timeout`: Connection timed out. (ETIMEDOUT)
+// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED)
+// - `connection-reset`: The connection was reset. (ECONNRESET)
+// - `connection-aborted`: The connection was aborted. (ECONNABORTED)
+// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH,
+// EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+// - `address-in-use`: Tried to perform an implicit bind, but there were
+// no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+// - `not-in-progress`: A connect operation is not in progress.
+// - `would-block`: Can't finish the operation, it is still in progress.
+// (EWOULDBLOCK, EAGAIN)
+//
+// # Implementors note
+// The POSIX equivalent of `start-connect` is the regular `connect` syscall.
+// Because all WASI sockets are non-blocking this is expected to return
+// EINPROGRESS, which should be translated to `ok()` in WASI.
+//
+// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT`
+// with a timeout of 0 on the socket descriptor. Followed by a check for
+// the `SO_ERROR` socket option, in case the poll signaled readiness.
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+// - <https://man.freebsd.org/cgi/man.cgi?connect>
+//
+// start-connect: func(network: borrow<network>, remote-address: ip-socket-address)
+// -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) StartConnect(network_ network.Network, remoteAddress network.IPSocketAddress) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ network0 := cm.Reinterpret[uint32](network_)
+ remoteAddress0, remoteAddress1, remoteAddress2, remoteAddress3, remoteAddress4, remoteAddress5, remoteAddress6, remoteAddress7, remoteAddress8, remoteAddress9, remoteAddress10, remoteAddress11 := lower_IPSocketAddress(remoteAddress)
+ wasmimport_TCPSocketStartConnect((uint32)(self0), (uint32)(network0), (uint32)(remoteAddress0), (uint32)(remoteAddress1), (uint32)(remoteAddress2), (uint32)(remoteAddress3), (uint32)(remoteAddress4), (uint32)(remoteAddress5), (uint32)(remoteAddress6), (uint32)(remoteAddress7), (uint32)(remoteAddress8), (uint32)(remoteAddress9), (uint32)(remoteAddress10), (uint32)(remoteAddress11), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.start-connect
+//go:noescape
+func wasmimport_TCPSocketStartConnect(self0 uint32, network0 uint32, remoteAddress0 uint32, remoteAddress1 uint32, remoteAddress2 uint32, remoteAddress3 uint32, remoteAddress4 uint32, remoteAddress5 uint32, remoteAddress6 uint32, remoteAddress7 uint32, remoteAddress8 uint32, remoteAddress9 uint32, remoteAddress10 uint32, remoteAddress11 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// StartListen represents the imported method "start-listen".
+//
+// Start listening for new connections.
+//
+// Transitions the socket into the `listening` state.
+//
+// Unlike POSIX, the socket must already be explicitly bound.
+//
+// # Typical errors
+// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ)
+// - `invalid-state`: The socket is already in the `connected` state.
+// (EISCONN, EINVAL on BSD)
+// - `invalid-state`: The socket is already in the `listening` state.
+// - `address-in-use`: Tried to perform an implicit bind, but there were
+// no ephemeral ports available. (EADDRINUSE)
+// - `not-in-progress`: A listen operation is not in progress.
+// - `would-block`: Can't finish the operation, it is still in progress.
+// (EWOULDBLOCK, EAGAIN)
+//
+// # Implementors note
+// Unlike in POSIX, in WASI the listen operation is async. This enables
+// interactive WASI hosts to inject permission prompts. Runtimes that
+// don't want to make use of this ability can simply call the native
+// `listen` as part of either `start-listen` or `finish-listen`.
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html>
+// - <https://man7.org/linux/man-pages/man2/listen.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen>
+// - <https://man.freebsd.org/cgi/man.cgi?query=listen&sektion=2>
+//
+// start-listen: func() -> result<_, error-code>
+//
+//go:nosplit
+func (self TCPSocket) StartListen() (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_TCPSocketStartListen((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.start-listen
+//go:noescape
+func wasmimport_TCPSocketStartListen(self0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// Subscribe represents the imported method "subscribe".
+//
+// Create a `pollable` which can be used to poll for, or block on,
+// completion of any of the asynchronous operations of this socket.
+//
+// When `finish-bind`, `finish-listen`, `finish-connect` or `accept`
+// return `error(would-block)`, this pollable can be used to wait for
+// their success or failure, after which the method can be retried.
+//
+// The pollable is not limited to the async operation that happens to be
+// in progress at the time of calling `subscribe` (if any). Theoretically,
+// `subscribe` only has to be called once per socket and can then be
+// (re)used for the remainder of the socket's lifetime.
+//
+// See <https://github.com/WebAssembly/wasi-sockets/TcpSocketOperationalSemantics.md#Pollable-readiness>
+// for a more information.
+//
+// Note: this function is here for WASI Preview2 only.
+// It's planned to be removed when `future` is natively supported in Preview3.
+//
+// subscribe: func() -> pollable
+//
+//go:nosplit
+func (self TCPSocket) Subscribe() (result poll.Pollable) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_TCPSocketSubscribe((uint32)(self0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]tcp-socket.subscribe
+//go:noescape
+func wasmimport_TCPSocketSubscribe(self0 uint32) (result0 uint32)
diff --git a/src/internal/wasi/sockets/v0.2.0/udp-create-socket/empty.s b/src/internal/wasi/sockets/v0.2.0/udp-create-socket/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/udp-create-socket/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/sockets/v0.2.0/udp-create-socket/udp-create-socket.wit.go b/src/internal/wasi/sockets/v0.2.0/udp-create-socket/udp-create-socket.wit.go
new file mode 100644
index 000000000..a73695a9c
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/udp-create-socket/udp-create-socket.wit.go
@@ -0,0 +1,54 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package udpcreatesocket represents the imported interface "wasi:sockets/[email protected]".
+package udpcreatesocket
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/sockets/v0.2.0/network"
+ "internal/wasi/sockets/v0.2.0/udp"
+)
+
+// CreateUDPSocket represents the imported function "create-udp-socket".
+//
+// Create a new UDP socket.
+//
+// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
+// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
+//
+// This function does not require a network capability handle. This is considered
+// to be safe because
+// at time of creation, the socket is not bound to any `network` yet. Up to the moment
+// `bind` is called,
+// the socket is effectively an in-memory configuration object, unable to communicate
+// with the outside world.
+//
+// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous
+// operations.
+//
+// # Typical errors
+// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT)
+// - `new-socket-limit`: The new socket resource could not be created because of
+// a system limit. (EMFILE, ENFILE)
+//
+// # References:
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html>
+// - <https://man7.org/linux/man-pages/man2/socket.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw>
+// - <https://man.freebsd.org/cgi/man.cgi?query=socket&sektion=2>
+//
+// create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket,
+// error-code>
+//
+//go:nosplit
+func CreateUDPSocket(addressFamily network.IPAddressFamily) (result cm.OKResult[udp.UDPSocket, network.ErrorCode]) {
+ addressFamily0 := (uint32)(addressFamily)
+ wasmimport_CreateUDPSocket((uint32)(addressFamily0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] create-udp-socket
+//go:noescape
+func wasmimport_CreateUDPSocket(addressFamily0 uint32, result *cm.OKResult[udp.UDPSocket, network.ErrorCode])
diff --git a/src/internal/wasi/sockets/v0.2.0/udp/abi.go b/src/internal/wasi/sockets/v0.2.0/udp/abi.go
new file mode 100644
index 000000000..c62d3322d
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/udp/abi.go
@@ -0,0 +1,92 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+package udp
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/sockets/v0.2.0/network"
+)
+
+func lower_IPv4Address(v network.IPv4Address) (f0 uint32, f1 uint32, f2 uint32, f3 uint32) {
+ f0 = (uint32)(v[0])
+ f1 = (uint32)(v[1])
+ f2 = (uint32)(v[2])
+ f3 = (uint32)(v[3])
+ return
+}
+
+func lower_IPv4SocketAddress(v network.IPv4SocketAddress) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32) {
+ f0 = (uint32)(v.Port)
+ f1, f2, f3, f4 = lower_IPv4Address(v.Address)
+ return
+}
+
+func lower_IPv6Address(v network.IPv6Address) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32, f5 uint32, f6 uint32, f7 uint32) {
+ f0 = (uint32)(v[0])
+ f1 = (uint32)(v[1])
+ f2 = (uint32)(v[2])
+ f3 = (uint32)(v[3])
+ f4 = (uint32)(v[4])
+ f5 = (uint32)(v[5])
+ f6 = (uint32)(v[6])
+ f7 = (uint32)(v[7])
+ return
+}
+
+func lower_IPv6SocketAddress(v network.IPv6SocketAddress) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32, f5 uint32, f6 uint32, f7 uint32, f8 uint32, f9 uint32, f10 uint32) {
+ f0 = (uint32)(v.Port)
+ f1 = (uint32)(v.FlowInfo)
+ f2, f3, f4, f5, f6, f7, f8, f9 = lower_IPv6Address(v.Address)
+ f10 = (uint32)(v.ScopeID)
+ return
+}
+
+func lower_IPSocketAddress(v network.IPSocketAddress) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32, f5 uint32, f6 uint32, f7 uint32, f8 uint32, f9 uint32, f10 uint32, f11 uint32) {
+ f0 = (uint32)(cm.Tag(&v))
+ switch f0 {
+ case 0: // ipv4
+ v1, v2, v3, v4, v5 := lower_IPv4SocketAddress(*v.IPv4())
+ f1 = (uint32)(v1)
+ f2 = (uint32)(v2)
+ f3 = (uint32)(v3)
+ f4 = (uint32)(v4)
+ f5 = (uint32)(v5)
+ case 1: // ipv6
+ v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 := lower_IPv6SocketAddress(*v.IPv6())
+ f1 = (uint32)(v1)
+ f2 = (uint32)(v2)
+ f3 = (uint32)(v3)
+ f4 = (uint32)(v4)
+ f5 = (uint32)(v5)
+ f6 = (uint32)(v6)
+ f7 = (uint32)(v7)
+ f8 = (uint32)(v8)
+ f9 = (uint32)(v9)
+ f10 = (uint32)(v10)
+ f11 = (uint32)(v11)
+ }
+ return
+}
+
+func lower_OptionIPSocketAddress(v cm.Option[network.IPSocketAddress]) (f0 uint32, f1 uint32, f2 uint32, f3 uint32, f4 uint32, f5 uint32, f6 uint32, f7 uint32, f8 uint32, f9 uint32, f10 uint32, f11 uint32, f12 uint32) {
+ some := v.Some()
+ if some != nil {
+ f0 = 1
+ v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 := lower_IPSocketAddress(*some)
+ f1 = (uint32)(v1)
+ f2 = (uint32)(v2)
+ f3 = (uint32)(v3)
+ f4 = (uint32)(v4)
+ f5 = (uint32)(v5)
+ f6 = (uint32)(v6)
+ f7 = (uint32)(v7)
+ f8 = (uint32)(v8)
+ f9 = (uint32)(v9)
+ f10 = (uint32)(v10)
+ f11 = (uint32)(v11)
+ f12 = (uint32)(v12)
+ }
+ return
+}
diff --git a/src/internal/wasi/sockets/v0.2.0/udp/empty.s b/src/internal/wasi/sockets/v0.2.0/udp/empty.s
new file mode 100644
index 000000000..5444f2006
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/udp/empty.s
@@ -0,0 +1,3 @@
+// This file exists for testing this package without WebAssembly,
+// allowing empty function bodies with a //go:wasmimport directive.
+// See https://pkg.go.dev/cmd/compile for more information.
diff --git a/src/internal/wasi/sockets/v0.2.0/udp/udp.wit.go b/src/internal/wasi/sockets/v0.2.0/udp/udp.wit.go
new file mode 100644
index 000000000..41b3b9ac0
--- /dev/null
+++ b/src/internal/wasi/sockets/v0.2.0/udp/udp.wit.go
@@ -0,0 +1,643 @@
+// Code generated by wit-bindgen-go. DO NOT EDIT.
+
+//go:build !wasip1
+
+// Package udp represents the imported interface "wasi:sockets/[email protected]".
+package udp
+
+import (
+ "github.com/ydnar/wasm-tools-go/cm"
+ "internal/wasi/io/v0.2.0/poll"
+ "internal/wasi/sockets/v0.2.0/network"
+)
+
+// IncomingDatagram represents the record "wasi:sockets/[email protected]#incoming-datagram".
+//
+// A received datagram.
+//
+// record incoming-datagram {
+// data: list<u8>,
+// remote-address: ip-socket-address,
+// }
+type IncomingDatagram struct {
+ // The payload.
+ //
+ // Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes.
+ Data cm.List[uint8]
+
+ // The source address.
+ //
+ // This field is guaranteed to match the remote address the stream was initialized
+ // with, if any.
+ //
+ // Equivalent to the `src_addr` out parameter of `recvfrom`.
+ RemoteAddress network.IPSocketAddress
+}
+
+// OutgoingDatagram represents the record "wasi:sockets/[email protected]#outgoing-datagram".
+//
+// A datagram to be sent out.
+//
+// record outgoing-datagram {
+// data: list<u8>,
+// remote-address: option<ip-socket-address>,
+// }
+type OutgoingDatagram struct {
+ // The payload.
+ Data cm.List[uint8]
+
+ // The destination address.
+ //
+ // The requirements on this field depend on how the stream was initialized:
+ // - with a remote address: this field must be None or match the stream's remote address
+ // exactly.
+ // - without a remote address: this field is required.
+ //
+ // If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise
+ // it is equivalent to `sendto`.
+ RemoteAddress cm.Option[network.IPSocketAddress]
+}
+
+// UDPSocket represents the imported resource "wasi:sockets/[email protected]#udp-socket".
+//
+// A UDP socket handle.
+//
+// resource udp-socket
+type UDPSocket cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "udp-socket".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self UDPSocket) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_UDPSocketResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [resource-drop]udp-socket
+//go:noescape
+func wasmimport_UDPSocketResourceDrop(self0 uint32)
+
+// AddressFamily represents the imported method "address-family".
+//
+// Whether this is a IPv4 or IPv6 socket.
+//
+// Equivalent to the SO_DOMAIN socket option.
+//
+// address-family: func() -> ip-address-family
+//
+//go:nosplit
+func (self UDPSocket) AddressFamily() (result network.IPAddressFamily) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_UDPSocketAddressFamily((uint32)(self0))
+ result = (network.IPAddressFamily)((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.address-family
+//go:noescape
+func wasmimport_UDPSocketAddressFamily(self0 uint32) (result0 uint32)
+
+// FinishBind represents the imported method "finish-bind".
+//
+// finish-bind: func() -> result<_, error-code>
+//
+//go:nosplit
+func (self UDPSocket) FinishBind() (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_UDPSocketFinishBind((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.finish-bind
+//go:noescape
+func wasmimport_UDPSocketFinishBind(self0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// LocalAddress represents the imported method "local-address".
+//
+// Get the current bound address.
+//
+// POSIX mentions:
+// > If the socket has not been bound to a local name, the value
+// > stored in the object pointed to by `address` is unspecified.
+//
+// WASI is stricter and requires `local-address` to return `invalid-state` when the
+// socket hasn't been bound yet.
+//
+// # Typical errors
+// - `invalid-state`: The socket is not bound to any local address.
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html>
+// - <https://man7.org/linux/man-pages/man2/getsockname.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getsockname>
+// - <https://man.freebsd.org/cgi/man.cgi?getsockname>
+//
+// local-address: func() -> result<ip-socket-address, error-code>
+//
+//go:nosplit
+func (self UDPSocket) LocalAddress() (result cm.OKResult[network.IPSocketAddress, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_UDPSocketLocalAddress((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.local-address
+//go:noescape
+func wasmimport_UDPSocketLocalAddress(self0 uint32, result *cm.OKResult[network.IPSocketAddress, network.ErrorCode])
+
+// ReceiveBufferSize represents the imported method "receive-buffer-size".
+//
+// The kernel buffer space reserved for sends/receives on this socket.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+// Any other value will never cause an error, but it might be silently clamped and/or
+// rounded.
+// I.e. after setting a value, reading the same setting back may return a different
+// value.
+//
+// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+//
+// # Typical errors
+// - `invalid-argument`: (set) The provided value was 0.
+//
+// receive-buffer-size: func() -> result<u64, error-code>
+//
+//go:nosplit
+func (self UDPSocket) ReceiveBufferSize() (result cm.OKResult[uint64, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_UDPSocketReceiveBufferSize((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.receive-buffer-size
+//go:noescape
+func wasmimport_UDPSocketReceiveBufferSize(self0 uint32, result *cm.OKResult[uint64, network.ErrorCode])
+
+// RemoteAddress represents the imported method "remote-address".
+//
+// Get the address the socket is currently streaming to.
+//
+// # Typical errors
+// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html>
+// - <https://man7.org/linux/man-pages/man2/getpeername.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-getpeername>
+// - <https://man.freebsd.org/cgi/man.cgi?query=getpeername&sektion=2&n=1>
+//
+// remote-address: func() -> result<ip-socket-address, error-code>
+//
+//go:nosplit
+func (self UDPSocket) RemoteAddress() (result cm.OKResult[network.IPSocketAddress, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_UDPSocketRemoteAddress((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.remote-address
+//go:noescape
+func wasmimport_UDPSocketRemoteAddress(self0 uint32, result *cm.OKResult[network.IPSocketAddress, network.ErrorCode])
+
+// SendBufferSize represents the imported method "send-buffer-size".
+//
+// send-buffer-size: func() -> result<u64, error-code>
+//
+//go:nosplit
+func (self UDPSocket) SendBufferSize() (result cm.OKResult[uint64, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_UDPSocketSendBufferSize((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.send-buffer-size
+//go:noescape
+func wasmimport_UDPSocketSendBufferSize(self0 uint32, result *cm.OKResult[uint64, network.ErrorCode])
+
+// SetReceiveBufferSize represents the imported method "set-receive-buffer-size".
+//
+// set-receive-buffer-size: func(value: u64) -> result<_, error-code>
+//
+//go:nosplit
+func (self UDPSocket) SetReceiveBufferSize(value uint64) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint64)(value)
+ wasmimport_UDPSocketSetReceiveBufferSize((uint32)(self0), (uint64)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.set-receive-buffer-size
+//go:noescape
+func wasmimport_UDPSocketSetReceiveBufferSize(self0 uint32, value0 uint64, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetSendBufferSize represents the imported method "set-send-buffer-size".
+//
+// set-send-buffer-size: func(value: u64) -> result<_, error-code>
+//
+//go:nosplit
+func (self UDPSocket) SetSendBufferSize(value uint64) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint64)(value)
+ wasmimport_UDPSocketSetSendBufferSize((uint32)(self0), (uint64)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.set-send-buffer-size
+//go:noescape
+func wasmimport_UDPSocketSetSendBufferSize(self0 uint32, value0 uint64, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// SetUnicastHopLimit represents the imported method "set-unicast-hop-limit".
+//
+// set-unicast-hop-limit: func(value: u8) -> result<_, error-code>
+//
+//go:nosplit
+func (self UDPSocket) SetUnicastHopLimit(value uint8) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ value0 := (uint32)(value)
+ wasmimport_UDPSocketSetUnicastHopLimit((uint32)(self0), (uint32)(value0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.set-unicast-hop-limit
+//go:noescape
+func wasmimport_UDPSocketSetUnicastHopLimit(self0 uint32, value0 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// StartBind represents the imported method "start-bind".
+//
+// Bind the socket to a specific network on the provided IP address and port.
+//
+// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the
+// implementation to decide which
+// network interface(s) to bind to.
+// If the port is zero, the socket will be bound to a random free port.
+//
+// # Typical errors
+// - `invalid-argument`: The `local-address` has the wrong address family.
+// (EAFNOSUPPORT, EFAULT on Windows)
+// - `invalid-state`: The socket is already bound. (EINVAL)
+// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS
+// on Windows)
+// - `address-in-use`: Address is already in use. (EADDRINUSE)
+// - `address-not-bindable`: `local-address` is not an address that the `network`
+// can bind to. (EADDRNOTAVAIL)
+// - `not-in-progress`: A `bind` operation is not in progress.
+// - `would-block`: Can't finish the operation, it is still in progress.
+// (EWOULDBLOCK, EAGAIN)
+//
+// # Implementors note
+// Unlike in POSIX, in WASI the bind operation is async. This enables
+// interactive WASI hosts to inject permission prompts. Runtimes that
+// don't want to make use of this ability can simply call the native
+// `bind` as part of either `start-bind` or `finish-bind`.
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html>
+// - <https://man7.org/linux/man-pages/man2/bind.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind>
+// - <https://man.freebsd.org/cgi/man.cgi?query=bind&sektion=2&format=html>
+//
+// start-bind: func(network: borrow<network>, local-address: ip-socket-address) ->
+// result<_, error-code>
+//
+//go:nosplit
+func (self UDPSocket) StartBind(network_ network.Network, localAddress network.IPSocketAddress) (result cm.ErrResult[struct{}, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ network0 := cm.Reinterpret[uint32](network_)
+ localAddress0, localAddress1, localAddress2, localAddress3, localAddress4, localAddress5, localAddress6, localAddress7, localAddress8, localAddress9, localAddress10, localAddress11 := lower_IPSocketAddress(localAddress)
+ wasmimport_UDPSocketStartBind((uint32)(self0), (uint32)(network0), (uint32)(localAddress0), (uint32)(localAddress1), (uint32)(localAddress2), (uint32)(localAddress3), (uint32)(localAddress4), (uint32)(localAddress5), (uint32)(localAddress6), (uint32)(localAddress7), (uint32)(localAddress8), (uint32)(localAddress9), (uint32)(localAddress10), (uint32)(localAddress11), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.start-bind
+//go:noescape
+func wasmimport_UDPSocketStartBind(self0 uint32, network0 uint32, localAddress0 uint32, localAddress1 uint32, localAddress2 uint32, localAddress3 uint32, localAddress4 uint32, localAddress5 uint32, localAddress6 uint32, localAddress7 uint32, localAddress8 uint32, localAddress9 uint32, localAddress10 uint32, localAddress11 uint32, result *cm.ErrResult[struct{}, network.ErrorCode])
+
+// Stream represents the imported method "stream".
+//
+// Set up inbound & outbound communication channels, optionally to a specific peer.
+//
+// This function only changes the local socket configuration and does not generate
+// any network traffic.
+// On success, the `remote-address` of the socket is updated. The `local-address`
+// may be updated as well,
+// based on the best network path to `remote-address`.
+//
+// When a `remote-address` is provided, the returned streams are limited to communicating
+// with that specific peer:
+// - `send` can only be used to send to this destination.
+// - `receive` will only return datagrams sent from the provided `remote-address`.
+//
+// This method may be called multiple times on the same socket to change its association,
+// but
+// only the most recently returned pair of streams will be operational. Implementations
+// may trap if
+// the streams returned by a previous invocation haven't been dropped yet before calling
+// `stream` again.
+//
+// The POSIX equivalent in pseudo-code is:
+//
+// if (was previously connected) {
+// connect(s, AF_UNSPEC)
+// }
+// if (remote_address is Some) {
+// connect(s, remote_address)
+// }
+//
+// Unlike in POSIX, the socket must already be explicitly bound.
+//
+// # Typical errors
+// - `invalid-argument`: The `remote-address` has the wrong address family.
+// (EAFNOSUPPORT)
+// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY
+// (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
+// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ,
+// EADDRNOTAVAIL)
+// - `invalid-state`: The socket is not bound.
+// - `address-in-use`: Tried to perform an implicit bind, but there were
+// no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET,
+// ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+// - `connection-refused`: The connection was refused. (ECONNREFUSED)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html>
+// - <https://man7.org/linux/man-pages/man2/connect.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect>
+// - <https://man.freebsd.org/cgi/man.cgi?connect>
+//
+// %stream: func(remote-address: option<ip-socket-address>) -> result<tuple<incoming-datagram-stream,
+// outgoing-datagram-stream>, error-code>
+//
+//go:nosplit
+func (self UDPSocket) Stream(remoteAddress cm.Option[network.IPSocketAddress]) (result cm.OKResult[cm.Tuple[IncomingDatagramStream, OutgoingDatagramStream], network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ remoteAddress0, remoteAddress1, remoteAddress2, remoteAddress3, remoteAddress4, remoteAddress5, remoteAddress6, remoteAddress7, remoteAddress8, remoteAddress9, remoteAddress10, remoteAddress11, remoteAddress12 := lower_OptionIPSocketAddress(remoteAddress)
+ wasmimport_UDPSocketStream((uint32)(self0), (uint32)(remoteAddress0), (uint32)(remoteAddress1), (uint32)(remoteAddress2), (uint32)(remoteAddress3), (uint32)(remoteAddress4), (uint32)(remoteAddress5), (uint32)(remoteAddress6), (uint32)(remoteAddress7), (uint32)(remoteAddress8), (uint32)(remoteAddress9), (uint32)(remoteAddress10), (uint32)(remoteAddress11), (uint32)(remoteAddress12), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.stream
+//go:noescape
+func wasmimport_UDPSocketStream(self0 uint32, remoteAddress0 uint32, remoteAddress1 uint32, remoteAddress2 uint32, remoteAddress3 uint32, remoteAddress4 uint32, remoteAddress5 uint32, remoteAddress6 uint32, remoteAddress7 uint32, remoteAddress8 uint32, remoteAddress9 uint32, remoteAddress10 uint32, remoteAddress11 uint32, remoteAddress12 uint32, result *cm.OKResult[cm.Tuple[IncomingDatagramStream, OutgoingDatagramStream], network.ErrorCode])
+
+// Subscribe represents the imported method "subscribe".
+//
+// Create a `pollable` which will resolve once the socket is ready for I/O.
+//
+// Note: this function is here for WASI Preview2 only.
+// It's planned to be removed when `future` is natively supported in Preview3.
+//
+// subscribe: func() -> pollable
+//
+//go:nosplit
+func (self UDPSocket) Subscribe() (result poll.Pollable) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_UDPSocketSubscribe((uint32)(self0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.subscribe
+//go:noescape
+func wasmimport_UDPSocketSubscribe(self0 uint32) (result0 uint32)
+
+// UnicastHopLimit represents the imported method "unicast-hop-limit".
+//
+// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+//
+// If the provided value is 0, an `invalid-argument` error is returned.
+//
+// # Typical errors
+// - `invalid-argument`: (set) The TTL value must be 1 or higher.
+//
+// unicast-hop-limit: func() -> result<u8, error-code>
+//
+//go:nosplit
+func (self UDPSocket) UnicastHopLimit() (result cm.OKResult[uint8, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_UDPSocketUnicastHopLimit((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]udp-socket.unicast-hop-limit
+//go:noescape
+func wasmimport_UDPSocketUnicastHopLimit(self0 uint32, result *cm.OKResult[uint8, network.ErrorCode])
+
+// IncomingDatagramStream represents the imported resource "wasi:sockets/[email protected]#incoming-datagram-stream".
+//
+// resource incoming-datagram-stream
+type IncomingDatagramStream cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "incoming-datagram-stream".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self IncomingDatagramStream) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_IncomingDatagramStreamResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [resource-drop]incoming-datagram-stream
+//go:noescape
+func wasmimport_IncomingDatagramStreamResourceDrop(self0 uint32)
+
+// Receive represents the imported method "receive".
+//
+// Receive messages on the socket.
+//
+// This function attempts to receive up to `max-results` datagrams on the socket without
+// blocking.
+// The returned list may contain fewer elements than requested, but never more.
+//
+// This function returns successfully with an empty list when either:
+// - `max-results` is 0, or:
+// - `max-results` is greater than 0, but no results are immediately available.
+// This function never returns `error(would-block)`.
+//
+// # Typical errors
+// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET
+// on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+// - `connection-refused`: The connection was refused. (ECONNREFUSED)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html>
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html>
+// - <https://man7.org/linux/man-pages/man2/recv.2.html>
+// - <https://man7.org/linux/man-pages/man2/recvmmsg.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom>
+// - <https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms741687(v=vs.85)>
+// - <https://man.freebsd.org/cgi/man.cgi?query=recv&sektion=2>
+//
+// receive: func(max-results: u64) -> result<list<incoming-datagram>, error-code>
+//
+//go:nosplit
+func (self IncomingDatagramStream) Receive(maxResults uint64) (result cm.OKResult[cm.List[IncomingDatagram], network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ maxResults0 := (uint64)(maxResults)
+ wasmimport_IncomingDatagramStreamReceive((uint32)(self0), (uint64)(maxResults0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]incoming-datagram-stream.receive
+//go:noescape
+func wasmimport_IncomingDatagramStreamReceive(self0 uint32, maxResults0 uint64, result *cm.OKResult[cm.List[IncomingDatagram], network.ErrorCode])
+
+// Subscribe represents the imported method "subscribe".
+//
+// Create a `pollable` which will resolve once the stream is ready to receive again.
+//
+// Note: this function is here for WASI Preview2 only.
+// It's planned to be removed when `future` is natively supported in Preview3.
+//
+// subscribe: func() -> pollable
+//
+//go:nosplit
+func (self IncomingDatagramStream) Subscribe() (result poll.Pollable) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_IncomingDatagramStreamSubscribe((uint32)(self0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]incoming-datagram-stream.subscribe
+//go:noescape
+func wasmimport_IncomingDatagramStreamSubscribe(self0 uint32) (result0 uint32)
+
+// OutgoingDatagramStream represents the imported resource "wasi:sockets/[email protected]#outgoing-datagram-stream".
+//
+// resource outgoing-datagram-stream
+type OutgoingDatagramStream cm.Resource
+
+// ResourceDrop represents the imported resource-drop for resource "outgoing-datagram-stream".
+//
+// Drops a resource handle.
+//
+//go:nosplit
+func (self OutgoingDatagramStream) ResourceDrop() {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_OutgoingDatagramStreamResourceDrop((uint32)(self0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [resource-drop]outgoing-datagram-stream
+//go:noescape
+func wasmimport_OutgoingDatagramStreamResourceDrop(self0 uint32)
+
+// CheckSend represents the imported method "check-send".
+//
+// Check readiness for sending. This function never blocks.
+//
+// Returns the number of datagrams permitted for the next call to `send`,
+// or an error. Calling `send` with more datagrams than this function has
+// permitted will trap.
+//
+// When this function returns ok(0), the `subscribe` pollable will
+// become ready when this function will report at least ok(1), or an
+// error.
+//
+// Never returns `would-block`.
+//
+// check-send: func() -> result<u64, error-code>
+//
+//go:nosplit
+func (self OutgoingDatagramStream) CheckSend() (result cm.OKResult[uint64, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ wasmimport_OutgoingDatagramStreamCheckSend((uint32)(self0), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]outgoing-datagram-stream.check-send
+//go:noescape
+func wasmimport_OutgoingDatagramStreamCheckSend(self0 uint32, result *cm.OKResult[uint64, network.ErrorCode])
+
+// Send represents the imported method "send".
+//
+// Send messages on the socket.
+//
+// This function attempts to send all provided `datagrams` on the socket without blocking
+// and
+// returns how many messages were actually sent (or queued for sending). This function
+// never
+// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)`
+// is returned.
+//
+// This function semantically behaves the same as iterating the `datagrams` list and
+// sequentially
+// sending each individual datagram until either the end of the list has been reached
+// or the first error occurred.
+// If at least one datagram has been sent successfully, this function never returns
+// an error.
+//
+// If the input list is empty, the function returns `ok(0)`.
+//
+// Each call to `send` must be permitted by a preceding `check-send`. Implementations
+// must trap if
+// either `check-send` was not called or `datagrams` contains more items than `check-send`
+// permitted.
+//
+// # Typical errors
+// - `invalid-argument`: The `remote-address` has the wrong address family.
+// (EAFNOSUPPORT)
+// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY
+// (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
+// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ,
+// EADDRNOTAVAIL)
+// - `invalid-argument`: The socket is in "connected" mode and `remote-address`
+// is `some` value that does not match the address passed to `stream`. (EISCONN)
+// - `invalid-argument`: The socket is not "connected" and no value for `remote-address`
+// was provided. (EDESTADDRREQ)
+// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET,
+// ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+// - `connection-refused`: The connection was refused. (ECONNREFUSED)
+// - `datagram-too-large`: The datagram is too large. (EMSGSIZE)
+//
+// # References
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html>
+// - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html>
+// - <https://man7.org/linux/man-pages/man2/send.2.html>
+// - <https://man7.org/linux/man-pages/man2/sendmmsg.2.html>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto>
+// - <https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasendmsg>
+// - <https://man.freebsd.org/cgi/man.cgi?query=send&sektion=2>
+//
+// send: func(datagrams: list<outgoing-datagram>) -> result<u64, error-code>
+//
+//go:nosplit
+func (self OutgoingDatagramStream) Send(datagrams cm.List[OutgoingDatagram]) (result cm.OKResult[uint64, network.ErrorCode]) {
+ self0 := cm.Reinterpret[uint32](self)
+ datagrams0, datagrams1 := cm.LowerList(datagrams)
+ wasmimport_OutgoingDatagramStreamSend((uint32)(self0), (*OutgoingDatagram)(datagrams0), (uint32)(datagrams1), &result)
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]outgoing-datagram-stream.send
+//go:noescape
+func wasmimport_OutgoingDatagramStreamSend(self0 uint32, datagrams0 *OutgoingDatagram, datagrams1 uint32, result *cm.OKResult[uint64, network.ErrorCode])
+
+// Subscribe represents the imported method "subscribe".
+//
+// Create a `pollable` which will resolve once the stream is ready to send again.
+//
+// Note: this function is here for WASI Preview2 only.
+// It's planned to be removed when `future` is natively supported in Preview3.
+//
+// subscribe: func() -> pollable
+//
+//go:nosplit
+func (self OutgoingDatagramStream) Subscribe() (result poll.Pollable) {
+ self0 := cm.Reinterpret[uint32](self)
+ result0 := wasmimport_OutgoingDatagramStreamSubscribe((uint32)(self0))
+ result = cm.Reinterpret[poll.Pollable]((uint32)(result0))
+ return
+}
+
+//go:wasmimport wasi:sockets/[email protected] [method]outgoing-datagram-stream.subscribe
+//go:noescape
+func wasmimport_OutgoingDatagramStreamSubscribe(self0 uint32) (result0 uint32)
diff --git a/src/os/dir_test.go b/src/os/dir_test.go
index d661c98b4..e51e290b3 100644
--- a/src/os/dir_test.go
+++ b/src/os/dir_test.go
@@ -1,4 +1,4 @@
-//go:build darwin || (linux && !baremetal && !js && !wasip1 && !386 && !arm)
+//go:build darwin || (linux && !baremetal && !js && !wasip1 && !wasip2 && !386 && !arm)
package os_test
diff --git a/src/os/dir_unix.go b/src/os/dir_unix.go
index 227dc9188..03624e9cf 100644
--- a/src/os/dir_unix.go
+++ b/src/os/dir_unix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build linux && !baremetal && !wasip1 && !wasm_unknown
+//go:build linux && !baremetal && !wasip1 && !wasip2 && !wasm_unknown
package os
diff --git a/src/os/dir_wasip1.go b/src/os/dir_wasi.go
index 0be0da4d6..6d9313110 100644
--- a/src/os/dir_wasip1.go
+++ b/src/os/dir_wasi.go
@@ -6,7 +6,7 @@
// fairly similar: we use fdopendir, fdclosedir, and readdir from wasi-libc in
// a similar way that the darwin code uses functions from libc.
-//go:build wasip1
+//go:build wasip1 || wasip2
package os
diff --git a/src/os/dirent_linux.go b/src/os/dirent_linux.go
index 790c26890..92e2b3a65 100644
--- a/src/os/dirent_linux.go
+++ b/src/os/dirent_linux.go
@@ -1,4 +1,4 @@
-//go:build !baremetal && !js && !wasip1 && !wasm_unknown
+//go:build !baremetal && !js && !wasip1 && !wasip2 && !wasm_unknown
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/env_unix_test.go b/src/os/env_unix_test.go
index 034f48154..93dff91a1 100644
--- a/src/os/env_unix_test.go
+++ b/src/os/env_unix_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build darwin || linux || wasip1
+//go:build darwin || linux || wasip1 || wasip2
package os_test
diff --git a/src/os/exec_posix.go b/src/os/exec_posix.go
index 0a968d9a1..720368572 100644
--- a/src/os/exec_posix.go
+++ b/src/os/exec_posix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || wasip1 || windows
+//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || wasip1 || wasip2 || windows
package os
diff --git a/src/os/file_other.go b/src/os/file_other.go
index 82552c1a8..d359b0fb6 100644
--- a/src/os/file_other.go
+++ b/src/os/file_other.go
@@ -1,4 +1,4 @@
-//go:build baremetal || (tinygo.wasm && !wasip1)
+//go:build baremetal || (tinygo.wasm && !wasip1 && !wasip2)
package os
diff --git a/src/os/file_unix.go b/src/os/file_unix.go
index 25c2161e3..badfc71ff 100644
--- a/src/os/file_unix.go
+++ b/src/os/file_unix.go
@@ -1,4 +1,4 @@
-//go:build darwin || (linux && !baremetal && !wasm_unknown) || wasip1
+//go:build darwin || (linux && !baremetal && !wasm_unknown) || wasip1 || wasip2
// target wasi sets GOOS=linux and thus the +linux build tag,
// even though it doesn't show up in "tinygo info target -wasi"
diff --git a/src/os/getpagesize_test.go b/src/os/getpagesize_test.go
index 80475552b..5017a9b80 100644
--- a/src/os/getpagesize_test.go
+++ b/src/os/getpagesize_test.go
@@ -1,4 +1,4 @@
-//go:build windows || darwin || (linux && !baremetal) || wasip1
+//go:build windows || darwin || (linux && !baremetal) || wasip1 || wasip2
package os_test
diff --git a/src/os/os_anyos_test.go b/src/os/os_anyos_test.go
index 8a082d652..67c609949 100644
--- a/src/os/os_anyos_test.go
+++ b/src/os/os_anyos_test.go
@@ -1,4 +1,4 @@
-//go:build windows || darwin || (linux && !baremetal) || wasip1
+//go:build windows || darwin || (linux && !baremetal) || wasip1 || wasip2
package os_test
@@ -275,7 +275,7 @@ func TestDirFS(t *testing.T) {
t.Log("TODO: implement Readdir for Windows")
return
}
- if runtime.GOOS == "wasip1" {
+ if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
t.Log("TODO: allow foo/bar/. as synonym for path foo/bar on wasi?")
return
}
@@ -296,7 +296,7 @@ func TestDirFSPathsValid(t *testing.T) {
t.Log("skipping on Windows")
return
}
- if runtime.GOOS == "wasip1" {
+ if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
t.Log("skipping on wasi because it fails on wasi on windows")
return
}
diff --git a/src/os/os_chmod_test.go b/src/os/os_chmod_test.go
index b8f7d354f..3db6467ad 100644
--- a/src/os/os_chmod_test.go
+++ b/src/os/os_chmod_test.go
@@ -1,4 +1,4 @@
-//go:build !baremetal && !js && !wasip1
+//go:build !baremetal && !js && !wasip1 && !wasip2
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/os_hardlink_test.go b/src/os/os_hardlink_test.go
index 96f1c9bbb..9fa1ecb75 100644
--- a/src/os/os_hardlink_test.go
+++ b/src/os/os_hardlink_test.go
@@ -1,4 +1,4 @@
-//go:build !windows && !baremetal && !js && !wasi && !wasip1 && !wasm_unknown
+//go:build !windows && !baremetal && !js && !wasip1 && !wasm_unknown
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/os_symlink_test.go b/src/os/os_symlink_test.go
index f885dd32c..baa7047b2 100644
--- a/src/os/os_symlink_test.go
+++ b/src/os/os_symlink_test.go
@@ -1,4 +1,4 @@
-//go:build !windows && !baremetal && !js && !wasip1
+//go:build !windows && !baremetal && !js && !wasip1 && !wasip2
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/pipe_test.go b/src/os/pipe_test.go
index b0553ffdb..98c86089f 100644
--- a/src/os/pipe_test.go
+++ b/src/os/pipe_test.go
@@ -1,4 +1,4 @@
-//go:build windows || darwin || (linux && !baremetal && !wasi)
+//go:build windows || darwin || (linux && !baremetal && !wasip1 && !wasip2)
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/read_test.go b/src/os/read_test.go
index 679d96113..68eb02966 100644
--- a/src/os/read_test.go
+++ b/src/os/read_test.go
@@ -1,4 +1,4 @@
-//go:build !baremetal && !js && !wasip1
+//go:build !baremetal && !js && !wasip1 && !wasip2
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go
index d49162768..40fc80137 100644
--- a/src/os/removeall_noat.go
+++ b/src/os/removeall_noat.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !baremetal && !js && !wasip1 && !wasm_unknown
+//go:build !baremetal && !js && !wasip1 && !wasip2 && !wasm_unknown
package os
diff --git a/src/os/removeall_other.go b/src/os/removeall_other.go
index 75346bc83..dc65aaab2 100644
--- a/src/os/removeall_other.go
+++ b/src/os/removeall_other.go
@@ -1,4 +1,4 @@
-//go:build baremetal || js || wasi || wasip1 || wasm_unknown
+//go:build baremetal || js || wasip1 || wasip2 || wasm_unknown
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/removeall_test.go b/src/os/removeall_test.go
index ea7c83b4e..9f49e6793 100644
--- a/src/os/removeall_test.go
+++ b/src/os/removeall_test.go
@@ -1,4 +1,4 @@
-//go:build darwin || (linux && !baremetal && !js && !wasi)
+//go:build darwin || (linux && !baremetal && !js && !wasip1 && !wasip2)
// TODO: implement ReadDir on windows
diff --git a/src/os/seek_unix_bad.go b/src/os/seek_unix_bad.go
index 4243ced64..047c6346a 100644
--- a/src/os/seek_unix_bad.go
+++ b/src/os/seek_unix_bad.go
@@ -1,4 +1,4 @@
-//go:build (linux && !baremetal && 386) || (linux && !baremetal && arm && !wasi)
+//go:build (linux && !baremetal && 386) || (linux && !baremetal && arm && !wasip1 && !wasip2)
package os
diff --git a/src/os/stat_linuxlike.go b/src/os/stat_linuxlike.go
index 59ee64970..7c512ec04 100644
--- a/src/os/stat_linuxlike.go
+++ b/src/os/stat_linuxlike.go
@@ -1,4 +1,4 @@
-//go:build (linux && !baremetal && !wasm_unknown) || wasip1
+//go:build (linux && !baremetal && !wasm_unknown) || wasip1 || wasip2
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/stat_other.go b/src/os/stat_other.go
index d3e0af6ed..234c59db2 100644
--- a/src/os/stat_other.go
+++ b/src/os/stat_other.go
@@ -1,4 +1,4 @@
-//go:build baremetal || (tinygo.wasm && !wasip1)
+//go:build baremetal || (tinygo.wasm && !wasip1 && !wasip2)
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/stat_unix.go b/src/os/stat_unix.go
index a3aa491f3..af5110cf8 100644
--- a/src/os/stat_unix.go
+++ b/src/os/stat_unix.go
@@ -1,4 +1,4 @@
-//go:build darwin || (linux && !baremetal && !wasm_unknown) || wasip1
+//go:build darwin || (linux && !baremetal && !wasm_unknown) || wasip1 || wasip2
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/os/tempfile_test.go b/src/os/tempfile_test.go
index cf3fd46d7..d5ade369a 100644
--- a/src/os/tempfile_test.go
+++ b/src/os/tempfile_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !baremetal && !js && !wasip1
+//go:build !baremetal && !js && !wasip1 && !wasip2
package os_test
diff --git a/src/os/types_unix.go b/src/os/types_unix.go
index 3a552dfd9..0629b3bbc 100644
--- a/src/os/types_unix.go
+++ b/src/os/types_unix.go
@@ -1,4 +1,4 @@
-//go:build darwin || (linux && !baremetal && !wasm_unknown) || wasip1
+//go:build darwin || (linux && !baremetal && !wasm_unknown) || wasip1 || wasip2
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
index 9780e5401..7218ea653 100644
--- a/src/runtime/os_linux.go
+++ b/src/runtime/os_linux.go
@@ -1,4 +1,4 @@
-//go:build linux && !baremetal && !nintendoswitch && !wasip1 && !wasm_unknown
+//go:build linux && !baremetal && !nintendoswitch && !wasip1 && !wasm_unknown && !wasip2
package runtime
diff --git a/src/runtime/os_other.go b/src/runtime/os_other.go
index c8330bafc..33814c436 100644
--- a/src/runtime/os_other.go
+++ b/src/runtime/os_other.go
@@ -1,4 +1,4 @@
-//go:build linux && (baremetal || nintendoswitch || wasi || wasm_unknown)
+//go:build linux && (baremetal || nintendoswitch || wasm_unknown)
// Other systems that aren't operating systems supported by the Go toolchain
// need to pretend to be an existing operating system. Linux seems like a good
diff --git a/src/runtime/os_wasip2.go b/src/runtime/os_wasip2.go
new file mode 100644
index 000000000..baecfb3ab
--- /dev/null
+++ b/src/runtime/os_wasip2.go
@@ -0,0 +1,5 @@
+//go:build wasip2
+
+package runtime
+
+const GOOS = "wasip2"
diff --git a/src/runtime/runtime_tinygowasm.go b/src/runtime/runtime_tinygowasm.go
index 399860353..b69a91539 100644
--- a/src/runtime/runtime_tinygowasm.go
+++ b/src/runtime/runtime_tinygowasm.go
@@ -1,4 +1,4 @@
-//go:build tinygo.wasm && !wasm_unknown
+//go:build tinygo.wasm && !wasm_unknown && !wasip2
package runtime
diff --git a/src/runtime/runtime_tinygowasmp2.go b/src/runtime/runtime_tinygowasmp2.go
new file mode 100644
index 000000000..96d7db0ff
--- /dev/null
+++ b/src/runtime/runtime_tinygowasmp2.go
@@ -0,0 +1,76 @@
+//go:build wasip2
+
+package runtime
+
+import (
+ exit "internal/wasi/cli/v0.2.0/exit"
+ stdout "internal/wasi/cli/v0.2.0/stdout"
+ monotonicclock "internal/wasi/clocks/v0.2.0/monotonic-clock"
+ wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
+ random "internal/wasi/random/v0.2.0/random"
+
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+const putcharBufferSize = 120
+
+// Using global variables to avoid heap allocation.
+var (
+ putcharStdout = stdout.GetStdout()
+ putcharBuffer = [putcharBufferSize]byte{}
+ putcharPosition uint = 0
+)
+
+func putchar(c byte) {
+ putcharBuffer[putcharPosition] = c
+ putcharPosition++
+ if c == '\n' || putcharPosition >= putcharBufferSize {
+ list := cm.NewList(&putcharBuffer[0], putcharPosition)
+ putcharStdout.BlockingWriteAndFlush(list) // error return ignored; can't do anything anyways
+ putcharPosition = 0
+ }
+}
+
+func getchar() byte {
+ // dummy, TODO
+ return 0
+}
+
+func buffered() int {
+ // dummy, TODO
+ return 0
+}
+
+//go:linkname now time.now
+func now() (sec int64, nsec int32, mono int64) {
+ now := wallclock.Now()
+ sec = int64(now.Seconds)
+ nsec = int32(now.Nanoseconds)
+ mono = int64(monotonicclock.Now())
+ return
+}
+
+// Abort executes the wasm 'unreachable' instruction.
+func abort() {
+ trap()
+}
+
+//go:linkname syscall_Exit syscall.Exit
+func syscall_Exit(code int) {
+ exit.Exit(code != 0)
+}
+
+// TinyGo does not yet support any form of parallelism on WebAssembly, so these
+// can be left empty.
+
+//go:linkname procPin sync/atomic.runtime_procPin
+func procPin() {
+}
+
+//go:linkname procUnpin sync/atomic.runtime_procUnpin
+func procUnpin() {
+}
+
+func hardwareRand() (n uint64, ok bool) {
+ return random.GetRandomU64(), true
+}
diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go
index 323c8909a..50eb11dea 100644
--- a/src/runtime/runtime_unix.go
+++ b/src/runtime/runtime_unix.go
@@ -1,4 +1,4 @@
-//go:build (darwin || (linux && !baremetal && !wasip1 && !wasm_unknown)) && !nintendoswitch
+//go:build (darwin || (linux && !baremetal && !wasip1 && !wasm_unknown && !wasip2)) && !nintendoswitch
package runtime
diff --git a/src/runtime/runtime_wasm_js_scheduler.go b/src/runtime/runtime_wasm_js_scheduler.go
index d5e1012de..0edde29eb 100644
--- a/src/runtime/runtime_wasm_js_scheduler.go
+++ b/src/runtime/runtime_wasm_js_scheduler.go
@@ -1,4 +1,4 @@
-//go:build wasm && !wasi && !scheduler.none && !wasip1 && !wasm_unknown
+//go:build wasm && !wasi && !scheduler.none && !wasip1 && !wasip2 && !wasm_unknown
package runtime
diff --git a/src/runtime/runtime_wasm_wasip2.go b/src/runtime/runtime_wasm_wasip2.go
new file mode 100644
index 000000000..ca189aad6
--- /dev/null
+++ b/src/runtime/runtime_wasm_wasip2.go
@@ -0,0 +1,61 @@
+//go:build wasip2
+
+package runtime
+
+import (
+ "unsafe"
+
+ "internal/wasi/cli/v0.2.0/environment"
+ monotonicclock "internal/wasi/clocks/v0.2.0/monotonic-clock"
+)
+
+type timeUnit int64
+
+//export wasi:cli/[email protected]#run
+func __wasi_cli_run_run() uint32 {
+ _start()
+ return 0
+}
+
+//export _start
+func _start() {
+ // These need to be initialized early so that the heap can be initialized.
+ heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
+ heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
+ run()
+}
+
+func init() {
+}
+
+var args []string
+
+//go:linkname os_runtime_args os.runtime_args
+func os_runtime_args() []string {
+ if args == nil {
+ args = environment.GetArguments().Slice()
+ }
+ return args
+}
+
+//export cabi_realloc
+func cabi_realloc(ptr, oldsize, align, newsize unsafe.Pointer) unsafe.Pointer {
+ return realloc(ptr, uintptr(newsize))
+}
+
+func ticksToNanoseconds(ticks timeUnit) int64 {
+ return int64(ticks)
+}
+
+func nanosecondsToTicks(ns int64) timeUnit {
+ return timeUnit(ns)
+}
+
+func sleepTicks(d timeUnit) {
+ p := monotonicclock.SubscribeDuration(monotonicclock.Duration(d))
+ p.Block()
+}
+
+func ticks() timeUnit {
+ return timeUnit(monotonicclock.Now())
+}
diff --git a/src/syscall/env_libc.go b/src/syscall/env_libc.go
new file mode 100644
index 000000000..f558901cf
--- /dev/null
+++ b/src/syscall/env_libc.go
@@ -0,0 +1,59 @@
+//go:build darwin || nintendoswitch || wasip1
+
+package syscall
+
+import (
+ "unsafe"
+)
+
+func Environ() []string {
+
+ // This function combines all the environment into a single allocation.
+ // While this optimizes for memory usage and garbage collector
+ // overhead, it does run the risk of potentially pinning a "large"
+ // allocation if a user holds onto a single environment variable or
+ // value. Having each variable be its own allocation would make the
+ // trade-off in the other direction.
+
+ // calculate total memory required
+ var length uintptr
+ var vars int
+ for environ := libc_environ; *environ != nil; {
+ length += libc_strlen(*environ)
+ vars++
+ environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
+ }
+
+ // allocate our backing slice for the strings
+ b := make([]byte, length)
+ // and the slice we're going to return
+ envs := make([]string, 0, vars)
+
+ // loop over the environment again, this time copying over the data to the backing slice
+ for environ := libc_environ; *environ != nil; {
+ length = libc_strlen(*environ)
+ // construct a Go string pointing at the libc-allocated environment variable data
+ var envVar string
+ rawEnvVar := (*struct {
+ ptr unsafe.Pointer
+ length uintptr
+ })(unsafe.Pointer(&envVar))
+ rawEnvVar.ptr = *environ
+ rawEnvVar.length = length
+ // pull off the number of bytes we need for this environment variable
+ var bs []byte
+ bs, b = b[:length], b[length:]
+ // copy over the bytes to the Go heap
+ copy(bs, envVar)
+ // convert trimmed slice to string
+ s := *(*string)(unsafe.Pointer(&bs))
+ // add s to our list of environment variables
+ envs = append(envs, s)
+ // environ++
+ environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
+ }
+ return envs
+}
+
+//go:extern environ
+var libc_environ *unsafe.Pointer
diff --git a/src/syscall/env_wasip2.go b/src/syscall/env_wasip2.go
new file mode 100644
index 000000000..970400d64
--- /dev/null
+++ b/src/syscall/env_wasip2.go
@@ -0,0 +1,11 @@
+//go:build wasip2
+
+package syscall
+
+func Environ() []string {
+ var env []string
+ for k, v := range libc_envs {
+ env = append(env, k+"="+v)
+ }
+ return env
+}
diff --git a/src/syscall/errno_other.go b/src/syscall/errno_other.go
index 839b5f435..b988fbc1e 100644
--- a/src/syscall/errno_other.go
+++ b/src/syscall/errno_other.go
@@ -1,4 +1,4 @@
-//go:build !wasip1 && !darwin
+//go:build !wasip1 && !wasip2 && !darwin
package syscall
diff --git a/src/syscall/errno_wasip1.go b/src/syscall/errno_wasip1.go
new file mode 100644
index 000000000..c494d7da0
--- /dev/null
+++ b/src/syscall/errno_wasip1.go
@@ -0,0 +1,8 @@
+//go:build wasip1
+
+package syscall
+
+// Use a go:extern definition to access the errno from wasi-libc
+//
+//go:extern errno
+var libcErrno Errno
diff --git a/src/syscall/errno_wasip2.go b/src/syscall/errno_wasip2.go
new file mode 100644
index 000000000..39f1f8b40
--- /dev/null
+++ b/src/syscall/errno_wasip2.go
@@ -0,0 +1,7 @@
+//go:build wasip2
+
+package syscall
+
+// The errno for libc_wasip2.go
+
+var libcErrno Errno
diff --git a/src/syscall/file_emulated.go b/src/syscall/file_emulated.go
index 8dd3bc14f..7b50d4f7e 100644
--- a/src/syscall/file_emulated.go
+++ b/src/syscall/file_emulated.go
@@ -1,4 +1,4 @@
-//go:build baremetal || (wasm && !wasip1) || wasm_unknown
+//go:build baremetal || (wasm && !wasip1 && !wasip2) || wasm_unknown
// This file emulates some file-related functions that are only available
// under a real operating system.
diff --git a/src/syscall/file_hosted.go b/src/syscall/file_hosted.go
index f448f018d..a079f400f 100644
--- a/src/syscall/file_hosted.go
+++ b/src/syscall/file_hosted.go
@@ -1,4 +1,4 @@
-//go:build !(baremetal || (wasm && !wasip1) || wasm_unknown)
+//go:build !(baremetal || (wasm && !wasip1 && !wasip2) || wasm_unknown)
// This file assumes there is a libc available that runs on a real operating
// system.
diff --git a/src/syscall/libc_wasip2.go b/src/syscall/libc_wasip2.go
new file mode 100644
index 000000000..ea3d3327d
--- /dev/null
+++ b/src/syscall/libc_wasip2.go
@@ -0,0 +1,1356 @@
+//go:build wasip2
+
+// mini libc wrapping wasi preview2 calls in a libc api
+
+package syscall
+
+import (
+ "unsafe"
+
+ "internal/wasi/cli/v0.2.0/environment"
+ "internal/wasi/cli/v0.2.0/stderr"
+ "internal/wasi/cli/v0.2.0/stdin"
+ "internal/wasi/cli/v0.2.0/stdout"
+ wallclock "internal/wasi/clocks/v0.2.0/wall-clock"
+ "internal/wasi/filesystem/v0.2.0/preopens"
+ "internal/wasi/filesystem/v0.2.0/types"
+ ioerror "internal/wasi/io/v0.2.0/error"
+ "internal/wasi/io/v0.2.0/streams"
+ "internal/wasi/random/v0.2.0/random"
+
+ "github.com/ydnar/wasm-tools-go/cm"
+)
+
+func goString(cstr *byte) string {
+ return unsafe.String(cstr, strlen(cstr))
+}
+
+//export strlen
+func strlen(cstr *byte) uintptr {
+ if cstr == nil {
+ return 0
+ }
+ ptr := unsafe.Pointer(cstr)
+ var i uintptr
+ for p := (*byte)(ptr); *p != 0; p = (*byte)(unsafe.Add(unsafe.Pointer(p), 1)) {
+ i++
+ }
+ return i
+}
+
+// ssize_t write(int fd, const void *buf, size_t count)
+//
+//export write
+func write(fd int32, buf *byte, count uint) int {
+ if stream, ok := wasiStreams[fd]; ok {
+ return writeStream(stream, buf, count, 0)
+ }
+
+ stream, ok := wasiFiles[fd]
+ if !ok {
+ libcErrno = EBADF
+ return -1
+ }
+ if stream.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return -1
+ }
+
+ n := pwrite(fd, buf, count, int64(stream.offset))
+ if n == -1 {
+ return -1
+ }
+ stream.offset += int64(n)
+ return int(n)
+}
+
+// ssize_t read(int fd, void *buf, size_t count);
+//
+//export read
+func read(fd int32, buf *byte, count uint) int {
+ if stream, ok := wasiStreams[fd]; ok {
+ return readStream(stream, buf, count, 0)
+ }
+
+ stream, ok := wasiFiles[fd]
+ if !ok {
+ libcErrno = EBADF
+ return -1
+ }
+ if stream.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return -1
+ }
+
+ n := pread(fd, buf, count, int64(stream.offset))
+ if n == -1 {
+ // error during pread
+ return -1
+ }
+ stream.offset += int64(n)
+ return int(n)
+}
+
+// At the moment, each time we have a file read or write we create a new stream. Future implementations
+// could change the current in or out file stream lazily. We could do this by tracking input and output
+// offsets individually, and if they don't match the current main offset, reopen the file stream at that location.
+
+type wasiFile struct {
+ d types.Descriptor
+ oflag int32 // orignal open flags: O_RDONLY, O_WRONLY, O_RDWR
+ offset int64 // current fd offset; updated with each read/write
+ refs int
+}
+
+// Need to figure out which system calls we're using:
+// stdin/stdout/stderr want streams, so we use stream read/write
+// but for regular files we can use the descriptor and explicitly write a buffer to the offset?
+// The mismatch comes from trying to combine these.
+
+var wasiFiles map[int32]*wasiFile = make(map[int32]*wasiFile)
+
+func findFreeFD() int32 {
+ var newfd int32
+ for wasiStreams[newfd] != nil || wasiFiles[newfd] != nil {
+ newfd++
+ }
+ return newfd
+}
+
+var wasiErrno ioerror.Error
+
+type wasiStream struct {
+ in *streams.InputStream
+ out *streams.OutputStream
+ refs int
+}
+
+// This holds entries for stdin/stdout/stderr.
+
+var wasiStreams map[int32]*wasiStream
+
+func init() {
+ sin := stdin.GetStdin()
+ sout := stdout.GetStdout()
+ serr := stderr.GetStderr()
+ wasiStreams = map[int32]*wasiStream{
+ 0: &wasiStream{
+ in: &sin,
+ refs: 1,
+ },
+ 1: &wasiStream{
+ out: &sout,
+ refs: 1,
+ },
+ 2: &wasiStream{
+ out: &serr,
+ refs: 1,
+ },
+ }
+}
+
+func readStream(stream *wasiStream, buf *byte, count uint, offset int64) int {
+ if stream.in == nil {
+ // not a stream we can read from
+ libcErrno = EBADF
+ return -1
+ }
+
+ if offset != 0 {
+ libcErrno = EINVAL
+ return -1
+ }
+
+ libcErrno = 0
+ result := stream.in.BlockingRead(uint64(count))
+ if err := result.Err(); err != nil {
+ if err.Closed() {
+ libcErrno = 0
+ return 0
+ } else if err := err.LastOperationFailed(); err != nil {
+ wasiErrno = *err
+ libcErrno = EWASIERROR
+ }
+ return -1
+ }
+
+ dst := unsafe.Slice(buf, count)
+ list := result.OK()
+ copy(dst, list.Slice())
+ return int(list.Len())
+}
+
+func writeStream(stream *wasiStream, buf *byte, count uint, offset int64) int {
+ if stream.out == nil {
+ // not a stream we can write to
+ libcErrno = EBADF
+ return -1
+ }
+
+ if offset != 0 {
+ libcErrno = EINVAL
+ return -1
+ }
+
+ src := unsafe.Slice(buf, count)
+ var remaining = count
+
+ // The blocking-write-and-flush call allows a maximum of 4096 bytes at a time.
+ // We loop here by instead of doing subscribe/check-write/poll-one/write by hand.
+ for remaining > 0 {
+ len := uint(4096)
+ if len > remaining {
+ len = remaining
+ }
+ result := stream.out.BlockingWriteAndFlush(cm.ToList(src[:len]))
+ if err := result.Err(); err != nil {
+ if err.Closed() {
+ libcErrno = 0
+ return 0
+ } else if err := err.LastOperationFailed(); err != nil {
+ wasiErrno = *err
+ libcErrno = EWASIERROR
+ }
+ return -1
+ }
+ remaining -= len
+ }
+
+ return int(count)
+}
+
+//go:linkname memcpy runtime.memcpy
+func memcpy(dst, src unsafe.Pointer, size uintptr)
+
+// ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+//
+//export pread
+func pread(fd int32, buf *byte, count uint, offset int64) int {
+ // TODO(dgryski): Need to be consistent about all these checks; EBADF/EINVAL/... ?
+
+ if stream, ok := wasiStreams[fd]; ok {
+ return readStream(stream, buf, count, offset)
+
+ }
+
+ streams, ok := wasiFiles[fd]
+ if !ok {
+ // TODO(dgryski): EINVAL?
+ libcErrno = EBADF
+ return -1
+ }
+ if streams.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return -1
+ }
+ if streams.oflag&O_RDONLY == 0 {
+ libcErrno = EBADF
+ return -1
+ }
+
+ result := streams.d.Read(types.FileSize(count), types.FileSize(offset))
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ list := result.OK().F0
+ copy(unsafe.Slice(buf, count), list.Slice())
+
+ // TODO(dgryski): EOF bool is ignored?
+ return int(list.Len())
+}
+
+// ssize_t pwrite(int fd, void *buf, size_t count, off_t offset);
+//
+//export pwrite
+func pwrite(fd int32, buf *byte, count uint, offset int64) int {
+ // TODO(dgryski): Need to be consistent about all these checks; EBADF/EINVAL/... ?
+ if stream, ok := wasiStreams[fd]; ok {
+ return writeStream(stream, buf, count, 0)
+ }
+
+ streams, ok := wasiFiles[fd]
+ if !ok {
+ // TODO(dgryski): EINVAL?
+ libcErrno = EBADF
+ return -1
+ }
+ if streams.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return -1
+ }
+ if streams.oflag&O_WRONLY == 0 {
+ libcErrno = EBADF
+ return -1
+ }
+
+ result := streams.d.Write(cm.NewList(buf, count), types.FileSize(offset))
+ if err := result.Err(); err != nil {
+ // TODO(dgryski):
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return int(*result.OK())
+}
+
+// ssize_t lseek(int fd, off_t offset, int whence);
+//
+//export lseek
+func lseek(fd int32, offset int64, whence int) int64 {
+ if _, ok := wasiStreams[fd]; ok {
+ // can't lseek a stream
+ libcErrno = EBADF
+ return -1
+ }
+
+ stream, ok := wasiFiles[fd]
+ if !ok {
+ libcErrno = EBADF
+ return -1
+ }
+ if stream.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return -1
+ }
+
+ switch whence {
+ case 0: // SEEK_SET
+ stream.offset = offset
+ case 1: // SEEK_CUR
+ stream.offset += offset
+ case 2: // SEEK_END
+ result := stream.d.Stat()
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+ stream.offset = int64(result.OK().Size) + offset
+ }
+
+ return int64(stream.offset)
+}
+
+// int close(int fd)
+//
+//export close
+func close(fd int32) int32 {
+ if streams, ok := wasiStreams[fd]; ok {
+ if streams.out != nil {
+ // ignore any error
+ streams.out.BlockingFlush()
+ }
+
+ if streams.refs--; streams.refs == 0 {
+ if streams.out != nil {
+ streams.out.ResourceDrop()
+ streams.out = nil
+ }
+ if streams.in != nil {
+ streams.in.ResourceDrop()
+ streams.in = nil
+ }
+ }
+
+ delete(wasiStreams, fd)
+ return 0
+ }
+
+ streams, ok := wasiFiles[fd]
+ if !ok {
+ libcErrno = EBADF
+ return -1
+ }
+ if streams.refs--; streams.refs == 0 && streams.d != cm.ResourceNone {
+ streams.d.ResourceDrop()
+ streams.d = 0
+ }
+ delete(wasiFiles, fd)
+
+ return 0
+}
+
+// int dup(int fd)
+//
+//export dup
+func dup(fd int32) int32 {
+ // is fd a stream?
+ if stream, ok := wasiStreams[fd]; ok {
+ newfd := findFreeFD()
+ stream.refs++
+ wasiStreams[newfd] = stream
+ return newfd
+ }
+
+ // is fd a file?
+ if file, ok := wasiFiles[fd]; ok {
+ // scan for first free file descriptor
+ newfd := findFreeFD()
+ file.refs++
+ wasiFiles[newfd] = file
+ return newfd
+ }
+
+ // unknown file descriptor
+ libcErrno = EBADF
+ return -1
+}
+
+// void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
+//
+//export mmap
+func mmap(addr unsafe.Pointer, length uintptr, prot, flags, fd int32, offset uintptr) unsafe.Pointer {
+ libcErrno = ENOSYS
+ return unsafe.Pointer(^uintptr(0))
+}
+
+// int munmap(void *addr, size_t length);
+//
+//export munmap
+func munmap(addr unsafe.Pointer, length uintptr) int32 {
+ libcErrno = ENOSYS
+ return -1
+}
+
+// int mprotect(void *addr, size_t len, int prot);
+//
+//export mprotect
+func mprotect(addr unsafe.Pointer, len uintptr, prot int32) int32 {
+ libcErrno = ENOSYS
+ return -1
+}
+
+// int chmod(const char *pathname, mode_t mode);
+//
+//export chmod
+func chmod(pathname *byte, mode uint32) int32 {
+ return 0
+}
+
+// int mkdir(const char *pathname, mode_t mode);
+//
+//export mkdir
+func mkdir(pathname *byte, mode uint32) int32 {
+
+ path := goString(pathname)
+ dir, relPath := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := dir.d.CreateDirectoryAt(relPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return 0
+}
+
+// int rmdir(const char *pathname);
+//
+//export rmdir
+func rmdir(pathname *byte) int32 {
+ path := goString(pathname)
+ dir, relPath := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := dir.d.RemoveDirectoryAt(relPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return 0
+}
+
+// int rename(const char *from, *to);
+//
+//export rename
+func rename(from, to *byte) int32 {
+ fromPath := goString(from)
+ fromDir, fromRelPath := findPreopenForPath(fromPath)
+ if fromDir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ toPath := goString(to)
+ toDir, toRelPath := findPreopenForPath(toPath)
+ if toDir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := fromDir.d.RenameAt(fromRelPath, toDir.d, toRelPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return 0
+}
+
+// int symlink(const char *from, *to);
+//
+//export symlink
+func symlink(from, to *byte) int32 {
+ fromPath := goString(from)
+ fromDir, fromRelPath := findPreopenForPath(fromPath)
+ if fromDir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ toPath := goString(to)
+ toDir, toRelPath := findPreopenForPath(toPath)
+ if toDir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ if fromDir.d != toDir.d {
+ libcErrno = EACCES
+ return -1
+ }
+
+ // TODO(dgryski): check fromDir == toDir?
+
+ result := fromDir.d.SymlinkAt(fromRelPath, toRelPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return 0
+}
+
+// int link(const char *from, *to);
+//
+//export link
+func link(from, to *byte) int32 {
+ fromPath := goString(from)
+ fromDir, fromRelPath := findPreopenForPath(fromPath)
+ if fromDir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ toPath := goString(to)
+ toDir, toRelPath := findPreopenForPath(toPath)
+ if toDir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ if fromDir.d != toDir.d {
+ libcErrno = EACCES
+ return -1
+ }
+
+ // TODO(dgryski): check fromDir == toDir?
+
+ result := fromDir.d.LinkAt(0, fromRelPath, toDir.d, toRelPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return 0
+}
+
+// int fsync(int fd);
+//
+//export fsync
+func fsync(fd int32) int32 {
+ if _, ok := wasiStreams[fd]; ok {
+ // can't sync a stream
+ libcErrno = EBADF
+ return -1
+ }
+
+ streams, ok := wasiFiles[fd]
+ if !ok {
+ libcErrno = EBADF
+ return -1
+ }
+ if streams.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return -1
+ }
+ if streams.oflag&O_WRONLY == 0 {
+ libcErrno = EBADF
+ return -1
+ }
+
+ result := streams.d.SyncData()
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return 0
+}
+
+// ssize_t readlink(const char *path, void *buf, size_t count);
+//
+//export readlink
+func readlink(pathname *byte, buf *byte, count uint) int {
+ path := goString(pathname)
+ dir, relPath := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := dir.d.ReadLinkAt(relPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ s := *result.OK()
+ size := uintptr(count)
+ if size > uintptr(len(s)) {
+ size = uintptr(len(s))
+ }
+
+ memcpy(unsafe.Pointer(buf), unsafe.Pointer(unsafe.StringData(s)), size)
+ return int(size)
+}
+
+// int unlink(const char *pathname);
+//
+//export unlink
+func unlink(pathname *byte) int32 {
+ path := goString(pathname)
+ dir, relPath := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := dir.d.UnlinkFileAt(relPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ return 0
+}
+
+// int getpagesize(void);
+//
+//export getpagesize
+func getpagesize() int {
+ return 65536
+}
+
+// int stat(const char *path, struct stat * buf);
+//
+//export stat
+func stat(pathname *byte, dst *Stat_t) int32 {
+ path := goString(pathname)
+ dir, relPath := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := dir.d.StatAt(0, relPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ setStatFromWASIStat(dst, result.OK())
+
+ return 0
+}
+
+// int fstat(int fd, struct stat * buf);
+//
+//export fstat
+func fstat(fd int32, dst *Stat_t) int32 {
+ if _, ok := wasiStreams[fd]; ok {
+ // TODO(dgryski): fill in stat buffer for stdin etc
+ return -1
+ }
+
+ stream, ok := wasiFiles[fd]
+ if !ok {
+ libcErrno = EBADF
+ return -1
+ }
+ if stream.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return -1
+ }
+ result := stream.d.Stat()
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ setStatFromWASIStat(dst, result.OK())
+
+ return 0
+}
+
+func setStatFromWASIStat(sstat *Stat_t, wstat *types.DescriptorStat) {
+ // This will cause problems for people who want to compare inodes
+ sstat.Dev = 0
+ sstat.Ino = 0
+ sstat.Rdev = 0
+
+ sstat.Nlink = uint64(wstat.LinkCount)
+
+ sstat.Mode = p2fileTypeToStatType(wstat.Type)
+
+ // No uid/gid
+ sstat.Uid = 0
+ sstat.Gid = 0
+ sstat.Size = int64(wstat.Size)
+
+ // made up numbers
+ sstat.Blksize = 512
+ sstat.Blocks = (sstat.Size + 511) / int64(sstat.Blksize)
+
+ setOptTime := func(t *Timespec, o *wallclock.DateTime) {
+ t.Sec = 0
+ t.Nsec = 0
+ if o != nil {
+ t.Sec = int32(o.Seconds)
+ t.Nsec = int64(o.Nanoseconds)
+ }
+ }
+
+ setOptTime(&sstat.Atim, wstat.DataAccessTimestamp.Some())
+ setOptTime(&sstat.Mtim, wstat.DataModificationTimestamp.Some())
+ setOptTime(&sstat.Ctim, wstat.StatusChangeTimestamp.Some())
+}
+
+// int lstat(const char *path, struct stat * buf);
+//
+//export lstat
+func lstat(pathname *byte, dst *Stat_t) int32 {
+ path := goString(pathname)
+ dir, relPath := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := dir.d.StatAt(0, relPath)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ setStatFromWASIStat(dst, result.OK())
+
+ return 0
+}
+
+func init() {
+ populateEnvironment()
+ populatePreopens()
+}
+
+type wasiDir struct {
+ d types.Descriptor // wasip2 descriptor
+ root string // root path for this descriptor
+ rel string // relative path under root
+}
+
+var libcCWD wasiDir
+
+var wasiPreopens map[string]types.Descriptor
+
+func populatePreopens() {
+
+ var cwd string
+
+ // find CWD
+ result := environment.InitialCWD()
+ if s := result.Some(); s != nil {
+ cwd = *s
+ } else if s, _ := Getenv("PWD"); s != "" {
+ cwd = s
+ }
+
+ dirs := preopens.GetDirectories().Slice()
+ preopens := make(map[string]types.Descriptor, len(dirs))
+ for _, tup := range dirs {
+ desc, path := tup.F0, tup.F1
+ if path == cwd {
+ libcCWD.d = desc
+ libcCWD.root = path
+ libcCWD.rel = ""
+ }
+ preopens[path] = desc
+ }
+ wasiPreopens = preopens
+}
+
+// -- BEGIN fs_wasip1.go --
+// The following section has been taken from upstream Go with the following copyright:
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:nosplit
+func appendCleanPath(buf []byte, path string, lookupParent bool) ([]byte, bool) {
+ i := 0
+ for i < len(path) {
+ for i < len(path) && path[i] == '/' {
+ i++
+ }
+
+ j := i
+ for j < len(path) && path[j] != '/' {
+ j++
+ }
+
+ s := path[i:j]
+ i = j
+
+ switch s {
+ case "":
+ continue
+ case ".":
+ continue
+ case "..":
+ if !lookupParent {
+ k := len(buf)
+ for k > 0 && buf[k-1] != '/' {
+ k--
+ }
+ for k > 1 && buf[k-1] == '/' {
+ k--
+ }
+ buf = buf[:k]
+ if k == 0 {
+ lookupParent = true
+ } else {
+ s = ""
+ continue
+ }
+ }
+ default:
+ lookupParent = false
+ }
+
+ if len(buf) > 0 && buf[len(buf)-1] != '/' {
+ buf = append(buf, '/')
+ }
+ buf = append(buf, s...)
+ }
+ return buf, lookupParent
+}
+
+// joinPath concatenates dir and file paths, producing a cleaned path where
+// "." and ".." have been removed, unless dir is relative and the references
+// to parent directories in file represented a location relative to a parent
+// of dir.
+//
+// This function is used for path resolution of all wasi functions expecting
+// a path argument; the returned string is heap allocated, which we may want
+// to optimize in the future. Instead of returning a string, the function
+// could append the result to an output buffer that the functions in this
+// file can manage to have allocated on the stack (e.g. initializing to a
+// fixed capacity). Since it will significantly increase code complexity,
+// we prefer to optimize for readability and maintainability at this time.
+func joinPath(dir, file string) string {
+ buf := make([]byte, 0, len(dir)+len(file)+1)
+ if isAbs(dir) {
+ buf = append(buf, '/')
+ }
+
+ buf, lookupParent := appendCleanPath(buf, dir, true)
+ buf, _ = appendCleanPath(buf, file, lookupParent)
+ // The appendCleanPath function cleans the path so it does not inject
+ // references to the current directory. If both the dir and file args
+ // were ".", this results in the output buffer being empty so we handle
+ // this condition here.
+ if len(buf) == 0 {
+ buf = append(buf, '.')
+ }
+ // If the file ended with a '/' we make sure that the output also ends
+ // with a '/'. This is needed to ensure that programs have a mechanism
+ // to represent dereferencing symbolic links pointing to directories.
+ if buf[len(buf)-1] != '/' && isDir(file) {
+ buf = append(buf, '/')
+ }
+ return unsafe.String(&buf[0], len(buf))
+}
+
+func isAbs(path string) bool {
+ return hasPrefix(path, "/")
+}
+
+func isDir(path string) bool {
+ return hasSuffix(path, "/")
+}
+
+func hasPrefix(s, p string) bool {
+ return len(s) >= len(p) && s[:len(p)] == p
+}
+
+func hasSuffix(s, x string) bool {
+ return len(s) >= len(x) && s[len(s)-len(x):] == x
+}
+
+// findPreopenForPath finds which preopen it relates to and return that descriptor/root and the path relative to that directory descriptor/root
+func findPreopenForPath(path string) (wasiDir, string) {
+ dir := "/"
+ var wasidir wasiDir
+
+ if !isAbs(path) {
+ dir = libcCWD.root
+ wasidir = libcCWD
+ if libcCWD.rel != "" && libcCWD.rel != "." && libcCWD.rel != "./" {
+ path = libcCWD.rel + "/" + path
+ }
+ }
+ path = joinPath(dir, path)
+
+ var best string
+ for k, v := range wasiPreopens {
+ if len(k) > len(best) && hasPrefix(path, k) {
+ wasidir = wasiDir{d: v, root: k}
+ best = wasidir.root
+ }
+ }
+
+ if hasPrefix(path, wasidir.root) {
+ path = path[len(wasidir.root):]
+ }
+ for isAbs(path) {
+ path = path[1:]
+ }
+ if len(path) == 0 {
+ path = "."
+ }
+
+ return wasidir, path
+}
+
+// -- END fs_wasip1.go --
+
+// int open(const char *pathname, int flags, mode_t mode);
+//
+//export open
+func open(pathname *byte, flags int32, mode uint32) int32 {
+ path := goString(pathname)
+ dir, relPath := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ var dflags types.DescriptorFlags
+ if (flags & O_RDONLY) == O_RDONLY {
+ dflags |= types.DescriptorFlagsRead
+ }
+ if (flags & O_WRONLY) == O_WRONLY {
+ dflags |= types.DescriptorFlagsWrite
+ }
+
+ var oflags types.OpenFlags
+ if flags&O_CREAT == O_CREAT {
+ oflags |= types.OpenFlagsCreate
+ }
+ if flags&O_DIRECTORY == O_DIRECTORY {
+ oflags |= types.OpenFlagsDirectory
+ }
+ if flags&O_EXCL == O_EXCL {
+ oflags |= types.OpenFlagsExclusive
+ }
+ if flags&O_TRUNC == O_TRUNC {
+ oflags |= types.OpenFlagsTruncate
+ }
+
+ // By default, follow symlinks for open() unless O_NOFOLLOW was passed
+ var pflags types.PathFlags = types.PathFlagsSymlinkFollow
+ if flags&O_NOFOLLOW == O_NOFOLLOW {
+ // O_NOFOLLOW was passed, so turn off SymlinkFollow
+ pflags &^= types.PathFlagsSymlinkFollow
+ }
+
+ result := dir.d.OpenAt(pflags, relPath, oflags, dflags)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ stream := wasiFile{
+ d: *result.OK(),
+ oflag: flags,
+ refs: 1,
+ }
+
+ if flags&(O_WRONLY|O_APPEND) == (O_WRONLY | O_APPEND) {
+ result := stream.d.Stat()
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+ stream.offset = int64(result.OK().Size)
+ }
+
+ libcfd := findFreeFD()
+
+ wasiFiles[libcfd] = &stream
+
+ return int32(libcfd)
+}
+
+func errorCodeToErrno(err types.ErrorCode) Errno {
+ switch err {
+ case types.ErrorCodeAccess:
+ return EACCES
+ case types.ErrorCodeWouldBlock:
+ return EAGAIN
+ case types.ErrorCodeAlready:
+ return EALREADY
+ case types.ErrorCodeBadDescriptor:
+ return EBADF
+ case types.ErrorCodeBusy:
+ return EBUSY
+ case types.ErrorCodeDeadlock:
+ return EDEADLK
+ case types.ErrorCodeQuota:
+ return EDQUOT
+ case types.ErrorCodeExist:
+ return EEXIST
+ case types.ErrorCodeFileTooLarge:
+ return EFBIG
+ case types.ErrorCodeIllegalByteSequence:
+ return EILSEQ
+ case types.ErrorCodeInProgress:
+ return EINPROGRESS
+ case types.ErrorCodeInterrupted:
+ return EINTR
+ case types.ErrorCodeInvalid:
+ return EINVAL
+ case types.ErrorCodeIO:
+ return EIO
+ case types.ErrorCodeIsDirectory:
+ return EISDIR
+ case types.ErrorCodeLoop:
+ return ELOOP
+ case types.ErrorCodeTooManyLinks:
+ return EMLINK
+ case types.ErrorCodeMessageSize:
+ return EMSGSIZE
+ case types.ErrorCodeNameTooLong:
+ return ENAMETOOLONG
+ case types.ErrorCodeNoDevice:
+ return ENODEV
+ case types.ErrorCodeNoEntry:
+ return ENOENT
+ case types.ErrorCodeNoLock:
+ return ENOLCK
+ case types.ErrorCodeInsufficientMemory:
+ return ENOMEM
+ case types.ErrorCodeInsufficientSpace:
+ return ENOSPC
+ case types.ErrorCodeNotDirectory:
+ return ENOTDIR
+ case types.ErrorCodeNotEmpty:
+ return ENOTEMPTY
+ case types.ErrorCodeNotRecoverable:
+ return ENOTRECOVERABLE
+ case types.ErrorCodeUnsupported:
+ return ENOSYS
+ case types.ErrorCodeNoTTY:
+ return ENOTTY
+ case types.ErrorCodeNoSuchDevice:
+ return ENXIO
+ case types.ErrorCodeOverflow:
+ return EOVERFLOW
+ case types.ErrorCodeNotPermitted:
+ return EPERM
+ case types.ErrorCodePipe:
+ return EPIPE
+ case types.ErrorCodeReadOnly:
+ return EROFS
+ case types.ErrorCodeInvalidSeek:
+ return ESPIPE
+ case types.ErrorCodeTextFileBusy:
+ return ETXTBSY
+ case types.ErrorCodeCrossDevice:
+ return EXDEV
+ }
+ return Errno(err)
+}
+
+type libc_DIR struct {
+ d types.DirectoryEntryStream
+}
+
+// DIR *fdopendir(int);
+//
+//export fdopendir
+func fdopendir(fd int32) unsafe.Pointer {
+ if _, ok := wasiStreams[fd]; ok {
+ libcErrno = EBADF
+ return nil
+ }
+
+ stream, ok := wasiFiles[fd]
+ if !ok {
+ libcErrno = EBADF
+ return nil
+ }
+ if stream.d == cm.ResourceNone {
+ libcErrno = EBADF
+ return nil
+ }
+
+ result := stream.d.ReadDirectory()
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return nil
+ }
+
+ return unsafe.Pointer(&libc_DIR{d: *result.OK()})
+}
+
+// int fdclosedir(DIR *);
+//
+//export fdclosedir
+func fdclosedir(dirp unsafe.Pointer) int32 {
+ if dirp == nil {
+ return 0
+
+ }
+ dir := (*libc_DIR)(dirp)
+ if dir.d == cm.ResourceNone {
+ return 0
+ }
+
+ dir.d.ResourceDrop()
+ dir.d = cm.ResourceNone
+
+ return 0
+}
+
+// struct dirent *readdir(DIR *);
+//
+//export readdir
+func readdir(dirp unsafe.Pointer) *Dirent {
+ if dirp == nil {
+ return nil
+
+ }
+ dir := (*libc_DIR)(dirp)
+ if dir.d == cm.ResourceNone {
+ return nil
+ }
+
+ result := dir.d.ReadDirectoryEntry()
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return nil
+ }
+
+ entry := result.OK().Some()
+ if entry == nil {
+ libcErrno = 0
+ return nil
+ }
+
+ // The dirent C struct uses a flexible array member to indicate that the
+ // directory name is laid out in memory right after the struct data:
+ //
+ // struct dirent {
+ // ino_t d_ino;
+ // unsigned char d_type;
+ // char d_name[];
+ // };
+ buf := make([]byte, unsafe.Sizeof(Dirent{})+uintptr(len(entry.Name)))
+ dirent := (*Dirent)((unsafe.Pointer)(&buf[0]))
+
+ // No inodes in wasi
+ dirent.Ino = 0
+ dirent.Type = p2fileTypeToDirentType(entry.Type)
+ copy(buf[unsafe.Offsetof(dirent.Type)+1:], entry.Name)
+
+ return dirent
+}
+
+func p2fileTypeToDirentType(t types.DescriptorType) uint8 {
+ switch t {
+ case types.DescriptorTypeUnknown:
+ return DT_UNKNOWN
+ case types.DescriptorTypeBlockDevice:
+ return DT_BLK
+ case types.DescriptorTypeCharacterDevice:
+ return DT_CHR
+ case types.DescriptorTypeDirectory:
+ return DT_DIR
+ case types.DescriptorTypeFIFO:
+ return DT_FIFO
+ case types.DescriptorTypeSymbolicLink:
+ return DT_LNK
+ case types.DescriptorTypeRegularFile:
+ return DT_REG
+ case types.DescriptorTypeSocket:
+ return DT_FIFO
+ }
+
+ return DT_UNKNOWN
+}
+
+func p2fileTypeToStatType(t types.DescriptorType) uint32 {
+ switch t {
+ case types.DescriptorTypeUnknown:
+ return 0
+ case types.DescriptorTypeBlockDevice:
+ return S_IFBLK
+ case types.DescriptorTypeCharacterDevice:
+ return S_IFCHR
+ case types.DescriptorTypeDirectory:
+ return S_IFDIR
+ case types.DescriptorTypeFIFO:
+ return S_IFIFO
+ case types.DescriptorTypeSymbolicLink:
+ return S_IFLNK
+ case types.DescriptorTypeRegularFile:
+ return S_IFREG
+ case types.DescriptorTypeSocket:
+ return S_IFSOCK
+ }
+
+ return 0
+}
+
+var libc_envs map[string]string
+
+func populateEnvironment() {
+ libc_envs = make(map[string]string)
+ for _, kv := range environment.GetEnvironment().Slice() {
+ libc_envs[kv[0]] = kv[1]
+ }
+}
+
+// char * getenv(const char *name);
+//
+//export getenv
+func getenv(key *byte) *byte {
+ k := goString(key)
+
+ v, ok := libc_envs[k]
+ if !ok {
+ return nil
+ }
+
+ // The new allocation is zero-filled; allocating an extra byte and then
+ // copying the data over will leave the last byte untouched,
+ // null-terminating the string.
+ vbytes := make([]byte, len(v)+1)
+ copy(vbytes, v)
+ return unsafe.SliceData(vbytes)
+}
+
+// int setenv(const char *name, const char *value, int overwrite);
+//
+//export setenv
+func setenv(key, value *byte, overwrite int) int {
+ k := goString(key)
+ if _, ok := libc_envs[k]; ok && overwrite == 0 {
+ return 0
+ }
+
+ v := goString(value)
+ libc_envs[k] = v
+
+ return 0
+}
+
+// int unsetenv(const char *name);
+//
+//export unsetenv
+func unsetenv(key *byte) int {
+ k := goString(key)
+ delete(libc_envs, k)
+ return 0
+}
+
+// void arc4random_buf (void *, size_t);
+//
+//export arc4random_buf
+func arc4random_buf(p unsafe.Pointer, l uint) {
+ result := random.GetRandomBytes(uint64(l))
+ s := result.Slice()
+ memcpy(unsafe.Pointer(p), unsafe.Pointer(unsafe.SliceData(s)), uintptr(l))
+}
+
+// int chdir(char *name)
+//
+//export chdir
+func chdir(name *byte) int {
+ path := goString(name) + "/"
+
+ if !isAbs(path) {
+ path = joinPath(libcCWD.root+"/"+libcCWD.rel+"/", path)
+ }
+
+ if path == "." {
+ return 0
+ }
+
+ dir, rel := findPreopenForPath(path)
+ if dir.d == cm.ResourceNone {
+ libcErrno = EACCES
+ return -1
+ }
+
+ result := dir.d.OpenAt(types.PathFlagsSymlinkFollow, rel, types.OpenFlagsDirectory, types.DescriptorFlagsRead)
+ if err := result.Err(); err != nil {
+ libcErrno = errorCodeToErrno(*err)
+ return -1
+ }
+
+ libcCWD = dir
+ // keep the same cwd base but update "rel" to point to new base path
+ libcCWD.rel = rel
+
+ return 0
+}
+
+// char *getcwd(char *buf, size_t size)
+//
+//export getcwd
+func getcwd(buf *byte, size uint) *byte {
+
+ cwd := libcCWD.root
+ if libcCWD.rel != "" && libcCWD.rel != "." && libcCWD.rel != "./" {
+ cwd += libcCWD.rel
+ }
+
+ if buf == nil {
+ b := make([]byte, len(cwd)+1)
+ buf = unsafe.SliceData(b)
+ } else if size == 0 {
+ libcErrno = EINVAL
+ return nil
+ }
+
+ if size < uint(len(cwd)+1) {
+ libcErrno = ERANGE
+ return nil
+ }
+
+ s := unsafe.Slice(buf, size)
+ s[size-1] = 0 // Enforce NULL termination
+ copy(s, cwd)
+ return buf
+}
diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go
index fb2e23968..eed18cbd9 100644
--- a/src/syscall/syscall_libc.go
+++ b/src/syscall/syscall_libc.go
@@ -1,4 +1,4 @@
-//go:build darwin || nintendoswitch || wasi || wasip1
+//go:build darwin || nintendoswitch || wasip1 || wasip2
package syscall
@@ -260,55 +260,6 @@ func Mprotect(b []byte, prot int) (err error) {
return
}
-func Environ() []string {
-
- // This function combines all the environment into a single allocation.
- // While this optimizes for memory usage and garbage collector
- // overhead, it does run the risk of potentially pinning a "large"
- // allocation if a user holds onto a single environment variable or
- // value. Having each variable be its own allocation would make the
- // trade-off in the other direction.
-
- // calculate total memory required
- var length uintptr
- var vars int
- for environ := libc_environ; *environ != nil; {
- length += libc_strlen(*environ)
- vars++
- environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
- }
-
- // allocate our backing slice for the strings
- b := make([]byte, length)
- // and the slice we're going to return
- envs := make([]string, 0, vars)
-
- // loop over the environment again, this time copying over the data to the backing slice
- for environ := libc_environ; *environ != nil; {
- length = libc_strlen(*environ)
- // construct a Go string pointing at the libc-allocated environment variable data
- var envVar string
- rawEnvVar := (*struct {
- ptr unsafe.Pointer
- length uintptr
- })(unsafe.Pointer(&envVar))
- rawEnvVar.ptr = *environ
- rawEnvVar.length = length
- // pull off the number of bytes we need for this environment variable
- var bs []byte
- bs, b = b[:length], b[length:]
- // copy over the bytes to the Go heap
- copy(bs, envVar)
- // convert trimmed slice to string
- s := *(*string)(unsafe.Pointer(&bs))
- // add s to our list of environment variables
- envs = append(envs, s)
- // environ++
- environ = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(environ), unsafe.Sizeof(environ)))
- }
- return envs
-}
-
// BytePtrFromString returns a pointer to a NUL-terminated array of
// bytes containing the text of s. If s contains a NUL byte at any
// location, it returns (nil, EINVAL).
@@ -445,6 +396,3 @@ func libc_readlink(path *byte, buf *byte, count uint) int
//
//export unlink
func libc_unlink(pathname *byte) int32
-
-//go:extern environ
-var libc_environ *unsafe.Pointer
diff --git a/src/syscall/syscall_libc_wasip1.go b/src/syscall/syscall_libc_wasi.go
index 42a5b096d..2d83a8714 100644
--- a/src/syscall/syscall_libc_wasip1.go
+++ b/src/syscall/syscall_libc_wasi.go
@@ -1,4 +1,4 @@
-//go:build wasip1
+//go:build wasip1 || wasip2
package syscall
@@ -70,9 +70,10 @@ const (
__WASI_FILETYPE_SYMBOLIC_LINK = 7
// ../../lib/wasi-libc/libc-bottom-half/headers/public/__header_fcntl.h
- O_RDONLY = 0x04000000
- O_WRONLY = 0x10000000
- O_RDWR = O_RDONLY | O_WRONLY
+ O_NOFOLLOW = 0x01000000
+ O_RDONLY = 0x04000000
+ O_WRONLY = 0x10000000
+ O_RDWR = O_RDONLY | O_WRONLY
O_CREAT = __WASI_OFLAGS_CREAT << 12
O_TRUNC = __WASI_OFLAGS_TRUNC << 12
@@ -118,11 +119,9 @@ const (
PATH_MAX = 4096
)
-//go:extern errno
-var libcErrno uintptr
-
func getErrno() error {
- return Errno(libcErrno)
+ // libcErrno is the errno from wasi-libc for wasip1 and the errno for libc_wasip2 for wasip2
+ return libcErrno
}
func (e Errno) Is(target error) bool {
@@ -195,6 +194,7 @@ const (
ENOTCONN Errno = 53 /* Socket is not connected */
ENOTDIR Errno = 54 /* Not a directory */
ENOTEMPTY Errno = 55 /* Directory not empty */
+ ENOTRECOVERABLE Errno = 56 /* State not recoverable */
ENOTSOCK Errno = 57 /* Socket operation on non-socket */
ESOCKTNOSUPPORT Errno = 58 /* Socket type not supported */
EOPNOTSUPP Errno = 58 /* Operation not supported on transport endpoint */
@@ -213,10 +213,15 @@ const (
ESRCH Errno = 71 /* No such process */
ESTALE Errno = 72
ETIMEDOUT Errno = 73 /* Connection timed out */
+ ETXTBSY Errno = 74 /* Text file busy */
EXDEV Errno = 75 /* Cross-device link */
ENOTCAPABLE Errno = 76 /* Extension: Capabilities insufficient. */
+
+ EWASIERROR Errno = 255 /* Unknown WASI error */
)
+// TODO(ydnar): remove Timespec for WASI Preview 2 (seconds is uint64).
+//
// https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/__struct_timespec.h
type Timespec struct {
Sec int32
@@ -411,6 +416,11 @@ type Utsname struct {
Domainname [65]int8
}
+//go:linkname faccessat syscall.Faccessat
+func faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
+ return ENOSYS
+}
+
// Stub Utsname, needed because WASI pretends to be linux/arm.
func Uname(buf *Utsname) (err error)
diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go
index 9f5249b0c..eecba519a 100644
--- a/src/testing/testing_test.go
+++ b/src/testing/testing_test.go
@@ -26,7 +26,7 @@ func TestMain(m *testing.M) {
}
func TestTempDirInCleanup(t *testing.T) {
- if runtime.GOOS == "wasip1" {
+ if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
t.Log("Skipping. TODO: implement RemoveAll for wasi")
return
}
@@ -63,7 +63,7 @@ func TestTempDirInBenchmark(t *testing.T) {
}
func TestTempDir(t *testing.T) {
- if runtime.GOOS == "wasip1" {
+ if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
t.Log("Skipping. TODO: implement RemoveAll for wasi")
return
}
diff --git a/src/vendor/github.com/ydnar/wasm-tools-go b/src/vendor/github.com/ydnar/wasm-tools-go
new file mode 160000
+Subproject ca8d06b46fa45bdc90b76060fc9bbf7ab9af5ee
diff --git a/targets/wasip2.json b/targets/wasip2.json
new file mode 100644
index 000000000..3d6f68c6e
--- /dev/null
+++ b/targets/wasip2.json
@@ -0,0 +1,28 @@
+{
+ "llvm-target": "wasm32-unknown-wasi",
+ "cpu": "generic",
+ "features": "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext",
+ "build-tags": ["tinygo.wasm", "wasip2"],
+ "goos": "linux",
+ "goarch": "arm",
+ "linker": "wasm-ld",
+ "libc": "wasmbuiltins",
+ "rtlib": "compiler-rt",
+ "scheduler": "asyncify",
+ "default-stack-size": 65536,
+ "cflags": [
+ "-mbulk-memory",
+ "-mnontrapping-fptoint",
+ "-msign-ext"
+ ],
+ "ldflags": [
+ "--stack-first",
+ "--no-demangle"
+ ],
+ "extra-files": [
+ "src/runtime/asm_tinygowasm.S"
+ ],
+ "emulator": "wasmtime --wasm component-model --dir={tmpDir}::/tmp {}",
+ "wit-package": "{root}/lib/wasi-cli/wit/",
+ "wit-world": "wasi:cli/command"
+}
diff --git a/tools/tgtestjson.sh b/tools/tgtestjson.sh
new file mode 100755
index 000000000..169da5852
--- /dev/null
+++ b/tools/tgtestjson.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+# Run tests and convert output to json with go tool test2json.
+# This is a workaround for the lack of -json output in tinygo test.
+# Some variables must be set in the environment beforehand.
+# TODO: let's just add -json support to tinygo test.
+
+TINYGO="${TINYGO:-tinygo}"
+PACKAGES="${PACKAGES:-"./tests"}"
+TARGET="${TARGET:-wasip2}"
+TESTOPTS="${TESTOPTS:-"-x -work"}"
+
+# go clean -testcache
+for pkg in $PACKAGES; do
+ # Example invocation with test2json in BigGo:
+ # go test -test.v=test2json ./$pkg 2>&1 | go tool test2json -p $pkg
+
+ # Uncomment to see resolved commands in output
+ # >&2 echo "${TINYGO} test -v -target $TARGET $TESTOPTS $pkg 2>&1 | go tool test2json -p $pkg"
+ "${TINYGO}" test -v -target $TARGET $TESTOPTS $pkg 2>&1 | go tool test2json -p $pkg
+
+done