diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-01-02 15:05:27 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-01-21 23:53:22 -0800 |
commit | 5a8ea165926cb0737ab03bc48c18dc5198ab5305 (patch) | |
tree | 962dc3357c57f019f85658f99e2e753e30201c27 /libgo/go/syscall | |
parent | 6ac6529e155c9baa0aaaed7aca06bd38ebda5b43 (diff) | |
download | gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.zip gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.gz gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.bz2 |
libgo: update to Go1.14beta1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/214297
Diffstat (limited to 'libgo/go/syscall')
36 files changed, 589 insertions, 151 deletions
diff --git a/libgo/go/syscall/dirent.go b/libgo/go/syscall/dirent.go index 58c34c3..5726449 100644 --- a/libgo/go/syscall/dirent.go +++ b/libgo/go/syscall/dirent.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package syscall diff --git a/libgo/go/syscall/dirent_bsd_test.go b/libgo/go/syscall/dirent_test.go index 43b667b..f631533 100644 --- a/libgo/go/syscall/dirent_bsd_test.go +++ b/libgo/go/syscall/dirent_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd netbsd openbsd +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package syscall_test diff --git a/libgo/go/syscall/env_plan9.go b/libgo/go/syscall/env_plan9.go index 9a8a837..e403a25 100644 --- a/libgo/go/syscall/env_plan9.go +++ b/libgo/go/syscall/env_plan9.go @@ -74,7 +74,21 @@ func Setenv(key, value string) error { } func Clearenv() { - RawSyscall(SYS_RFORK, RFCENVG, 0, 0) + // Creating a new environment group using rfork(RFCENVG) can race + // with access to files in /env (e.g. from Setenv or Getenv). + // Remove all environment variables in current environment group instead. + fd, err := open("/env", O_RDONLY) + if err != nil { + return + } + defer Close(fd) + files, err := readdirnames(fd) + if err != nil { + return + } + for _, key := range files { + Remove("/env/" + key) + } } func Unsetenv(key string) error { diff --git a/libgo/go/syscall/env_unix.go b/libgo/go/syscall/env_unix.go index c22af18..3564f7f 100644 --- a/libgo/go/syscall/env_unix.go +++ b/libgo/go/syscall/env_unix.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris // Unix environment variables. diff --git a/libgo/go/syscall/exec_linux_test.go b/libgo/go/syscall/exec_linux_test.go index 7bd3910..1a44f04 100644 --- a/libgo/go/syscall/exec_linux_test.go +++ b/libgo/go/syscall/exec_linux_test.go @@ -34,6 +34,14 @@ func isLXC() bool { } func skipInContainer(t *testing.T) { + // TODO: the callers of this func are using this func to skip + // tests when running as some sort of "fake root" that's uid 0 + // but lacks certain Linux capabilities. Most of the Go builds + // run in privileged containers, though, where root is much + // closer (if not identical) to the real root. We should test + // for what we need exactly (which capabilities are active?), + // instead of just assuming "docker == bad". Then we'd get more test + // coverage on a bunch of builders too. if isDocker() { t.Skip("skip this test in Docker container") } @@ -42,6 +50,18 @@ func skipInContainer(t *testing.T) { } } +func skipNoUserNamespaces(t *testing.T) { + if _, err := os.Stat("/proc/self/ns/user"); err != nil { + if os.IsNotExist(err) { + t.Skip("kernel doesn't support user namespaces") + } + if os.IsPermission(err) { + t.Skip("unable to test user namespaces due to permissions") + } + t.Fatalf("Failed to stat /proc/self/ns/user: %v", err) + } +} + func skipUnprivilegedUserClone(t *testing.T) { // Skip the test if the sysctl that prevents unprivileged user // from creating user namespaces is enabled. @@ -64,15 +84,7 @@ func isChrooted(t *testing.T) bool { func checkUserNS(t *testing.T) { skipInContainer(t) - if _, err := os.Stat("/proc/self/ns/user"); err != nil { - if os.IsNotExist(err) { - t.Skip("kernel doesn't support user namespaces") - } - if os.IsPermission(err) { - t.Skip("unable to test user namespaces due to permissions") - } - t.Fatalf("Failed to stat /proc/self/ns/user: %v", err) - } + skipNoUserNamespaces(t) if isChrooted(t) { // create_user_ns in the kernel (see // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c) @@ -305,6 +317,7 @@ func TestGroupCleanupUserNamespace(t *testing.T) { "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)", "uid=0(root) gid=0(root) groups=0(root),65534", "uid=0(root) gid=0(root) groups=0(root),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody)", // Alpine; see https://golang.org/issue/19938 + "uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023", // CentOS with SELinux context, see https://golang.org/issue/34547 } for _, e := range expected { if strOut == e { @@ -336,14 +349,6 @@ func TestUnshareMountNameSpace(t *testing.T) { t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") } - // When running under the Go continuous build, skip tests for - // now when under Kubernetes. (where things are root but not quite) - // Both of these are our own environment variables. - // See Issue 12815. - if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { - t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") - } - d, err := ioutil.TempDir("", "unshare") if err != nil { t.Fatalf("tempdir: %v", err) @@ -386,14 +391,6 @@ func TestUnshareMountNameSpaceChroot(t *testing.T) { t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") } - // When running under the Go continuous build, skip tests for - // now when under Kubernetes. (where things are root but not quite) - // Both of these are our own environment variables. - // See Issue 12815. - if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { - t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") - } - d, err := ioutil.TempDir("", "unshare") if err != nil { t.Fatalf("tempdir: %v", err) @@ -573,6 +570,7 @@ func TestAmbientCaps(t *testing.T) { } func TestAmbientCapsUserns(t *testing.T) { + checkUserNS(t) testAmbientCaps(t, true) } @@ -580,14 +578,6 @@ func testAmbientCaps(t *testing.T, userns bool) { skipInContainer(t) mustSupportAmbientCaps(t) - // When running under the Go continuous build, skip tests for - // now when under Kubernetes. (where things are root but not quite) - // Both of these are our own environment variables. - // See Issue 12815. - if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { - t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") - } - skipUnprivilegedUserClone(t) // skip on android, due to lack of lookup support diff --git a/libgo/go/syscall/fs_js.go b/libgo/go/syscall/fs_js.go index 1b835c5..16d9f58 100644 --- a/libgo/go/syscall/fs_js.go +++ b/libgo/go/syscall/fs_js.go @@ -34,6 +34,7 @@ var ( type jsFile struct { path string entries []string + dirIdx int // entries[:dirIdx] have already been returned in ReadDirent pos int64 seeked bool } @@ -141,8 +142,8 @@ func ReadDirent(fd int, buf []byte) (int, error) { } n := 0 - for len(f.entries) > 0 { - entry := f.entries[0] + for f.dirIdx < len(f.entries) { + entry := f.entries[f.dirIdx] l := 2 + len(entry) if l > len(buf) { break @@ -152,7 +153,7 @@ func ReadDirent(fd int, buf []byte) (int, error) { copy(buf[2:], entry) buf = buf[l:] n += l - f.entries = f.entries[1:] + f.dirIdx++ } return n, nil @@ -259,7 +260,7 @@ func Lchown(path string, uid, gid int) error { if err := checkPath(path); err != nil { return err } - if jsFS.Get("lchown") == js.Undefined() { + if jsFS.Get("lchown").IsUndefined() { // fs.lchown is unavailable on Linux until Node.js 10.6.0 // TODO(neelance): remove when we require at least this Node.js version return ENOSYS @@ -404,6 +405,14 @@ func Write(fd int, b []byte) (int, error) { return n, err } + if faketime && (fd == 1 || fd == 2) { + n := faketimeWrite(fd, b) + if n < 0 { + return 0, errnoErr(Errno(-n)) + } + return n, nil + } + buf := uint8Array.New(len(b)) js.CopyBytesToJS(buf, b) n, err := fsCall("write", fd, buf, 0, len(b), nil) @@ -462,6 +471,7 @@ func Seek(fd int, offset int64, whence int) (int64, error) { } f.seeked = true + f.dirIdx = 0 // Reset directory read position. See issue 35767. f.pos = newPos return newPos, nil } @@ -489,7 +499,7 @@ func fsCall(name string, args ...interface{}) (js.Value, error) { var res callResult if len(args) >= 1 { // on Node.js 8, fs.utimes calls the callback without any arguments - if jsErr := args[0]; jsErr != js.Null() { + if jsErr := args[0]; !jsErr.IsNull() { res.err = mapJSError(jsErr) } } diff --git a/libgo/go/syscall/getdirentries_test.go b/libgo/go/syscall/getdirentries_test.go index 8505a0b..2a3419c 100644 --- a/libgo/go/syscall/getdirentries_test.go +++ b/libgo/go/syscall/getdirentries_test.go @@ -66,7 +66,11 @@ func testGetdirentries(t *testing.T, count int) { } data := buf[:n] for len(data) > 0 { - dirent := (*syscall.Dirent)(unsafe.Pointer(&data[0])) + // If multiple Dirents are written into buf, sometimes when we reach the final one, + // we have cap(buf) < Sizeof(Dirent). So use an appropriate slice to copy from data. + var dirent syscall.Dirent + copy((*[unsafe.Sizeof(dirent)]byte)(unsafe.Pointer(&dirent))[:], data) + data = data[dirent.Reclen:] name := make([]byte, dirent.Namlen) for i := 0; i < int(dirent.Namlen); i++ { diff --git a/libgo/go/syscall/js/export_test.go b/libgo/go/syscall/js/export_test.go new file mode 100644 index 0000000..1b5ed3c --- /dev/null +++ b/libgo/go/syscall/js/export_test.go @@ -0,0 +1,9 @@ +// 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 js + +var JSGo = jsGo diff --git a/libgo/go/syscall/js/func.go b/libgo/go/syscall/js/func.go index 6b7f39b..6c145c9 100644 --- a/libgo/go/syscall/js/func.go +++ b/libgo/go/syscall/js/func.go @@ -64,7 +64,7 @@ func init() { func handleEvent() { cb := jsGo.Get("_pendingEvent") - if cb == Null() { + if cb.IsNull() { return } jsGo.Set("_pendingEvent", Null()) diff --git a/libgo/go/syscall/js/js.go b/libgo/go/syscall/js/js.go index 7300d2c..8a04399 100644 --- a/libgo/go/syscall/js/js.go +++ b/libgo/go/syscall/js/js.go @@ -12,6 +12,7 @@ package js import ( + "runtime" "unsafe" ) @@ -20,7 +21,7 @@ import ( // The JavaScript value "undefined" is represented by the value 0. // A JavaScript number (64-bit float, except 0 and NaN) is represented by its IEEE 754 binary representation. // All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as -// an ID and bits 32-33 used to differentiate between string, symbol, function and object. +// an ID and bits 32-34 used to differentiate between string, symbol, function and object. type ref uint64 // nanHead are the upper 32 bits of a ref which are set if the value is not encoded as an IEEE 754 number (see above). @@ -33,21 +34,45 @@ type Wrapper interface { } // Value represents a JavaScript value. The zero value is the JavaScript value "undefined". +// Values can be checked for equality with the Equal method. type Value struct { - ref ref + _ [0]func() // uncomparable; to make == not compile + ref ref // identifies a JavaScript value, see ref type + gcPtr *ref // used to trigger the finalizer when the Value is not referenced any more } +const ( + // the type flags need to be in sync with wasm_exec.js + typeFlagNone = iota + typeFlagObject + typeFlagString + typeFlagSymbol + typeFlagFunction +) + // JSValue implements Wrapper interface. func (v Value) JSValue() Value { return v } -func makeValue(v ref) Value { - return Value{ref: v} +func makeValue(r ref) Value { + var gcPtr *ref + typeFlag := (r >> 32) & 7 + if (r>>32)&nanHead == nanHead && typeFlag != typeFlagNone { + gcPtr = new(ref) + *gcPtr = r + runtime.SetFinalizer(gcPtr, func(p *ref) { + finalizeRef(*p) + }) + } + + return Value{ref: r, gcPtr: gcPtr} } -func predefValue(id uint32) Value { - return Value{ref: nanHead<<32 | ref(id)} +func finalizeRef(r ref) + +func predefValue(id uint32, typeFlag byte) Value { + return Value{ref: (nanHead|ref(typeFlag))<<32 | ref(id)} } func floatValue(f float64) Value { @@ -73,28 +98,48 @@ func (e Error) Error() string { var ( valueUndefined = Value{ref: 0} - valueNaN = predefValue(0) - valueZero = predefValue(1) - valueNull = predefValue(2) - valueTrue = predefValue(3) - valueFalse = predefValue(4) - valueGlobal = predefValue(5) - jsGo = predefValue(6) // instance of the Go class in JavaScript + valueNaN = predefValue(0, typeFlagNone) + valueZero = predefValue(1, typeFlagNone) + valueNull = predefValue(2, typeFlagNone) + valueTrue = predefValue(3, typeFlagNone) + valueFalse = predefValue(4, typeFlagNone) + valueGlobal = predefValue(5, typeFlagObject) + jsGo = predefValue(6, typeFlagObject) // instance of the Go class in JavaScript objectConstructor = valueGlobal.Get("Object") arrayConstructor = valueGlobal.Get("Array") ) +// Equal reports whether v and w are equal according to JavaScript's === operator. +func (v Value) Equal(w Value) bool { + return v.ref == w.ref && v.ref != valueNaN.ref +} + // Undefined returns the JavaScript value "undefined". func Undefined() Value { return valueUndefined } +// IsUndefined reports whether v is the JavaScript value "undefined". +func (v Value) IsUndefined() bool { + return v.ref == valueUndefined.ref +} + // Null returns the JavaScript value "null". func Null() Value { return valueNull } +// IsNull reports whether v is the JavaScript value "null". +func (v Value) IsNull() bool { + return v.ref == valueNull.ref +} + +// IsNaN reports whether v is the JavaScript value "NaN". +func (v Value) IsNaN() bool { + return v.ref == valueNaN.ref +} + // Global returns the JavaScript global object, usually "window" or "global". func Global() Value { return valueGlobal @@ -232,16 +277,18 @@ func (v Value) Type() Type { if v.isNumber() { return TypeNumber } - typeFlag := v.ref >> 32 & 3 + typeFlag := (v.ref >> 32) & 7 switch typeFlag { - case 1: + case typeFlagObject: + return TypeObject + case typeFlagString: return TypeString - case 2: + case typeFlagSymbol: return TypeSymbol - case 3: + case typeFlagFunction: return TypeFunction default: - return TypeObject + panic("bad type flag") } } @@ -251,7 +298,9 @@ func (v Value) Get(p string) Value { if vType := v.Type(); !vType.isObject() { panic(&ValueError{"Value.Get", vType}) } - return makeValue(valueGet(v.ref, p)) + r := makeValue(valueGet(v.ref, p)) + runtime.KeepAlive(v) + return r } func valueGet(v ref, p string) ref @@ -262,18 +311,35 @@ func (v Value) Set(p string, x interface{}) { if vType := v.Type(); !vType.isObject() { panic(&ValueError{"Value.Set", vType}) } - valueSet(v.ref, p, ValueOf(x).ref) + xv := ValueOf(x) + valueSet(v.ref, p, xv.ref) + runtime.KeepAlive(v) + runtime.KeepAlive(xv) } func valueSet(v ref, p string, x ref) +// Delete deletes the JavaScript property p of value v. +// It panics if v is not a JavaScript object. +func (v Value) Delete(p string) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.Delete", vType}) + } + valueDelete(v.ref, p) + runtime.KeepAlive(v) +} + +func valueDelete(v ref, p string) + // Index returns JavaScript index i of value v. // It panics if v is not a JavaScript object. func (v Value) Index(i int) Value { if vType := v.Type(); !vType.isObject() { panic(&ValueError{"Value.Index", vType}) } - return makeValue(valueIndex(v.ref, i)) + r := makeValue(valueIndex(v.ref, i)) + runtime.KeepAlive(v) + return r } func valueIndex(v ref, i int) ref @@ -284,17 +350,23 @@ func (v Value) SetIndex(i int, x interface{}) { if vType := v.Type(); !vType.isObject() { panic(&ValueError{"Value.SetIndex", vType}) } - valueSetIndex(v.ref, i, ValueOf(x).ref) + xv := ValueOf(x) + valueSetIndex(v.ref, i, xv.ref) + runtime.KeepAlive(v) + runtime.KeepAlive(xv) } func valueSetIndex(v ref, i int, x ref) -func makeArgs(args []interface{}) []ref { - argVals := make([]ref, len(args)) +func makeArgs(args []interface{}) ([]Value, []ref) { + argVals := make([]Value, len(args)) + argRefs := make([]ref, len(args)) for i, arg := range args { - argVals[i] = ValueOf(arg).ref + v := ValueOf(arg) + argVals[i] = v + argRefs[i] = v.ref } - return argVals + return argVals, argRefs } // Length returns the JavaScript property "length" of v. @@ -303,7 +375,9 @@ func (v Value) Length() int { if vType := v.Type(); !vType.isObject() { panic(&ValueError{"Value.SetIndex", vType}) } - return valueLength(v.ref) + r := valueLength(v.ref) + runtime.KeepAlive(v) + return r } func valueLength(v ref) int @@ -312,7 +386,10 @@ func valueLength(v ref) int // It panics if v has no method m. // The arguments get mapped to JavaScript values according to the ValueOf function. func (v Value) Call(m string, args ...interface{}) Value { - res, ok := valueCall(v.ref, m, makeArgs(args)) + argVals, argRefs := makeArgs(args) + res, ok := valueCall(v.ref, m, argRefs) + runtime.KeepAlive(v) + runtime.KeepAlive(argVals) if !ok { if vType := v.Type(); !vType.isObject() { // check here to avoid overhead in success case panic(&ValueError{"Value.Call", vType}) @@ -331,7 +408,10 @@ func valueCall(v ref, m string, args []ref) (ref, bool) // It panics if v is not a JavaScript function. // The arguments get mapped to JavaScript values according to the ValueOf function. func (v Value) Invoke(args ...interface{}) Value { - res, ok := valueInvoke(v.ref, makeArgs(args)) + argVals, argRefs := makeArgs(args) + res, ok := valueInvoke(v.ref, argRefs) + runtime.KeepAlive(v) + runtime.KeepAlive(argVals) if !ok { if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case panic(&ValueError{"Value.Invoke", vType}) @@ -347,7 +427,10 @@ func valueInvoke(v ref, args []ref) (ref, bool) // It panics if v is not a JavaScript function. // The arguments get mapped to JavaScript values according to the ValueOf function. func (v Value) New(args ...interface{}) Value { - res, ok := valueNew(v.ref, makeArgs(args)) + argVals, argRefs := makeArgs(args) + res, ok := valueNew(v.ref, argRefs) + runtime.KeepAlive(v) + runtime.KeepAlive(argVals) if !ok { if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case panic(&ValueError{"Value.Invoke", vType}) @@ -362,7 +445,7 @@ func valueNew(v ref, args []ref) (ref, bool) func (v Value) isNumber() bool { return v.ref == valueZero.ref || v.ref == valueNaN.ref || - (v.ref != valueUndefined.ref && v.ref>>32&nanHead != nanHead) + (v.ref != valueUndefined.ref && (v.ref>>32)&nanHead != nanHead) } func (v Value) float(method string) float64 { @@ -427,15 +510,15 @@ func (v Value) Truthy() bool { func (v Value) String() string { switch v.Type() { case TypeString: - return jsString(v.ref) + return jsString(v) case TypeUndefined: return "<undefined>" case TypeNull: return "<null>" case TypeBoolean: - return "<boolean: " + jsString(v.ref) + ">" + return "<boolean: " + jsString(v) + ">" case TypeNumber: - return "<number: " + jsString(v.ref) + ">" + return "<number: " + jsString(v) + ">" case TypeSymbol: return "<symbol>" case TypeObject: @@ -447,10 +530,12 @@ func (v Value) String() string { } } -func jsString(v ref) string { - str, length := valuePrepareString(v) +func jsString(v Value) string { + str, length := valuePrepareString(v.ref) + runtime.KeepAlive(v) b := make([]byte, length) valueLoadString(str, b) + finalizeRef(str) return string(b) } @@ -460,7 +545,10 @@ func valueLoadString(v ref, b []byte) // InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator. func (v Value) InstanceOf(t Value) bool { - return valueInstanceOf(v.ref, t.ref) + r := valueInstanceOf(v.ref, t.ref) + runtime.KeepAlive(v) + runtime.KeepAlive(t) + return r } func valueInstanceOf(v ref, t ref) bool @@ -482,6 +570,7 @@ func (e *ValueError) Error() string { // CopyBytesToGo panics if src is not an Uint8Array. func CopyBytesToGo(dst []byte, src Value) int { n, ok := copyBytesToGo(dst, src.ref) + runtime.KeepAlive(src) if !ok { panic("syscall/js: CopyBytesToGo: expected src to be an Uint8Array") } @@ -495,6 +584,7 @@ func copyBytesToGo(dst []byte, src ref) (int, bool) // CopyBytesToJS panics if dst is not an Uint8Array. func CopyBytesToJS(dst Value, src []byte) int { n, ok := copyBytesToJS(dst.ref, src) + runtime.KeepAlive(dst) if !ok { panic("syscall/js: CopyBytesToJS: expected dst to be an Uint8Array") } diff --git a/libgo/go/syscall/js/js_test.go b/libgo/go/syscall/js/js_test.go index 7a1e346..fea4c13 100644 --- a/libgo/go/syscall/js/js_test.go +++ b/libgo/go/syscall/js/js_test.go @@ -18,6 +18,7 @@ package js_test import ( "fmt" "math" + "runtime" "syscall/js" "testing" ) @@ -53,7 +54,7 @@ func TestBool(t *testing.T) { if got := dummys.Get("otherBool").Bool(); got != want { t.Errorf("got %#v, want %#v", got, want) } - if dummys.Get("someBool") != dummys.Get("someBool") { + if !dummys.Get("someBool").Equal(dummys.Get("someBool")) { t.Errorf("same value not equal") } } @@ -68,7 +69,7 @@ func TestString(t *testing.T) { if got := dummys.Get("otherString").String(); got != want { t.Errorf("got %#v, want %#v", got, want) } - if dummys.Get("someString") != dummys.Get("someString") { + if !dummys.Get("someString").Equal(dummys.Get("someString")) { t.Errorf("same value not equal") } @@ -105,7 +106,7 @@ func TestInt(t *testing.T) { if got := dummys.Get("otherInt").Int(); got != want { t.Errorf("got %#v, want %#v", got, want) } - if dummys.Get("someInt") != dummys.Get("someInt") { + if !dummys.Get("someInt").Equal(dummys.Get("someInt")) { t.Errorf("same value not equal") } if got := dummys.Get("zero").Int(); got != 0 { @@ -141,20 +142,20 @@ func TestFloat(t *testing.T) { if got := dummys.Get("otherFloat").Float(); got != want { t.Errorf("got %#v, want %#v", got, want) } - if dummys.Get("someFloat") != dummys.Get("someFloat") { + if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) { t.Errorf("same value not equal") } } func TestObject(t *testing.T) { - if dummys.Get("someArray") != dummys.Get("someArray") { + if !dummys.Get("someArray").Equal(dummys.Get("someArray")) { t.Errorf("same value not equal") } // An object and its prototype should not be equal. proto := js.Global().Get("Object").Get("prototype") o := js.Global().Call("eval", "new Object()") - if proto == o { + if proto.Equal(o) { t.Errorf("object equals to its prototype") } } @@ -167,26 +168,66 @@ func TestFrozenObject(t *testing.T) { } } +func TestEqual(t *testing.T) { + if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) { + t.Errorf("same float is not equal") + } + if !dummys.Get("emptyObj").Equal(dummys.Get("emptyObj")) { + t.Errorf("same object is not equal") + } + if dummys.Get("someFloat").Equal(dummys.Get("someInt")) { + t.Errorf("different values are not unequal") + } +} + func TestNaN(t *testing.T) { - want := js.ValueOf(math.NaN()) - got := dummys.Get("NaN") - if got != want { - t.Errorf("got %#v, want %#v", got, want) + if !dummys.Get("NaN").IsNaN() { + t.Errorf("JS NaN is not NaN") + } + if !js.ValueOf(math.NaN()).IsNaN() { + t.Errorf("Go NaN is not NaN") + } + if dummys.Get("NaN").Equal(dummys.Get("NaN")) { + t.Errorf("NaN is equal to NaN") } } func TestUndefined(t *testing.T) { - dummys.Set("test", js.Undefined()) - if dummys == js.Undefined() || dummys.Get("test") != js.Undefined() || dummys.Get("xyz") != js.Undefined() { - t.Errorf("js.Undefined expected") + if !js.Undefined().IsUndefined() { + t.Errorf("undefined is not undefined") + } + if !js.Undefined().Equal(js.Undefined()) { + t.Errorf("undefined is not equal to undefined") + } + if dummys.IsUndefined() { + t.Errorf("object is undefined") + } + if js.Undefined().IsNull() { + t.Errorf("undefined is null") + } + if dummys.Set("test", js.Undefined()); !dummys.Get("test").IsUndefined() { + t.Errorf("could not set undefined") } } func TestNull(t *testing.T) { - dummys.Set("test1", nil) - dummys.Set("test2", js.Null()) - if dummys == js.Null() || dummys.Get("test1") != js.Null() || dummys.Get("test2") != js.Null() { - t.Errorf("js.Null expected") + if !js.Null().IsNull() { + t.Errorf("null is not null") + } + if !js.Null().Equal(js.Null()) { + t.Errorf("null is not equal to null") + } + if dummys.IsNull() { + t.Errorf("object is null") + } + if js.Null().IsUndefined() { + t.Errorf("null is undefined") + } + if dummys.Set("test", js.Null()); !dummys.Get("test").IsNull() { + t.Errorf("could not set null") + } + if dummys.Set("test", nil); !dummys.Get("test").IsNull() { + t.Errorf("could not set nil") } } @@ -212,6 +253,18 @@ func TestSet(t *testing.T) { }) } +func TestDelete(t *testing.T) { + dummys.Set("test", 42) + dummys.Delete("test") + if dummys.Call("hasOwnProperty", "test").Bool() { + t.Errorf("property still exists") + } + + expectValueError(t, func() { + dummys.Get("zero").Delete("badField") + }) +} + func TestIndex(t *testing.T) { if got := dummys.Get("someArray").Index(1).Int(); got != 42 { t.Errorf("got %#v, want %#v", got, 42) @@ -328,7 +381,7 @@ func TestValueOf(t *testing.T) { func TestZeroValue(t *testing.T) { var v js.Value - if v != js.Undefined() { + if !v.IsUndefined() { t.Error("zero js.Value is not js.Undefined()") } } @@ -366,6 +419,25 @@ func TestInvokeFunction(t *testing.T) { } } +func TestInterleavedFunctions(t *testing.T) { + c1 := make(chan struct{}) + c2 := make(chan struct{}) + + js.Global().Get("setTimeout").Invoke(js.FuncOf(func(this js.Value, args []js.Value) interface{} { + c1 <- struct{}{} + <-c2 + return nil + }), 0) + + <-c1 + c2 <- struct{}{} + // this goroutine is running, but the callback of setTimeout did not return yet, invoke another function now + f := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + return nil + }) + f.Invoke() +} + func ExampleFuncOf() { var cb js.Func cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { @@ -484,3 +556,38 @@ func TestCopyBytesToJS(t *testing.T) { }) } } + +func TestGarbageCollection(t *testing.T) { + before := js.JSGo.Get("_values").Length() + for i := 0; i < 1000; i++ { + _ = js.Global().Get("Object").New().Call("toString").String() + runtime.GC() + } + after := js.JSGo.Get("_values").Length() + if after-before > 500 { + t.Errorf("garbage collection ineffective") + } +} + +// BenchmarkDOM is a simple benchmark which emulates a webapp making DOM operations. +// It creates a div, and sets its id. Then searches by that id and sets some data. +// Finally it removes that div. +func BenchmarkDOM(b *testing.B) { + document := js.Global().Get("document") + if document.IsUndefined() { + b.Skip("Not a browser environment. Skipping.") + } + const data = "someString" + for i := 0; i < b.N; i++ { + div := document.Call("createElement", "div") + div.Call("setAttribute", "id", "myDiv") + document.Get("body").Call("appendChild", div) + myDiv := document.Call("getElementById", "myDiv") + myDiv.Set("innerHTML", data) + + if got, want := myDiv.Get("innerHTML").String(), data; got != want { + b.Errorf("got %s, want %s", got, want) + } + document.Get("body").Call("removeChild", div) + } +} diff --git a/libgo/go/syscall/lsf_linux.go b/libgo/go/syscall/lsf_linux.go index 16b702a..f38de62 100644 --- a/libgo/go/syscall/lsf_linux.go +++ b/libgo/go/syscall/lsf_linux.go @@ -23,6 +23,8 @@ func LsfJump(code, k, jt, jf int) *SockFilter { // Deprecated: Use golang.org/x/net/bpf instead. func LsfSocket(ifindex, proto int) (int, error) { var lsall SockaddrLinklayer + // This is missing SOCK_CLOEXEC, but adding the flag + // could break callers. s, e := Socket(AF_PACKET, SOCK_RAW, proto) if e != nil { return 0, e @@ -46,7 +48,7 @@ type iflags struct { // Deprecated: Use golang.org/x/net/bpf instead. func SetLsfPromisc(name string, m bool) error { - s, e := Socket(AF_INET, SOCK_DGRAM, 0) + s, e := cloexecSocket(AF_INET, SOCK_DGRAM, 0) if e != nil { return e } diff --git a/libgo/go/syscall/net_js.go b/libgo/go/syscall/net_js.go index d5bf1f4..25f171b 100644 --- a/libgo/go/syscall/net_js.go +++ b/libgo/go/syscall/net_js.go @@ -42,7 +42,7 @@ const ( const ( _ = iota F_DUPFD_CLOEXEC - SYS_FCNTL = 500 // unsupported; same value as net_nacl.go + SYS_FCNTL = 500 // unsupported ) type Sockaddr interface { diff --git a/libgo/go/syscall/netlink_linux.go b/libgo/go/syscall/netlink_linux.go index 1cda8c7..0937ff7 100644 --- a/libgo/go/syscall/netlink_linux.go +++ b/libgo/go/syscall/netlink_linux.go @@ -50,7 +50,7 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte { // NetlinkRIB returns routing information base, as known as RIB, which // consists of network facility information, states and parameters. func NetlinkRIB(proto, family int) ([]byte, error) { - s, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) + s, err := cloexecSocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) if err != nil { return nil, err } diff --git a/libgo/go/syscall/route_freebsd_64bit.go b/libgo/go/syscall/route_freebsd_64bit.go index 728837e..e70ba3d 100644 --- a/libgo/go/syscall/route_freebsd_64bit.go +++ b/libgo/go/syscall/route_freebsd_64bit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build freebsd,amd64 +// +build freebsd,amd64 freebsd,arm64 package syscall diff --git a/libgo/go/syscall/security_windows.go b/libgo/go/syscall/security_windows.go index db80d98..3a75759 100644 --- a/libgo/go/syscall/security_windows.go +++ b/libgo/go/syscall/security_windows.go @@ -163,7 +163,7 @@ func (sid *SID) String() (string, error) { return "", e } defer LocalFree((Handle)(unsafe.Pointer(s))) - return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]), nil + return utf16PtrToString(s, 256), nil } // Len returns the length, in bytes, of a valid security identifier sid. diff --git a/libgo/go/syscall/sock_cloexec_linux.go b/libgo/go/syscall/sock_cloexec_linux.go new file mode 100644 index 0000000..600cf25 --- /dev/null +++ b/libgo/go/syscall/sock_cloexec_linux.go @@ -0,0 +1,29 @@ +// Copyright 2019 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 syscall + +// This is a stripped down version of sysSocket from net/sock_cloexec.go. +func cloexecSocket(family, sotype, proto int) (int, error) { + s, err := Socket(family, sotype|SOCK_CLOEXEC, proto) + switch err { + case nil: + return s, nil + default: + return -1, err + case EINVAL: + } + + ForkLock.RLock() + s, err = Socket(family, sotype, proto) + if err == nil { + CloseOnExec(s) + } + ForkLock.RUnlock() + if err != nil { + Close(s) + return -1, err + } + return s, nil +} diff --git a/libgo/go/syscall/sockcmsg_dragonfly.go b/libgo/go/syscall/sockcmsg_dragonfly.go new file mode 100644 index 0000000..d217d9e --- /dev/null +++ b/libgo/go/syscall/sockcmsg_dragonfly.go @@ -0,0 +1,16 @@ +// Copyright 2019 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 syscall + +// Round the length of a raw sockaddr up to align it properly. +func cmsgAlignOf(salen int) int { + salign := sizeofPtr + if sizeofPtr == 8 && !supportsABI(_dragonflyABIChangeVersion) { + // 64-bit Dragonfly before the September 2019 ABI changes still requires + // 32-bit aligned access to network subsystem. + salign = 4 + } + return (salen + salign - 1) & ^(salign - 1) +} diff --git a/libgo/go/syscall/sockcmsg_linux.go b/libgo/go/syscall/sockcmsg_linux.go index 4cb9075..d97667c 100644 --- a/libgo/go/syscall/sockcmsg_linux.go +++ b/libgo/go/syscall/sockcmsg_linux.go @@ -17,7 +17,7 @@ func UnixCredentials(ucred *Ucred) []byte { h.Level = SOL_SOCKET h.Type = SCM_CREDENTIALS h.SetLen(CmsgLen(SizeofUcred)) - *((*Ucred)(cmsgData(h))) = *ucred + *(*Ucred)(h.data(0)) = *ucred return b } diff --git a/libgo/go/syscall/sockcmsg_unix.go b/libgo/go/syscall/sockcmsg_unix.go index dd74417..6530bea 100644 --- a/libgo/go/syscall/sockcmsg_unix.go +++ b/libgo/go/syscall/sockcmsg_unix.go @@ -9,35 +9,9 @@ package syscall import ( - "runtime" "unsafe" ) -// Round the length of a raw sockaddr up to align it properly. -func cmsgAlignOf(salen int) int { - salign := int(sizeofPtr) - - switch runtime.GOOS { - case "aix": - // There is no alignment on AIX. - salign = 1 - case "darwin", "dragonfly", "illumos", "solaris": - // NOTE: It seems like 64-bit Darwin, DragonFly BSD and - // Solaris kernels still require 32-bit aligned access to - // network subsystem. - if sizeofPtr == 8 { - salign = 4 - } - case "netbsd", "openbsd": - // NetBSD and OpenBSD armv7 require 64-bit alignment. - if runtime.GOARCH == "arm" { - salign = 8 - } - } - - return (salen + salign - 1) & ^(salign - 1) -} - // CmsgLen returns the value to store in the Len field of the Cmsghdr // structure, taking into account any necessary alignment. func CmsgLen(datalen int) int { @@ -50,8 +24,8 @@ func CmsgSpace(datalen int) int { return cmsgAlignOf(SizeofCmsghdr) + cmsgAlignOf(datalen) } -func cmsgData(h *Cmsghdr) unsafe.Pointer { - return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr))) +func (h *Cmsghdr) data(offset uintptr) unsafe.Pointer { + return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)) + offset) } // SocketControlMessage represents a socket control message. @@ -94,10 +68,8 @@ func UnixRights(fds ...int) []byte { h.Level = SOL_SOCKET h.Type = SCM_RIGHTS h.SetLen(CmsgLen(datalen)) - data := cmsgData(h) - for _, fd := range fds { - *(*int32)(data) = int32(fd) - data = unsafe.Pointer(uintptr(data) + 4) + for i, fd := range fds { + *(*int32)(h.data(4 * uintptr(i))) = int32(fd) } return b } diff --git a/libgo/go/syscall/sockcmsg_unix_other.go b/libgo/go/syscall/sockcmsg_unix_other.go new file mode 100644 index 0000000..65d9f18 --- /dev/null +++ b/libgo/go/syscall/sockcmsg_unix_other.go @@ -0,0 +1,38 @@ +// Copyright 2019 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 aix darwin freebsd linux netbsd openbsd solaris + +package syscall + +import ( + "runtime" +) + +// Round the length of a raw sockaddr up to align it properly. +func cmsgAlignOf(salen int) int { + salign := int(sizeofPtr) + + // dragonfly needs to check ABI version at runtime, see cmsgAlignOf in + // sockcmsg_dragonfly.go + switch runtime.GOOS { + case "aix": + // There is no alignment on AIX. + salign = 1 + case "darwin", "illumos", "solaris": + // NOTE: It seems like 64-bit Darwin, Illumos and Solaris + // kernels still require 32-bit aligned access to network + // subsystem. + if sizeofPtr == 8 { + salign = 4 + } + case "netbsd", "openbsd": + // NetBSD and OpenBSD armv7 require 64-bit alignment. + if runtime.GOARCH == "arm" { + salign = 8 + } + } + + return (salen + salign - 1) & ^(salign - 1) +} diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index 9b74afe..4e1187b 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -28,7 +28,7 @@ package syscall import "unsafe" -//go:generate go run mksyscall_windows.go -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -systemdll -output zsyscall_windows.go syscall_windows.go security_windows.go // StringByteSlice converts a string to a NUL-terminated []byte, // If s contains a NUL byte this function panics instead of diff --git a/libgo/go/syscall/syscall_aix.go b/libgo/go/syscall/syscall_aix.go index 231ff40..f4cac01 100644 --- a/libgo/go/syscall/syscall_aix.go +++ b/libgo/go/syscall/syscall_aix.go @@ -23,5 +23,9 @@ func direntReclen(buf []byte) (uint64, bool) { } func direntNamlen(buf []byte) (uint64, bool) { - return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen)) + reclen, ok := direntReclen(buf) + if !ok { + return 0, false + } + return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true } diff --git a/libgo/go/syscall/syscall_dragonfly.go b/libgo/go/syscall/syscall_dragonfly.go index 3e6617c..930bb46 100644 --- a/libgo/go/syscall/syscall_dragonfly.go +++ b/libgo/go/syscall/syscall_dragonfly.go @@ -4,7 +4,25 @@ package syscall -import "unsafe" +import ( + "sync" + "unsafe" +) + +// See version list in https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sys/sys/param.h +var ( + osreldateOnce sync.Once + osreldate uint32 +) + +// First __DragonFly_version after September 2019 ABI changes +// http://lists.dragonflybsd.org/pipermail/users/2019-September/358280.html +const _dragonflyABIChangeVersion = 500705 + +func supportsABI(ver uint32) bool { + osreldateOnce.Do(func() { osreldate, _ = SysctlUint32("kern.osreldate") }) + return osreldate >= ver +} func direntIno(buf []byte) (uint64, bool) { return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno)) diff --git a/libgo/go/syscall/syscall_errno.go b/libgo/go/syscall/syscall_errno.go index a339ae8..0d781de 100644 --- a/libgo/go/syscall/syscall_errno.go +++ b/libgo/go/syscall/syscall_errno.go @@ -13,6 +13,12 @@ import "internal/oserror" // if errno != 0 { // err = errno // } +// +// Errno values can be tested against error values from the os package +// using errors.Is. For example: +// +// _, _, err := syscall.Syscall(...) +// if errors.Is(err, os.ErrNotExist) ... type Errno uintptr func (e Errno) Error() string { @@ -32,7 +38,7 @@ func (e Errno) Is(target error) bool { } func (e Errno) Temporary() bool { - return e == EINTR || e == EMFILE || e.Timeout() + return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout() } func (e Errno) Timeout() bool { diff --git a/libgo/go/syscall/syscall_freebsd_arm64.go b/libgo/go/syscall/syscall_freebsd_arm64.go new file mode 100644 index 0000000..b561141 --- /dev/null +++ b/libgo/go/syscall/syscall_freebsd_arm64.go @@ -0,0 +1,33 @@ +// Copyright 2019 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 syscall + +import "unsafe" + +func setTimespec(sec, nsec int64) Timespec { + return Timespec{Sec: sec, Nsec: nsec} +} + +func setTimeval(sec, usec int64) Timeval { + return Timeval{Sec: sec, Usec: usec} +} + +func SetKevent(k *Kevent_t, fd, mode, flags int) { + k.Ident = uint64(fd) + k.Filter = int16(mode) + k.Flags = uint16(flags) +} + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} diff --git a/libgo/go/syscall/syscall_js.go b/libgo/go/syscall/syscall_js.go index 175fe47..dfb4a27 100644 --- a/libgo/go/syscall/syscall_js.go +++ b/libgo/go/syscall/syscall_js.go @@ -44,6 +44,12 @@ const PathMax = 256 // if errno != 0 { // err = errno // } +// +// Errno values can be tested against error values from the os package +// using errors.Is. For example: +// +// _, _, err := syscall.Syscall(...) +// if errors.Is(err, os.ErrNotExist) ... type Errno uintptr func (e Errno) Error() string { @@ -297,9 +303,10 @@ func Getegid() int { return jsProcess.Call("getegid").Int() } -func Getgroups() ([]int, error) { +func Getgroups() (groups []int, err error) { + defer recoverErr(&err) array := jsProcess.Call("getgroups") - groups := make([]int, array.Length()) + groups = make([]int, array.Length()) for i := range groups { groups[i] = array.Index(i).Int() } diff --git a/libgo/go/syscall/syscall_linux_riscv64.go b/libgo/go/syscall/syscall_linux_riscv64.go new file mode 100644 index 0000000..e9aab94 --- /dev/null +++ b/libgo/go/syscall/syscall_linux_riscv64.go @@ -0,0 +1,23 @@ +// Copyright 2019 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 syscall + +import "unsafe" + +func (r *PtraceRegs) PC() uint64 { return r.Pc } + +func (r *PtraceRegs) SetPC(pc uint64) { r.Pc = pc } + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint64(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint64(length) +} diff --git a/libgo/go/syscall/syscall_linux_test.go b/libgo/go/syscall/syscall_linux_test.go index 2ad2365..97059c8 100644 --- a/libgo/go/syscall/syscall_linux_test.go +++ b/libgo/go/syscall/syscall_linux_test.go @@ -299,6 +299,14 @@ func TestSyscallNoError(t *testing.T) { t.Skip("skipping on non-32bit architecture") } + // See https://golang.org/issue/35422 + // On MIPS, Linux returns whether the syscall had an error in a separate + // register (R7), not using a negative return value as on other + // architectures. + if runtime.GOARCH == "mips" || runtime.GOARCH == "mipsle" { + t.Skipf("skipping on %s", runtime.GOARCH) + } + if os.Getuid() != 0 { t.Skip("skipping root only test") } @@ -367,7 +375,8 @@ func TestSyscallNoError(t *testing.T) { if filesystemIsNoSUID(tmpBinary) { t.Skip("skipping test when temp dir is mounted nosuid") } - t.Errorf("expected %s, got %s", want, got) + // formatted so the values are aligned for easier comparison + t.Errorf("expected %s,\ngot %s", want, got) } } diff --git a/libgo/go/syscall/syscall_test.go b/libgo/go/syscall/syscall_test.go index 2a9d90e..5390f8a 100644 --- a/libgo/go/syscall/syscall_test.go +++ b/libgo/go/syscall/syscall_test.go @@ -62,7 +62,7 @@ func TestExecErrPermutedFds(t *testing.T) { } func TestGettimeofday(t *testing.T) { - if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + if runtime.GOOS == "js" { t.Skip("not implemented on " + runtime.GOOS) } tv := &syscall.Timeval{} diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index 37999a2..16e4d480 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -165,7 +165,14 @@ func Write(fd int, p []byte) (n int, err error) { if race.Enabled { race.ReleaseMerge(unsafe.Pointer(&ioSync)) } - n, err = write(fd, p) + if faketime && (fd == 1 || fd == 2) { + n = faketimeWrite(fd, p) + if n < 0 { + n, err = 0, errnoErr(Errno(-n)) + } + } else { + n, err = write(fd, p) + } if race.Enabled && n > 0 { race.ReadRange(unsafe.Pointer(&p[0]), n) } diff --git a/libgo/go/syscall/syscall_unix_test.go b/libgo/go/syscall/syscall_unix_test.go index da259b9..b99e07d 100644 --- a/libgo/go/syscall/syscall_unix_test.go +++ b/libgo/go/syscall/syscall_unix_test.go @@ -386,3 +386,9 @@ func TestSetsockoptString(t *testing.T) { t.Fatalf("SetsockoptString: did not fail") } } + +func TestENFILETemporary(t *testing.T) { + if !syscall.ENFILE.Temporary() { + t.Error("ENFILE is not treated as a temporary error") + } +} diff --git a/libgo/go/syscall/tables_nacljs.go b/libgo/go/syscall/tables_js.go index 1c265f2..a7c4f8c 100644 --- a/libgo/go/syscall/tables_nacljs.go +++ b/libgo/go/syscall/tables_js.go @@ -2,13 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build nacl js,wasm +// +build js,wasm package syscall import "runtime" -// TODO: generate with runtime/mknacl.sh, allow override with IRT. +// These were originally used by Nacl, then later also used by +// js/wasm. Now that they're only used by js/wasm, these numbers are +// just arbitrary. +// +// TODO: delete? replace with something meaningful? const ( sys_null = 1 sys_nameservice = 2 diff --git a/libgo/go/syscall/time_fake.go b/libgo/go/syscall/time_fake.go new file mode 100644 index 0000000..5dec57a --- /dev/null +++ b/libgo/go/syscall/time_fake.go @@ -0,0 +1,26 @@ +// Copyright 2019 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 faketime + +package syscall + +import "unsafe" + +const faketime = true + +// When faketime is enabled, we redirect writes to FDs 1 and 2 through +// the runtime's write function, since that adds the framing that +// reports the emulated time. + +//go:linkname runtimeWrite runtime.write +func runtimeWrite(fd uintptr, p unsafe.Pointer, n int32) int32 + +func faketimeWrite(fd int, p []byte) int { + var pp *byte + if len(p) > 0 { + pp = &p[0] + } + return int(runtimeWrite(uintptr(fd), unsafe.Pointer(pp), int32(len(p)))) +} diff --git a/libgo/go/syscall/time_nofake.go b/libgo/go/syscall/time_nofake.go new file mode 100644 index 0000000..c94cef8 --- /dev/null +++ b/libgo/go/syscall/time_nofake.go @@ -0,0 +1,14 @@ +// Copyright 2019 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 !faketime + +package syscall + +const faketime = false + +func faketimeWrite(fd int, p []byte) int { + // This should never be called since faketime is false. + panic("not implemented") +} diff --git a/libgo/go/syscall/timestruct.go b/libgo/go/syscall/timestruct.go index e2b7704..bd0b3de 100644 --- a/libgo/go/syscall/timestruct.go +++ b/libgo/go/syscall/timestruct.go @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build aix darwin dragonfly freebsd hurd js,wasm linux nacl netbsd openbsd solaris +// +build aix darwin dragonfly freebsd hurd js,wasm linux netbsd openbsd solaris package syscall // TimespecToNsec converts a Timespec value into a number of // nanoseconds since the Unix epoch. -func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } +func TimespecToNsec(ts Timespec) int64 { return ts.Nano() } // NsecToTimespec takes a number of nanoseconds since the Unix epoch // and returns the corresponding Timespec value. @@ -24,7 +24,7 @@ func NsecToTimespec(nsec int64) Timespec { // TimevalToNsec converts a Timeval value into a number of nanoseconds // since the Unix epoch. -func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 } +func TimevalToNsec(tv Timeval) int64 { return tv.Nano() } // NsecToTimeval takes a number of nanoseconds since the Unix epoch // and returns the corresponding Timeval value. |