gojs: backfills errno tests and updates links (#1110)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-02-09 12:38:22 -05:00
committed by GitHub
parent addd312dda
commit f18bb221c4
17 changed files with 338 additions and 144 deletions

114
internal/gojs/errno.go Normal file
View File

@@ -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
}
}

139
internal/gojs/errno_test.go Normal file
View File

@@ -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)
})
}
}

View File

@@ -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 {

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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).