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