This commit adds the js/wasm architecture to the os package.
Access to the actual file system is supported through Node.js.
Updates #18892
Change-Id: I6fa642fb294ca020b2c545649d4324d981aa0408
Reviewed-on: https://go-review.googlesource.com/109977
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
},
+ // func getRandomData(r []byte)
+ "runtime.getRandomData": (sp) => {
+ crypto.getRandomValues(loadSlice(sp + 8));
+ },
+
// func boolVal(value bool) Value
"syscall/js.boolVal": (sp) => {
storeValue(sp + 16, mem().getUint8(sp + 8) !== 0);
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build nacl
+// +build nacl js,wasm
package poll
if pd.closing {
return errClosing(isFile)
}
+ if isFile { // TODO(neelance): wasm: Use callbacks from JS to block until the read/write finished.
+ return nil
+ }
return ErrTimeout
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
package poll
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package poll
// This file implements sysSocket and accept for platforms that do not
// provide a fast path for setting SetNonblock and CloseOnExec.
-// +build darwin nacl solaris
+// +build darwin js,wasm nacl solaris
package poll
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package unix
+
+func IsNonblock(fd int) (nonblocking bool, err error) {
+ return false, nil
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os_test
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package exec
+
+import (
+ "errors"
+)
+
+// ErrNotFound is the error resulting if a path search failed to find an executable file.
+var ErrNotFound = errors.New("executable file not found in $PATH")
+
+// LookPath searches for an executable named file in the
+// directories named by the PATH environment variable.
+// If file contains a slash, it is tried directly and the PATH is not consulted.
+// The result may be an absolute path or a path relative to the current directory.
+func LookPath(file string) (string, error) {
+ // Wasm can not execute processes, so act as if there are no executables at all.
+ return "", &Error{file, ErrNotFound}
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build linux netbsd dragonfly nacl
+// +build linux netbsd dragonfly nacl js,wasm
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os
func TestSeekError(t *testing.T) {
switch runtime.GOOS {
- case "plan9", "nacl":
+ case "js", "nacl", "plan9":
t.Skipf("skipping test on %v", runtime.GOOS)
}
t.Skip("skipping on Windows; issue 19098")
case "plan9":
t.Skip("skipping on Plan 9; does not support runtime poller")
+ case "js":
+ t.Skip("skipping on js; no support for os.Pipe")
}
threads := 100
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly nacl solaris
+// +build darwin dragonfly js,wasm nacl solaris
package os
// license that can be found in the LICENSE file.
// Test broken pipes on Unix systems.
-// +build !windows,!plan9,!nacl
+// +build !windows,!plan9,!nacl,!js
package os_test
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows
package signal
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build js,wasm nacl
+
package os
import (
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd nacl netbsd openbsd
+// +build darwin dragonfly freebsd js,wasm nacl netbsd openbsd
package os
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package os
+
+// supportsCloseOnExec reports whether the platform supports the
+// O_CLOEXEC flag.
+const supportsCloseOnExec = false
// license that can be found in the LICENSE file.
// +build !nacl
+// +build !js
// +build !plan9
// +build !windows
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd !android,linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm !android,linux nacl netbsd openbsd solaris
// +build !cgo osusergo
package user
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly nacl netbsd openbsd solaris
+// +build darwin dragonfly js,wasm nacl netbsd openbsd solaris
package os
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package filepath
)
func TestWriteHeapDumpNonempty(t *testing.T) {
- if runtime.GOOS == "nacl" {
- t.Skip("WriteHeapDump is not available on NaCl.")
+ if runtime.GOOS == "nacl" || runtime.GOOS == "js" {
+ t.Skipf("WriteHeapDump is not available on %s.", runtime.GOOS)
}
f, err := ioutil.TempFile("", "heapdumptest")
if err != nil {
}
func TestWriteHeapDumpFinalizers(t *testing.T) {
- if runtime.GOOS == "nacl" {
- t.Skip("WriteHeapDump is not available on NaCl.")
+ if runtime.GOOS == "nacl" || runtime.GOOS == "js" {
+ t.Skipf("WriteHeapDump is not available on %s.", runtime.GOOS)
}
f, err := ioutil.TempFile("", "heapdumptest")
if err != nil {
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package runtime
+
+import (
+ "unsafe"
+)
+
+func exit(code int32)
+
+func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
+ if fd > 2 {
+ throw("runtime.write to fd > 2 is unsupported")
+ }
+ wasmWrite(fd, p, n)
+ return n
+}
+
+// Stubs so tests can link correctly. These should never be called.
+func open(name *byte, mode, perm int32) int32 { panic("not implemented") }
+func closefd(fd int32) int32 { panic("not implemented") }
+func read(fd int32, p unsafe.Pointer, n int32) int32 { panic("not implemented") }
+
+//go:noescape
+func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
+
+func usleep(usec uint32)
+
+func exitThread(wait *uint32)
+
+type mOS struct{}
+
+func osyield()
+
+const _SIGSEGV = 0xb
+
+func sigpanic() {
+ g := getg()
+ if !canpanic(g) {
+ throw("unexpected signal during runtime execution")
+ }
+
+ // js only invokes the exception handler for memory faults.
+ g.sig = _SIGSEGV
+ panicmem()
+}
+
+type sigset struct{}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
+func mpreinit(mp *m) {
+ mp.gsignal = malg(32 * 1024)
+ mp.gsignal.m = mp
+}
+
+//go:nosplit
+func msigsave(mp *m) {
+}
+
+//go:nosplit
+func msigrestore(sigmask sigset) {
+}
+
+//go:nosplit
+//go:nowritebarrierrec
+func clearSignalHandlers() {
+}
+
+//go:nosplit
+func sigblock() {
+}
+
+// Called to initialize a new m (including the bootstrap m).
+// Called on the new thread, cannot allocate memory.
+func minit() {
+}
+
+// Called from dropm to undo the effect of an minit.
+func unminit() {
+}
+
+func osinit() {
+ ncpu = 1
+ getg().m.procid = 2
+ physPageSize = 64 * 1024
+}
+
+// wasm has no signals
+const _NSIG = 0
+
+func signame(sig uint32) string {
+ return ""
+}
+
+func crash() {
+ *(*int32)(nil) = 0
+}
+
+func getRandomData(r []byte)
+
+func goenvs() {
+ goenvs_unix()
+}
+
+func initsig(preinit bool) {
+}
+
+// May run with m.p==nil, so write barriers are not allowed.
+//go:nowritebarrier
+func newosproc(mp *m) {
+ panic("newosproc: not implemented")
+}
+
+func setProcessCPUProfiler(hz int32) {}
+func setThreadCPUProfiler(hz int32) {}
+func sigdisable(uint32) {}
+func sigenable(uint32) {}
+func sigignore(uint32) {}
+
+//go:linkname os_sigpipe os.sigpipe
+func os_sigpipe() {
+ throw("too many writes on closed pipe")
+}
+
+//go:nosplit
+func cputicks() int64 {
+ // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand().
+ // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler.
+ // TODO: need more entropy to better seed fastrand.
+ return nanotime()
+}
+
+//go:linkname syscall_now syscall.now
+func syscall_now() (sec int64, nsec int32) {
+ sec, nsec, _ = time_now()
+ return
+}
+
+// gsignalStack is unused on js.
+type gsignalStack struct{}
TEXT ·walltime(SB), NOSPLIT, $0
CallImport
RET
+
+TEXT ·getRandomData(SB), NOSPLIT, $0
+ CallImport
+ RET
}
func TestTraceStress(t *testing.T) {
+ if runtime.GOOS == "js" {
+ t.Skip("no os.Pipe on js")
+ }
if IsEnabled() {
t.Skip("skipping because -test.trace is set")
}
// Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine.
// And concurrently with all that start/stop trace 3 times.
func TestTraceStressStartStop(t *testing.T) {
+ if runtime.GOOS == "js" {
+ t.Skip("no os.Pipe on js")
+ }
if IsEnabled() {
t.Skip("skipping because -test.trace is set")
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package syscall
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
-// +build 386 amd64 amd64p32 arm arm64 ppc64le mips64le mipsle
+// +build 386 amd64 amd64p32 arm arm64 ppc64le mips64le mipsle wasm
package syscall
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+// +build darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
// Unix environment variables.
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package syscall
+
+import (
+ "io"
+ "sync"
+ "syscall/js"
+)
+
+// Provided by package runtime.
+func now() (sec int64, nsec int32)
+
+var jsProcess = js.Global.Get("process")
+var jsFS = js.Global.Get("fs")
+var constants = jsFS.Get("constants")
+
+var (
+ nodeWRONLY = constants.Get("O_WRONLY").Int()
+ nodeRDWR = constants.Get("O_RDWR").Int()
+ nodeCREATE = constants.Get("O_CREAT").Int()
+ nodeTRUNC = constants.Get("O_TRUNC").Int()
+ nodeAPPEND = constants.Get("O_APPEND").Int()
+ nodeEXCL = constants.Get("O_EXCL").Int()
+ nodeNONBLOCK = constants.Get("O_NONBLOCK").Int()
+ nodeSYNC = constants.Get("O_SYNC").Int()
+)
+
+type jsFile struct {
+ path string
+ entries []string
+ pos int64
+ seeked bool
+}
+
+var filesMu sync.Mutex
+var files = map[int]*jsFile{
+ 0: &jsFile{},
+ 1: &jsFile{},
+ 2: &jsFile{},
+}
+
+func fdToFile(fd int) (*jsFile, error) {
+ filesMu.Lock()
+ f, ok := files[fd]
+ filesMu.Unlock()
+ if !ok {
+ return nil, EBADF
+ }
+ return f, nil
+}
+
+func Open(path string, openmode int, perm uint32) (int, error) {
+ if err := checkPath(path); err != nil {
+ return 0, err
+ }
+
+ flags := 0
+ if openmode&O_WRONLY != 0 {
+ flags |= nodeWRONLY
+ }
+ if openmode&O_RDWR != 0 {
+ flags |= nodeRDWR
+ }
+ if openmode&O_CREATE != 0 {
+ flags |= nodeCREATE
+ }
+ if openmode&O_TRUNC != 0 {
+ flags |= nodeTRUNC
+ }
+ if openmode&O_APPEND != 0 {
+ flags |= nodeAPPEND
+ }
+ if openmode&O_EXCL != 0 {
+ flags |= nodeEXCL
+ }
+ if openmode&O_NONBLOCK != 0 {
+ flags |= nodeNONBLOCK
+ }
+ if openmode&O_SYNC != 0 {
+ flags |= nodeSYNC
+ }
+
+ jsFD, err := fsCall("openSync", path, flags, perm)
+ if err != nil {
+ return 0, err
+ }
+ fd := jsFD.Int()
+
+ var entries []string
+ if stat, err := fsCall("fstatSync", fd); err == nil && stat.Call("isDirectory").Bool() {
+ dir, err := fsCall("readdirSync", path)
+ if err != nil {
+ return 0, err
+ }
+ entries = make([]string, dir.Length())
+ for i := range entries {
+ entries[i] = dir.Index(i).String()
+ }
+ }
+
+ f := &jsFile{
+ path: path,
+ entries: entries,
+ }
+ filesMu.Lock()
+ files[fd] = f
+ filesMu.Unlock()
+ return fd, nil
+}
+
+func Close(fd int) error {
+ filesMu.Lock()
+ delete(files, fd)
+ filesMu.Unlock()
+ _, err := fsCall("closeSync", fd)
+ return err
+}
+
+func CloseOnExec(fd int) {
+ // nothing to do - no exec
+}
+
+func Mkdir(path string, perm uint32) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ _, err := fsCall("mkdirSync", path, perm)
+ return err
+}
+
+func ReadDirent(fd int, buf []byte) (int, error) {
+ f, err := fdToFile(fd)
+ if err != nil {
+ return 0, err
+ }
+ if f.entries == nil {
+ return 0, EINVAL
+ }
+
+ n := 0
+ for len(f.entries) > 0 {
+ entry := f.entries[0]
+ l := 2 + len(entry)
+ if l > len(buf) {
+ break
+ }
+ buf[0] = byte(l)
+ buf[1] = byte(l >> 8)
+ copy(buf[2:], entry)
+ buf = buf[l:]
+ n += l
+ f.entries = f.entries[1:]
+ }
+
+ return n, nil
+}
+
+func setStat(st *Stat_t, jsSt js.Value) {
+ st.Dev = int64(jsSt.Get("dev").Int())
+ st.Ino = uint64(jsSt.Get("ino").Int())
+ st.Mode = uint32(jsSt.Get("mode").Int())
+ st.Nlink = uint32(jsSt.Get("nlink").Int())
+ st.Uid = uint32(jsSt.Get("uid").Int())
+ st.Gid = uint32(jsSt.Get("gid").Int())
+ st.Rdev = int64(jsSt.Get("rdev").Int())
+ st.Size = int64(jsSt.Get("size").Int())
+ st.Blksize = int32(jsSt.Get("blksize").Int())
+ st.Blocks = int32(jsSt.Get("blocks").Int())
+ atime := int64(jsSt.Get("atimeMs").Int())
+ st.Atime = atime / 1000
+ st.AtimeNsec = (atime % 1000) * 1000000
+ mtime := int64(jsSt.Get("mtimeMs").Int())
+ st.Mtime = mtime / 1000
+ st.MtimeNsec = (mtime % 1000) * 1000000
+ ctime := int64(jsSt.Get("ctimeMs").Int())
+ st.Ctime = ctime / 1000
+ st.CtimeNsec = (ctime % 1000) * 1000000
+}
+
+func Stat(path string, st *Stat_t) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ jsSt, err := fsCall("statSync", path)
+ if err != nil {
+ return err
+ }
+ setStat(st, jsSt)
+ return nil
+}
+
+func Lstat(path string, st *Stat_t) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ jsSt, err := fsCall("lstatSync", path)
+ if err != nil {
+ return err
+ }
+ setStat(st, jsSt)
+ return nil
+}
+
+func Fstat(fd int, st *Stat_t) error {
+ jsSt, err := fsCall("fstatSync", fd)
+ if err != nil {
+ return err
+ }
+ setStat(st, jsSt)
+ return nil
+}
+
+func Unlink(path string) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ _, err := fsCall("unlinkSync", path)
+ return err
+}
+
+func Rmdir(path string) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ _, err := fsCall("rmdirSync", path)
+ return err
+}
+
+func Chmod(path string, mode uint32) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ _, err := fsCall("chmodSync", path, mode)
+ return err
+}
+
+func Fchmod(fd int, mode uint32) error {
+ _, err := fsCall("fchmodSync", fd, mode)
+ return err
+}
+
+func Chown(path string, uid, gid int) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ return ENOSYS
+}
+
+func Fchown(fd int, uid, gid int) error {
+ return ENOSYS
+}
+
+func Lchown(path string, uid, gid int) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ return ENOSYS
+}
+
+func UtimesNano(path string, ts []Timespec) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ if len(ts) != 2 {
+ return EINVAL
+ }
+ atime := ts[0].Sec
+ mtime := ts[1].Sec
+ _, err := fsCall("utimesSync", path, atime, mtime)
+ return err
+}
+
+func Rename(from, to string) error {
+ if err := checkPath(from); err != nil {
+ return err
+ }
+ if err := checkPath(to); err != nil {
+ return err
+ }
+ _, err := fsCall("renameSync", from, to)
+ return err
+}
+
+func Truncate(path string, length int64) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ _, err := fsCall("truncateSync", path, length)
+ return err
+}
+
+func Ftruncate(fd int, length int64) error {
+ _, err := fsCall("ftruncateSync", fd, length)
+ return err
+}
+
+func Getcwd(buf []byte) (n int, err error) {
+ defer recoverErr(&err)
+ cwd := jsProcess.Call("cwd").String()
+ n = copy(buf, cwd)
+ return n, nil
+}
+
+func Chdir(path string) (err error) {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ defer recoverErr(&err)
+ jsProcess.Call("chdir", path)
+ return
+}
+
+func Fchdir(fd int) error {
+ f, err := fdToFile(fd)
+ if err != nil {
+ return err
+ }
+ return Chdir(f.path)
+}
+
+func Readlink(path string, buf []byte) (n int, err error) {
+ if err := checkPath(path); err != nil {
+ return 0, err
+ }
+ dst, err := fsCall("readlinkSync", path)
+ if err != nil {
+ return 0, err
+ }
+ n = copy(buf, dst.String())
+ return n, nil
+}
+
+func Link(path, link string) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ if err := checkPath(link); err != nil {
+ return err
+ }
+ _, err := fsCall("linkSync", path, link)
+ return err
+}
+
+func Symlink(path, link string) error {
+ if err := checkPath(path); err != nil {
+ return err
+ }
+ if err := checkPath(link); err != nil {
+ return err
+ }
+ _, err := fsCall("symlinkSync", path, link)
+ return err
+}
+
+func Fsync(fd int) error {
+ _, err := fsCall("fsyncSync", fd)
+ return err
+}
+
+func Read(fd int, b []byte) (int, error) {
+ f, err := fdToFile(fd)
+ if err != nil {
+ return 0, err
+ }
+
+ if f.seeked {
+ n, err := Pread(fd, b, f.pos)
+ f.pos += int64(n)
+ return n, err
+ }
+
+ n, err := fsCall("readSync", fd, b, 0, len(b))
+ if err != nil {
+ return 0, err
+ }
+ n2 := n.Int()
+ f.pos += int64(n2)
+ return n2, err
+}
+
+func Write(fd int, b []byte) (int, error) {
+ f, err := fdToFile(fd)
+ if err != nil {
+ return 0, err
+ }
+
+ if f.seeked {
+ n, err := Pwrite(fd, b, f.pos)
+ f.pos += int64(n)
+ return n, err
+ }
+
+ n, err := fsCall("writeSync", fd, b, 0, len(b))
+ if err != nil {
+ return 0, err
+ }
+ n2 := n.Int()
+ f.pos += int64(n2)
+ return n2, err
+}
+
+func Pread(fd int, b []byte, offset int64) (int, error) {
+ n, err := fsCall("readSync", fd, b, 0, len(b), offset)
+ if err != nil {
+ return 0, err
+ }
+ return n.Int(), nil
+}
+
+func Pwrite(fd int, b []byte, offset int64) (int, error) {
+ n, err := fsCall("writeSync", fd, b, 0, len(b), offset)
+ if err != nil {
+ return 0, err
+ }
+ return n.Int(), nil
+}
+
+func Seek(fd int, offset int64, whence int) (int64, error) {
+ f, err := fdToFile(fd)
+ if err != nil {
+ return 0, err
+ }
+
+ var newPos int64
+ switch whence {
+ case io.SeekStart:
+ newPos = offset
+ case io.SeekCurrent:
+ newPos = f.pos + offset
+ case io.SeekEnd:
+ var st Stat_t
+ if err := Fstat(fd, &st); err != nil {
+ return 0, err
+ }
+ newPos = st.Size + offset
+ default:
+ return 0, errnoErr(EINVAL)
+ }
+
+ if newPos < 0 {
+ return 0, errnoErr(EINVAL)
+ }
+
+ f.seeked = true
+ f.pos = newPos
+ return newPos, nil
+}
+
+func Dup(fd int) (int, error) {
+ return 0, ENOSYS
+}
+
+func Dup2(fd, newfd int) error {
+ return ENOSYS
+}
+
+func Pipe(fd []int) error {
+ return ENOSYS
+}
+
+func fsCall(name string, args ...interface{}) (res js.Value, err error) {
+ defer recoverErr(&err)
+ res = jsFS.Call(name, args...)
+ return
+}
+
+// checkPath checks that the path is not empty and that it contains no null characters.
+func checkPath(path string) error {
+ if path == "" {
+ return EINVAL
+ }
+ for i := 0; i < len(path); i++ {
+ if path[i] == '\x00' {
+ return EINVAL
+ }
+ }
+ return nil
+}
+
+func recoverErr(errPtr *error) {
+ if err := recover(); err != nil {
+ jsErr, ok := err.(js.Error)
+ if !ok {
+ panic(err)
+ }
+ errno, ok := errnoByCode[jsErr.Get("code").String()]
+ if !ok {
+ panic(err)
+ }
+ *errPtr = errnoErr(Errno(errno))
+ }
+}
--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js,wasm
+
+package syscall
+
+import (
+ "sync"
+ "unsafe"
+)
+
+const direntSize = 8 + 8 + 2 + 256
+
+type Dirent struct {
+ Reclen uint16
+ Name [256]byte
+}
+
+func direntIno(buf []byte) (uint64, bool) {
+ return 1, true
+}
+
+func direntReclen(buf []byte) (uint64, bool) {
+ return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
+}
+
+func direntNamlen(buf []byte) (uint64, bool) {
+ reclen, ok := direntReclen(buf)
+ if !ok {
+ return 0, false
+ }
+ return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
+}
+
+const PathMax = 256
+
+// An Errno is an unsigned number describing an error condition.
+// It implements the error interface. The zero Errno is by convention
+// a non-error, so code to convert from Errno to error should use:
+// err = nil
+// if errno != 0 {
+// err = errno
+// }
+type Errno uintptr
+
+func (e Errno) Error() string {
+ if 0 <= int(e) && int(e) < len(errorstr) {
+ s := errorstr[e]
+ if s != "" {
+ return s
+ }
+ }
+ return "errno " + itoa(int(e))
+}
+
+func (e Errno) Temporary() bool {
+ return e == EINTR || e == EMFILE || e.Timeout()
+}
+
+func (e Errno) Timeout() bool {
+ return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
+}
+
+// A Signal is a number describing a process signal.
+// It implements the os.Signal interface.
+type Signal int
+
+const (
+ _ Signal = iota
+ SIGCHLD
+ SIGINT
+ SIGKILL
+ SIGTRAP
+ SIGQUIT
+)
+
+func (s Signal) Signal() {}
+
+func (s Signal) String() string {
+ if 0 <= s && int(s) < len(signals) {
+ str := signals[s]
+ if str != "" {
+ return str
+ }
+ }
+ return "signal " + itoa(int(s))
+}
+
+var signals = [...]string{}
+
+// File system
+
+const (
+ Stdin = 0
+ Stdout = 1
+ Stderr = 2
+)
+
+const (
+ O_RDONLY = 0
+ O_WRONLY = 1
+ O_RDWR = 2
+
+ O_CREAT = 0100
+ O_CREATE = O_CREAT
+ O_TRUNC = 01000
+ O_APPEND = 02000
+ O_EXCL = 0200
+ O_NONBLOCK = 04000
+ O_SYNC = 010000
+
+ O_CLOEXEC = 0
+)
+
+const (
+ F_DUPFD = 0
+ F_GETFD = 1
+ F_SETFD = 2
+ F_GETFL = 3
+ F_SETFL = 4
+ F_GETOWN = 5
+ F_SETOWN = 6
+ F_GETLK = 7
+ F_SETLK = 8
+ F_SETLKW = 9
+ F_RGETLK = 10
+ F_RSETLK = 11
+ F_CNVT = 12
+ F_RSETLKW = 13
+
+ F_RDLCK = 1
+ F_WRLCK = 2
+ F_UNLCK = 3
+ F_UNLKSYS = 4
+)
+
+const (
+ S_IFMT = 0000370000
+ S_IFSHM_SYSV = 0000300000
+ S_IFSEMA = 0000270000
+ S_IFCOND = 0000260000
+ S_IFMUTEX = 0000250000
+ S_IFSHM = 0000240000
+ S_IFBOUNDSOCK = 0000230000
+ S_IFSOCKADDR = 0000220000
+ S_IFDSOCK = 0000210000
+
+ S_IFSOCK = 0000140000
+ S_IFLNK = 0000120000
+ S_IFREG = 0000100000
+ S_IFBLK = 0000060000
+ S_IFDIR = 0000040000
+ S_IFCHR = 0000020000
+ S_IFIFO = 0000010000
+
+ S_UNSUP = 0000370000
+
+ S_ISUID = 0004000
+ S_ISGID = 0002000
+ S_ISVTX = 0001000
+
+ S_IREAD = 0400
+ S_IWRITE = 0200
+ S_IEXEC = 0100
+
+ S_IRWXU = 0700
+ S_IRUSR = 0400
+ S_IWUSR = 0200
+ S_IXUSR = 0100
+
+ S_IRWXG = 070
+ S_IRGRP = 040
+ S_IWGRP = 020
+ S_IXGRP = 010
+
+ S_IRWXO = 07
+ S_IROTH = 04
+ S_IWOTH = 02
+ S_IXOTH = 01
+)
+
+type Stat_t struct {
+ Dev int64
+ Ino uint64
+ Mode uint32
+ Nlink uint32
+ Uid uint32
+ Gid uint32
+ Rdev int64
+ Size int64
+ Blksize int32
+ Blocks int32
+ Atime int64
+ AtimeNsec int64
+ Mtime int64
+ MtimeNsec int64
+ Ctime int64
+ CtimeNsec int64
+}
+
+// Processes
+// Not supported - just enough for package os.
+
+var ForkLock sync.RWMutex
+
+type WaitStatus uint32
+
+func (w WaitStatus) Exited() bool { return false }
+func (w WaitStatus) ExitStatus() int { return 0 }
+func (w WaitStatus) Signaled() bool { return false }
+func (w WaitStatus) Signal() Signal { return 0 }
+func (w WaitStatus) CoreDump() bool { return false }
+func (w WaitStatus) Stopped() bool { return false }
+func (w WaitStatus) Continued() bool { return false }
+func (w WaitStatus) StopSignal() Signal { return 0 }
+func (w WaitStatus) TrapCause() int { return 0 }
+
+// XXX made up
+type Rusage struct {
+ Utime Timeval
+ Stime Timeval
+}
+
+// XXX made up
+type ProcAttr struct {
+ Dir string
+ Env []string
+ Files []uintptr
+ Sys *SysProcAttr
+}
+
+type SysProcAttr struct {
+}
+
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
+ return 0, 0, ENOSYS
+}
+
+func Sysctl(key string) (string, error) {
+ if key == "kern.hostname" {
+ return "js", nil
+ }
+ return "", ENOSYS
+}
+
+const ImplementsGetwd = true
+
+func Getwd() (wd string, err error) {
+ var buf [PathMax]byte
+ n, err := Getcwd(buf[0:])
+ if err != nil {
+ return "", err
+ }
+ return string(buf[:n]), nil
+}
+
+func Getegid() int { return 1 }
+func Geteuid() int { return 1 }
+func Getgid() int { return 1 }
+func Getgroups() ([]int, error) { return []int{1}, nil }
+func Getppid() int { return 2 }
+func Getpid() int { return 3 }
+func Gettimeofday(tv *Timeval) error { return ENOSYS }
+func Getuid() int { return 1 }
+func Kill(pid int, signum Signal) error { return ENOSYS }
+func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
+ return 0, ENOSYS
+}
+func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
+ return 0, 0, ENOSYS
+}
+func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
+ return 0, ENOSYS
+}
+
+type Iovec struct{} // dummy
+
+type Timespec struct {
+ Sec int64
+ Nsec int64
+}
+
+type Timeval struct {
+ Sec int64
+ Usec int64
+}
+
+func setTimespec(sec, nsec int64) Timespec {
+ return Timespec{Sec: sec, Nsec: nsec}
+}
+
+func setTimeval(sec, usec int64) Timeval {
+ return Timeval{Sec: sec, Usec: usec}
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build nacl
+// +build nacl js,wasm
package syscall
+import "runtime"
+
// TODO: generate with runtime/mknacl.sh, allow override with IRT.
const (
sys_null = 1
EMLINK: "Too many links",
EPIPE: "Broken pipe",
ENAMETOOLONG: "File name too long",
- ENOSYS: "not implemented on Native Client",
+ ENOSYS: "not implemented on " + runtime.GOOS,
EDQUOT: "Quota exceeded",
EDOM: "Math arg out of domain of func",
ERANGE: "Math result not representable",
}
return e
}
+
+var errnoByCode = map[string]Errno{
+ "EPERM": EPERM,
+ "ENOENT": ENOENT,
+ "ESRCH": ESRCH,
+ "EINTR": EINTR,
+ "EIO": EIO,
+ "ENXIO": ENXIO,
+ "E2BIG": E2BIG,
+ "ENOEXEC": ENOEXEC,
+ "EBADF": EBADF,
+ "ECHILD": ECHILD,
+ "EAGAIN": EAGAIN,
+ "ENOMEM": ENOMEM,
+ "EACCES": EACCES,
+ "EFAULT": EFAULT,
+ "EBUSY": EBUSY,
+ "EEXIST": EEXIST,
+ "EXDEV": EXDEV,
+ "ENODEV": ENODEV,
+ "ENOTDIR": ENOTDIR,
+ "EISDIR": EISDIR,
+ "EINVAL": EINVAL,
+ "ENFILE": ENFILE,
+ "EMFILE": EMFILE,
+ "ENOTTY": ENOTTY,
+ "EFBIG": EFBIG,
+ "ENOSPC": ENOSPC,
+ "ESPIPE": ESPIPE,
+ "EROFS": EROFS,
+ "EMLINK": EMLINK,
+ "EPIPE": EPIPE,
+ "ENAMETOOLONG": ENAMETOOLONG,
+ "ENOSYS": ENOSYS,
+ "EDQUOT": EDQUOT,
+ "EDOM": EDOM,
+ "ERANGE": ERANGE,
+ "EDEADLK": EDEADLK,
+ "ENOLCK": ENOLCK,
+ "ENOTEMPTY": ENOTEMPTY,
+ "ELOOP": ELOOP,
+ "ENOMSG": ENOMSG,
+ "EIDRM": EIDRM,
+ "ECHRNG": ECHRNG,
+ "EL2NSYNC": EL2NSYNC,
+ "EL3HLT": EL3HLT,
+ "EL3RST": EL3RST,
+ "ELNRNG": ELNRNG,
+ "EUNATCH": EUNATCH,
+ "ENOCSI": ENOCSI,
+ "EL2HLT": EL2HLT,
+ "EBADE": EBADE,
+ "EBADR": EBADR,
+ "EXFULL": EXFULL,
+ "ENOANO": ENOANO,
+ "EBADRQC": EBADRQC,
+ "EBADSLT": EBADSLT,
+ "EDEADLOCK": EDEADLOCK,
+ "EBFONT": EBFONT,
+ "ENOSTR": ENOSTR,
+ "ENODATA": ENODATA,
+ "ETIME": ETIME,
+ "ENOSR": ENOSR,
+ "ENONET": ENONET,
+ "ENOPKG": ENOPKG,
+ "EREMOTE": EREMOTE,
+ "ENOLINK": ENOLINK,
+ "EADV": EADV,
+ "ESRMNT": ESRMNT,
+ "ECOMM": ECOMM,
+ "EPROTO": EPROTO,
+ "EMULTIHOP": EMULTIHOP,
+ "EDOTDOT": EDOTDOT,
+ "EBADMSG": EBADMSG,
+ "EOVERFLOW": EOVERFLOW,
+ "ENOTUNIQ": ENOTUNIQ,
+ "EBADFD": EBADFD,
+ "EREMCHG": EREMCHG,
+ "ELIBACC": ELIBACC,
+ "ELIBBAD": ELIBBAD,
+ "ELIBSCN": ELIBSCN,
+ "ELIBMAX": ELIBMAX,
+ "ELIBEXEC": ELIBEXEC,
+ "EILSEQ": EILSEQ,
+ "EUSERS": EUSERS,
+ "ENOTSOCK": ENOTSOCK,
+ "EDESTADDRREQ": EDESTADDRREQ,
+ "EMSGSIZE": EMSGSIZE,
+ "EPROTOTYPE": EPROTOTYPE,
+ "ENOPROTOOPT": ENOPROTOOPT,
+ "EPROTONOSUPPORT": EPROTONOSUPPORT,
+ "ESOCKTNOSUPPORT": ESOCKTNOSUPPORT,
+ "EOPNOTSUPP": EOPNOTSUPP,
+ "EPFNOSUPPORT": EPFNOSUPPORT,
+ "EAFNOSUPPORT": EAFNOSUPPORT,
+ "EADDRINUSE": EADDRINUSE,
+ "EADDRNOTAVAIL": EADDRNOTAVAIL,
+ "ENETDOWN": ENETDOWN,
+ "ENETUNREACH": ENETUNREACH,
+ "ENETRESET": ENETRESET,
+ "ECONNABORTED": ECONNABORTED,
+ "ECONNRESET": ECONNRESET,
+ "ENOBUFS": ENOBUFS,
+ "EISCONN": EISCONN,
+ "ENOTCONN": ENOTCONN,
+ "ESHUTDOWN": ESHUTDOWN,
+ "ETOOMANYREFS": ETOOMANYREFS,
+ "ETIMEDOUT": ETIMEDOUT,
+ "ECONNREFUSED": ECONNREFUSED,
+ "EHOSTDOWN": EHOSTDOWN,
+ "EHOSTUNREACH": EHOSTUNREACH,
+ "EALREADY": EALREADY,
+ "EINPROGRESS": EINPROGRESS,
+ "ESTALE": ESTALE,
+ "ENOTSUP": ENOTSUP,
+ "ENOMEDIUM": ENOMEDIUM,
+ "ECANCELED": ECANCELED,
+ "ELBIN": ELBIN,
+ "EFTYPE": EFTYPE,
+ "ENMFILE": ENMFILE,
+ "EPROCLIM": EPROCLIM,
+ "ENOSHARE": ENOSHARE,
+ "ECASECLASH": ECASECLASH,
+ "EWOULDBLOCK": EWOULDBLOCK,
+}