Fix "PR c++/92804 ICE trying to use concept as a nested-name-specifier"
[official-gcc.git] / libgo / go / syscall / fs_js.go
blob16d9f58b8c4a48789ea3392107f391274a43885e
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // +build js,wasm
7 package syscall
9 import (
10 "errors"
11 "io"
12 "sync"
13 "syscall/js"
16 // Provided by package runtime.
17 func now() (sec int64, nsec int32)
19 var jsProcess = js.Global().Get("process")
20 var jsFS = js.Global().Get("fs")
21 var constants = jsFS.Get("constants")
23 var uint8Array = js.Global().Get("Uint8Array")
25 var (
26 nodeWRONLY = constants.Get("O_WRONLY").Int()
27 nodeRDWR = constants.Get("O_RDWR").Int()
28 nodeCREATE = constants.Get("O_CREAT").Int()
29 nodeTRUNC = constants.Get("O_TRUNC").Int()
30 nodeAPPEND = constants.Get("O_APPEND").Int()
31 nodeEXCL = constants.Get("O_EXCL").Int()
34 type jsFile struct {
35 path string
36 entries []string
37 dirIdx int // entries[:dirIdx] have already been returned in ReadDirent
38 pos int64
39 seeked bool
42 var filesMu sync.Mutex
43 var files = map[int]*jsFile{
44 0: {},
45 1: {},
46 2: {},
49 func fdToFile(fd int) (*jsFile, error) {
50 filesMu.Lock()
51 f, ok := files[fd]
52 filesMu.Unlock()
53 if !ok {
54 return nil, EBADF
56 return f, nil
59 func Open(path string, openmode int, perm uint32) (int, error) {
60 if err := checkPath(path); err != nil {
61 return 0, err
64 flags := 0
65 if openmode&O_WRONLY != 0 {
66 flags |= nodeWRONLY
68 if openmode&O_RDWR != 0 {
69 flags |= nodeRDWR
71 if openmode&O_CREATE != 0 {
72 flags |= nodeCREATE
74 if openmode&O_TRUNC != 0 {
75 flags |= nodeTRUNC
77 if openmode&O_APPEND != 0 {
78 flags |= nodeAPPEND
80 if openmode&O_EXCL != 0 {
81 flags |= nodeEXCL
83 if openmode&O_SYNC != 0 {
84 return 0, errors.New("syscall.Open: O_SYNC is not supported by js/wasm")
87 jsFD, err := fsCall("open", path, flags, perm)
88 if err != nil {
89 return 0, err
91 fd := jsFD.Int()
93 var entries []string
94 if stat, err := fsCall("fstat", fd); err == nil && stat.Call("isDirectory").Bool() {
95 dir, err := fsCall("readdir", path)
96 if err != nil {
97 return 0, err
99 entries = make([]string, dir.Length())
100 for i := range entries {
101 entries[i] = dir.Index(i).String()
105 f := &jsFile{
106 path: path,
107 entries: entries,
109 filesMu.Lock()
110 files[fd] = f
111 filesMu.Unlock()
112 return fd, nil
115 func Close(fd int) error {
116 filesMu.Lock()
117 delete(files, fd)
118 filesMu.Unlock()
119 _, err := fsCall("close", fd)
120 return err
123 func CloseOnExec(fd int) {
124 // nothing to do - no exec
127 func Mkdir(path string, perm uint32) error {
128 if err := checkPath(path); err != nil {
129 return err
131 _, err := fsCall("mkdir", path, perm)
132 return err
135 func ReadDirent(fd int, buf []byte) (int, error) {
136 f, err := fdToFile(fd)
137 if err != nil {
138 return 0, err
140 if f.entries == nil {
141 return 0, EINVAL
144 n := 0
145 for f.dirIdx < len(f.entries) {
146 entry := f.entries[f.dirIdx]
147 l := 2 + len(entry)
148 if l > len(buf) {
149 break
151 buf[0] = byte(l)
152 buf[1] = byte(l >> 8)
153 copy(buf[2:], entry)
154 buf = buf[l:]
155 n += l
156 f.dirIdx++
159 return n, nil
162 func setStat(st *Stat_t, jsSt js.Value) {
163 st.Dev = int64(jsSt.Get("dev").Int())
164 st.Ino = uint64(jsSt.Get("ino").Int())
165 st.Mode = uint32(jsSt.Get("mode").Int())
166 st.Nlink = uint32(jsSt.Get("nlink").Int())
167 st.Uid = uint32(jsSt.Get("uid").Int())
168 st.Gid = uint32(jsSt.Get("gid").Int())
169 st.Rdev = int64(jsSt.Get("rdev").Int())
170 st.Size = int64(jsSt.Get("size").Int())
171 st.Blksize = int32(jsSt.Get("blksize").Int())
172 st.Blocks = int32(jsSt.Get("blocks").Int())
173 atime := int64(jsSt.Get("atimeMs").Int())
174 st.Atime = atime / 1000
175 st.AtimeNsec = (atime % 1000) * 1000000
176 mtime := int64(jsSt.Get("mtimeMs").Int())
177 st.Mtime = mtime / 1000
178 st.MtimeNsec = (mtime % 1000) * 1000000
179 ctime := int64(jsSt.Get("ctimeMs").Int())
180 st.Ctime = ctime / 1000
181 st.CtimeNsec = (ctime % 1000) * 1000000
184 func Stat(path string, st *Stat_t) error {
185 if err := checkPath(path); err != nil {
186 return err
188 jsSt, err := fsCall("stat", path)
189 if err != nil {
190 return err
192 setStat(st, jsSt)
193 return nil
196 func Lstat(path string, st *Stat_t) error {
197 if err := checkPath(path); err != nil {
198 return err
200 jsSt, err := fsCall("lstat", path)
201 if err != nil {
202 return err
204 setStat(st, jsSt)
205 return nil
208 func Fstat(fd int, st *Stat_t) error {
209 jsSt, err := fsCall("fstat", fd)
210 if err != nil {
211 return err
213 setStat(st, jsSt)
214 return nil
217 func Unlink(path string) error {
218 if err := checkPath(path); err != nil {
219 return err
221 _, err := fsCall("unlink", path)
222 return err
225 func Rmdir(path string) error {
226 if err := checkPath(path); err != nil {
227 return err
229 _, err := fsCall("rmdir", path)
230 return err
233 func Chmod(path string, mode uint32) error {
234 if err := checkPath(path); err != nil {
235 return err
237 _, err := fsCall("chmod", path, mode)
238 return err
241 func Fchmod(fd int, mode uint32) error {
242 _, err := fsCall("fchmod", fd, mode)
243 return err
246 func Chown(path string, uid, gid int) error {
247 if err := checkPath(path); err != nil {
248 return err
250 _, err := fsCall("chown", path, uint32(uid), uint32(gid))
251 return err
254 func Fchown(fd int, uid, gid int) error {
255 _, err := fsCall("fchown", fd, uint32(uid), uint32(gid))
256 return err
259 func Lchown(path string, uid, gid int) error {
260 if err := checkPath(path); err != nil {
261 return err
263 if jsFS.Get("lchown").IsUndefined() {
264 // fs.lchown is unavailable on Linux until Node.js 10.6.0
265 // TODO(neelance): remove when we require at least this Node.js version
266 return ENOSYS
268 _, err := fsCall("lchown", path, uint32(uid), uint32(gid))
269 return err
272 func UtimesNano(path string, ts []Timespec) error {
273 if err := checkPath(path); err != nil {
274 return err
276 if len(ts) != 2 {
277 return EINVAL
279 atime := ts[0].Sec
280 mtime := ts[1].Sec
281 _, err := fsCall("utimes", path, atime, mtime)
282 return err
285 func Rename(from, to string) error {
286 if err := checkPath(from); err != nil {
287 return err
289 if err := checkPath(to); err != nil {
290 return err
292 _, err := fsCall("rename", from, to)
293 return err
296 func Truncate(path string, length int64) error {
297 if err := checkPath(path); err != nil {
298 return err
300 _, err := fsCall("truncate", path, length)
301 return err
304 func Ftruncate(fd int, length int64) error {
305 _, err := fsCall("ftruncate", fd, length)
306 return err
309 func Getcwd(buf []byte) (n int, err error) {
310 defer recoverErr(&err)
311 cwd := jsProcess.Call("cwd").String()
312 n = copy(buf, cwd)
313 return
316 func Chdir(path string) (err error) {
317 if err := checkPath(path); err != nil {
318 return err
320 defer recoverErr(&err)
321 jsProcess.Call("chdir", path)
322 return
325 func Fchdir(fd int) error {
326 f, err := fdToFile(fd)
327 if err != nil {
328 return err
330 return Chdir(f.path)
333 func Readlink(path string, buf []byte) (n int, err error) {
334 if err := checkPath(path); err != nil {
335 return 0, err
337 dst, err := fsCall("readlink", path)
338 if err != nil {
339 return 0, err
341 n = copy(buf, dst.String())
342 return n, nil
345 func Link(path, link string) error {
346 if err := checkPath(path); err != nil {
347 return err
349 if err := checkPath(link); err != nil {
350 return err
352 _, err := fsCall("link", path, link)
353 return err
356 func Symlink(path, link string) error {
357 if err := checkPath(path); err != nil {
358 return err
360 if err := checkPath(link); err != nil {
361 return err
363 _, err := fsCall("symlink", path, link)
364 return err
367 func Fsync(fd int) error {
368 _, err := fsCall("fsync", fd)
369 return err
372 func Read(fd int, b []byte) (int, error) {
373 f, err := fdToFile(fd)
374 if err != nil {
375 return 0, err
378 if f.seeked {
379 n, err := Pread(fd, b, f.pos)
380 f.pos += int64(n)
381 return n, err
384 buf := uint8Array.New(len(b))
385 n, err := fsCall("read", fd, buf, 0, len(b), nil)
386 if err != nil {
387 return 0, err
389 js.CopyBytesToGo(b, buf)
391 n2 := n.Int()
392 f.pos += int64(n2)
393 return n2, err
396 func Write(fd int, b []byte) (int, error) {
397 f, err := fdToFile(fd)
398 if err != nil {
399 return 0, err
402 if f.seeked {
403 n, err := Pwrite(fd, b, f.pos)
404 f.pos += int64(n)
405 return n, err
408 if faketime && (fd == 1 || fd == 2) {
409 n := faketimeWrite(fd, b)
410 if n < 0 {
411 return 0, errnoErr(Errno(-n))
413 return n, nil
416 buf := uint8Array.New(len(b))
417 js.CopyBytesToJS(buf, b)
418 n, err := fsCall("write", fd, buf, 0, len(b), nil)
419 if err != nil {
420 return 0, err
422 n2 := n.Int()
423 f.pos += int64(n2)
424 return n2, err
427 func Pread(fd int, b []byte, offset int64) (int, error) {
428 buf := uint8Array.New(len(b))
429 n, err := fsCall("read", fd, buf, 0, len(b), offset)
430 if err != nil {
431 return 0, err
433 js.CopyBytesToGo(b, buf)
434 return n.Int(), nil
437 func Pwrite(fd int, b []byte, offset int64) (int, error) {
438 buf := uint8Array.New(len(b))
439 js.CopyBytesToJS(buf, b)
440 n, err := fsCall("write", fd, buf, 0, len(b), offset)
441 if err != nil {
442 return 0, err
444 return n.Int(), nil
447 func Seek(fd int, offset int64, whence int) (int64, error) {
448 f, err := fdToFile(fd)
449 if err != nil {
450 return 0, err
453 var newPos int64
454 switch whence {
455 case io.SeekStart:
456 newPos = offset
457 case io.SeekCurrent:
458 newPos = f.pos + offset
459 case io.SeekEnd:
460 var st Stat_t
461 if err := Fstat(fd, &st); err != nil {
462 return 0, err
464 newPos = st.Size + offset
465 default:
466 return 0, errnoErr(EINVAL)
469 if newPos < 0 {
470 return 0, errnoErr(EINVAL)
473 f.seeked = true
474 f.dirIdx = 0 // Reset directory read position. See issue 35767.
475 f.pos = newPos
476 return newPos, nil
479 func Dup(fd int) (int, error) {
480 return 0, ENOSYS
483 func Dup2(fd, newfd int) error {
484 return ENOSYS
487 func Pipe(fd []int) error {
488 return ENOSYS
491 func fsCall(name string, args ...interface{}) (js.Value, error) {
492 type callResult struct {
493 val js.Value
494 err error
497 c := make(chan callResult, 1)
498 jsFS.Call(name, append(args, js.FuncOf(func(this js.Value, args []js.Value) interface{} {
499 var res callResult
501 if len(args) >= 1 { // on Node.js 8, fs.utimes calls the callback without any arguments
502 if jsErr := args[0]; !jsErr.IsNull() {
503 res.err = mapJSError(jsErr)
507 res.val = js.Undefined()
508 if len(args) >= 2 {
509 res.val = args[1]
512 c <- res
513 return nil
514 }))...)
515 res := <-c
516 return res.val, res.err
519 // checkPath checks that the path is not empty and that it contains no null characters.
520 func checkPath(path string) error {
521 if path == "" {
522 return EINVAL
524 for i := 0; i < len(path); i++ {
525 if path[i] == '\x00' {
526 return EINVAL
529 return nil
532 func recoverErr(errPtr *error) {
533 if err := recover(); err != nil {
534 jsErr, ok := err.(js.Error)
535 if !ok {
536 panic(err)
538 *errPtr = mapJSError(jsErr.Value)
542 // mapJSError maps an error given by Node.js to the appropriate Go error
543 func mapJSError(jsErr js.Value) error {
544 errno, ok := errnoByCode[jsErr.Get("code").String()]
545 if !ok {
546 panic(jsErr)
548 return errnoErr(Errno(errno))