gojs: stubs all remaining filesystem calls to ENOSYS (#1001)

This stubs all remaining syscalls for `GOARCH=wasm GOOS=js` to return
ENOSYS, instead of panic'ing. This allows us to see the parameters it
receives.

For example:
```
==> go.syscall/js.valueCall(fs.truncate(path=/tmp/_Go_TestTruncate135754730,length=0))
<== (err=function not implemented,ok=false)
```

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2023-01-03 15:54:29 +08:00
committed by GitHub
parent ef3937ffed
commit 65c7d9dd1b
11 changed files with 417 additions and 205 deletions

View File

@@ -252,7 +252,7 @@ func TestRun(t *testing.T) {
wazeroOpts: []string{"--hostlogging=filesystem", fmt.Sprintf("--mount=%s:/", filepath.Dir(bearPath))},
wasmArgs: []string{"/bear.txt"},
stdOut: "pooh\n",
stdErr: fmt.Sprintf(`==> go.syscall/js.valueCall(fs.open(name=/bear.txt,flags=,perm=----------))
stdErr: fmt.Sprintf(`==> go.syscall/js.valueCall(fs.open(path=/bear.txt,flags=,perm=----------))
<== (err=<nil>,fd=4)
==> go.syscall/js.valueCall(fs.fstat(fd=4))
<== (err=<nil>,stat={isDir=false,mode=%[1]s,size=5,mtimeMs=%[2]d})

View File

@@ -25,7 +25,7 @@ func newJsGlobal(rt http.RoundTripper) *jsVal {
"fs": jsfs,
"Date": jsDateConstructor,
}).
addFunction("fetch", &fetch{})
addFunction("fetch", httpFetch{})
}
var (
@@ -48,13 +48,13 @@ var (
"pid": float64(1), // Get("pid").Int() in syscall_js.go for syscall.Getpid
"ppid": goos.RefValueZero, // Get("ppid").Int() in syscall_js.go for syscall.Getppid
}).
addFunction("cwd", &cwd{}). // syscall.Cwd in fs_js.go
addFunction("chdir", &chdir{}). // syscall.Chdir in fs_js.go
addFunction("getuid", &returnZero{}). // syscall.Getuid in syscall_js.go
addFunction("getgid", &returnZero{}). // syscall.Getgid in syscall_js.go
addFunction("geteuid", &returnZero{}). // syscall.Geteuid in syscall_js.go
addFunction("getgroups", &returnSliceOfZero{}). // syscall.Getgroups in syscall_js.go
addFunction("umask", &returnArg0{}) // syscall.Umask in syscall_js.go
addFunction("cwd", processCwd{}). // syscall.Cwd in fs_js.go
addFunction("chdir", processChdir{}). // syscall.Chdir in fs_js.go
addFunction("getuid", returnZero{}). // syscall.Getuid in syscall_js.go
addFunction("getgid", returnZero{}). // syscall.Getgid in syscall_js.go
addFunction("geteuid", returnZero{}). // syscall.Geteuid in syscall_js.go
addFunction("getgroups", returnSliceOfZero{}). // syscall.Getgroups in syscall_js.go
addFunction("umask", returnArg0{}) // syscall.Umask in syscall_js.go
// uint8ArrayConstructor = js.Global().Get("Uint8Array")
// // fs_js.go, rand_js.go, roundtrip_js.go init

View File

@@ -16,12 +16,12 @@ import (
//
// This is defined as `Get("crypto")` in rand_js.go init
var jsCrypto = newJsVal(goos.RefJsCrypto, "crypto").
addFunction("getRandomValues", &getRandomValues{})
addFunction("getRandomValues", cryptoGetRandomValues{})
type getRandomValues struct{}
// cryptoGetRandomValues implements jsFn
type cryptoGetRandomValues struct{}
// invoke implements jsFn.invoke
func (*getRandomValues) invoke(_ context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
func (cryptoGetRandomValues) invoke(_ context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
randSource := mod.(*wasm.CallContext).Sys.RandSource()
r := args[0].(*byteArray)

View File

@@ -3,20 +3,31 @@ package custom
const (
NameCallback = "callback"
NameFs = "fs"
NameFsOpen = "open"
NameFsStat = "stat"
NameFsFstat = "fstat"
NameFsLstat = "lstat"
NameFsClose = "close"
NameFsWrite = "write"
NameFsRead = "read"
NameFsReaddir = "readdir"
NameFsMkdir = "mkdir"
NameFsRmdir = "rmdir"
NameFsRename = "rename"
NameFsUnlink = "unlink"
NameFsUtimes = "utimes"
NameFs = "fs"
NameFsOpen = "open"
NameFsStat = "stat"
NameFsFstat = "fstat"
NameFsLstat = "lstat"
NameFsClose = "close"
NameFsWrite = "write"
NameFsRead = "read"
NameFsReaddir = "readdir"
NameFsMkdir = "mkdir"
NameFsRmdir = "rmdir"
NameFsRename = "rename"
NameFsUnlink = "unlink"
NameFsUtimes = "utimes"
NameFsChmod = "chmod"
NameFsFchmod = "fchmod"
NameFsChown = "chown"
NameFsFchown = "fchown"
NameFsLchown = "lchown"
NameFsTruncate = "truncate"
NameFsFtruncate = "ftruncate"
NameFsReadlink = "readlink"
NameFsLink = "link"
NameFsSymlink = "symlink"
NameFsFsync = "fsync"
)
// FsNameSection are the functions defined in the object named NameFs. Results
@@ -25,12 +36,12 @@ const (
var FsNameSection = map[string]*Names{
NameFsOpen: {
Name: NameFsOpen,
ParamNames: []string{"name", "flags", "perm", NameCallback},
ParamNames: []string{"path", "flags", "perm", NameCallback},
ResultNames: []string{"err", "fd"},
},
NameFsStat: {
Name: NameFsStat,
ParamNames: []string{"name", NameCallback},
ParamNames: []string{"path", NameCallback},
ResultNames: []string{"err", "stat"},
},
NameFsFstat: {
@@ -40,7 +51,7 @@ var FsNameSection = map[string]*Names{
},
NameFsLstat: {
Name: NameFsLstat,
ParamNames: []string{"name", NameCallback},
ParamNames: []string{"path", NameCallback},
ResultNames: []string{"err", "stat"},
},
NameFsClose: {
@@ -60,7 +71,7 @@ var FsNameSection = map[string]*Names{
},
NameFsReaddir: {
Name: NameFsReaddir,
ParamNames: []string{"name", NameCallback},
ParamNames: []string{"path", NameCallback},
ResultNames: []string{"err", "dirents"},
},
NameFsMkdir: {
@@ -88,4 +99,59 @@ var FsNameSection = map[string]*Names{
ParamNames: []string{"path", "atime", "mtime", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsChmod: {
Name: NameFsChmod,
ParamNames: []string{"path", "mode", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsFchmod: {
Name: NameFsFchmod,
ParamNames: []string{"fd", "mode", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsChown: {
Name: NameFsChown,
ParamNames: []string{"path", "uid", "gid", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsFchown: {
Name: NameFsFchown,
ParamNames: []string{"fd", "uid", "gid", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsLchown: {
Name: NameFsLchown,
ParamNames: []string{"path", "uid", "gid", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsTruncate: {
Name: NameFsTruncate,
ParamNames: []string{"path", "length", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsFtruncate: {
Name: NameFsFtruncate,
ParamNames: []string{"fd", "length", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsReadlink: {
Name: NameFsReadlink,
ParamNames: []string{"path", NameCallback},
ResultNames: []string{"err", "dst"},
},
NameFsLink: {
Name: NameFsLink,
ParamNames: []string{"path", "link", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsSymlink: {
Name: NameFsSymlink,
ParamNames: []string{"path", "link", NameCallback},
ResultNames: []string{"err", "ok"},
},
NameFsFsync: {
Name: NameFsFsync,
ParamNames: []string{"fd", NameCallback},
ResultNames: []string{"err", "ok"},
},
}

View File

@@ -26,32 +26,30 @@ var (
addProperties(map[string]interface{}{
"constants": jsfsConstants, // = jsfs.Get("constants") // init
}).
addFunction(custom.NameFsOpen, &jsfsOpen{}).
addFunction(custom.NameFsStat, &jsfsStat{}).
addFunction(custom.NameFsFstat, &jsfsFstat{}).
addFunction(custom.NameFsLstat, &jsfsStat{}). // because fs.FS doesn't support symlink
addFunction(custom.NameFsClose, &jsfsClose{}).
addFunction(custom.NameFsRead, &jsfsRead{}).
addFunction(custom.NameFsWrite, &jsfsWrite{}).
addFunction(custom.NameFsReaddir, &jsfsReaddir{}).
addFunction(custom.NameFsMkdir, &jsfsMkdir{}).
addFunction(custom.NameFsRmdir, &jsfsRmdir{}).
addFunction(custom.NameFsRename, &jsfsRename{}).
addFunction(custom.NameFsUnlink, &jsfsUnlink{}).
addFunction(custom.NameFsUtimes, &jsfsUtimes{})
// TODO: stub all these with syscall.ENOSYS
// * _, err := fsCall("chmod", path, mode) // syscall.Chmod
// * _, err := fsCall("fchmod", fd, mode) // syscall.Fchmod
// * _, err := fsCall("chown", path, uint32(uid), uint32(gid)) // syscall.Chown
// * _, err := fsCall("fchown", fd, uint32(uid), uint32(gid)) // syscall.Fchown
// * _, err := fsCall("lchown", path, uint32(uid), uint32(gid)) // syscall.Lchown
// * _, err := fsCall("truncate", path, length) // syscall.Truncate
// * _, err := fsCall("ftruncate", fd, length) // syscall.Ftruncate
// * dst, err := fsCall("readlink", path) // syscall.Readlink
// * _, err := fsCall("link", path, link) // syscall.Link
// * _, err := fsCall("symlink", path, link) // syscall.Symlink
// * _, err := fsCall("fsync", fd) // syscall.Fsync
addFunction(custom.NameFsOpen, jsfsOpen{}).
addFunction(custom.NameFsStat, jsfsStat{}).
addFunction(custom.NameFsFstat, jsfsFstat{}).
addFunction(custom.NameFsLstat, jsfsLstat{}).
addFunction(custom.NameFsClose, jsfsClose{}).
addFunction(custom.NameFsRead, jsfsRead{}).
addFunction(custom.NameFsWrite, jsfsWrite{}).
addFunction(custom.NameFsReaddir, jsfsReaddir{}).
addFunction(custom.NameFsMkdir, jsfsMkdir{}).
addFunction(custom.NameFsRmdir, jsfsRmdir{}).
addFunction(custom.NameFsRename, jsfsRename{}).
addFunction(custom.NameFsUnlink, jsfsUnlink{}).
addFunction(custom.NameFsUtimes, jsfsUtimes{}).
addFunction(custom.NameFsChmod, jsfsChmod{}).
addFunction(custom.NameFsFchmod, jsfsFchmod{}).
addFunction(custom.NameFsChown, jsfsChown{}).
addFunction(custom.NameFsFchown, jsfsFchown{}).
addFunction(custom.NameFsLchown, jsfsLchown{}).
addFunction(custom.NameFsTruncate, jsfsTruncate{}).
addFunction(custom.NameFsFtruncate, jsfsFtruncate{}).
addFunction(custom.NameFsReadlink, jsfsReadlink{}).
addFunction(custom.NameFsLink, jsfsLink{}).
addFunction(custom.NameFsSymlink, jsfsSymlink{}).
addFunction(custom.NameFsFsync, jsfsFsync{})
// jsfsConstants = jsfs Get("constants") // fs_js.go init
jsfsConstants = newJsVal(goos.RefJsfsConstants, "constants").
@@ -83,33 +81,33 @@ var (
oEXCL = float64(os.O_EXCL)
)
// jsfsOpen implements fs.Open
// jsfsOpen implements implements jsFn for syscall.Open
//
// jsFD /* Int */, err := fsCall("open", path, flags, perm)
type jsfsOpen struct{}
// invoke implements jsFn.invoke
func (*jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
name := args[0].(string)
func (jsfsOpen) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
flags := toUint64(args[1]) // flags are derived from constants like oWRONLY
perm := toUint32(args[2])
callback := args[3].(funcWrapper)
fd, err := syscallOpen(mod, name, flags, perm)
fsc := mod.(*wasm.CallContext).Sys.FS()
fd, err := fsc.OpenFile(path, int(flags), fs.FileMode(perm))
return callback.invoke(ctx, mod, goos.RefJsfs, err, fd) // note: error first
}
// jsfsStat is used for syscall.Stat
// jsfsStat implements jsFn for syscall.Stat
//
// jsSt, err := fsCall("stat", path)
type jsfsStat struct{}
// invoke implements jsFn.invoke
func (*jsfsStat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
name := args[0].(string)
func (jsfsStat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
callback := args[1].(funcWrapper)
stat, err := syscallStat(mod, name)
stat, err := syscallStat(mod, path)
return callback.invoke(ctx, mod, goos.RefJsfs, err, stat) // note: error first
}
@@ -124,13 +122,26 @@ func syscallStat(mod api.Module, name string) (*jsSt, error) {
}
}
// jsfsStat is used for syscall.Open
// jsfsLstat implements jsFn for syscall.Lstat
//
// jsSt, err := fsCall("lstat", path)
type jsfsLstat struct{}
func (jsfsLstat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
callback := args[1].(funcWrapper)
lstat, err := syscallStat(mod, path) // TODO switch to lstat syscall
return callback.invoke(ctx, mod, goos.RefJsfs, err, lstat) // note: error first
}
// jsfsFstat implements jsFn for syscall.Open
//
// stat, err := fsCall("fstat", fd); err == nil && stat.Call("isDirectory").Bool()
type jsfsFstat struct{}
// invoke implements jsFn.invoke
func (*jsfsFstat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
func (jsfsFstat) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
fd := toUint32(args[0])
@@ -210,38 +221,30 @@ func getJsMode(mode fs.FileMode) (jsMode uint32) {
return
}
// jsfsClose is used for syscall.Close
//
// _, err := fsCall("close", fd)
// jsfsClose implements jsFn for syscall.Close
type jsfsClose struct{}
// invoke implements jsFn.invoke
func (*jsfsClose) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
func (jsfsClose) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
fd := toUint32(args[0])
callback := args[1].(funcWrapper)
err := syscallClose(fsc, fd)
return callback.invoke(ctx, mod, goos.RefJsfs, err, true) // note: error first
}
// syscallClose is like syscall.Close
func syscallClose(fsc *internalsys.FSContext, fd uint32) (err error) {
var err error
if ok := fsc.CloseFile(fd); !ok {
err = syscall.EBADF // already closed
}
return
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsRead is used in syscall.Read and syscall.Pread, called by
// jsfsRead implements jsFn for syscall.Read and syscall.Pread, called by
// src/internal/poll/fd_unix.go poll.Read.
//
// n, err := fsCall("read", fd, buf, 0, len(b), nil)
type jsfsRead struct{}
// invoke implements jsFn.invoke
func (*jsfsRead) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
func (jsfsRead) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fd := toUint32(args[0])
buf, ok := args[1].(*byteArray)
if !ok {
@@ -285,15 +288,14 @@ func syscallRead(mod api.Module, fd uint32, offset interface{}, p []byte) (n uin
return
}
// jsfsWrite is used in syscall.Write and syscall.Pwrite.
// jsfsWrite implements jsFn for syscall.Write and syscall.Pwrite.
//
// Notably, offset is non-nil in Pwrite.
//
// n, err := fsCall("write", fd, buf, 0, len(b), nil)
type jsfsWrite struct{}
// invoke implements jsFn.invoke
func (*jsfsWrite) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
func (jsfsWrite) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fd := toUint32(args[0])
buf, ok := args[1].(*byteArray)
if !ok {
@@ -327,18 +329,17 @@ func syscallWrite(mod api.Module, fd uint32, offset interface{}, p []byte) (n ui
return
}
// jsfsReaddir is used in syscall.Open
// jsfsReaddir implements jsFn for syscall.Open
//
// dir, err := fsCall("readdir", path)
// dir.Length(), dir.Index(i).String()
type jsfsReaddir struct{}
// invoke implements jsFn.invoke
func (*jsfsReaddir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
name := args[0].(string)
func (jsfsReaddir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
callback := args[1].(funcWrapper)
stat, err := syscallReaddir(ctx, mod, name)
stat, err := syscallReaddir(ctx, mod, path)
return callback.invoke(ctx, mod, goos.RefJsfs, err, stat) // note: error first
}
@@ -366,40 +367,38 @@ func syscallReaddir(_ context.Context, mod api.Module, name string) (*objectArra
}
}
// returnZero implements jsFn
type returnZero struct{}
// invoke implements jsFn.invoke
func (*returnZero) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
func (returnZero) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
return goos.RefValueZero, nil
}
// returnSliceOfZero implements jsFn
type returnSliceOfZero struct{}
// invoke implements jsFn.invoke
func (*returnSliceOfZero) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
func (returnSliceOfZero) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
return &objectArray{slice: []interface{}{goos.RefValueZero}}, nil
}
// returnArg0 implements jsFn
type returnArg0 struct{}
// invoke implements jsFn.invoke
func (*returnArg0) invoke(_ context.Context, _ api.Module, args ...interface{}) (interface{}, error) {
func (returnArg0) invoke(_ context.Context, _ api.Module, args ...interface{}) (interface{}, error) {
return args[0], nil
}
// cwd for fs.Open syscall.Getcwd in fs_js.go
type cwd struct{}
// processCwd implements jsFn for fs.Open syscall.Getcwd in fs_js.go
type processCwd struct{}
// invoke implements jsFn.invoke
func (*cwd) invoke(ctx context.Context, _ api.Module, _ ...interface{}) (interface{}, error) {
func (processCwd) invoke(ctx context.Context, _ api.Module, _ ...interface{}) (interface{}, error) {
return getState(ctx).cwd, nil
}
// chdir for fs.Open syscall.Chdir in fs_js.go
type chdir struct{}
// processChdir implements jsFn for fs.Open syscall.Chdir in fs_js.go
type processChdir struct{}
// invoke implements jsFn.invoke
func (*chdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
func (processChdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
path := args[0].(string)
@@ -421,112 +420,261 @@ func (*chdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (
}
}
// jsfsMkdir implements fs.Mkdir
// jsfsMkdir implements implements jsFn for fs.Mkdir
//
// jsFD /* Int */, err := fsCall("mkdir", path, perm)
type jsfsMkdir struct{}
// invoke implements jsFn.invoke
func (*jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
name := args[0].(string)
func (jsfsMkdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
perm := toUint32(args[1])
callback := args[2].(funcWrapper)
fd, err := syscallMkdir(mod, name, perm)
fsc := mod.(*wasm.CallContext).Sys.FS()
fd, err := fsc.Mkdir(path, fs.FileMode(perm))
return callback.invoke(ctx, mod, goos.RefJsfs, err, fd) // note: error first
}
// syscallMkdir is like syscall.Mkdir
func syscallMkdir(mod api.Module, name string, perm uint32) (uint32, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
return fsc.Mkdir(name, fs.FileMode(perm))
}
// jsfsRmdir implements the following
// jsfsRmdir implements jsFn for the following
//
// _, err := fsCall("rmdir", path) // syscall.Rmdir
type jsfsRmdir struct{}
// invoke implements jsFn.invoke
func (*jsfsRmdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
name := args[0].(string)
func (jsfsRmdir) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
callback := args[1].(funcWrapper)
ok, err := syscallRmdir(mod, name)
return callback.invoke(ctx, mod, goos.RefJsfs, err, ok) // note: error first
}
// syscallRmdir is like syscall.Rmdir
func syscallRmdir(mod api.Module, name string) (interface{}, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
err := fsc.Rmdir(name)
return err != nil, err
err := fsc.Rmdir(path)
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsRename implements the following
// jsfsRename implements jsFn for the following
//
// - _, err := fsCall("rename", from, to) // syscall.Rename
// _, err := fsCall("rename", from, to) // syscall.Rename
type jsfsRename struct{}
// invoke implements jsFn.invoke
func (*jsfsRename) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
func (jsfsRename) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
from := args[0].(string)
to := args[1].(string)
callback := args[2].(funcWrapper)
ok, err := syscallRename(mod, from, to)
return callback.invoke(ctx, mod, goos.RefJsfs, err, ok) // note: error first
}
// syscallRename is like syscall.Rename
func syscallRename(mod api.Module, from, to string) (interface{}, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
err := fsc.Rename(from, to)
return err != nil, err
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsUnlink implements the following
// jsfsUnlink implements jsFn for the following
//
// _, err := fsCall("unlink", path) // syscall.Unlink
type jsfsUnlink struct{}
// invoke implements jsFn.invoke
func (*jsfsUnlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
name := args[0].(string)
func (jsfsUnlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
callback := args[1].(funcWrapper)
ok, err := syscallUnlink(mod, name)
return callback.invoke(ctx, mod, goos.RefJsfs, err, ok) // note: error first
}
// syscallUnlink is like syscall.Unlink
func syscallUnlink(mod api.Module, name string) (interface{}, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
err := fsc.Unlink(name)
return err != nil, err
err := fsc.Unlink(path)
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsUtimes implements the following
// jsfsUtimes implements jsFn for the following
//
// _, err := fsCall("utimes", path, atime, mtime) // syscall.UtimesNano
type jsfsUtimes struct{}
// invoke implements jsFn.invoke
func (*jsfsUtimes) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
name := args[0].(string)
func (jsfsUtimes) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
atimeSec := toInt64(args[1])
mtimeSec := toInt64(args[2])
callback := args[3].(funcWrapper)
ok, err := syscallUtimes(mod, name, atimeSec, mtimeSec)
return callback.invoke(ctx, mod, goos.RefJsfs, err, ok) // note: error first
fsc := mod.(*wasm.CallContext).Sys.FS()
err := fsc.Utimes(path, atimeSec*1e9, mtimeSec*1e9)
return jsfsInvoke(ctx, mod, callback, err)
}
// syscallUtimes is like syscall.Utimes
func syscallUtimes(mod api.Module, name string, atimeSec, mtimeSec int64) (interface{}, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
err := fsc.Utimes(name, atimeSec*1e9, mtimeSec*1e9)
return err != nil, err
// jsfsChmod implements jsFn for the following
//
// _, err := fsCall("chmod", path, mode) // syscall.Chmod
type jsfsChmod struct{}
func (jsfsChmod) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
mode := toUint32(args[1])
callback := args[2].(funcWrapper)
_, _ = path, mode // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsFchmod implements jsFn for the following
//
// _, err := fsCall("fchmod", fd, mode) // syscall.Fchmod
type jsfsFchmod struct{}
func (jsfsFchmod) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fd := toUint32(args[0])
mode := toUint32(args[1])
callback := args[2].(funcWrapper)
_, _ = fd, mode // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsChown implements jsFn for the following
//
// _, err := fsCall("chown", path, uint32(uid), uint32(gid)) // syscall.Chown
type jsfsChown struct{}
func (jsfsChown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
uid := toUint32(args[1])
gid := toUint32(args[2])
callback := args[3].(funcWrapper)
_, _, _ = path, uid, gid // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsFchown implements jsFn for the following
//
// _, err := fsCall("fchown", fd, uint32(uid), uint32(gid)) // syscall.Fchown
type jsfsFchown struct{}
func (jsfsFchown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fd := toUint32(args[0])
uid := toUint32(args[1])
gid := toUint32(args[2])
callback := args[3].(funcWrapper)
_, _, _ = fd, uid, gid // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsLchown implements jsFn for the following
//
// _, err := fsCall("lchown", path, uint32(uid), uint32(gid)) // syscall.Lchown
type jsfsLchown struct{}
func (jsfsLchown) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
uid := toUint32(args[1])
gid := toUint32(args[2])
callback := args[3].(funcWrapper)
_, _, _ = path, uid, gid // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsTruncate implements jsFn for the following
//
// _, err := fsCall("truncate", path, length) // syscall.Truncate
type jsfsTruncate struct{}
func (jsfsTruncate) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
length := toInt64(args[1])
callback := args[2].(funcWrapper)
_, _ = path, length // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsFtruncate implements jsFn for the following
//
// _, err := fsCall("ftruncate", fd, length) // syscall.Ftruncate
type jsfsFtruncate struct{}
func (jsfsFtruncate) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fd := toUint32(args[0])
length := toInt64(args[1])
callback := args[2].(funcWrapper)
_, _ = fd, length // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsReadlink implements jsFn for syscall.Readlink
//
// dst, err := fsCall("readlink", path) // syscall.Readlink
type jsfsReadlink struct{}
func (jsfsReadlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
callback := args[1].(funcWrapper)
_ = path // TODO
var dst string
var err error = syscall.ENOSYS
return callback.invoke(ctx, mod, goos.RefJsfs, err, dst) // note: error first
}
// jsfsLink implements jsFn for the following
//
// _, err := fsCall("link", path, link) // syscall.Link
type jsfsLink struct{}
func (jsfsLink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
link := args[1].(string)
callback := args[2].(funcWrapper)
_, _ = path, link // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsSymlink implements jsFn for the following
//
// _, err := fsCall("symlink", path, link) // syscall.Symlink
type jsfsSymlink struct{}
func (jsfsSymlink) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
path := args[0].(string)
link := args[1].(string)
callback := args[2].(funcWrapper)
_, _ = path, link // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsfsFsync implements jsFn for the following
//
// _, err := fsCall("fsync", fd) // syscall.Fsync
type jsfsFsync struct{}
func (jsfsFsync) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
fd := toUint32(args[0])
callback := args[1].(funcWrapper)
_ = fd // TODO
var err error = syscall.ENOSYS
return jsfsInvoke(ctx, mod, callback, err)
}
// jsSt is pre-parsed from fs_js.go setStat to avoid thrashing
@@ -592,3 +740,7 @@ func (s *jsSt) call(_ context.Context, _ api.Module, _ goos.Ref, method string,
}
panic(fmt.Sprintf("TODO: stat.%s", method))
}
func jsfsInvoke(ctx context.Context, mod api.Module, callback funcWrapper, err error) (interface{}, error) {
return callback.invoke(ctx, mod, goos.RefJsfs, err, err == nil) // note: error first
}

View File

@@ -24,7 +24,7 @@ func getRoundTripper(ctx context.Context) http.RoundTripper {
return nil
}
// fetch is used to implement http.RoundTripper
// httpFetch implements jsFn for http.RoundTripper
//
// Reference in roundtrip_js.go init
//
@@ -33,10 +33,9 @@ func getRoundTripper(ctx context.Context) http.RoundTripper {
// In http.Transport RoundTrip, this returns a promise
//
// fetchPromise := js.Global().Call("fetch", req.URL.String(), opt)
type fetch struct{}
type httpFetch struct{}
// invoke implements jsFn.invoke
func (*fetch) invoke(ctx context.Context, _ api.Module, args ...interface{}) (interface{}, error) {
func (httpFetch) invoke(ctx context.Context, _ api.Module, args ...interface{}) (interface{}, error) {
rt := getRoundTripper(ctx)
if rt == nil {
panic("unexpected to reach here without roundtripper as property is nil checked")

View File

@@ -9,6 +9,8 @@ import (
)
// jsFn is a jsCall.call function, configured via jsVal.addFunction.
//
// Note: This is not a `func` because we need it to be a hashable type.
type jsFn interface {
invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error)
}

View File

@@ -53,7 +53,7 @@ func syscallValueCallParamLogger(ctx context.Context, mod api.Module, w logging.
if m == custom.NameFsOpen {
w.WriteString("fs.open(") //nolint
w.WriteString("name=") //nolint
w.WriteString("path=") //nolint
w.WriteString(args[0].(string)) //nolint
w.WriteString(",flags=") //nolint
writeOFlags(w, int(args[1].(float64)))

View File

@@ -12,7 +12,6 @@ import (
"github.com/tetratelabs/wazero/internal/gojs/custom"
"github.com/tetratelabs/wazero/internal/gojs/goarch"
"github.com/tetratelabs/wazero/internal/gojs/goos"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/sys"
)
@@ -405,18 +404,12 @@ func mapJSError(err error) *syscallErr {
}
}
// syscallOpen is like syscall.Open
func syscallOpen(mod api.Module, name string, flags uint64, perm uint32) (uint32, error) {
fsc := mod.(*wasm.CallContext).Sys.FS()
return fsc.OpenFile(name, int(flags), fs.FileMode(perm))
}
// funcWrapper is the result of go's js.FuncOf ("_makeFuncWrapper" here).
//
// This ID is managed on the Go side an increments (possibly rolling over).
type funcWrapper uint32
// jsFn implements jsFn.invoke
// invoke implements jsFn
func (f funcWrapper) invoke(ctx context.Context, mod api.Module, args ...interface{}) (interface{}, error) {
e := &event{id: uint32(f), this: args[0].(goos.Ref)}

View File

@@ -16,12 +16,12 @@ var (
// jsDate is used inline in zoneinfo_js.go for time.initLocal.
// `.Call("getTimezoneOffset").Int()` returns a timezone offset.
jsDate = newJsVal(goos.RefJsDate, "jsDate").
addFunction("getTimezoneOffset", &getTimezoneOffset{})
addFunction("getTimezoneOffset", jsDateGetTimezoneOffset{})
)
type getTimezoneOffset struct{}
// jsDateGetTimezoneOffset implements jsFn
type jsDateGetTimezoneOffset struct{}
// invoke implements jsFn.invoke
func (*getTimezoneOffset) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
func (jsDateGetTimezoneOffset) invoke(context.Context, api.Module, ...interface{}) (interface{}, error) {
return uint32(0), nil // UTC
}

View File

@@ -7,7 +7,7 @@ import (
"io"
"io/fs"
"os"
"path"
pathutil "path"
"syscall"
"time"
@@ -298,11 +298,11 @@ func (c *FSContext) Mkdir(name string, perm fs.FileMode) (newFD uint32, err erro
}
// OpenFile is like syscall.Open and returns the file descriptor of the new file or an error.
func (c *FSContext) OpenFile(name string, flags int, perm fs.FileMode) (newFD uint32, err error) {
func (c *FSContext) OpenFile(path string, flags int, perm fs.FileMode) (newFD uint32, err error) {
var f fs.File
if wfs, ok := c.fs.(syscallfs.FS); ok {
name = c.cleanPath(name)
f, err = wfs.OpenFile(name, flags, perm)
path = c.cleanPath(path)
f, err = wfs.OpenFile(path, flags, perm)
} else {
// While os.Open says it is read-only, in reality the files returned
// are often writable. Fail only on the flags which won't work.
@@ -315,7 +315,7 @@ func (c *FSContext) OpenFile(name string, flags int, perm fs.FileMode) (newFD ui
return 0, syscall.ENOSYS
default:
// only time fs.FS is used
f, err = c.fs.Open(c.cleanPath(name))
f, err = c.fs.Open(c.cleanPath(path))
}
}
@@ -323,22 +323,22 @@ func (c *FSContext) OpenFile(name string, flags int, perm fs.FileMode) (newFD ui
return 0, err
}
newFD = c.openedFiles.Insert(&FileEntry{Name: path.Base(name), File: f})
newFD = c.openedFiles.Insert(&FileEntry{Name: pathutil.Base(path), File: f})
return newFD, nil
}
// Rmdir is like syscall.Rmdir.
func (c *FSContext) Rmdir(name string) (err error) {
func (c *FSContext) Rmdir(path string) (err error) {
if wfs, ok := c.fs.(syscallfs.FS); ok {
name = c.cleanPath(name)
return wfs.Rmdir(name)
path = c.cleanPath(path)
return wfs.Rmdir(path)
}
err = syscall.ENOSYS
return
}
func (c *FSContext) StatPath(name string) (fs.FileInfo, error) {
fd, err := c.OpenFile(name, os.O_RDONLY, 0)
func (c *FSContext) StatPath(path string) (fs.FileInfo, error) {
fd, err := c.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
@@ -358,35 +358,35 @@ func (c *FSContext) Rename(from, to string) (err error) {
}
// Unlink is like syscall.Unlink.
func (c *FSContext) Unlink(name string) (err error) {
func (c *FSContext) Unlink(path string) (err error) {
if wfs, ok := c.fs.(syscallfs.FS); ok {
name = c.cleanPath(name)
return wfs.Unlink(name)
path = c.cleanPath(path)
return wfs.Unlink(path)
}
err = syscall.ENOSYS
return
}
// Utimes is like syscall.Utimes.
func (c *FSContext) Utimes(name string, atimeNsec, mtimeNsec int64) (err error) {
func (c *FSContext) Utimes(path string, atimeNsec, mtimeNsec int64) (err error) {
if wfs, ok := c.fs.(syscallfs.FS); ok {
name = c.cleanPath(name)
return wfs.Utimes(name, atimeNsec, mtimeNsec)
path = c.cleanPath(path)
return wfs.Utimes(path, atimeNsec, mtimeNsec)
}
err = syscall.ENOSYS
return
}
func (c *FSContext) cleanPath(name string) string {
if len(name) == 0 {
return name
func (c *FSContext) cleanPath(path string) string {
if len(path) == 0 {
return path
}
// fs.ValidFile cannot be rooted (start with '/')
cleaned := name
if name[0] == '/' {
cleaned = name[1:]
cleaned := path
if path[0] == '/' {
cleaned = path[1:]
}
cleaned = path.Clean(cleaned) // e.g. "sub/." -> "sub"
cleaned = pathutil.Clean(cleaned) // e.g. "sub/." -> "sub"
return cleaned
}