diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 538c4570..ca24140c 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -149,8 +149,8 @@ jobs: # https://github.com/tinygo-org/tinygo/blob/v0.26.0/Makefile#L271-L319 # Note: # - index/suffixarray is extremely slow, so skip it. - # - compress/zlib is skipped as it depends on the local files https://github.com/golang/go/blob/40a0986959167e7b82ae16a35a5dd2a347609eff/src/compress/zlib/writer_test.go#L16-L30 - # - debug/macho is skipped as it depends on the local files https://github.com/golang/go/blob/40a0986959167e7b82ae16a35a5dd2a347609eff/src/debug/macho/file_test.go#L25 + # - compress/zlib is skipped as it depends on the local files https://github.com/golang/go/blob/go1.20/src/compress/zlib/writer_test.go#L16-L30 + # - debug/macho is skipped as it depends on the local files https://github.com/golang/go/blob/go1.20/src/debug/macho/file_test.go#L25 run: | mkdir ${{ env.BINARY_OUT }} for value in container/heap \ diff --git a/RATIONALE.md b/RATIONALE.md index 1a164106..a6504fc2 100644 --- a/RATIONALE.md +++ b/RATIONALE.md @@ -565,7 +565,7 @@ value (possibly `PWD`). Those unable to control the compiled code should only use absolute paths in configuration. See -* https://github.com/golang/go/blob/go1.19rc2/src/syscall/fs_js.go#L324 +* https://github.com/golang/go/blob/go1.20/src/syscall/fs_js.go#L324 * https://github.com/WebAssembly/wasi-libc/pull/214#issue-673090117 * https://github.com/ziglang/zig/blob/53a9ee699a35a3d245ab6d1dac1f0687a4dcb42c/src/main.zig#L32 @@ -740,7 +740,7 @@ doesn't and emulating them in spite of that would result in no difference except hire overhead to the majority of our users. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html -See https://github.com/golang/go/blob/252324e879e32f948d885f787decf8af06f82be9/src/os/dir_unix.go#L108-L111 +See https://github.com/golang/go/blob/go1.20/src/os/dir_unix.go#L108-L110 ## sys.Walltime and Nanotime @@ -793,7 +793,7 @@ See https://go.googlesource.com/proposal/+/master/design/12914-monotonic.md WebAssembly time imports do not have the same concern. In fact even Go's imports for clocks split walltime from nanotime readings. -See https://github.com/golang/go/blob/252324e879e32f948d885f787decf8af06f82be9/misc/wasm/wasm_exec.js#L243-L255 +See https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L243-L255 Finally, Go's clock is not an interface. WebAssembly users who want determinism or security need to be able to substitute an alternative clock implementation @@ -807,7 +807,7 @@ value. For now, we return fixed values of 1us for realtime and 1ns for monotonic often lower precision than monotonic clocks. In the future, this could be improved by having OS+arch specific assembly to make syscalls. -For example, Go implements time.Now for linux-amd64 with this [assembly](https://github.com/golang/go/blob/f19e4001808863d2ebfe9d1975476513d030c381/src/runtime/time_linux_amd64.s). +For example, Go implements time.Now for linux-amd64 with this [assembly](https://github.com/golang/go/blob/go1.20/src/runtime/time_linux_amd64.s). Because retrieving resolution is not generally called often, unlike getting time, it could be appropriate to only implement the fallback logic that does not use VDSO (executing syscalls in user mode). The syscall for clock_getres is 229 and should be usable. https://pkg.go.dev/syscall#pkg-constants. @@ -885,7 +885,7 @@ This is due to the same reason for the limitation on the number of functions abo While the the spec does not clarify a limitation of function stack values, wazero limits this to 2^27 = 134,217,728. The reason is that we internally represent all the values as 64-bit integers regardless of its types (including f32, f64), and 2^27 values means -1 GiB = (2^30). 1 GiB is the reasonable for most applications [as we see a Goroutine has 250 MB as a limit on the stack for 32-bit arch](https://github.com/golang/go/blob/f296b7a6f045325a230f77e9bda1470b1270f817/src/runtime/proc.go#L120), considering that WebAssembly is (currently) 32-bit environment. +1 GiB = (2^30). 1 GiB is the reasonable for most applications [as we see a Goroutine has 250 MB as a limit on the stack for 32-bit arch](https://github.com/golang/go/blob/go1.20/src/runtime/proc.go#L152-L159), considering that WebAssembly is (currently) 32-bit environment. All the functions are statically analyzed at module instantiation phase, and if a function can potentially reach this limit, an error is returned. @@ -948,6 +948,6 @@ In lieu of formal documentation, we infer this pattern works from other sources * `sync.WaitGroup` by definition must support calling `Add` from other goroutines. Internally, it uses atomics. * rsc in golang/go#5045 writes "atomics guarantee sequential consistency among the atomic variables". -See https://github.com/golang/go/blob/011fd002457da0823da5f06b099fcf6e21444b00/src/sync/waitgroup.go#L64 +See https://github.com/golang/go/blob/go1.20/src/sync/waitgroup.go#L64 See https://github.com/golang/go/issues/5045#issuecomment-252730563 See https://www.youtube.com/watch?v=VmrEG-3bWyM diff --git a/imports/go/README.md b/imports/go/README.md index 9bcb8078..d5e09a75 100644 --- a/imports/go/README.md +++ b/imports/go/README.md @@ -18,4 +18,4 @@ Accordingly, wazero cannot guarantee this will work from release to release, or that usage will be relatively free of bugs. Due to this and the relatively high implementation overhead, most will choose TinyGo instead. -[1]: https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js +[1]: https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js diff --git a/internal/gojs/errno.go b/internal/gojs/errno.go new file mode 100644 index 00000000..b7d6a707 --- /dev/null +++ b/internal/gojs/errno.go @@ -0,0 +1,114 @@ +package gojs + +import ( + "errors" + "io/fs" + "os" + "syscall" +) + +// Errno is a (GOARCH=wasm) error, which must match a key in mapJSError. +// +// See https://github.com/golang/go/blob/go1.20/src/syscall/tables_js.go#L371-L494 +type Errno struct { + s string +} + +// Error implements error. +func (e *Errno) Error() string { + return e.s +} + +// This order match constants from wasi_snapshot_preview1.ErrnoSuccess for +// easier maintenance. +var ( + // ErrnoBadf Bad file descriptor. + ErrnoBadf = &Errno{"EBADF"} + // ErrnoExist File exists. + ErrnoExist = &Errno{"EEXIST"} + // ErrnoInval Invalid argument. + ErrnoInval = &Errno{"EINVAL"} + // ErrnoIo I/O error. + ErrnoIo = &Errno{"EIO"} + // ErrnoIsdir Is a directory. + ErrnoIsdir = &Errno{"EISDIR"} + // ErrnoLoop Too many levels of symbolic links. + ErrnoLoop = &Errno{"ELOOP"} + // ErrnoNametoolong Filename too long. + ErrnoNametoolong = &Errno{"ENAMETOOLONG"} + // ErrnoNoent No such file or directory. + ErrnoNoent = &Errno{"ENOENT"} + // ErrnoNosys function not supported. + ErrnoNosys = &Errno{"ENOSYS"} + // ErrnoNotdir Not a directory or a symbolic link to a directory. + ErrnoNotdir = &Errno{"ENOTDIR"} + // ErrnoNotempty Directory not empty. + ErrnoNotempty = &Errno{"ENOTEMPTY"} + // ErrnoNotsup Not supported, or operation not supported on socket. + ErrnoNotsup = &Errno{"ENOTSUP"} + // ErrnoPerm Operation not permitted. + ErrnoPerm = &Errno{"EPERM"} +) + +// ToErrno maps I/O errors as the message must be the code, ex. "EINVAL", not +// the message, e.g. "invalid argument". +// +// This should match wasi_snapshot_preview1.ToErrno for maintenance ease. +func ToErrno(err error) *Errno { + if pe, ok := err.(*os.PathError); ok { + err = pe.Unwrap() + } + if se, ok := err.(syscall.Errno); ok { + return errnoFromSyscall(se) + } + // Below are all the fs.ErrXXX in fs.go. errors.Is is more expensive, so + // try it last. Note: Once we have our own file type, we should never see + // these. + switch { + case errors.Is(err, fs.ErrInvalid): + return ErrnoInval + case errors.Is(err, fs.ErrPermission): + return ErrnoPerm + case errors.Is(err, fs.ErrExist): + return ErrnoExist + case errors.Is(err, fs.ErrNotExist): + return ErrnoNoent + case errors.Is(err, fs.ErrClosed): + return ErrnoBadf + default: + return ErrnoIo + } +} + +func errnoFromSyscall(errno syscall.Errno) *Errno { + switch errno { + case syscall.EBADF: + return ErrnoBadf + case syscall.EEXIST: + return ErrnoExist + case syscall.EINVAL: + return ErrnoInval + case syscall.EIO: + return ErrnoIo + case syscall.EISDIR: + return ErrnoIsdir + case syscall.ELOOP: + return ErrnoLoop + case syscall.ENAMETOOLONG: + return ErrnoNametoolong + case syscall.ENOENT: + return ErrnoNoent + case syscall.ENOSYS: + return ErrnoNosys + case syscall.ENOTDIR: + return ErrnoNotdir + case syscall.ENOTEMPTY: + return ErrnoNotempty + case syscall.ENOTSUP: + return ErrnoNotsup + case syscall.EPERM: + return ErrnoPerm + default: + return ErrnoIo + } +} diff --git a/internal/gojs/errno_test.go b/internal/gojs/errno_test.go new file mode 100644 index 00000000..fe242434 --- /dev/null +++ b/internal/gojs/errno_test.go @@ -0,0 +1,139 @@ +package gojs + +import ( + "errors" + "fmt" + "io/fs" + "os" + "syscall" + "testing" + + "github.com/tetratelabs/wazero/internal/testing/require" +) + +func TestToErrno(t *testing.T) { + tests := []struct { + name string + input error + expected *Errno + }{ + { + name: "syscall.EBADF", + input: syscall.EBADF, + expected: ErrnoBadf, + }, + { + name: "syscall.EEXIST", + input: syscall.EEXIST, + expected: ErrnoExist, + }, + { + name: "syscall.EINVAL", + input: syscall.EINVAL, + expected: ErrnoInval, + }, + { + name: "syscall.EIO", + input: syscall.EIO, + expected: ErrnoIo, + }, + { + name: "syscall.EISDIR", + input: syscall.EISDIR, + expected: ErrnoIsdir, + }, + { + name: "syscall.ELOOP", + input: syscall.ELOOP, + expected: ErrnoLoop, + }, + { + name: "syscall.ENAMETOOLONG", + input: syscall.ENAMETOOLONG, + expected: ErrnoNametoolong, + }, + { + name: "syscall.ENOENT", + input: syscall.ENOENT, + expected: ErrnoNoent, + }, + { + name: "syscall.ENOSYS", + input: syscall.ENOSYS, + expected: ErrnoNosys, + }, + { + name: "syscall.ENOTDIR", + input: syscall.ENOTDIR, + expected: ErrnoNotdir, + }, + { + name: "syscall.ENOTEMPTY", + input: syscall.ENOTEMPTY, + expected: ErrnoNotempty, + }, + { + name: "syscall.ENOTSUP", + input: syscall.ENOTSUP, + expected: ErrnoNotsup, + }, + { + name: "syscall.EPERM", + input: syscall.EPERM, + expected: ErrnoPerm, + }, + { + name: "syscall.Errno unexpected == ErrnoIo", + input: syscall.Errno(0xfe), + expected: ErrnoIo, + }, + { + name: "PathError ErrInvalid", + input: &os.PathError{Err: fs.ErrInvalid}, + expected: ErrnoInval, + }, + { + name: "PathError ErrPermission", + input: &os.PathError{Err: fs.ErrPermission}, + expected: ErrnoPerm, + }, + { + name: "PathError ErrExist", + input: &os.PathError{Err: fs.ErrExist}, + expected: ErrnoExist, + }, + { + name: "PathError ErrNotExist", + input: &os.PathError{Err: fs.ErrNotExist}, + expected: ErrnoNoent, + }, + { + name: "PathError ErrClosed", + input: &os.PathError{Err: fs.ErrClosed}, + expected: ErrnoBadf, + }, + { + name: "PathError unknown == ErrnoIo", + input: &os.PathError{Err: errors.New("ice cream")}, + expected: ErrnoIo, + }, + { + name: "unknown == ErrnoIo", + input: errors.New("ice cream"), + expected: ErrnoIo, + }, + { + name: "very wrapped unknown == ErrnoIo", + input: fmt.Errorf("%w", fmt.Errorf("%w", fmt.Errorf("%w", errors.New("ice cream")))), + expected: ErrnoIo, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + errno := ToErrno(tc.input) + require.Equal(t, tc.expected, errno) + }) + } +} diff --git a/internal/gojs/fs.go b/internal/gojs/fs.go index 249d6114..29322472 100644 --- a/internal/gojs/fs.go +++ b/internal/gojs/fs.go @@ -411,7 +411,7 @@ func (processChdir) invoke(ctx context.Context, mod api.Module, args ...interfac path := args[0].(string) if s, err := syscallStat(mod, path); err != nil { - return nil, mapJSError(err) + return nil, ToErrno(err) } else if !s.isDir { return nil, syscall.ENOTDIR } else { diff --git a/internal/gojs/goarch/wasm.go b/internal/gojs/goarch/wasm.go index 0c69d3bb..d7d95d88 100644 --- a/internal/gojs/goarch/wasm.go +++ b/internal/gojs/goarch/wasm.go @@ -148,10 +148,10 @@ func (s *stack) SetResultUint32(i int, v uint32) { // GetSP gets the stack pointer, which is needed prior to storing a value when // in an operation that can trigger a Go event handler. // -// See https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L210-L213 +// See https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L210-L213 func GetSP(mod api.Module) uint32 { // Cheat by reading global[0] directly instead of through a function proxy. - // https://github.com/golang/go/blob/go1.19/src/runtime/rt0_js_wasm.s#L87-L90 + // https://github.com/golang/go/blob/go1.20/src/runtime/rt0_js_wasm.s#L87-L90 return uint32(mod.(*wasm.CallContext).GlobalVal(0)) } diff --git a/internal/gojs/js.go b/internal/gojs/js.go index 03eff6ae..bf419ada 100644 --- a/internal/gojs/js.go +++ b/internal/gojs/js.go @@ -45,7 +45,7 @@ func (v *jsVal) addFunction(method string, fn jsFn) *jsVal { v.functions[method] = fn // If fn returns an error, js.Call does a type lookup to verify it is a // function. - // See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L389 + // See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L389 v.properties[method] = fn return v } diff --git a/internal/gojs/runtime.go b/internal/gojs/runtime.go index ed545326..b86e9ff9 100644 --- a/internal/gojs/runtime.go +++ b/internal/gojs/runtime.go @@ -14,14 +14,14 @@ import ( // Debug has unknown use, so stubbed. // -// See https://github.com/golang/go/blob/go1.19/src/cmd/link/internal/wasm/asm.go#L133-L138 +// See https://github.com/golang/go/blob/go1.20/src/cmd/link/internal/wasm/asm.go#L131-L136 var Debug = goarch.StubFunction(custom.NameDebug) // TODO: should this call runtime.Breakpoint()? // WasmExit implements runtime.wasmExit which supports runtime.exit. // -// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.go#L28 +// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.go#L24 var WasmExit = goarch.NewFunc(custom.NameRuntimeWasmExit, wasmExit) func wasmExit(ctx context.Context, mod api.Module, stack goarch.Stack) { @@ -34,7 +34,7 @@ func wasmExit(ctx context.Context, mod api.Module, stack goarch.Stack) { // WasmWrite implements runtime.wasmWrite which supports runtime.write and // runtime.writeErr. This implements `println`. // -// See https://github.com/golang/go/blob/go1.19/src/runtime/os_js.go#L29 +// See https://github.com/golang/go/blob/go1.20/src/runtime/os_js.go#L30 var WasmWrite = goarch.NewFunc(custom.NameRuntimeWasmWrite, wasmWrite) func wasmWrite(_ context.Context, mod api.Module, stack goarch.Stack) { @@ -52,7 +52,7 @@ func wasmWrite(_ context.Context, mod api.Module, stack goarch.Stack) { // ResetMemoryDataView signals wasm.OpcodeMemoryGrow happened, indicating any // cached view of memory should be reset. // -// See https://github.com/golang/go/blob/go1.19/src/runtime/mem_js.go#L82 +// See https://github.com/golang/go/blob/go1.20/src/runtime/mem_js.go#L82 var ResetMemoryDataView = goarch.NewFunc(custom.NameRuntimeResetMemoryDataView, resetMemoryDataView) func resetMemoryDataView(context.Context, api.Module, goarch.Stack) { @@ -63,7 +63,7 @@ func resetMemoryDataView(context.Context, api.Module, goarch.Stack) { // Nanotime1 implements runtime.nanotime which supports time.Since. // -// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L184 +// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L117 var Nanotime1 = goarch.NewFunc(custom.NameRuntimeNanotime1, nanotime1) func nanotime1(_ context.Context, mod api.Module, stack goarch.Stack) { @@ -74,7 +74,7 @@ func nanotime1(_ context.Context, mod api.Module, stack goarch.Stack) { // Walltime implements runtime.walltime which supports time.Now. // -// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L188 +// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L121 var Walltime = goarch.NewFunc(custom.NameRuntimeWalltime, walltime) func walltime(_ context.Context, mod api.Module, stack goarch.Stack) { @@ -90,7 +90,7 @@ func walltime(_ context.Context, mod api.Module, stack goarch.Stack) { // Unlike other most functions prefixed by "runtime.", this both launches a // goroutine and invokes code compiled into wasm "resume". // -// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L192 +// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L125 var ScheduleTimeoutEvent = goarch.NewFunc(custom.NameRuntimeScheduleTimeoutEvent, scheduleTimeoutEvent) // Note: Signal handling is not implemented in GOOS=js. @@ -122,7 +122,7 @@ func scheduleTimeoutEvent(ctx context.Context, mod api.Module, stack goarch.Stac // ClearTimeoutEvent implements runtime.clearTimeoutEvent which supports // runtime.notetsleepg used by runtime.signal_recv. // -// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L196 +// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L129 var ClearTimeoutEvent = goarch.NewFunc(custom.NameRuntimeClearTimeoutEvent, clearTimeoutEvent) // Note: Signal handling is not implemented in GOOS=js. @@ -138,7 +138,7 @@ func clearTimeoutEvent(ctx context.Context, _ api.Module, stack goarch.Stack) { // GetRandomData implements runtime.getRandomData, which initializes the seed // for runtime.fastrand. // -// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L200 +// See https://github.com/golang/go/blob/go1.20/src/runtime/sys_wasm.s#L133 var GetRandomData = goarch.NewFunc(custom.NameRuntimeGetRandomData, getRandomData) func getRandomData(_ context.Context, mod api.Module, stack goarch.Stack) { diff --git a/internal/gojs/state.go b/internal/gojs/state.go index f6fd3fbf..ae83dc97 100644 --- a/internal/gojs/state.go +++ b/internal/gojs/state.go @@ -63,7 +63,7 @@ var NaN = math.NaN() // LoadValue reads up to 8 bytes at the memory offset `addr` to return the // value written by storeValue. // -// See https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L122-L133 +// See https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L122-L133 func LoadValue(ctx context.Context, ref goos.Ref) interface{} { //nolint switch ref { case 0: @@ -110,12 +110,12 @@ func LoadValue(ctx context.Context, ref goos.Ref) interface{} { //nolint } } -// storeRef stores a value prior to returning to wasm from a host function. +// storeValue stores a value prior to returning to wasm from a host function. // This returns 8 bytes to represent either the value or a reference to it. // Any side effects besides memory must be cleaned up on wasmExit. // -// See https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L135-L183 -func storeRef(ctx context.Context, v interface{}) goos.Ref { //nolint +// See https://github.com/golang/go/blob/de4748c47c67392a57f250714509f590f68ad395/misc/wasm/wasm_exec.js#L135-L183 +func storeValue(ctx context.Context, v interface{}) goos.Ref { //nolint // allow-list because we control all implementations if v == goos.Undefined { return goos.RefValueUndefined diff --git a/internal/gojs/syscall.go b/internal/gojs/syscall.go index 2c25255b..1edc7885 100644 --- a/internal/gojs/syscall.go +++ b/internal/gojs/syscall.go @@ -2,11 +2,8 @@ package gojs import ( "context" - "errors" "fmt" - "io/fs" "net/http" - "syscall" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/gojs/custom" @@ -18,7 +15,7 @@ import ( // FinalizeRef implements js.finalizeRef, which is used as a // runtime.SetFinalizer on the given reference. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L61 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L61 var FinalizeRef = goos.NewFunc(custom.NameSyscallFinalizeRef, finalizeRef) func finalizeRef(ctx context.Context, _ api.Module, stack goos.Stack) { @@ -32,14 +29,14 @@ func finalizeRef(ctx context.Context, _ api.Module, stack goos.Stack) { // StringVal implements js.stringVal, which is used to load the string for // `js.ValueOf(x)`. For example, this is used when setting HTTP headers. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L212 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L305-L308 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L212 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L305-L308 var StringVal = goos.NewFunc(custom.NameSyscallStringVal, stringVal) func stringVal(ctx context.Context, mod api.Module, stack goos.Stack) { x := stack.ParamString(mod.Memory(), 0) - r := storeRef(ctx, x) + r := storeValue(ctx, x) stack.SetResultRef(0, r) } @@ -48,8 +45,8 @@ func stringVal(ctx context.Context, mod api.Module, stack goos.Stack) { // by name, e.g. `v.Get("address")`. Notably, this is used by js.handleEvent to // get the pending event. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L295 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L311-L316 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L295 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L311-L316 var ValueGet = goos.NewFunc(custom.NameSyscallValueGet, valueGet) func valueGet(ctx context.Context, mod api.Module, stack goos.Stack) { @@ -64,7 +61,7 @@ func valueGet(ctx context.Context, mod api.Module, stack goos.Stack) { case "message": // js (GOOS=js) error, can be anything. result = e.Error() case "code": // syscall (GOARCH=wasm) error, must match key in mapJSError in fs_js.go - result = mapJSError(e).Error() + result = ToErrno(e).Error() default: panic(fmt.Errorf("TODO: valueGet(v=%v, p=%s)", v, p)) } @@ -72,7 +69,7 @@ func valueGet(ctx context.Context, mod api.Module, stack goos.Stack) { panic(fmt.Errorf("TODO: valueGet(v=%v, p=%s)", v, p)) } - r := storeRef(ctx, result) + r := storeValue(ctx, result) stack.SetResultRef(0, r) } @@ -80,8 +77,8 @@ func valueGet(ctx context.Context, mod api.Module, stack goos.Stack) { // by name, e.g. `v.Set("address", a)`. Notably, this is used by js.handleEvent // set the event result. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L309 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L318-L322 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L309 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L318-L322 var ValueSet = goos.NewFunc(custom.NameSyscallValueSet, valueSet) func valueSet(ctx context.Context, mod api.Module, stack goos.Stack) { @@ -114,15 +111,15 @@ func valueSet(ctx context.Context, mod api.Module, stack goos.Stack) { // ValueDelete is stubbed as it isn't used in Go's main source tree. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L321 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L321 var ValueDelete = goarch.StubFunction(custom.NameSyscallValueDelete) // ValueIndex implements js.valueIndex, which is used to load a js.Value property // by index, e.g. `v.Index(0)`. Notably, this is used by js.handleEvent to read // event arguments // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L334 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L331-L334 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L334 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L331-L334 var ValueIndex = goos.NewFunc(custom.NameSyscallValueIndex, valueIndex) func valueIndex(ctx context.Context, _ api.Module, stack goos.Stack) { @@ -131,21 +128,21 @@ func valueIndex(ctx context.Context, _ api.Module, stack goos.Stack) { result := v.(*objectArray).slice[i] - r := storeRef(ctx, result) + r := storeValue(ctx, result) stack.SetResultRef(0, r) } // ValueSetIndex is stubbed as it is only used for js.ValueOf when the input is // []interface{}, which doesn't appear to occur in Go's source tree. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L348 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L348 var ValueSetIndex = goarch.StubFunction(custom.NameSyscallValueSetIndex) // ValueCall implements js.valueCall, which is used to call a js.Value function // by name, e.g. `document.Call("createElement", "div")`. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L394 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L343-L358 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L394 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L343-L358 var ValueCall = goos.NewFunc(custom.NameSyscallValueCall, valueCall) func valueCall(ctx context.Context, mod api.Module, stack goos.Stack) { @@ -164,9 +161,9 @@ func valueCall(ctx context.Context, mod api.Module, stack goos.Stack) { var res goos.Ref var ok bool if result, err := c.call(ctx, mod, vRef, m, args...); err != nil { - res = storeRef(ctx, err) + res = storeValue(ctx, err) } else { - res = storeRef(ctx, result) + res = storeValue(ctx, result) ok = true } @@ -177,14 +174,14 @@ func valueCall(ctx context.Context, mod api.Module, stack goos.Stack) { // ValueInvoke is stubbed as it isn't used in Go's main source tree. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L413 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L413 var ValueInvoke = goarch.StubFunction(custom.NameSyscallValueInvoke) // ValueNew implements js.valueNew, which is used to call a js.Value, e.g. // `array.New(2)`. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L432 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L380-L391 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L432 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L378-L392 var ValueNew = goos.NewFunc(custom.NameSyscallValueNew, valueNew) func valueNew(ctx context.Context, mod api.Module, stack goos.Stack) { @@ -198,7 +195,7 @@ func valueNew(ctx context.Context, mod api.Module, stack goos.Stack) { switch vRef { case goos.RefArrayConstructor: result := &objectArray{} - res = storeRef(ctx, result) + res = storeValue(ctx, result) ok = true case goos.RefUint8ArrayConstructor: var result interface{} @@ -212,15 +209,15 @@ func valueNew(ctx context.Context, mod api.Module, stack goos.Stack) { } else { panic(fmt.Errorf("TODO: valueNew(v=%v, args=%v)", vRef, args)) } - res = storeRef(ctx, result) + res = storeValue(ctx, result) ok = true case goos.RefObjectConstructor: result := &object{properties: map[string]interface{}{}} - res = storeRef(ctx, result) + res = storeValue(ctx, result) ok = true case goos.RefHttpHeadersConstructor: result := &headers{headers: http.Header{}} - res = storeRef(ctx, result) + res = storeValue(ctx, result) ok = true case goos.RefJsDateConstructor: res = goos.RefJsDate @@ -237,8 +234,8 @@ func valueNew(ctx context.Context, mod api.Module, stack goos.Stack) { // ValueLength implements js.valueLength, which is used to load the length // property of a value, e.g. `array.length`. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L372 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L396-L397 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L372 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L395-L398 var ValueLength = goos.NewFunc(custom.NameSyscallValueLength, valueLength) func valueLength(ctx context.Context, _ api.Module, stack goos.Stack) { @@ -254,8 +251,8 @@ func valueLength(ctx context.Context, _ api.Module, stack goos.Stack) { // number types. Notably, http.Transport uses this in RoundTrip to coerce the // URL to a string. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L531 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L402-L405 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L531 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L401-L406 var ValuePrepareString = goos.NewFunc(custom.NameSyscallValuePrepareString, valuePrepareString) func valuePrepareString(ctx context.Context, _ api.Module, stack goos.Stack) { @@ -263,7 +260,7 @@ func valuePrepareString(ctx context.Context, _ api.Module, stack goos.Stack) { s := valueString(v) - sRef := storeRef(ctx, s) + sRef := storeValue(ctx, s) sLen := uint32(len(s)) stack.SetResultRef(0, sRef) @@ -273,9 +270,8 @@ func valuePrepareString(ctx context.Context, _ api.Module, stack goos.Stack) { // ValueLoadString implements js.valueLoadString, which is used copy a string // value for `o.String()`. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L533 -// -// https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L410-L412 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L533 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L409-L413 var ValueLoadString = goos.NewFunc(custom.NameSyscallValueLoadString, valueLoadString) func valueLoadString(ctx context.Context, mod api.Module, stack goos.Stack) { @@ -288,7 +284,7 @@ func valueLoadString(ctx context.Context, mod api.Module, stack goos.Stack) { // ValueInstanceOf is stubbed as it isn't used in Go's main source tree. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L543 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L543 var ValueInstanceOf = goarch.StubFunction(custom.NameSyscallValueInstanceOf) // CopyBytesToGo copies a JavaScript managed byte array to linear memory. @@ -299,8 +295,8 @@ var ValueInstanceOf = goarch.StubFunction(custom.NameSyscallValueInstanceOf) // - n is the count of bytes written. // - ok is false if the src was not a uint8Array. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L569 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L424-L433 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L569 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L437-L449 var CopyBytesToGo = goos.NewFunc(custom.NameSyscallCopyBytesToGo, copyBytesToGo) func copyBytesToGo(ctx context.Context, mod api.Module, stack goos.Stack) { @@ -327,8 +323,8 @@ func copyBytesToGo(ctx context.Context, mod api.Module, stack goos.Stack) { // - n is the count of bytes written. // - ok is false if the dst was not a uint8Array. // -// See https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go#L583 -// and https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js#L438-L448 +// See https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go#L583 +// and https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js#L438-L448 var CopyBytesToJS = goos.NewFunc(custom.NameSyscallCopyBytesToJS, copyBytesToJS) func copyBytesToJS(ctx context.Context, mod api.Module, stack goos.Stack) { @@ -349,61 +345,6 @@ func copyBytesToJS(ctx context.Context, mod api.Module, stack goos.Stack) { stack.SetResultBool(1, ok) } -// syscallErr is a (GOARCH=wasm) error, which must match a key in mapJSError. -// -// See https://github.com/golang/go/blob/go1.19/src/syscall/tables_js.go#L371-L494 -type syscallErr struct { - s string -} - -// Error implements error. -func (e *syscallErr) Error() string { - return e.s -} - -// While usually I/O returns the correct errors, being explicit helps reduce -// chance of problems. -var ( - ebadf = &syscallErr{"EBADF"} - einval = &syscallErr{"EBADF"} - eisdir = &syscallErr{"EISDIR"} - eexist = &syscallErr{"EEXIST"} - enoent = &syscallErr{"ENOENT"} - enosys = &syscallErr{"ENOSYS"} - enotdir = &syscallErr{"ENOTDIR"} - enotempty = &syscallErr{"ENOTEMPTY"} -) - -// mapJSError maps I/O errors as the message must be the code, ex. "EINVAL", -// not the message, ex. "invalid argument". -func mapJSError(err error) *syscallErr { - if e, ok := err.(*syscallErr); ok { - return e - } - switch { - case errors.Is(err, syscall.EBADF), errors.Is(err, fs.ErrClosed): - return ebadf - case errors.Is(err, syscall.EINVAL), errors.Is(err, fs.ErrInvalid): - return einval - case errors.Is(err, syscall.EISDIR): - return eisdir - case errors.Is(err, syscall.ENOTEMPTY): - return enotempty - case errors.Is(err, syscall.EEXIST), errors.Is(err, fs.ErrExist): - return eexist - case errors.Is(err, syscall.ENOENT), errors.Is(err, fs.ErrNotExist): - return enoent - case errors.Is(err, syscall.ENOSYS): - return enosys - case errors.Is(err, syscall.ENOTDIR): - return enotdir - default: - // panic so we can map the error before reaching JavaScript, which - // can't see the error message as it just prints "object". - panic(fmt.Errorf("unmapped error: %v", err)) - } -} - // funcWrapper is the result of go's js.FuncOf ("_makeFuncWrapper" here). // // This ID is managed on the Go side an increments (possibly rolling over). diff --git a/internal/integration_test/vs/bench.go b/internal/integration_test/vs/bench.go index 66d2a662..767f19de 100644 --- a/internal/integration_test/vs/bench.go +++ b/internal/integration_test/vs/bench.go @@ -66,7 +66,7 @@ func runCallBenchmark(rt Runtime, rtCfg *RuntimeConfig, call func(Module, int) e result := testing.Benchmark(func(b *testing.B) { benchmarkCall(b, rt, rtCfg, call) }) - // https://github.com/golang/go/blob/fd09e88722e0af150bf8960e95e8da500ad91001/src/testing/benchmark.go#L428-L432 + // https://github.com/golang/go/blob/go1.20/src/testing/benchmark.go#L428-L432 nsOp := float64(result.T.Nanoseconds()) / float64(result.N) return nsOp } diff --git a/internal/leb128/leb128.go b/internal/leb128/leb128.go index 52eccfe4..a3105172 100644 --- a/internal/leb128/leb128.go +++ b/internal/leb128/leb128.go @@ -109,7 +109,7 @@ func LoadUint32(buf []byte) (ret uint32, bytesRead uint64, err error) { } func decodeUint32(next nextByte) (ret uint32, bytesRead uint64, err error) { - // Derived from https://github.com/golang/go/blob/aafad20b617ee63d58fcd4f6e0d98fe27760678c/src/encoding/binary/varint.go + // Derived from https://github.com/golang/go/blob/go1.20/src/encoding/binary/varint.go // with the modification on the overflow handling tailored for 32-bits. var s uint32 for i := 0; i < maxVarintLen32; i++ { @@ -136,7 +136,7 @@ func LoadUint64(buf []byte) (ret uint64, bytesRead uint64, err error) { return 0, 0, io.EOF } - // Derived from https://github.com/golang/go/blob/aafad20b617ee63d58fcd4f6e0d98fe27760678c/src/encoding/binary/varint.go + // Derived from https://github.com/golang/go/blob/go1.20/src/encoding/binary/varint.go var s uint64 for i := 0; i < maxVarintLen64; i++ { if i >= bufLen { diff --git a/internal/platform/open_file_windows.go b/internal/platform/open_file_windows.go index 2c17e35d..f19ef9da 100644 --- a/internal/platform/open_file_windows.go +++ b/internal/platform/open_file_windows.go @@ -37,7 +37,7 @@ func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) { } // The following is lifted from syscall_windows.go to add support for setting FILE_SHARE_DELETE. -// https://github.com/golang/go/blob/go1.19.5/src/syscall/syscall_windows.go#L307-L375 +// https://github.com/golang/go/blob/go1.20/src/syscall/syscall_windows.go#L308-L379 func open(path string, mode int, perm uint32) (fd syscall.Handle, err error) { mode &= ^(O_DIRECTORY | O_NOFOLLOW) // erase placeholders if len(path) == 0 { diff --git a/internal/platform/stat_windows.go b/internal/platform/stat_windows.go index 115935d8..a829b621 100644 --- a/internal/platform/stat_windows.go +++ b/internal/platform/stat_windows.go @@ -45,7 +45,7 @@ func stat(f fs.File, t os.FileInfo) (atimeNsec, mtimeNsec, ctimeNsec int64, nlin if err = syscall.GetFileInformationByHandle(handle, &info); err != nil { // If the file descriptor is already closed, we have to re-open just like // os.Stat does to allow the results on the closed files. - // https://github.com/golang/go/blob/go1.19.5/src/os/stat_windows.go#L93-L101 + // https://github.com/golang/go/blob/go1.20/src/os/stat_windows.go#L86 // // TODO: once we have our File/Sta type, this shouldn't be necessary. // But for now, ignore the error to pass the std library test for bad file descriptor. diff --git a/internal/sys/fs.go b/internal/sys/fs.go index de0f0136..e2e6652b 100644 --- a/internal/sys/fs.go +++ b/internal/sys/fs.go @@ -367,7 +367,7 @@ func (c *FSContext) ChangeOpenFlag(fd uint32, flag int) error { // syscall package, the feasibility of doing so really depends on the platform. For examples: // // * This appendMode (bool) cannot be changed later. - // https://github.com/golang/go/blob/2da8a55584aa65ce1b67431bb8ecebf66229d462/src/os/file_unix.go#L60 + // https://github.com/golang/go/blob/go1.20/src/os/file_unix.go#L60 // * On Windows, re-opening it is the only way to emulate the behavior. // https://github.com/bytecodealliance/system-interface/blob/62b97f9776b86235f318c3a6e308395a1187439b/src/fs/fd_flags.rs#L196 // diff --git a/site/content/languages/go.md b/site/content/languages/go.md index a2828060..023dc8b2 100644 --- a/site/content/languages/go.md +++ b/site/content/languages/go.md @@ -348,25 +348,25 @@ through any file system calls without filtering. Specifically, `globalThis.fs = require("fs")` allows code compiled to wasm any file access the host operating system's underlying controls permit. -[1]: https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js -[2]: https://github.com/golang/go/blob/go1.19/src/cmd/link/internal/wasm/asm.go +[1]: https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec.js +[2]: https://github.com/golang/go/blob/go1.20/src/cmd/link/internal/wasm/asm.go [3]: https://github.com/WebAssembly/wabt -[4]: https://github.com/golang/proposal/blob/master/design/42372-wasmexport.md -[5]: https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go -[6]: https://github.com/golang/go/blob/go1.19/src/cmd/internal/obj/wasm/wasmobj.go#L794-L812 -[7]: https://github.com/golang/go/blob/go1.19/src/runtime/rt0_js_wasm.s#L17-L21 -[8]: https://github.com/golang/go/blob/go1.19/src/syscall/syscall_js.go#L292-L306 -[9]: https://github.com/golang/go/blob/go1.19/src/syscall/js/func.go#L41-L44 -[10]: https://github.com/golang/go/blob/go1.19/src/runtime/internal/atomic/atomic_wasm.go#L5-L6 +[4]: https://github.com/golang/proposal/blob/6130999a9134112b156deb52da81a3cf219a6509/design/42372-wasmexport.md +[5]: https://github.com/golang/go/blob/go1.20/src/syscall/js/js.go +[6]: https://github.com/golang/go/blob/go1.20/src/cmd/internal/obj/wasm/wasmobj.go#L796-L810 +[7]: https://github.com/golang/go/blob/go1.20/src/runtime/rt0_js_wasm.s#L17-L21 +[8]: https://github.com/golang/go/blob/go1.20/src/syscall/syscall_js.go#L292-L306 +[9]: https://github.com/golang/go/blob/go1.20/src/syscall/js/func.go#L41-L51 +[10]: https://github.com/golang/go/blob/go1.20/src/runtime/internal/atomic/atomic_wasm.go#L5-L6 [11]: https://github.com/WebAssembly/proposals -[12]: https://github.com/golang/go/blob/go1.19/src/cmd/link/internal/ld/data.go#L2457 -[13]: https://github.com/golang/go/blob/go1.19/src/syscall/tables_js.go#L371-L494 +[12]: https://github.com/golang/go/blob/go1.20/src/cmd/link/internal/ld/data.go#L2457 +[13]: https://github.com/golang/go/blob/go1.20/src/syscall/tables_js.go#L371-L494 [14]: https://github.com/tetratelabs/wazero/tree/main/imports/go/example [15]: https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/ -[16]: https://github.com/golang/go/blob/go1.19/src/internal/buildcfg/cfg.go#L133-L147 +[16]: https://github.com/golang/go/blob/go1.20/src/internal/buildcfg/cfg.go#L136-L150 [17]: https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/nontrapping-float-to-int-conversion/Overview.md [18]: https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/sign-extension-ops/Overview.md [19]: https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/ -[20]: https://github.com/golang/go/blob/go1.19/CONTRIBUTING.md -[21]: https://github.com/golang/go/blob/go1.19/src/os/os_test.go#L110-L116 -[22]: https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec_node.js +[20]: https://github.com/golang/go/blob/go1.20/CONTRIBUTING.md +[21]: https://github.com/golang/go/blob/go1.20/src/os/os_test.go#L109-L116 +[22]: https://github.com/golang/go/blob/go1.20/misc/wasm/wasm_exec_node.js