diff --git a/.gitignore b/.gitignore index 9d4f702f8c..9994ee3dca 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 c3e7e47bb8..edd7b215c1 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 2d7da5c590..08b16454a5 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/syscall -p syscall --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 @@ -434,11 +439,38 @@ 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-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-wip: + $(TINYGO) test -target wasip2 -x -v $(TEST_PACKAGES_FAST) ./tests/runtime_wasi +tinygo-test-wasip2-dev: + $(TINYGO) test -target wasip2 -wit-package $$(tinygo env TINYGOROOT)/lib/wasi-cli/wit/ -wit-world wasi:cli/command -x -work encoding/csv +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 diff --git a/builder/build.go b/builder/build.go index 5a6683ae6f..f19a8cafee 100644 --- a/builder/build.go +++ b/builder/build.go @@ -832,7 +832,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe "--output", result.Executable, ) - cmd := exec.Command(goenv.Get("WASMOPT"), args...) + wasmopt := goenv.Get("WASMOPT") + if config.Options.PrintCommands != nil { + config.Options.PrintCommands(wasmopt, args...) + } + cmd := exec.Command(wasmopt, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -842,6 +846,80 @@ 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 { + // Preserve a copy of the failure if -work option is used + if config.Options.Work { + dest := filepath.Join( + filepath.Dir(result.Executable), + fmt.Sprintf("%s.embed-fail.wasm", strings.Replace(pkgName, "/", "_", -1)), + ) + copyCmd := exec.Command("cp", "-f", result.Executable, dest) + copyCmd.Run() + } + 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 { + // Preserve a copy of the failure if -work option is used + if config.Options.Work { + dest := filepath.Join( + filepath.Dir(result.Executable), + fmt.Sprintf("%s.comp-fail.wasm", strings.Replace(pkgName, "/", "_", -1)), + ) + copyCmd := exec.Command("cp", "-f", result.Executable, dest) + copyCmd.Run() + } + 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/compileopts/options.go b/compileopts/options.go index debdaf08cd..cf663cc1a7 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 66f44b7ccf..946c48b30f 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 bf5ac5f1b7..0171273dcb 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 @@ -375,19 +375,26 @@ func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) { // Check whether the type maps directly to a WebAssembly type, according to: // https://github.com/golang/go/issues/59149 +// TODO(ydnar): document why we relaxed this for WASI Preview 2. func isValidWasmType(typ types.Type, isReturn bool) 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.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: + return true } + case *types.Struct: + return true + case *types.Pointer: + return isValidWasmType(typ.Elem(), isReturn) } return false } diff --git a/compiler/testdata/errors.go b/compiler/testdata/errors.go index 5778a931e1..8032ad62c3 100644 --- a/compiler/testdata/errors.go +++ b/compiler/testdata/errors.go @@ -16,14 +16,6 @@ type Uint uint32 //go:wasmimport modulename validparam func validparam(a int32, b uint64, c float64, d unsafe.Pointer, e Uint) -// 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 []byte -// ERROR: //go:wasmimport modulename invalidparam: unsupported parameter type *int32 -// -//go:wasmimport modulename invalidparam -func invalidparam(a int, b string, c []byte, d *int32) - //go:wasmimport modulename validreturn func validreturn() int32 @@ -36,8 +28,3 @@ func manyreturns() (int32, int32) // //go:wasmimport modulename invalidreturn func invalidreturn() int - -// ERROR: //go:wasmimport modulename invalidUnsafePointerReturn: unsupported result type unsafe.Pointer -// -//go:wasmimport modulename invalidUnsafePointerReturn -func invalidUnsafePointerReturn() unsafe.Pointer diff --git a/goenv/goenv.go b/goenv/goenv.go index be1c631ca9..187bcc6df1 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 index 0000000000..6ae8261709 --- /dev/null +++ b/lib/wasi-cli @@ -0,0 +1 @@ +Subproject commit 6ae82617096e83e6606047736e84ac397b788631 diff --git a/loader/goroot.go b/loader/goroot.go index ea2f705dc9..9352df64e2 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -240,6 +240,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool { "internal/fuzz/": false, "internal/reflectlite/": false, "internal/task/": false, + "internal/wasm/": false, "machine/": false, "net/": true, "net/http/": false, @@ -249,6 +250,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 { @@ -259,6 +262,7 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool { if needsSyscallPackage { paths["syscall/"] = true // include syscall/js + paths["syscall/wasi/"] = false } return paths } diff --git a/main.go b/main.go index b10f45cd73..35df4ea8bb 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.GOOS() { + case "wasip1": + dirs = dirsToModuleRootRel(result.MainDir, result.ModuleRoot) + case "wasip2", "linux": + dirs = dirsToModuleRootAbs(result.MainDir, result.ModuleRoot) + default: + return fmt.Errorf("unknown GOOS target: %v", config.GOOS()) + } + 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,7 +365,7 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options return passed, err } -func dirsToModuleRoot(maindir, modroot string) []string { +func dirsToModuleRootRel(maindir, modroot string) []string { var dirs = []string{"."} last := ".." // strip off path elements until we hit the module root @@ -369,6 +378,19 @@ func dirsToModuleRoot(maindir, modroot string) []string { 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 +} + // Flash builds and flashes the built binary to the given serial port. func Flash(pkgName, port string, options *compileopts.Options) error { config, err := builder.NewConfig(options) @@ -820,7 +842,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) } @@ -1464,6 +1485,12 @@ func main() { flag.StringVar(&outpath, "o", "", "output filename") } + var witPackage, witWorld string + if command == "help" || command == "build" || command == "test" { + 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") @@ -1543,6 +1570,8 @@ func main() { Monitor: *monitor, BaudRate: *baudrate, Timeout: *timeout, + WitPackage: witPackage, + WitWorld: witWorld, } if *printCommands { options.PrintCommands = printCommand diff --git a/src/crypto/rand/rand_arc4random.go b/src/crypto/rand/rand_arc4random.go index 1b1796b4d6..f9dd322499 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 // 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 e9a8d485e9..53554529b4 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/os/dir_test.go b/src/os/dir_test.go index d661c98b44..e51e290b39 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 a531e0a639..e77761bf1e 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 +//go:build linux && !baremetal && !wasip1 && !wasip2 package os diff --git a/src/os/dir_wasip1.go b/src/os/dir_wasi.go similarity index 98% rename from src/os/dir_wasip1.go rename to src/os/dir_wasi.go index 0be0da4d64..6d9313110e 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 90f7086db8..7bc1f138b7 100644 --- a/src/os/dirent_linux.go +++ b/src/os/dirent_linux.go @@ -1,4 +1,4 @@ -//go:build !baremetal && !js && !wasip1 +//go:build !baremetal && !js && !wasip1 && !wasip2 // 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 034f481544..93dff91a14 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 3ccb6963bb..f48e652feb 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 e7fabddcaf..994a6709f5 100644 --- a/src/os/file_other.go +++ b/src/os/file_other.go @@ -1,4 +1,4 @@ -//go:build baremetal || (wasm && !wasip1) +//go:build baremetal || (wasm && !wasip1 && !wasip2) package os diff --git a/src/os/file_unix.go b/src/os/file_unix.go index 665fb0937e..1efe55be30 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -1,4 +1,4 @@ -//go:build darwin || (linux && !baremetal) || wasip1 +//go:build darwin || (linux && !baremetal) || 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 80475552be..5017a9b807 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 8a082d6522..67c609949e 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 b8f7d354f4..3db6467add 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_symlink_test.go b/src/os/os_symlink_test.go index f885dd32c4..baa7047b27 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 b0553ffdb4..98c86089fd 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 679d961132..68eb02966b 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 8b03756e31..4e09b32091 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 +//go:build !baremetal && !js && !wasip1 && !wasip2 package os diff --git a/src/os/removeall_other.go b/src/os/removeall_other.go index ec055a9875..33531d4510 100644 --- a/src/os/removeall_other.go +++ b/src/os/removeall_other.go @@ -1,4 +1,4 @@ -//go:build baremetal || js || wasi || wasip1 +//go:build baremetal || js || wasi || 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_test.go b/src/os/removeall_test.go index ea7c83b4e4..9f49e6793b 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/stat_linuxlike.go b/src/os/stat_linuxlike.go index f2ff8a5f61..c2ac1784d5 100644 --- a/src/os/stat_linuxlike.go +++ b/src/os/stat_linuxlike.go @@ -1,4 +1,4 @@ -//go:build (linux && !baremetal) || wasip1 +//go:build (linux && !baremetal) || 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/tempfile_test.go b/src/os/tempfile_test.go index cf3fd46d70..d5ade369a0 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 943fc00f52..d6eb44f244 100644 --- a/src/os/types_unix.go +++ b/src/os/types_unix.go @@ -1,4 +1,4 @@ -//go:build darwin || (linux && !baremetal) || wasip1 +//go:build darwin || (linux && !baremetal) || 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 9780e54018..7218ea653a 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_wasip2.go b/src/runtime/os_wasip2.go new file mode 100644 index 0000000000..baecfb3ab9 --- /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 3998603539..b69a91539a 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 0000000000..bc409cb1a6 --- /dev/null +++ b/src/runtime/runtime_tinygowasmp2.go @@ -0,0 +1,80 @@ +//go:build wasip2 + +package runtime + +import ( + exit "syscall/wasi/cli/v0.2.0/exit" + stdout "syscall/wasi/cli/v0.2.0/stdout" + monotonicclock "syscall/wasi/clocks/v0.2.0/monotonic-clock" + wallclock "syscall/wasi/clocks/v0.2.0/wall-clock" + random "syscall/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) + result := putcharStdout.BlockingWriteAndFlush(list) + if err := result.Err(); err != nil { + // TODO(ydnar): handle error case + panic(*err) + } + 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 323c8909ac..50eb11dead 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_wasip2.go b/src/runtime/runtime_wasm_wasip2.go new file mode 100644 index 0000000000..c46b9dfcfc --- /dev/null +++ b/src/runtime/runtime_wasm_wasip2.go @@ -0,0 +1,71 @@ +//go:build wasip2 + +package runtime + +import ( + "unsafe" + + "syscall/wasi/cli/v0.2.0/environment" + monotonicclock "syscall/wasi/clocks/v0.2.0/monotonic-clock" +) + +type timeUnit int64 + +// libc constructors +// +//export __wasm_call_ctors +func __wasm_call_ctors() + +//export wasi:cli/run@0.2.0#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() +} + +// Read the command line arguments from WASI. +// For example, they can be passed to a program with wasmtime like this: +// +// wasmtime run ./program.wasm arg1 arg2 +func init() { + __wasm_call_ctors() +} + +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 0000000000..fc42392405 --- /dev/null +++ b/src/syscall/env_libc.go @@ -0,0 +1,59 @@ +//go:build (darwin || nintendoswitch || wasi || wasip1) && !wasip2 + +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 0000000000..970400d644 --- /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 839b5f4357..b988fbc1e0 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 0000000000..c494d7da09 --- /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 0000000000..39f1f8b403 --- /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 8dd3bc14fc..7b50d4f7e8 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 f448f018d7..a079f400fb 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 0000000000..2b300503fa --- /dev/null +++ b/src/syscall/libc_wasip2.go @@ -0,0 +1,1292 @@ +//go:build wasip2 + +// mini libc wrapping wasi preview2 calls in a libc api + +package syscall + +import ( + "unsafe" + + "syscall/wasi/cli/v0.2.0/environment" + "syscall/wasi/cli/v0.2.0/stderr" + "syscall/wasi/cli/v0.2.0/stdin" + "syscall/wasi/cli/v0.2.0/stdout" + wallclock "syscall/wasi/clocks/v0.2.0/wall-clock" + "syscall/wasi/filesystem/v0.2.0/preopens" + "syscall/wasi/filesystem/v0.2.0/types" + ioerror "syscall/wasi/io/v0.2.0/error" + "syscall/wasi/io/v0.2.0/streams" + "syscall/wasi/random/v0.2.0/random" + + "github.com/ydnar/wasm-tools-go/cm" +) + +func goString(cstr *byte) string { + return unsafe.String(cstr, strlen(cstr)) +} + +//go: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) +// +//go: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); +// +//go: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 +} + +// 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 +} + +// 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, + }, + 1: &wasiStream{ + out: &sout, + }, + 2: &wasiStream{ + out: &serr, + }, + } +} + +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); +// +//go: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); +// +//go: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); +// +//go: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) +// +//go:export close +func close(fd int32) int32 { + if _, ok := wasiStreams[fd]; ok { + // TODO(dgryski): Do we need to do any stdin/stdout/stderr cleanup here? + delete(wasiStreams, fd) + return 0 + } + + streams, ok := wasiFiles[fd] + if !ok { + libcErrno = EBADF + return -1 + } + if streams.d != cm.ResourceNone { + streams.d.ResourceDrop() + streams.d = 0 + } + delete(wasiFiles, fd) + + return 0 +} + +// int dup(int fd) +// +//go:export dup +func dup(fd int32) int32 { + // is fd a stream? + if stream, ok := wasiStreams[fd]; ok { + newfd := findFreeFD() + wasiStreams[newfd] = stream + return newfd + } + + // is fd a file? + if file, ok := wasiFiles[fd]; ok { + // scan for first free file descriptor + newfd := findFreeFD() + 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); +// +//go: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); +// +//go:export munmap +func munmap(addr unsafe.Pointer, length uintptr) int32 { + libcErrno = ENOSYS + return -1 +} + +// int mprotect(void *addr, size_t len, int prot); +// +//go:export mprotect +func mprotect(addr unsafe.Pointer, len uintptr, prot int32) int32 { + libcErrno = ENOSYS + return -1 +} + +// int chmod(const char *pathname, mode_t mode); +// +//go:export chmod +func chmod(pathname *byte, mode uint32) int32 { + return 0 +} + +// int mkdir(const char *pathname, mode_t mode); +// +//go: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); +// +//go: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); +// +//go: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); +// +//go: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 fsync(int fd); +// +//go: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); +// +//go: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); +// +//go: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); +// +//go:export getpagesize +func getpagesize() int { + return 65536 +} + +// int stat(const char *path, struct stat * buf); +// +//go: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); +// +//go: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); +// +//go: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 +} + +// From fs_wasip1.go + +//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 +} + +// int open(const char *pathname, int flags, mode_t mode); +// +//go: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, + } + + 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); +// +//go: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 *); +// +//go: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 *); +// +//go: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); +// +//go: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); +// +//go: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); +// +//go:export unsetenv +func unsetenv(key *byte) int { + k := goString(key) + delete(libc_envs, k) + return 0 +} + +// void arc4random_buf (void *, size_t); +// +//go: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) +// +//go: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) +// +//go: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 + } + + // TODO(dgryski): null termination? + copy(unsafe.Slice(buf, size), cwd) + return buf +} diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index 7a13245b6d..3e92f01258 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 || wasi || wasip1 || wasip2 package syscall @@ -250,55 +250,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). @@ -430,6 +381,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 similarity index 97% rename from src/syscall/syscall_libc_wasip1.go rename to src/syscall/syscall_libc_wasi.go index 5242690cc4..f7bcebf55b 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,8 @@ const ( PATH_MAX = 4096 ) -//go:extern errno -var libcErrno uintptr - func getErrno() error { - return Errno(libcErrno) + return libcErrno } func (e Errno) Is(target error) bool { @@ -195,6 +193,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 +212,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 diff --git a/src/syscall/wasi/cli/v0.2.0/environment/empty.s b/src/syscall/wasi/cli/v0.2.0/environment/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/environment/environment.wit.go b/src/syscall/wasi/cli/v0.2.0/environment/environment.wit.go new file mode 100644 index 0000000000..d2d41dcb28 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/environment/environment.wit.go @@ -0,0 +1,69 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package environment represents the interface "wasi:cli/environment@0.2.0". +package environment + +import ( + "github.com/ydnar/wasm-tools-go/cm" +) + +// GetArguments represents function "wasi:cli/environment@0.2.0#get-arguments". +// +// Get the POSIX-style arguments to the program. +// +// get-arguments: func() -> list +// +//go:nosplit +func GetArguments() cm.List[string] { + var result cm.List[string] + wasmimport_GetArguments(&result) + return result +} + +//go:wasmimport wasi:cli/environment@0.2.0 get-arguments +//go:noescape +func wasmimport_GetArguments(result *cm.List[string]) + +// GetEnvironment represents function "wasi:cli/environment@0.2.0#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> +// +//go:nosplit +func GetEnvironment() cm.List[[2]string] { + var result cm.List[[2]string] + wasmimport_GetEnvironment(&result) + return result +} + +//go:wasmimport wasi:cli/environment@0.2.0 get-environment +//go:noescape +func wasmimport_GetEnvironment(result *cm.List[[2]string]) + +// InitialCWD represents function "wasi:cli/environment@0.2.0#initial-cwd". +// +// Return a path that programs should use as their initial current working +// directory, interpreting `.` as shorthand for this. +// +// initial-cwd: func() -> option +// +//go:nosplit +func InitialCWD() cm.Option[string] { + var result cm.Option[string] + wasmimport_InitialCWD(&result) + return result +} + +//go:wasmimport wasi:cli/environment@0.2.0 initial-cwd +//go:noescape +func wasmimport_InitialCWD(result *cm.Option[string]) diff --git a/src/syscall/wasi/cli/v0.2.0/exit/empty.s b/src/syscall/wasi/cli/v0.2.0/exit/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/exit/exit.wit.go b/src/syscall/wasi/cli/v0.2.0/exit/exit.wit.go new file mode 100644 index 0000000000..7f6ab0ac2e --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/exit/exit.wit.go @@ -0,0 +1,25 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package exit represents the interface "wasi:cli/exit@0.2.0". +package exit + +import ( + "github.com/ydnar/wasm-tools-go/cm" +) + +// Exit represents function "wasi:cli/exit@0.2.0#exit". +// +// Exit the current instance and any linked instances. +// +// exit: func(status: result) +// +//go:nosplit +func Exit(status cm.Result) { + wasmimport_Exit(status) +} + +//go:wasmimport wasi:cli/exit@0.2.0 exit +//go:noescape +func wasmimport_Exit(status cm.Result) diff --git a/src/syscall/wasi/cli/v0.2.0/run/empty.s b/src/syscall/wasi/cli/v0.2.0/run/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/run/run.wit.go b/src/syscall/wasi/cli/v0.2.0/run/run.wit.go new file mode 100644 index 0000000000..865cef98bd --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/run/run.wit.go @@ -0,0 +1,25 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package run represents the interface "wasi:cli/run@0.2.0". +package run + +import ( + "github.com/ydnar/wasm-tools-go/cm" +) + +// Run represents function "wasi:cli/run@0.2.0#run". +// +// Run the program. +// +// run: func() -> result +// +//go:nosplit +func Run() cm.Result { + return wasmimport_Run() +} + +//go:wasmimport wasi:cli/run@0.2.0 run +//go:noescape +func wasmimport_Run() cm.Result diff --git a/src/syscall/wasi/cli/v0.2.0/stderr/empty.s b/src/syscall/wasi/cli/v0.2.0/stderr/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/stderr/stderr.wit.go b/src/syscall/wasi/cli/v0.2.0/stderr/stderr.wit.go new file mode 100644 index 0000000000..ba1ab5c790 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/stderr/stderr.wit.go @@ -0,0 +1,28 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package stderr represents the interface "wasi:cli/stderr@0.2.0". +package stderr + +import ( + "syscall/wasi/io/v0.2.0/streams" +) + +// OutputStream represents the resource "wasi:io/streams@0.2.0#output-stream". +// +// See [streams.OutputStream] for more information. +type OutputStream = streams.OutputStream + +// GetStderr represents function "wasi:cli/stderr@0.2.0#get-stderr". +// +// get-stderr: func() -> own +// +//go:nosplit +func GetStderr() OutputStream { + return wasmimport_GetStderr() +} + +//go:wasmimport wasi:cli/stderr@0.2.0 get-stderr +//go:noescape +func wasmimport_GetStderr() OutputStream diff --git a/src/syscall/wasi/cli/v0.2.0/stdin/empty.s b/src/syscall/wasi/cli/v0.2.0/stdin/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/stdin/stdin.wit.go b/src/syscall/wasi/cli/v0.2.0/stdin/stdin.wit.go new file mode 100644 index 0000000000..0cc7bdcb90 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/stdin/stdin.wit.go @@ -0,0 +1,28 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package stdin represents the interface "wasi:cli/stdin@0.2.0". +package stdin + +import ( + "syscall/wasi/io/v0.2.0/streams" +) + +// InputStream represents the resource "wasi:io/streams@0.2.0#input-stream". +// +// See [streams.InputStream] for more information. +type InputStream = streams.InputStream + +// GetStdin represents function "wasi:cli/stdin@0.2.0#get-stdin". +// +// get-stdin: func() -> own +// +//go:nosplit +func GetStdin() InputStream { + return wasmimport_GetStdin() +} + +//go:wasmimport wasi:cli/stdin@0.2.0 get-stdin +//go:noescape +func wasmimport_GetStdin() InputStream diff --git a/src/syscall/wasi/cli/v0.2.0/stdout/empty.s b/src/syscall/wasi/cli/v0.2.0/stdout/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/stdout/stdout.wit.go b/src/syscall/wasi/cli/v0.2.0/stdout/stdout.wit.go new file mode 100644 index 0000000000..5abc534668 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/stdout/stdout.wit.go @@ -0,0 +1,28 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package stdout represents the interface "wasi:cli/stdout@0.2.0". +package stdout + +import ( + "syscall/wasi/io/v0.2.0/streams" +) + +// OutputStream represents the resource "wasi:io/streams@0.2.0#output-stream". +// +// See [streams.OutputStream] for more information. +type OutputStream = streams.OutputStream + +// GetStdout represents function "wasi:cli/stdout@0.2.0#get-stdout". +// +// get-stdout: func() -> own +// +//go:nosplit +func GetStdout() OutputStream { + return wasmimport_GetStdout() +} + +//go:wasmimport wasi:cli/stdout@0.2.0 get-stdout +//go:noescape +func wasmimport_GetStdout() OutputStream diff --git a/src/syscall/wasi/cli/v0.2.0/terminal-input/empty.s b/src/syscall/wasi/cli/v0.2.0/terminal-input/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/terminal-input/terminal-input.wit.go b/src/syscall/wasi/cli/v0.2.0/terminal-input/terminal-input.wit.go new file mode 100644 index 0000000000..209f5d6ec9 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/terminal-input/terminal-input.wit.go @@ -0,0 +1,36 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package terminalinput represents the interface "wasi:cli/terminal-input@0.2.0". +// +// 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 resource "wasi:cli/terminal-input@0.2.0#terminal-input". +// +// The input side of a terminal. +// +// resource terminal-input +type TerminalInput cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self TerminalInput) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:cli/terminal-input@0.2.0 [resource-drop]terminal-input +//go:noescape +func (self TerminalInput) wasmimport_ResourceDrop() diff --git a/src/syscall/wasi/cli/v0.2.0/terminal-output/empty.s b/src/syscall/wasi/cli/v0.2.0/terminal-output/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/terminal-output/terminal-output.wit.go b/src/syscall/wasi/cli/v0.2.0/terminal-output/terminal-output.wit.go new file mode 100644 index 0000000000..1380721dac --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/terminal-output/terminal-output.wit.go @@ -0,0 +1,36 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package terminaloutput represents the interface "wasi:cli/terminal-output@0.2.0". +// +// 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 resource "wasi:cli/terminal-output@0.2.0#terminal-output". +// +// The output side of a terminal. +// +// resource terminal-output +type TerminalOutput cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self TerminalOutput) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:cli/terminal-output@0.2.0 [resource-drop]terminal-output +//go:noescape +func (self TerminalOutput) wasmimport_ResourceDrop() diff --git a/src/syscall/wasi/cli/v0.2.0/terminal-stderr/empty.s b/src/syscall/wasi/cli/v0.2.0/terminal-stderr/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/terminal-stderr/terminal-stderr.wit.go b/src/syscall/wasi/cli/v0.2.0/terminal-stderr/terminal-stderr.wit.go new file mode 100644 index 0000000000..5a06da3d53 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/terminal-stderr/terminal-stderr.wit.go @@ -0,0 +1,37 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package terminalstderr represents the interface "wasi:cli/terminal-stderr@0.2.0". +// +// 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 "syscall/wasi/cli/v0.2.0/terminal-output" +) + +// TerminalOutput represents the resource "wasi:cli/terminal-output@0.2.0#terminal-output". +// +// See [terminaloutput.TerminalOutput] for more information. +type TerminalOutput = terminaloutput.TerminalOutput + +// GetTerminalStderr represents function "wasi:cli/terminal-stderr@0.2.0#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> +// +//go:nosplit +func GetTerminalStderr() cm.Option[TerminalOutput] { + var result cm.Option[TerminalOutput] + wasmimport_GetTerminalStderr(&result) + return result +} + +//go:wasmimport wasi:cli/terminal-stderr@0.2.0 get-terminal-stderr +//go:noescape +func wasmimport_GetTerminalStderr(result *cm.Option[TerminalOutput]) diff --git a/src/syscall/wasi/cli/v0.2.0/terminal-stdin/empty.s b/src/syscall/wasi/cli/v0.2.0/terminal-stdin/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/terminal-stdin/terminal-stdin.wit.go b/src/syscall/wasi/cli/v0.2.0/terminal-stdin/terminal-stdin.wit.go new file mode 100644 index 0000000000..63b3068d35 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/terminal-stdin/terminal-stdin.wit.go @@ -0,0 +1,37 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package terminalstdin represents the interface "wasi:cli/terminal-stdin@0.2.0". +// +// 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 "syscall/wasi/cli/v0.2.0/terminal-input" +) + +// TerminalInput represents the resource "wasi:cli/terminal-input@0.2.0#terminal-input". +// +// See [terminalinput.TerminalInput] for more information. +type TerminalInput = terminalinput.TerminalInput + +// GetTerminalStdin represents function "wasi:cli/terminal-stdin@0.2.0#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> +// +//go:nosplit +func GetTerminalStdin() cm.Option[TerminalInput] { + var result cm.Option[TerminalInput] + wasmimport_GetTerminalStdin(&result) + return result +} + +//go:wasmimport wasi:cli/terminal-stdin@0.2.0 get-terminal-stdin +//go:noescape +func wasmimport_GetTerminalStdin(result *cm.Option[TerminalInput]) diff --git a/src/syscall/wasi/cli/v0.2.0/terminal-stdout/empty.s b/src/syscall/wasi/cli/v0.2.0/terminal-stdout/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/cli/v0.2.0/terminal-stdout/terminal-stdout.wit.go b/src/syscall/wasi/cli/v0.2.0/terminal-stdout/terminal-stdout.wit.go new file mode 100644 index 0000000000..979eb8d588 --- /dev/null +++ b/src/syscall/wasi/cli/v0.2.0/terminal-stdout/terminal-stdout.wit.go @@ -0,0 +1,37 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package terminalstdout represents the interface "wasi:cli/terminal-stdout@0.2.0". +// +// 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 "syscall/wasi/cli/v0.2.0/terminal-output" +) + +// TerminalOutput represents the resource "wasi:cli/terminal-output@0.2.0#terminal-output". +// +// See [terminaloutput.TerminalOutput] for more information. +type TerminalOutput = terminaloutput.TerminalOutput + +// GetTerminalStdout represents function "wasi:cli/terminal-stdout@0.2.0#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> +// +//go:nosplit +func GetTerminalStdout() cm.Option[TerminalOutput] { + var result cm.Option[TerminalOutput] + wasmimport_GetTerminalStdout(&result) + return result +} + +//go:wasmimport wasi:cli/terminal-stdout@0.2.0 get-terminal-stdout +//go:noescape +func wasmimport_GetTerminalStdout(result *cm.Option[TerminalOutput]) diff --git a/src/syscall/wasi/clocks/v0.2.0/monotonic-clock/empty.s b/src/syscall/wasi/clocks/v0.2.0/monotonic-clock/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/clocks/v0.2.0/monotonic-clock/monotonic-clock.wit.go b/src/syscall/wasi/clocks/v0.2.0/monotonic-clock/monotonic-clock.wit.go new file mode 100644 index 0000000000..615b14ac35 --- /dev/null +++ b/src/syscall/wasi/clocks/v0.2.0/monotonic-clock/monotonic-clock.wit.go @@ -0,0 +1,109 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package monotonicclock represents the interface "wasi:clocks/monotonic-clock@0.2.0". +// +// 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 ( + "syscall/wasi/io/v0.2.0/poll" +) + +// Duration represents the type "wasi:clocks/monotonic-clock@0.2.0#duration". +// +// A duration of time, in nanoseconds. +// +// type duration = u64 +type Duration uint64 + +// Instant represents the type "wasi:clocks/monotonic-clock@0.2.0#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 + +// Pollable represents the resource "wasi:io/poll@0.2.0#pollable". +// +// See [poll.Pollable] for more information. +type Pollable = poll.Pollable + +// Now represents function "wasi:clocks/monotonic-clock@0.2.0#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() Instant { + return wasmimport_Now() +} + +//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 now +//go:noescape +func wasmimport_Now() Instant + +// Resolution represents function "wasi:clocks/monotonic-clock@0.2.0#resolution". +// +// Query the resolution of the clock. Returns the duration of time +// corresponding to a clock tick. +// +// resolution: func() -> duration +// +//go:nosplit +func Resolution() Duration { + return wasmimport_Resolution() +} + +//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 resolution +//go:noescape +func wasmimport_Resolution() Duration + +// SubscribeDuration represents function "wasi:clocks/monotonic-clock@0.2.0#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) -> own +// +//go:nosplit +func SubscribeDuration(when Duration) Pollable { + return wasmimport_SubscribeDuration(when) +} + +//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-duration +//go:noescape +func wasmimport_SubscribeDuration(when Duration) Pollable + +// SubscribeInstant represents function "wasi:clocks/monotonic-clock@0.2.0#subscribe-instant". +// +// Create a `pollable` which will resolve once the specified instant +// occured. +// +// subscribe-instant: func(when: instant) -> own +// +//go:nosplit +func SubscribeInstant(when Instant) Pollable { + return wasmimport_SubscribeInstant(when) +} + +//go:wasmimport wasi:clocks/monotonic-clock@0.2.0 subscribe-instant +//go:noescape +func wasmimport_SubscribeInstant(when Instant) Pollable diff --git a/src/syscall/wasi/clocks/v0.2.0/wall-clock/empty.s b/src/syscall/wasi/clocks/v0.2.0/wall-clock/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/clocks/v0.2.0/wall-clock/wall-clock.wit.go b/src/syscall/wasi/clocks/v0.2.0/wall-clock/wall-clock.wit.go new file mode 100644 index 0000000000..90a966555a --- /dev/null +++ b/src/syscall/wasi/clocks/v0.2.0/wall-clock/wall-clock.wit.go @@ -0,0 +1,82 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package wallclock represents the interface "wasi:clocks/wall-clock@0.2.0". +// +// 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/wall-clock@0.2.0#datetime". +// +// A time and date in seconds plus nanoseconds. +// +// record datetime { +// seconds: u64, +// nanoseconds: u32, +// } +type DateTime struct { + Seconds uint64 + Nanoseconds uint32 +} + +// Now represents function "wasi:clocks/wall-clock@0.2.0#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() DateTime { + var result DateTime + wasmimport_Now(&result) + return result +} + +//go:wasmimport wasi:clocks/wall-clock@0.2.0 now +//go:noescape +func wasmimport_Now(result *DateTime) + +// Resolution represents function "wasi:clocks/wall-clock@0.2.0#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() DateTime { + var result DateTime + wasmimport_Resolution(&result) + return result +} + +//go:wasmimport wasi:clocks/wall-clock@0.2.0 resolution +//go:noescape +func wasmimport_Resolution(result *DateTime) diff --git a/src/syscall/wasi/filesystem/v0.2.0/preopens/empty.s b/src/syscall/wasi/filesystem/v0.2.0/preopens/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/filesystem/v0.2.0/preopens/preopens.wit.go b/src/syscall/wasi/filesystem/v0.2.0/preopens/preopens.wit.go new file mode 100644 index 0000000000..673267eab4 --- /dev/null +++ b/src/syscall/wasi/filesystem/v0.2.0/preopens/preopens.wit.go @@ -0,0 +1,33 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package preopens represents the interface "wasi:filesystem/preopens@0.2.0". +package preopens + +import ( + "github.com/ydnar/wasm-tools-go/cm" + "syscall/wasi/filesystem/v0.2.0/types" +) + +// Descriptor represents the resource "wasi:filesystem/types@0.2.0#descriptor". +// +// See [types.Descriptor] for more information. +type Descriptor = types.Descriptor + +// GetDirectories represents function "wasi:filesystem/preopens@0.2.0#get-directories". +// +// Return the set of preopened directories, and their path. +// +// get-directories: func() -> list, string>> +// +//go:nosplit +func GetDirectories() cm.List[cm.Tuple[Descriptor, string]] { + var result cm.List[cm.Tuple[Descriptor, string]] + wasmimport_GetDirectories(&result) + return result +} + +//go:wasmimport wasi:filesystem/preopens@0.2.0 get-directories +//go:noescape +func wasmimport_GetDirectories(result *cm.List[cm.Tuple[Descriptor, string]]) diff --git a/src/syscall/wasi/filesystem/v0.2.0/types/empty.s b/src/syscall/wasi/filesystem/v0.2.0/types/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/filesystem/v0.2.0/types/types.wit.go b/src/syscall/wasi/filesystem/v0.2.0/types/types.wit.go new file mode 100644 index 0000000000..bc84a1a3e5 --- /dev/null +++ b/src/syscall/wasi/filesystem/v0.2.0/types/types.wit.go @@ -0,0 +1,1259 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package types represents the interface "wasi:filesystem/types@0.2.0". +// +// 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 "syscall/wasi/clocks/v0.2.0/wall-clock" + ioerror "syscall/wasi/io/v0.2.0/error" + "syscall/wasi/io/v0.2.0/streams" +) + +// Advice represents the enum "wasi:filesystem/types@0.2.0#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 +) + +// DateTime represents the record "wasi:clocks/wall-clock@0.2.0#datetime". +// +// See [wallclock.DateTime] for more information. +type DateTime = wallclock.DateTime + +// Descriptor represents the resource "wasi:filesystem/types@0.2.0#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 Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self Descriptor) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [resource-drop]descriptor +//go:noescape +func (self Descriptor) wasmimport_ResourceDrop() + +// Advise represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_Advise(offset, length, advice, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.advise +//go:noescape +func (self Descriptor) wasmimport_Advise(offset FileSize, length FileSize, advice Advice, result *cm.ErrResult[struct{}, ErrorCode]) + +// AppendViaStream represents 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, error-code> +// +//go:nosplit +func (self Descriptor) AppendViaStream() cm.OKResult[OutputStream, ErrorCode] { + var result cm.OKResult[OutputStream, ErrorCode] + self.wasmimport_AppendViaStream(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.append-via-stream +//go:noescape +func (self Descriptor) wasmimport_AppendViaStream(result *cm.OKResult[OutputStream, ErrorCode]) + +// CreateDirectoryAt represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_CreateDirectoryAt(path, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.create-directory-at +//go:noescape +func (self Descriptor) wasmimport_CreateDirectoryAt(path string, result *cm.ErrResult[struct{}, ErrorCode]) + +// GetFlags represents 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 +// +//go:nosplit +func (self Descriptor) GetFlags() cm.OKResult[DescriptorFlags, ErrorCode] { + var result cm.OKResult[DescriptorFlags, ErrorCode] + self.wasmimport_GetFlags(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.get-flags +//go:noescape +func (self Descriptor) wasmimport_GetFlags(result *cm.OKResult[DescriptorFlags, ErrorCode]) + +// GetType represents 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 +// +//go:nosplit +func (self Descriptor) GetType() cm.OKResult[DescriptorType, ErrorCode] { + var result cm.OKResult[DescriptorType, ErrorCode] + self.wasmimport_GetType(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.get-type +//go:noescape +func (self Descriptor) wasmimport_GetType(result *cm.OKResult[DescriptorType, ErrorCode]) + +// IsSameObject represents 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) -> bool +// +//go:nosplit +func (self Descriptor) IsSameObject(other Descriptor) bool { + return self.wasmimport_IsSameObject(other) +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.is-same-object +//go:noescape +func (self Descriptor) wasmimport_IsSameObject(other Descriptor) bool + +// LinkAt represents 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, +// new-path: string) -> result<_, error-code> +// +//go:nosplit +func (self Descriptor) LinkAt(oldPathFlags PathFlags, oldPath string, newDescriptor Descriptor, newPath string) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_LinkAt(oldPathFlags, oldPath, newDescriptor, newPath, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.link-at +//go:noescape +func (self Descriptor) wasmimport_LinkAt(oldPathFlags PathFlags, oldPath string, newDescriptor Descriptor, newPath string, result *cm.ErrResult[struct{}, ErrorCode]) + +// MetadataHash represents 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 +// +//go:nosplit +func (self Descriptor) MetadataHash() cm.OKResult[MetadataHashValue, ErrorCode] { + var result cm.OKResult[MetadataHashValue, ErrorCode] + self.wasmimport_MetadataHash(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.metadata-hash +//go:noescape +func (self Descriptor) wasmimport_MetadataHash(result *cm.OKResult[MetadataHashValue, ErrorCode]) + +// MetadataHashAt represents 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 +// +//go:nosplit +func (self Descriptor) MetadataHashAt(pathFlags PathFlags, path string) cm.OKResult[MetadataHashValue, ErrorCode] { + var result cm.OKResult[MetadataHashValue, ErrorCode] + self.wasmimport_MetadataHashAt(pathFlags, path, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.metadata-hash-at +//go:noescape +func (self Descriptor) wasmimport_MetadataHashAt(pathFlags PathFlags, path string, result *cm.OKResult[MetadataHashValue, ErrorCode]) + +// OpenAt represents 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, error-code> +// +//go:nosplit +func (self Descriptor) OpenAt(pathFlags PathFlags, path string, openFlags OpenFlags, flags DescriptorFlags) cm.OKResult[Descriptor, ErrorCode] { + var result cm.OKResult[Descriptor, ErrorCode] + self.wasmimport_OpenAt(pathFlags, path, openFlags, flags, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.open-at +//go:noescape +func (self Descriptor) wasmimport_OpenAt(pathFlags PathFlags, path string, openFlags OpenFlags, flags DescriptorFlags, result *cm.OKResult[Descriptor, ErrorCode]) + +// Read represents 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`. +// +// Note: This is similar to `pread` in POSIX. +// +// read: func(length: filesize, offset: filesize) -> result, bool>, +// error-code> +// +//go:nosplit +func (self Descriptor) Read(length FileSize, offset FileSize) cm.OKResult[cm.Tuple[cm.List[uint8], bool], ErrorCode] { + var result cm.OKResult[cm.Tuple[cm.List[uint8], bool], ErrorCode] + self.wasmimport_Read(length, offset, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.read +//go:noescape +func (self Descriptor) wasmimport_Read(length FileSize, offset FileSize, result *cm.OKResult[cm.Tuple[cm.List[uint8], bool], ErrorCode]) + +// ReadDirectory represents 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, error-code> +// +//go:nosplit +func (self Descriptor) ReadDirectory() cm.OKResult[DirectoryEntryStream, ErrorCode] { + var result cm.OKResult[DirectoryEntryStream, ErrorCode] + self.wasmimport_ReadDirectory(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.read-directory +//go:noescape +func (self Descriptor) wasmimport_ReadDirectory(result *cm.OKResult[DirectoryEntryStream, ErrorCode]) + +// ReadViaStream represents 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, error-code> +// +//go:nosplit +func (self Descriptor) ReadViaStream(offset FileSize) cm.OKResult[InputStream, ErrorCode] { + var result cm.OKResult[InputStream, ErrorCode] + self.wasmimport_ReadViaStream(offset, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.read-via-stream +//go:noescape +func (self Descriptor) wasmimport_ReadViaStream(offset FileSize, result *cm.OKResult[InputStream, ErrorCode]) + +// ReadLinkAt represents 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 +// +//go:nosplit +func (self Descriptor) ReadLinkAt(path string) cm.OKResult[string, ErrorCode] { + var result cm.OKResult[string, ErrorCode] + self.wasmimport_ReadLinkAt(path, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.readlink-at +//go:noescape +func (self Descriptor) wasmimport_ReadLinkAt(path string, result *cm.OKResult[string, ErrorCode]) + +// RemoveDirectoryAt represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_RemoveDirectoryAt(path, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.remove-directory-at +//go:noescape +func (self Descriptor) wasmimport_RemoveDirectoryAt(path string, result *cm.ErrResult[struct{}, ErrorCode]) + +// RenameAt represents method "rename-at". +// +// Rename a filesystem object. +// +// Note: This is similar to `renameat` in POSIX. +// +// rename-at: func(old-path: string, new-descriptor: borrow, new-path: +// string) -> result<_, error-code> +// +//go:nosplit +func (self Descriptor) RenameAt(oldPath string, newDescriptor Descriptor, newPath string) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_RenameAt(oldPath, newDescriptor, newPath, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.rename-at +//go:noescape +func (self Descriptor) wasmimport_RenameAt(oldPath string, newDescriptor Descriptor, newPath string, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetSize represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetSize(size, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.set-size +//go:noescape +func (self Descriptor) wasmimport_SetSize(size FileSize, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetTimes represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetTimes(dataAccessTimestamp, dataModificationTimestamp, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.set-times +//go:noescape +func (self Descriptor) wasmimport_SetTimes(dataAccessTimestamp NewTimestamp, dataModificationTimestamp NewTimestamp, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetTimesAt represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetTimesAt(pathFlags, path, dataAccessTimestamp, dataModificationTimestamp, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.set-times-at +//go:noescape +func (self Descriptor) wasmimport_SetTimesAt(pathFlags PathFlags, path string, dataAccessTimestamp NewTimestamp, dataModificationTimestamp NewTimestamp, result *cm.ErrResult[struct{}, ErrorCode]) + +// Stat represents 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 +// +//go:nosplit +func (self Descriptor) Stat() cm.OKResult[DescriptorStat, ErrorCode] { + var result cm.OKResult[DescriptorStat, ErrorCode] + self.wasmimport_Stat(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.stat +//go:noescape +func (self Descriptor) wasmimport_Stat(result *cm.OKResult[DescriptorStat, ErrorCode]) + +// StatAt represents 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 +// +//go:nosplit +func (self Descriptor) StatAt(pathFlags PathFlags, path string) cm.OKResult[DescriptorStat, ErrorCode] { + var result cm.OKResult[DescriptorStat, ErrorCode] + self.wasmimport_StatAt(pathFlags, path, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.stat-at +//go:noescape +func (self Descriptor) wasmimport_StatAt(pathFlags PathFlags, path string, result *cm.OKResult[DescriptorStat, ErrorCode]) + +// SymlinkAt represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SymlinkAt(oldPath, newPath, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.symlink-at +//go:noescape +func (self Descriptor) wasmimport_SymlinkAt(oldPath string, newPath string, result *cm.ErrResult[struct{}, ErrorCode]) + +// Sync represents 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() cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_Sync(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.sync +//go:noescape +func (self Descriptor) wasmimport_Sync(result *cm.ErrResult[struct{}, ErrorCode]) + +// SyncData represents 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() cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SyncData(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.sync-data +//go:noescape +func (self Descriptor) wasmimport_SyncData(result *cm.ErrResult[struct{}, ErrorCode]) + +// UnlinkFileAt represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_UnlinkFileAt(path, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.unlink-file-at +//go:noescape +func (self Descriptor) wasmimport_UnlinkFileAt(path string, result *cm.ErrResult[struct{}, ErrorCode]) + +// Write represents 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`. +// +// Note: This is similar to `pwrite` in POSIX. +// +// write: func(buffer: list, offset: filesize) -> result +// +//go:nosplit +func (self Descriptor) Write(buffer cm.List[uint8], offset FileSize) cm.OKResult[FileSize, ErrorCode] { + var result cm.OKResult[FileSize, ErrorCode] + self.wasmimport_Write(buffer, offset, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.write +//go:noescape +func (self Descriptor) wasmimport_Write(buffer cm.List[uint8], offset FileSize, result *cm.OKResult[FileSize, ErrorCode]) + +// WriteViaStream represents 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, error-code> +// +//go:nosplit +func (self Descriptor) WriteViaStream(offset FileSize) cm.OKResult[OutputStream, ErrorCode] { + var result cm.OKResult[OutputStream, ErrorCode] + self.wasmimport_WriteViaStream(offset, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]descriptor.write-via-stream +//go:noescape +func (self Descriptor) wasmimport_WriteViaStream(offset FileSize, result *cm.OKResult[OutputStream, ErrorCode]) + +// DescriptorFlags represents the flags "wasi:filesystem/types@0.2.0#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 +) + +// DescriptorStat represents the record "wasi:filesystem/types@0.2.0#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, +// data-modification-timestamp: option, +// status-change-timestamp: option, +// } +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[DateTime] + + // Last data modification timestamp. + // + // If the `option` is none, the platform doesn't maintain a + // modification timestamp for this file. + DataModificationTimestamp cm.Option[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[DateTime] +} + +// DescriptorType represents the enum "wasi:filesystem/types@0.2.0#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 +) + +// DirectoryEntry represents the record "wasi:filesystem/types@0.2.0#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 +} + +// DirectoryEntryStream represents the resource "wasi:filesystem/types@0.2.0#directory-entry-stream". +// +// A stream of directory entries. +// +// resource directory-entry-stream +type DirectoryEntryStream cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self DirectoryEntryStream) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [resource-drop]directory-entry-stream +//go:noescape +func (self DirectoryEntryStream) wasmimport_ResourceDrop() + +// ReadDirectoryEntry represents method "read-directory-entry". +// +// Read a single directory entry from a `directory-entry-stream`. +// +// read-directory-entry: func() -> result, error-code> +// +//go:nosplit +func (self DirectoryEntryStream) ReadDirectoryEntry() cm.OKResult[cm.Option[DirectoryEntry], ErrorCode] { + var result cm.OKResult[cm.Option[DirectoryEntry], ErrorCode] + self.wasmimport_ReadDirectoryEntry(&result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 [method]directory-entry-stream.read-directory-entry +//go:noescape +func (self DirectoryEntryStream) wasmimport_ReadDirectoryEntry(result *cm.OKResult[cm.Option[DirectoryEntry], ErrorCode]) + +// Error represents the resource "wasi:io/error@0.2.0#error". +// +// See [ioerror.Error] for more information. +type Error = ioerror.Error + +// ErrorCode represents the enum "wasi:filesystem/types@0.2.0#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 +) + +// FileSize represents the type "wasi:filesystem/types@0.2.0#filesize". +// +// File size or length of a region within a file. +// +// type filesize = u64 +type FileSize uint64 + +// InputStream represents the resource "wasi:io/streams@0.2.0#input-stream". +// +// See [streams.InputStream] for more information. +type InputStream = streams.InputStream + +// LinkCount represents the type "wasi:filesystem/types@0.2.0#link-count". +// +// Number of hard links to an inode. +// +// type link-count = u64 +type LinkCount uint64 + +// MetadataHashValue represents the record "wasi:filesystem/types@0.2.0#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 +} + +// NewTimestamp represents the variant "wasi:filesystem/types@0.2.0#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, DateTime, 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 DateTime) NewTimestamp { + return cm.New[NewTimestamp](2, data) +} + +// Timestamp returns a non-nil *[DateTime] if [NewTimestamp] represents the variant case "timestamp". +func (self *NewTimestamp) Timestamp() *DateTime { + return cm.Case[DateTime](self, 2) +} + +// OpenFlags represents the flags "wasi:filesystem/types@0.2.0#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 +) + +// OutputStream represents the resource "wasi:io/streams@0.2.0#output-stream". +// +// See [streams.OutputStream] for more information. +type OutputStream = streams.OutputStream + +// PathFlags represents the flags "wasi:filesystem/types@0.2.0#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 +) + +// FilesystemErrorCode represents function "wasi:filesystem/types@0.2.0#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) -> option +// +//go:nosplit +func FilesystemErrorCode(err Error) cm.Option[ErrorCode] { + var result cm.Option[ErrorCode] + wasmimport_FilesystemErrorCode(err, &result) + return result +} + +//go:wasmimport wasi:filesystem/types@0.2.0 filesystem-error-code +//go:noescape +func wasmimport_FilesystemErrorCode(err Error, result *cm.Option[ErrorCode]) diff --git a/src/syscall/wasi/io/v0.2.0/error/empty.s b/src/syscall/wasi/io/v0.2.0/error/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/io/v0.2.0/error/error.wit.go b/src/syscall/wasi/io/v0.2.0/error/error.wit.go new file mode 100644 index 0000000000..e2d6cf76e6 --- /dev/null +++ b/src/syscall/wasi/io/v0.2.0/error/error.wit.go @@ -0,0 +1,71 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package ioerror represents the interface "wasi:io/error@0.2.0". +package ioerror + +import ( + "github.com/ydnar/wasm-tools-go/cm" +) + +// Error represents the resource "wasi:io/error@0.2.0#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` and returns +// `option`. +// +// 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 Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self Error) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:io/error@0.2.0 [resource-drop]error +//go:noescape +func (self Error) wasmimport_ResourceDrop() + +// ToDebugString represents 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() string { + var result string + self.wasmimport_ToDebugString(&result) + return result +} + +//go:wasmimport wasi:io/error@0.2.0 [method]error.to-debug-string +//go:noescape +func (self Error) wasmimport_ToDebugString(result *string) diff --git a/src/syscall/wasi/io/v0.2.0/poll/empty.s b/src/syscall/wasi/io/v0.2.0/poll/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/io/v0.2.0/poll/poll.wit.go b/src/syscall/wasi/io/v0.2.0/poll/poll.wit.go new file mode 100644 index 0000000000..61eb2db5c9 --- /dev/null +++ b/src/syscall/wasi/io/v0.2.0/poll/poll.wit.go @@ -0,0 +1,103 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package poll represents the interface "wasi:io/poll@0.2.0". +// +// 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 resource "wasi:io/poll@0.2.0#pollable". +// +// `pollable` represents a single I/O event which may be ready, or not. +// +// resource pollable +type Pollable cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self Pollable) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:io/poll@0.2.0 [resource-drop]pollable +//go:noescape +func (self Pollable) wasmimport_ResourceDrop() + +// Block represents 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() { + self.wasmimport_Block() +} + +//go:wasmimport wasi:io/poll@0.2.0 [method]pollable.block +//go:noescape +func (self Pollable) wasmimport_Block() + +// Ready represents 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() bool { + return self.wasmimport_Ready() +} + +//go:wasmimport wasi:io/poll@0.2.0 [method]pollable.ready +//go:noescape +func (self Pollable) wasmimport_Ready() bool + +// Poll represents function "wasi:io/poll@0.2.0#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` 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>) -> list +// +//go:nosplit +func Poll(in cm.List[Pollable]) cm.List[uint32] { + var result cm.List[uint32] + wasmimport_Poll(in, &result) + return result +} + +//go:wasmimport wasi:io/poll@0.2.0 poll +//go:noescape +func wasmimport_Poll(in cm.List[Pollable], result *cm.List[uint32]) diff --git a/src/syscall/wasi/io/v0.2.0/streams/empty.s b/src/syscall/wasi/io/v0.2.0/streams/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/io/v0.2.0/streams/streams.wit.go b/src/syscall/wasi/io/v0.2.0/streams/streams.wit.go new file mode 100644 index 0000000000..ba1fef9798 --- /dev/null +++ b/src/syscall/wasi/io/v0.2.0/streams/streams.wit.go @@ -0,0 +1,509 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package streams represents the interface "wasi:io/streams@0.2.0". +// +// 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 "syscall/wasi/io/v0.2.0/error" + "syscall/wasi/io/v0.2.0/poll" +) + +// Error represents the resource "wasi:io/error@0.2.0#error". +// +// See [ioerror.Error] for more information. +type Error = ioerror.Error + +// InputStream represents the resource "wasi:io/streams@0.2.0#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 Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self InputStream) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:io/streams@0.2.0 [resource-drop]input-stream +//go:noescape +func (self InputStream) wasmimport_ResourceDrop() + +// BlockingRead represents 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, stream-error> +// +//go:nosplit +func (self InputStream) BlockingRead(len_ uint64) cm.OKResult[cm.List[uint8], StreamError] { + var result cm.OKResult[cm.List[uint8], StreamError] + self.wasmimport_BlockingRead(len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.blocking-read +//go:noescape +func (self InputStream) wasmimport_BlockingRead(len_ uint64, result *cm.OKResult[cm.List[uint8], StreamError]) + +// BlockingSkip represents 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 +// +//go:nosplit +func (self InputStream) BlockingSkip(len_ uint64) cm.OKResult[uint64, StreamError] { + var result cm.OKResult[uint64, StreamError] + self.wasmimport_BlockingSkip(len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.blocking-skip +//go:noescape +func (self InputStream) wasmimport_BlockingSkip(len_ uint64, result *cm.OKResult[uint64, StreamError]) + +// Read represents 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, stream-error> +// +//go:nosplit +func (self InputStream) Read(len_ uint64) cm.OKResult[cm.List[uint8], StreamError] { + var result cm.OKResult[cm.List[uint8], StreamError] + self.wasmimport_Read(len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.read +//go:noescape +func (self InputStream) wasmimport_Read(len_ uint64, result *cm.OKResult[cm.List[uint8], StreamError]) + +// Skip represents 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 +// +//go:nosplit +func (self InputStream) Skip(len_ uint64) cm.OKResult[uint64, StreamError] { + var result cm.OKResult[uint64, StreamError] + self.wasmimport_Skip(len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.skip +//go:noescape +func (self InputStream) wasmimport_Skip(len_ uint64, result *cm.OKResult[uint64, StreamError]) + +// Subscribe represents 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() -> own +// +//go:nosplit +func (self InputStream) Subscribe() Pollable { + return self.wasmimport_Subscribe() +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]input-stream.subscribe +//go:noescape +func (self InputStream) wasmimport_Subscribe() Pollable + +// OutputStream represents the resource "wasi:io/streams@0.2.0#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 Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self OutputStream) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:io/streams@0.2.0 [resource-drop]output-stream +//go:noescape +func (self OutputStream) wasmimport_ResourceDrop() + +// BlockingFlush represents 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() cm.ErrResult[struct{}, StreamError] { + var result cm.ErrResult[struct{}, StreamError] + self.wasmimport_BlockingFlush(&result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.blocking-flush +//go:noescape +func (self OutputStream) wasmimport_BlockingFlush(result *cm.ErrResult[struct{}, StreamError]) + +// BlockingSplice represents 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, len: u64) -> result +// +//go:nosplit +func (self OutputStream) BlockingSplice(src InputStream, len_ uint64) cm.OKResult[uint64, StreamError] { + var result cm.OKResult[uint64, StreamError] + self.wasmimport_BlockingSplice(src, len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.blocking-splice +//go:noescape +func (self OutputStream) wasmimport_BlockingSplice(src InputStream, len_ uint64, result *cm.OKResult[uint64, StreamError]) + +// BlockingWriteAndFlush represents 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) -> result<_, stream-error> +// +//go:nosplit +func (self OutputStream) BlockingWriteAndFlush(contents cm.List[uint8]) cm.ErrResult[struct{}, StreamError] { + var result cm.ErrResult[struct{}, StreamError] + self.wasmimport_BlockingWriteAndFlush(contents, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.blocking-write-and-flush +//go:noescape +func (self OutputStream) wasmimport_BlockingWriteAndFlush(contents cm.List[uint8], result *cm.ErrResult[struct{}, StreamError]) + +// BlockingWriteZeroesAndFlush represents 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) cm.ErrResult[struct{}, StreamError] { + var result cm.ErrResult[struct{}, StreamError] + self.wasmimport_BlockingWriteZeroesAndFlush(len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.blocking-write-zeroes-and-flush +//go:noescape +func (self OutputStream) wasmimport_BlockingWriteZeroesAndFlush(len_ uint64, result *cm.ErrResult[struct{}, StreamError]) + +// CheckWrite represents 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 +// +//go:nosplit +func (self OutputStream) CheckWrite() cm.OKResult[uint64, StreamError] { + var result cm.OKResult[uint64, StreamError] + self.wasmimport_CheckWrite(&result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.check-write +//go:noescape +func (self OutputStream) wasmimport_CheckWrite(result *cm.OKResult[uint64, StreamError]) + +// Flush represents 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() cm.ErrResult[struct{}, StreamError] { + var result cm.ErrResult[struct{}, StreamError] + self.wasmimport_Flush(&result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.flush +//go:noescape +func (self OutputStream) wasmimport_Flush(result *cm.ErrResult[struct{}, StreamError]) + +// Splice represents 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, len: u64) -> result +// +//go:nosplit +func (self OutputStream) Splice(src InputStream, len_ uint64) cm.OKResult[uint64, StreamError] { + var result cm.OKResult[uint64, StreamError] + self.wasmimport_Splice(src, len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.splice +//go:noescape +func (self OutputStream) wasmimport_Splice(src InputStream, len_ uint64, result *cm.OKResult[uint64, StreamError]) + +// Subscribe represents 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() -> own +// +//go:nosplit +func (self OutputStream) Subscribe() Pollable { + return self.wasmimport_Subscribe() +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.subscribe +//go:noescape +func (self OutputStream) wasmimport_Subscribe() Pollable + +// Write represents 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) -> result<_, stream-error> +// +//go:nosplit +func (self OutputStream) Write(contents cm.List[uint8]) cm.ErrResult[struct{}, StreamError] { + var result cm.ErrResult[struct{}, StreamError] + self.wasmimport_Write(contents, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.write +//go:noescape +func (self OutputStream) wasmimport_Write(contents cm.List[uint8], result *cm.ErrResult[struct{}, StreamError]) + +// WriteZeroes represents 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) cm.ErrResult[struct{}, StreamError] { + var result cm.ErrResult[struct{}, StreamError] + self.wasmimport_WriteZeroes(len_, &result) + return result +} + +//go:wasmimport wasi:io/streams@0.2.0 [method]output-stream.write-zeroes +//go:noescape +func (self OutputStream) wasmimport_WriteZeroes(len_ uint64, result *cm.ErrResult[struct{}, StreamError]) + +// Pollable represents the resource "wasi:io/poll@0.2.0#pollable". +// +// See [poll.Pollable] for more information. +type Pollable = poll.Pollable + +// StreamError represents the variant "wasi:io/streams@0.2.0#stream-error". +// +// An error for input-stream and output-stream operations. +// +// variant stream-error { +// last-operation-failed(own), +// closed, +// } +type StreamError cm.Variant[uint8, Error, 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 Error) StreamError { + return cm.New[StreamError](0, data) +} + +// LastOperationFailed returns a non-nil *[Error] if [StreamError] represents the variant case "last-operation-failed". +func (self *StreamError) LastOperationFailed() *Error { + return cm.Case[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 +} diff --git a/src/syscall/wasi/random/v0.2.0/insecure-seed/empty.s b/src/syscall/wasi/random/v0.2.0/insecure-seed/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/random/v0.2.0/insecure-seed/insecure-seed.wit.go b/src/syscall/wasi/random/v0.2.0/insecure-seed/insecure-seed.wit.go new file mode 100644 index 0000000000..05c0a84e36 --- /dev/null +++ b/src/syscall/wasi/random/v0.2.0/insecure-seed/insecure-seed.wit.go @@ -0,0 +1,44 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package insecureseed represents the interface "wasi:random/insecure-seed@0.2.0". +// +// 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 function "wasi:random/insecure-seed@0.2.0#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 +// +//go:nosplit +func InsecureSeed() [2]uint64 { + var result [2]uint64 + wasmimport_InsecureSeed(&result) + return result +} + +//go:wasmimport wasi:random/insecure-seed@0.2.0 insecure-seed +//go:noescape +func wasmimport_InsecureSeed(result *[2]uint64) diff --git a/src/syscall/wasi/random/v0.2.0/insecure/empty.s b/src/syscall/wasi/random/v0.2.0/insecure/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/random/v0.2.0/insecure/insecure.wit.go b/src/syscall/wasi/random/v0.2.0/insecure/insecure.wit.go new file mode 100644 index 0000000000..f5d7c32526 --- /dev/null +++ b/src/syscall/wasi/random/v0.2.0/insecure/insecure.wit.go @@ -0,0 +1,57 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package insecure represents the interface "wasi:random/insecure@0.2.0". +// +// 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 function "wasi:random/insecure@0.2.0#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 +// +//go:nosplit +func GetInsecureRandomBytes(len_ uint64) cm.List[uint8] { + var result cm.List[uint8] + wasmimport_GetInsecureRandomBytes(len_, &result) + return result +} + +//go:wasmimport wasi:random/insecure@0.2.0 get-insecure-random-bytes +//go:noescape +func wasmimport_GetInsecureRandomBytes(len_ uint64, result *cm.List[uint8]) + +// GetInsecureRandomU64 represents function "wasi:random/insecure@0.2.0#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() uint64 { + return wasmimport_GetInsecureRandomU64() +} + +//go:wasmimport wasi:random/insecure@0.2.0 get-insecure-random-u64 +//go:noescape +func wasmimport_GetInsecureRandomU64() uint64 diff --git a/src/syscall/wasi/random/v0.2.0/random/empty.s b/src/syscall/wasi/random/v0.2.0/random/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/random/v0.2.0/random/random.wit.go b/src/syscall/wasi/random/v0.2.0/random/random.wit.go new file mode 100644 index 0000000000..700af66be1 --- /dev/null +++ b/src/syscall/wasi/random/v0.2.0/random/random.wit.go @@ -0,0 +1,61 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package random represents the interface "wasi:random/random@0.2.0". +// +// 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 function "wasi:random/random@0.2.0#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 +// +//go:nosplit +func GetRandomBytes(len_ uint64) cm.List[uint8] { + var result cm.List[uint8] + wasmimport_GetRandomBytes(len_, &result) + return result +} + +//go:wasmimport wasi:random/random@0.2.0 get-random-bytes +//go:noescape +func wasmimport_GetRandomBytes(len_ uint64, result *cm.List[uint8]) + +// GetRandomU64 represents function "wasi:random/random@0.2.0#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() uint64 { + return wasmimport_GetRandomU64() +} + +//go:wasmimport wasi:random/random@0.2.0 get-random-u64 +//go:noescape +func wasmimport_GetRandomU64() uint64 diff --git a/src/syscall/wasi/sockets/v0.2.0/instance-network/empty.s b/src/syscall/wasi/sockets/v0.2.0/instance-network/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/sockets/v0.2.0/instance-network/instance-network.wit.go b/src/syscall/wasi/sockets/v0.2.0/instance-network/instance-network.wit.go new file mode 100644 index 0000000000..ca76cc7bdf --- /dev/null +++ b/src/syscall/wasi/sockets/v0.2.0/instance-network/instance-network.wit.go @@ -0,0 +1,32 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package instancenetwork represents the interface "wasi:sockets/instance-network@0.2.0". +// +// This interface provides a value-export of the default network handle.. +package instancenetwork + +import ( + "syscall/wasi/sockets/v0.2.0/network" +) + +// Network represents the resource "wasi:sockets/network@0.2.0#network". +// +// See [network.Network] for more information. +type Network = network.Network + +// InstanceNetwork represents function "wasi:sockets/instance-network@0.2.0#instance-network". +// +// Get a handle to the default network. +// +// instance-network: func() -> own +// +//go:nosplit +func InstanceNetwork() Network { + return wasmimport_InstanceNetwork() +} + +//go:wasmimport wasi:sockets/instance-network@0.2.0 instance-network +//go:noescape +func wasmimport_InstanceNetwork() Network diff --git a/src/syscall/wasi/sockets/v0.2.0/ip-name-lookup/empty.s b/src/syscall/wasi/sockets/v0.2.0/ip-name-lookup/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/sockets/v0.2.0/ip-name-lookup/ip-name-lookup.wit.go b/src/syscall/wasi/sockets/v0.2.0/ip-name-lookup/ip-name-lookup.wit.go new file mode 100644 index 0000000000..0ddd7bea7c --- /dev/null +++ b/src/syscall/wasi/sockets/v0.2.0/ip-name-lookup/ip-name-lookup.wit.go @@ -0,0 +1,137 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package ipnamelookup represents the interface "wasi:sockets/ip-name-lookup@0.2.0". +package ipnamelookup + +import ( + "github.com/ydnar/wasm-tools-go/cm" + "syscall/wasi/io/v0.2.0/poll" + "syscall/wasi/sockets/v0.2.0/network" +) + +// ErrorCode represents the enum "wasi:sockets/network@0.2.0#error-code". +// +// See [network.ErrorCode] for more information. +type ErrorCode = network.ErrorCode + +// IPAddress represents the variant "wasi:sockets/network@0.2.0#ip-address". +// +// See [network.IPAddress] for more information. +type IPAddress = network.IPAddress + +// Network represents the resource "wasi:sockets/network@0.2.0#network". +// +// See [network.Network] for more information. +type Network = network.Network + +// Pollable represents the resource "wasi:io/poll@0.2.0#pollable". +// +// See [poll.Pollable] for more information. +type Pollable = poll.Pollable + +// ResolveAddressStream represents the resource "wasi:sockets/ip-name-lookup@0.2.0#resolve-address-stream". +// +// resource resolve-address-stream +type ResolveAddressStream cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self ResolveAddressStream) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:sockets/ip-name-lookup@0.2.0 [resource-drop]resolve-address-stream +//go:noescape +func (self ResolveAddressStream) wasmimport_ResourceDrop() + +// ResolveNextAddress represents 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, error-code> +// +//go:nosplit +func (self ResolveAddressStream) ResolveNextAddress() cm.OKResult[cm.Option[IPAddress], ErrorCode] { + var result cm.OKResult[cm.Option[IPAddress], ErrorCode] + self.wasmimport_ResolveNextAddress(&result) + return result +} + +//go:wasmimport wasi:sockets/ip-name-lookup@0.2.0 [method]resolve-address-stream.resolve-next-address +//go:noescape +func (self ResolveAddressStream) wasmimport_ResolveNextAddress(result *cm.OKResult[cm.Option[IPAddress], ErrorCode]) + +// Subscribe represents 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() -> own +// +//go:nosplit +func (self ResolveAddressStream) Subscribe() Pollable { + return self.wasmimport_Subscribe() +} + +//go:wasmimport wasi:sockets/ip-name-lookup@0.2.0 [method]resolve-address-stream.subscribe +//go:noescape +func (self ResolveAddressStream) wasmimport_Subscribe() Pollable + +// ResolveAddresses represents function "wasi:sockets/ip-name-lookup@0.2.0#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: +// - +// - +// - +// - +// +// resolve-addresses: func(network: borrow, name: string) -> result, +// error-code> +// +//go:nosplit +func ResolveAddresses(network_ Network, name string) cm.OKResult[ResolveAddressStream, ErrorCode] { + var result cm.OKResult[ResolveAddressStream, ErrorCode] + wasmimport_ResolveAddresses(network_, name, &result) + return result +} + +//go:wasmimport wasi:sockets/ip-name-lookup@0.2.0 resolve-addresses +//go:noescape +func wasmimport_ResolveAddresses(network_ Network, name string, result *cm.OKResult[ResolveAddressStream, ErrorCode]) diff --git a/src/syscall/wasi/sockets/v0.2.0/network/empty.s b/src/syscall/wasi/sockets/v0.2.0/network/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/sockets/v0.2.0/network/network.wit.go b/src/syscall/wasi/sockets/v0.2.0/network/network.wit.go new file mode 100644 index 0000000000..43aa8e79cf --- /dev/null +++ b/src/syscall/wasi/sockets/v0.2.0/network/network.wit.go @@ -0,0 +1,276 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package network represents the interface "wasi:sockets/network@0.2.0". +package network + +import ( + "github.com/ydnar/wasm-tools-go/cm" +) + +// ErrorCode represents the enum "wasi:sockets/network@0.2.0#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 +) + +// IPAddress represents the variant "wasi:sockets/network@0.2.0#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) +} + +// IPAddressFamily represents the enum "wasi:sockets/network@0.2.0#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 +) + +// IPSocketAddress represents the variant "wasi:sockets/network@0.2.0#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) +} + +// IPv4Address represents the tuple "wasi:sockets/network@0.2.0#ipv4-address". +// +// type ipv4-address = tuple +type IPv4Address [4]uint8 + +// IPv4SocketAddress represents the record "wasi:sockets/network@0.2.0#ipv4-socket-address". +// +// record ipv4-socket-address { +// port: u16, +// address: ipv4-address, +// } +type IPv4SocketAddress struct { + // sin_port + Port uint16 + + // sin_addr + Address IPv4Address +} + +// IPv6Address represents the tuple "wasi:sockets/network@0.2.0#ipv6-address". +// +// type ipv6-address = tuple +type IPv6Address [8]uint16 + +// IPv6SocketAddress represents the record "wasi:sockets/network@0.2.0#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 +} + +// Network represents the resource "wasi:sockets/network@0.2.0#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 Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self Network) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:sockets/network@0.2.0 [resource-drop]network +//go:noescape +func (self Network) wasmimport_ResourceDrop() diff --git a/src/syscall/wasi/sockets/v0.2.0/tcp-create-socket/empty.s b/src/syscall/wasi/sockets/v0.2.0/tcp-create-socket/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/sockets/v0.2.0/tcp-create-socket/tcp-create-socket.wit.go b/src/syscall/wasi/sockets/v0.2.0/tcp-create-socket/tcp-create-socket.wit.go new file mode 100644 index 0000000000..236d924fbb --- /dev/null +++ b/src/syscall/wasi/sockets/v0.2.0/tcp-create-socket/tcp-create-socket.wit.go @@ -0,0 +1,74 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package tcpcreatesocket represents the interface "wasi:sockets/tcp-create-socket@0.2.0". +package tcpcreatesocket + +import ( + "github.com/ydnar/wasm-tools-go/cm" + "syscall/wasi/sockets/v0.2.0/network" + "syscall/wasi/sockets/v0.2.0/tcp" +) + +// ErrorCode represents the enum "wasi:sockets/network@0.2.0#error-code". +// +// See [network.ErrorCode] for more information. +type ErrorCode = network.ErrorCode + +// IPAddressFamily represents the enum "wasi:sockets/network@0.2.0#ip-address-family". +// +// See [network.IPAddressFamily] for more information. +type IPAddressFamily = network.IPAddressFamily + +// Network represents the resource "wasi:sockets/network@0.2.0#network". +// +// See [network.Network] for more information. +type Network = network.Network + +// TCPSocket represents the resource "wasi:sockets/tcp@0.2.0#tcp-socket". +// +// See [tcp.TCPSocket] for more information. +type TCPSocket = tcp.TCPSocket + +// CreateTCPSocket represents function "wasi:sockets/tcp-create-socket@0.2.0#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 +// - +// - +// - +// - +// +// create-tcp-socket: func(address-family: ip-address-family) -> result, +// error-code> +// +//go:nosplit +func CreateTCPSocket(addressFamily IPAddressFamily) cm.OKResult[TCPSocket, ErrorCode] { + var result cm.OKResult[TCPSocket, ErrorCode] + wasmimport_CreateTCPSocket(addressFamily, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp-create-socket@0.2.0 create-tcp-socket +//go:noescape +func wasmimport_CreateTCPSocket(addressFamily IPAddressFamily, result *cm.OKResult[TCPSocket, ErrorCode]) diff --git a/src/syscall/wasi/sockets/v0.2.0/tcp/empty.s b/src/syscall/wasi/sockets/v0.2.0/tcp/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/sockets/v0.2.0/tcp/tcp.wit.go b/src/syscall/wasi/sockets/v0.2.0/tcp/tcp.wit.go new file mode 100644 index 0000000000..13bda28113 --- /dev/null +++ b/src/syscall/wasi/sockets/v0.2.0/tcp/tcp.wit.go @@ -0,0 +1,857 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package tcp represents the interface "wasi:sockets/tcp@0.2.0". +package tcp + +import ( + "github.com/ydnar/wasm-tools-go/cm" + monotonicclock "syscall/wasi/clocks/v0.2.0/monotonic-clock" + "syscall/wasi/io/v0.2.0/poll" + "syscall/wasi/io/v0.2.0/streams" + "syscall/wasi/sockets/v0.2.0/network" +) + +// Duration represents the type "wasi:clocks/monotonic-clock@0.2.0#duration". +// +// See [monotonicclock.Duration] for more information. +type Duration = monotonicclock.Duration + +// ErrorCode represents the enum "wasi:sockets/network@0.2.0#error-code". +// +// See [network.ErrorCode] for more information. +type ErrorCode = network.ErrorCode + +// InputStream represents the resource "wasi:io/streams@0.2.0#input-stream". +// +// See [streams.InputStream] for more information. +type InputStream = streams.InputStream + +// IPAddressFamily represents the enum "wasi:sockets/network@0.2.0#ip-address-family". +// +// See [network.IPAddressFamily] for more information. +type IPAddressFamily = network.IPAddressFamily + +// IPSocketAddress represents the variant "wasi:sockets/network@0.2.0#ip-socket-address". +// +// See [network.IPSocketAddress] for more information. +type IPSocketAddress = network.IPSocketAddress + +// Network represents the resource "wasi:sockets/network@0.2.0#network". +// +// See [network.Network] for more information. +type Network = network.Network + +// OutputStream represents the resource "wasi:io/streams@0.2.0#output-stream". +// +// See [streams.OutputStream] for more information. +type OutputStream = streams.OutputStream + +// Pollable represents the resource "wasi:io/poll@0.2.0#pollable". +// +// See [poll.Pollable] for more information. +type Pollable = poll.Pollable + +// ShutdownType represents the enum "wasi:sockets/tcp@0.2.0#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 resource "wasi:sockets/tcp@0.2.0#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 +// 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 Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self TCPSocket) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [resource-drop]tcp-socket +//go:noescape +func (self TCPSocket) wasmimport_ResourceDrop() + +// Accept represents 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 +// - +// - +// - +// - +// +// accept: func() -> result, own, own>, +// error-code> +// +//go:nosplit +func (self TCPSocket) Accept() cm.OKResult[cm.Tuple3[TCPSocket, InputStream, OutputStream], ErrorCode] { + var result cm.OKResult[cm.Tuple3[TCPSocket, InputStream, OutputStream], ErrorCode] + self.wasmimport_Accept(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.accept +//go:noescape +func (self TCPSocket) wasmimport_Accept(result *cm.OKResult[cm.Tuple3[TCPSocket, InputStream, OutputStream], ErrorCode]) + +// AddressFamily represents 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() IPAddressFamily { + return self.wasmimport_AddressFamily() +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.address-family +//go:noescape +func (self TCPSocket) wasmimport_AddressFamily() IPAddressFamily + +// FinishBind represents method "finish-bind". +// +// finish-bind: func() -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) FinishBind() cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_FinishBind(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.finish-bind +//go:noescape +func (self TCPSocket) wasmimport_FinishBind(result *cm.ErrResult[struct{}, ErrorCode]) + +// FinishConnect represents method "finish-connect". +// +// finish-connect: func() -> result, own>, +// error-code> +// +//go:nosplit +func (self TCPSocket) FinishConnect() cm.OKResult[cm.Tuple[InputStream, OutputStream], ErrorCode] { + var result cm.OKResult[cm.Tuple[InputStream, OutputStream], ErrorCode] + self.wasmimport_FinishConnect(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.finish-connect +//go:noescape +func (self TCPSocket) wasmimport_FinishConnect(result *cm.OKResult[cm.Tuple[InputStream, OutputStream], ErrorCode]) + +// FinishListen represents method "finish-listen". +// +// finish-listen: func() -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) FinishListen() cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_FinishListen(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.finish-listen +//go:noescape +func (self TCPSocket) wasmimport_FinishListen(result *cm.ErrResult[struct{}, ErrorCode]) + +// HopLimit represents 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 +// +//go:nosplit +func (self TCPSocket) HopLimit() cm.OKResult[uint8, ErrorCode] { + var result cm.OKResult[uint8, ErrorCode] + self.wasmimport_HopLimit(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.hop-limit +//go:noescape +func (self TCPSocket) wasmimport_HopLimit(result *cm.OKResult[uint8, ErrorCode]) + +// IsListening represents 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() bool { + return self.wasmimport_IsListening() +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.is-listening +//go:noescape +func (self TCPSocket) wasmimport_IsListening() bool + +// KeepAliveCount represents 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 +// +//go:nosplit +func (self TCPSocket) KeepAliveCount() cm.OKResult[uint32, ErrorCode] { + var result cm.OKResult[uint32, ErrorCode] + self.wasmimport_KeepAliveCount(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.keep-alive-count +//go:noescape +func (self TCPSocket) wasmimport_KeepAliveCount(result *cm.OKResult[uint32, ErrorCode]) + +// KeepAliveEnabled represents 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 +// +//go:nosplit +func (self TCPSocket) KeepAliveEnabled() cm.OKResult[bool, ErrorCode] { + var result cm.OKResult[bool, ErrorCode] + self.wasmimport_KeepAliveEnabled(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.keep-alive-enabled +//go:noescape +func (self TCPSocket) wasmimport_KeepAliveEnabled(result *cm.OKResult[bool, ErrorCode]) + +// KeepAliveIdleTime represents 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 +// +//go:nosplit +func (self TCPSocket) KeepAliveIdleTime() cm.OKResult[Duration, ErrorCode] { + var result cm.OKResult[Duration, ErrorCode] + self.wasmimport_KeepAliveIdleTime(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.keep-alive-idle-time +//go:noescape +func (self TCPSocket) wasmimport_KeepAliveIdleTime(result *cm.OKResult[Duration, ErrorCode]) + +// KeepAliveInterval represents 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 +// +//go:nosplit +func (self TCPSocket) KeepAliveInterval() cm.OKResult[Duration, ErrorCode] { + var result cm.OKResult[Duration, ErrorCode] + self.wasmimport_KeepAliveInterval(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.keep-alive-interval +//go:noescape +func (self TCPSocket) wasmimport_KeepAliveInterval(result *cm.OKResult[Duration, ErrorCode]) + +// LocalAddress represents 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 +// - +// - +// - +// - +// +// local-address: func() -> result +// +//go:nosplit +func (self TCPSocket) LocalAddress() cm.OKResult[IPSocketAddress, ErrorCode] { + var result cm.OKResult[IPSocketAddress, ErrorCode] + self.wasmimport_LocalAddress(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.local-address +//go:noescape +func (self TCPSocket) wasmimport_LocalAddress(result *cm.OKResult[IPSocketAddress, ErrorCode]) + +// ReceiveBufferSize represents 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 +// +//go:nosplit +func (self TCPSocket) ReceiveBufferSize() cm.OKResult[uint64, ErrorCode] { + var result cm.OKResult[uint64, ErrorCode] + self.wasmimport_ReceiveBufferSize(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.receive-buffer-size +//go:noescape +func (self TCPSocket) wasmimport_ReceiveBufferSize(result *cm.OKResult[uint64, ErrorCode]) + +// RemoteAddress represents method "remote-address". +// +// Get the remote address. +// +// # Typical errors +// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) +// +// # References +// - +// - +// - +// - +// +// remote-address: func() -> result +// +//go:nosplit +func (self TCPSocket) RemoteAddress() cm.OKResult[IPSocketAddress, ErrorCode] { + var result cm.OKResult[IPSocketAddress, ErrorCode] + self.wasmimport_RemoteAddress(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.remote-address +//go:noescape +func (self TCPSocket) wasmimport_RemoteAddress(result *cm.OKResult[IPSocketAddress, ErrorCode]) + +// SendBufferSize represents method "send-buffer-size". +// +// send-buffer-size: func() -> result +// +//go:nosplit +func (self TCPSocket) SendBufferSize() cm.OKResult[uint64, ErrorCode] { + var result cm.OKResult[uint64, ErrorCode] + self.wasmimport_SendBufferSize(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.send-buffer-size +//go:noescape +func (self TCPSocket) wasmimport_SendBufferSize(result *cm.OKResult[uint64, ErrorCode]) + +// SetHopLimit represents method "set-hop-limit". +// +// set-hop-limit: func(value: u8) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) SetHopLimit(value uint8) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetHopLimit(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-hop-limit +//go:noescape +func (self TCPSocket) wasmimport_SetHopLimit(value uint8, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetKeepAliveCount represents method "set-keep-alive-count". +// +// set-keep-alive-count: func(value: u32) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) SetKeepAliveCount(value uint32) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetKeepAliveCount(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-keep-alive-count +//go:noescape +func (self TCPSocket) wasmimport_SetKeepAliveCount(value uint32, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetKeepAliveEnabled represents method "set-keep-alive-enabled". +// +// set-keep-alive-enabled: func(value: bool) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) SetKeepAliveEnabled(value bool) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetKeepAliveEnabled(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-keep-alive-enabled +//go:noescape +func (self TCPSocket) wasmimport_SetKeepAliveEnabled(value bool, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetKeepAliveIdleTime represents method "set-keep-alive-idle-time". +// +// set-keep-alive-idle-time: func(value: duration) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) SetKeepAliveIdleTime(value Duration) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetKeepAliveIdleTime(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-keep-alive-idle-time +//go:noescape +func (self TCPSocket) wasmimport_SetKeepAliveIdleTime(value Duration, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetKeepAliveInterval represents method "set-keep-alive-interval". +// +// set-keep-alive-interval: func(value: duration) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) SetKeepAliveInterval(value Duration) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetKeepAliveInterval(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-keep-alive-interval +//go:noescape +func (self TCPSocket) wasmimport_SetKeepAliveInterval(value Duration, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetListenBacklogSize represents 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) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetListenBacklogSize(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-listen-backlog-size +//go:noescape +func (self TCPSocket) wasmimport_SetListenBacklogSize(value uint64, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetReceiveBufferSize represents method "set-receive-buffer-size". +// +// set-receive-buffer-size: func(value: u64) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) SetReceiveBufferSize(value uint64) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetReceiveBufferSize(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-receive-buffer-size +//go:noescape +func (self TCPSocket) wasmimport_SetReceiveBufferSize(value uint64, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetSendBufferSize represents method "set-send-buffer-size". +// +// set-send-buffer-size: func(value: u64) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) SetSendBufferSize(value uint64) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetSendBufferSize(value, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.set-send-buffer-size +//go:noescape +func (self TCPSocket) wasmimport_SetSendBufferSize(value uint64, result *cm.ErrResult[struct{}, ErrorCode]) + +// Shutdown represents 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 +// - +// - +// - +// - +// +// shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) Shutdown(shutdownType ShutdownType) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_Shutdown(shutdownType, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.shutdown +//go:noescape +func (self TCPSocket) wasmimport_Shutdown(shutdownType ShutdownType, result *cm.ErrResult[struct{}, ErrorCode]) + +// StartBind represents 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 +// - +// - +// - +// - +// +// start-bind: func(network: borrow, local-address: ip-socket-address) -> +// result<_, error-code> +// +//go:nosplit +func (self TCPSocket) StartBind(network_ Network, localAddress IPSocketAddress) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_StartBind(network_, localAddress, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.start-bind +//go:noescape +func (self TCPSocket) wasmimport_StartBind(network_ Network, localAddress IPSocketAddress, result *cm.ErrResult[struct{}, ErrorCode]) + +// StartConnect represents 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 +// - +// - +// - +// - +// +// start-connect: func(network: borrow, remote-address: ip-socket-address) +// -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) StartConnect(network_ Network, remoteAddress IPSocketAddress) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_StartConnect(network_, remoteAddress, &result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.start-connect +//go:noescape +func (self TCPSocket) wasmimport_StartConnect(network_ Network, remoteAddress IPSocketAddress, result *cm.ErrResult[struct{}, ErrorCode]) + +// StartListen represents 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 +// - +// - +// - +// - +// +// start-listen: func() -> result<_, error-code> +// +//go:nosplit +func (self TCPSocket) StartListen() cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_StartListen(&result) + return result +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.start-listen +//go:noescape +func (self TCPSocket) wasmimport_StartListen(result *cm.ErrResult[struct{}, ErrorCode]) + +// Subscribe represents 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 +// 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() -> own +// +//go:nosplit +func (self TCPSocket) Subscribe() Pollable { + return self.wasmimport_Subscribe() +} + +//go:wasmimport wasi:sockets/tcp@0.2.0 [method]tcp-socket.subscribe +//go:noescape +func (self TCPSocket) wasmimport_Subscribe() Pollable diff --git a/src/syscall/wasi/sockets/v0.2.0/udp-create-socket/empty.s b/src/syscall/wasi/sockets/v0.2.0/udp-create-socket/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/sockets/v0.2.0/udp-create-socket/udp-create-socket.wit.go b/src/syscall/wasi/sockets/v0.2.0/udp-create-socket/udp-create-socket.wit.go new file mode 100644 index 0000000000..4f51954beb --- /dev/null +++ b/src/syscall/wasi/sockets/v0.2.0/udp-create-socket/udp-create-socket.wit.go @@ -0,0 +1,74 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package udpcreatesocket represents the interface "wasi:sockets/udp-create-socket@0.2.0". +package udpcreatesocket + +import ( + "github.com/ydnar/wasm-tools-go/cm" + "syscall/wasi/sockets/v0.2.0/network" + "syscall/wasi/sockets/v0.2.0/udp" +) + +// ErrorCode represents the enum "wasi:sockets/network@0.2.0#error-code". +// +// See [network.ErrorCode] for more information. +type ErrorCode = network.ErrorCode + +// IPAddressFamily represents the enum "wasi:sockets/network@0.2.0#ip-address-family". +// +// See [network.IPAddressFamily] for more information. +type IPAddressFamily = network.IPAddressFamily + +// Network represents the resource "wasi:sockets/network@0.2.0#network". +// +// See [network.Network] for more information. +type Network = network.Network + +// UDPSocket represents the resource "wasi:sockets/udp@0.2.0#udp-socket". +// +// See [udp.UDPSocket] for more information. +type UDPSocket = udp.UDPSocket + +// CreateUDPSocket represents function "wasi:sockets/udp-create-socket@0.2.0#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: +// - +// - +// - +// - +// +// create-udp-socket: func(address-family: ip-address-family) -> result, +// error-code> +// +//go:nosplit +func CreateUDPSocket(addressFamily IPAddressFamily) cm.OKResult[UDPSocket, ErrorCode] { + var result cm.OKResult[UDPSocket, ErrorCode] + wasmimport_CreateUDPSocket(addressFamily, &result) + return result +} + +//go:wasmimport wasi:sockets/udp-create-socket@0.2.0 create-udp-socket +//go:noescape +func wasmimport_CreateUDPSocket(addressFamily IPAddressFamily, result *cm.OKResult[UDPSocket, ErrorCode]) diff --git a/src/syscall/wasi/sockets/v0.2.0/udp/empty.s b/src/syscall/wasi/sockets/v0.2.0/udp/empty.s new file mode 100644 index 0000000000..5444f20065 --- /dev/null +++ b/src/syscall/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/syscall/wasi/sockets/v0.2.0/udp/udp.wit.go b/src/syscall/wasi/sockets/v0.2.0/udp/udp.wit.go new file mode 100644 index 0000000000..799149b041 --- /dev/null +++ b/src/syscall/wasi/sockets/v0.2.0/udp/udp.wit.go @@ -0,0 +1,642 @@ +// Code generated by wit-bindgen-go. DO NOT EDIT. + +//go:build !wasip1 + +// Package udp represents the interface "wasi:sockets/udp@0.2.0". +package udp + +import ( + "github.com/ydnar/wasm-tools-go/cm" + "syscall/wasi/io/v0.2.0/poll" + "syscall/wasi/sockets/v0.2.0/network" +) + +// ErrorCode represents the enum "wasi:sockets/network@0.2.0#error-code". +// +// See [network.ErrorCode] for more information. +type ErrorCode = network.ErrorCode + +// IncomingDatagram represents the record "wasi:sockets/udp@0.2.0#incoming-datagram". +// +// A received datagram. +// +// record incoming-datagram { +// data: list, +// 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 IPSocketAddress +} + +// IncomingDatagramStream represents the resource "wasi:sockets/udp@0.2.0#incoming-datagram-stream". +// +// resource incoming-datagram-stream +type IncomingDatagramStream cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self IncomingDatagramStream) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [resource-drop]incoming-datagram-stream +//go:noescape +func (self IncomingDatagramStream) wasmimport_ResourceDrop() + +// Receive represents 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 +// - +// - +// - +// - +// - +// - +// - +// - +// +// receive: func(max-results: u64) -> result, error-code> +// +//go:nosplit +func (self IncomingDatagramStream) Receive(maxResults uint64) cm.OKResult[cm.List[IncomingDatagram], ErrorCode] { + var result cm.OKResult[cm.List[IncomingDatagram], ErrorCode] + self.wasmimport_Receive(maxResults, &result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]incoming-datagram-stream.receive +//go:noescape +func (self IncomingDatagramStream) wasmimport_Receive(maxResults uint64, result *cm.OKResult[cm.List[IncomingDatagram], ErrorCode]) + +// Subscribe represents 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() -> own +// +//go:nosplit +func (self IncomingDatagramStream) Subscribe() Pollable { + return self.wasmimport_Subscribe() +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]incoming-datagram-stream.subscribe +//go:noescape +func (self IncomingDatagramStream) wasmimport_Subscribe() Pollable + +// IPAddressFamily represents the enum "wasi:sockets/network@0.2.0#ip-address-family". +// +// See [network.IPAddressFamily] for more information. +type IPAddressFamily = network.IPAddressFamily + +// IPSocketAddress represents the variant "wasi:sockets/network@0.2.0#ip-socket-address". +// +// See [network.IPSocketAddress] for more information. +type IPSocketAddress = network.IPSocketAddress + +// Network represents the resource "wasi:sockets/network@0.2.0#network". +// +// See [network.Network] for more information. +type Network = network.Network + +// OutgoingDatagram represents the record "wasi:sockets/udp@0.2.0#outgoing-datagram". +// +// A datagram to be sent out. +// +// record outgoing-datagram { +// data: list, +// remote-address: option, +// } +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[IPSocketAddress] +} + +// OutgoingDatagramStream represents the resource "wasi:sockets/udp@0.2.0#outgoing-datagram-stream". +// +// resource outgoing-datagram-stream +type OutgoingDatagramStream cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self OutgoingDatagramStream) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [resource-drop]outgoing-datagram-stream +//go:noescape +func (self OutgoingDatagramStream) wasmimport_ResourceDrop() + +// CheckSend represents 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 +// +//go:nosplit +func (self OutgoingDatagramStream) CheckSend() cm.OKResult[uint64, ErrorCode] { + var result cm.OKResult[uint64, ErrorCode] + self.wasmimport_CheckSend(&result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]outgoing-datagram-stream.check-send +//go:noescape +func (self OutgoingDatagramStream) wasmimport_CheckSend(result *cm.OKResult[uint64, ErrorCode]) + +// Send represents 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 +// - +// - +// - +// - +// - +// - +// - +// - +// +// send: func(datagrams: list) -> result +// +//go:nosplit +func (self OutgoingDatagramStream) Send(datagrams cm.List[OutgoingDatagram]) cm.OKResult[uint64, ErrorCode] { + var result cm.OKResult[uint64, ErrorCode] + self.wasmimport_Send(datagrams, &result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]outgoing-datagram-stream.send +//go:noescape +func (self OutgoingDatagramStream) wasmimport_Send(datagrams cm.List[OutgoingDatagram], result *cm.OKResult[uint64, ErrorCode]) + +// Subscribe represents 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() -> own +// +//go:nosplit +func (self OutgoingDatagramStream) Subscribe() Pollable { + return self.wasmimport_Subscribe() +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]outgoing-datagram-stream.subscribe +//go:noescape +func (self OutgoingDatagramStream) wasmimport_Subscribe() Pollable + +// Pollable represents the resource "wasi:io/poll@0.2.0#pollable". +// +// See [poll.Pollable] for more information. +type Pollable = poll.Pollable + +// UDPSocket represents the resource "wasi:sockets/udp@0.2.0#udp-socket". +// +// A UDP socket handle. +// +// resource udp-socket +type UDPSocket cm.Resource + +// ResourceDrop represents the Canonical ABI function "resource-drop". +// +// Drops a resource handle. +// +//go:nosplit +func (self UDPSocket) ResourceDrop() { + self.wasmimport_ResourceDrop() +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [resource-drop]udp-socket +//go:noescape +func (self UDPSocket) wasmimport_ResourceDrop() + +// AddressFamily represents 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() IPAddressFamily { + return self.wasmimport_AddressFamily() +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.address-family +//go:noescape +func (self UDPSocket) wasmimport_AddressFamily() IPAddressFamily + +// FinishBind represents method "finish-bind". +// +// finish-bind: func() -> result<_, error-code> +// +//go:nosplit +func (self UDPSocket) FinishBind() cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_FinishBind(&result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.finish-bind +//go:noescape +func (self UDPSocket) wasmimport_FinishBind(result *cm.ErrResult[struct{}, ErrorCode]) + +// LocalAddress represents 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 +// - +// - +// - +// - +// +// local-address: func() -> result +// +//go:nosplit +func (self UDPSocket) LocalAddress() cm.OKResult[IPSocketAddress, ErrorCode] { + var result cm.OKResult[IPSocketAddress, ErrorCode] + self.wasmimport_LocalAddress(&result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.local-address +//go:noescape +func (self UDPSocket) wasmimport_LocalAddress(result *cm.OKResult[IPSocketAddress, ErrorCode]) + +// ReceiveBufferSize represents 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 +// +//go:nosplit +func (self UDPSocket) ReceiveBufferSize() cm.OKResult[uint64, ErrorCode] { + var result cm.OKResult[uint64, ErrorCode] + self.wasmimport_ReceiveBufferSize(&result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.receive-buffer-size +//go:noescape +func (self UDPSocket) wasmimport_ReceiveBufferSize(result *cm.OKResult[uint64, ErrorCode]) + +// RemoteAddress represents 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 +// - +// - +// - +// - +// +// remote-address: func() -> result +// +//go:nosplit +func (self UDPSocket) RemoteAddress() cm.OKResult[IPSocketAddress, ErrorCode] { + var result cm.OKResult[IPSocketAddress, ErrorCode] + self.wasmimport_RemoteAddress(&result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.remote-address +//go:noescape +func (self UDPSocket) wasmimport_RemoteAddress(result *cm.OKResult[IPSocketAddress, ErrorCode]) + +// SendBufferSize represents method "send-buffer-size". +// +// send-buffer-size: func() -> result +// +//go:nosplit +func (self UDPSocket) SendBufferSize() cm.OKResult[uint64, ErrorCode] { + var result cm.OKResult[uint64, ErrorCode] + self.wasmimport_SendBufferSize(&result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.send-buffer-size +//go:noescape +func (self UDPSocket) wasmimport_SendBufferSize(result *cm.OKResult[uint64, ErrorCode]) + +// SetReceiveBufferSize represents method "set-receive-buffer-size". +// +// set-receive-buffer-size: func(value: u64) -> result<_, error-code> +// +//go:nosplit +func (self UDPSocket) SetReceiveBufferSize(value uint64) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetReceiveBufferSize(value, &result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.set-receive-buffer-size +//go:noescape +func (self UDPSocket) wasmimport_SetReceiveBufferSize(value uint64, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetSendBufferSize represents method "set-send-buffer-size". +// +// set-send-buffer-size: func(value: u64) -> result<_, error-code> +// +//go:nosplit +func (self UDPSocket) SetSendBufferSize(value uint64) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetSendBufferSize(value, &result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.set-send-buffer-size +//go:noescape +func (self UDPSocket) wasmimport_SetSendBufferSize(value uint64, result *cm.ErrResult[struct{}, ErrorCode]) + +// SetUnicastHopLimit represents method "set-unicast-hop-limit". +// +// set-unicast-hop-limit: func(value: u8) -> result<_, error-code> +// +//go:nosplit +func (self UDPSocket) SetUnicastHopLimit(value uint8) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_SetUnicastHopLimit(value, &result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.set-unicast-hop-limit +//go:noescape +func (self UDPSocket) wasmimport_SetUnicastHopLimit(value uint8, result *cm.ErrResult[struct{}, ErrorCode]) + +// StartBind represents 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 +// - +// - +// - +// - +// +// start-bind: func(network: borrow, local-address: ip-socket-address) -> +// result<_, error-code> +// +//go:nosplit +func (self UDPSocket) StartBind(network_ Network, localAddress IPSocketAddress) cm.ErrResult[struct{}, ErrorCode] { + var result cm.ErrResult[struct{}, ErrorCode] + self.wasmimport_StartBind(network_, localAddress, &result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.start-bind +//go:noescape +func (self UDPSocket) wasmimport_StartBind(network_ Network, localAddress IPSocketAddress, result *cm.ErrResult[struct{}, ErrorCode]) + +// Stream represents 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 +// - +// - +// - +// - +// +// stream: func(remote-address: option) -> result, +// own>, error-code> +// +//go:nosplit +func (self UDPSocket) Stream(remoteAddress cm.Option[IPSocketAddress]) cm.OKResult[cm.Tuple[IncomingDatagramStream, OutgoingDatagramStream], ErrorCode] { + var result cm.OKResult[cm.Tuple[IncomingDatagramStream, OutgoingDatagramStream], ErrorCode] + self.wasmimport_Stream(remoteAddress, &result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.stream +//go:noescape +func (self UDPSocket) wasmimport_Stream(remoteAddress cm.Option[IPSocketAddress], result *cm.OKResult[cm.Tuple[IncomingDatagramStream, OutgoingDatagramStream], ErrorCode]) + +// Subscribe represents 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() -> own +// +//go:nosplit +func (self UDPSocket) Subscribe() Pollable { + return self.wasmimport_Subscribe() +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.subscribe +//go:noescape +func (self UDPSocket) wasmimport_Subscribe() Pollable + +// UnicastHopLimit represents 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 +// +//go:nosplit +func (self UDPSocket) UnicastHopLimit() cm.OKResult[uint8, ErrorCode] { + var result cm.OKResult[uint8, ErrorCode] + self.wasmimport_UnicastHopLimit(&result) + return result +} + +//go:wasmimport wasi:sockets/udp@0.2.0 [method]udp-socket.unicast-hop-limit +//go:noescape +func (self UDPSocket) wasmimport_UnicastHopLimit(result *cm.OKResult[uint8, ErrorCode]) diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go index 9f5249b0c3..eecba519af 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 index 0000000000..478042fc23 --- /dev/null +++ b/src/vendor/github.com/ydnar/wasm-tools-go @@ -0,0 +1 @@ +Subproject commit 478042fc23c1ccb9837b2b280a098d6cee4cb955 diff --git a/targets/wasip2.json b/targets/wasip2.json new file mode 100644 index 0000000000..3d6f68c6ec --- /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 0000000000..169da5852c --- /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