diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-12-12 23:40:51 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-12-12 23:40:51 +0000 |
commit | ab61e9c4da707f3bc7b177c0c8f92daccdb142dc (patch) | |
tree | 0c68629fac9d7c6f103b401c9063ef00ed259f06 /libgo/go | |
parent | 6e456f4cf4deee3e2ccd9849286f59b90644c48b (diff) | |
download | gcc-ab61e9c4da707f3bc7b177c0c8f92daccdb142dc.zip gcc-ab61e9c4da707f3bc7b177c0c8f92daccdb142dc.tar.gz gcc-ab61e9c4da707f3bc7b177c0c8f92daccdb142dc.tar.bz2 |
libgo: Update to weekly.2011-11-18.
From-SVN: r182266
Diffstat (limited to 'libgo/go')
211 files changed, 6000 insertions, 3929 deletions
diff --git a/libgo/go/bufio/bufio_test.go b/libgo/go/bufio/bufio_test.go index 1f89395..54029cd 100644 --- a/libgo/go/bufio/bufio_test.go +++ b/libgo/go/bufio/bufio_test.go @@ -10,7 +10,6 @@ import ( "fmt" "io" "io/ioutil" - "os" "strings" "testing" "testing/iotest" @@ -425,9 +424,9 @@ var errorWriterTests = []errorWriterTest{ {0, 1, nil, io.ErrShortWrite}, {1, 2, nil, io.ErrShortWrite}, {1, 1, nil, nil}, - {0, 1, os.EPIPE, os.EPIPE}, - {1, 2, os.EPIPE, os.EPIPE}, - {1, 1, os.EPIPE, os.EPIPE}, + {0, 1, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 2, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 1, io.ErrClosedPipe, io.ErrClosedPipe}, } func TestWriteErrors(t *testing.T) { diff --git a/libgo/go/builtin/builtin.go b/libgo/go/builtin/builtin.go index 5a7aaf3..e81616c 100644 --- a/libgo/go/builtin/builtin.go +++ b/libgo/go/builtin/builtin.go @@ -91,6 +91,11 @@ type rune rune // invocation. type Type int +// Type1 is here for the purposes of documentation only. It is a stand-in +// for any Go type, but represents the same type for any given function +// invocation. +type Type1 int + // IntegerType is here for the purposes of documentation only. It is a stand-in // for any integer type: int, uint, int8 etc. type IntegerType int @@ -119,6 +124,11 @@ func append(slice []Type, elems ...Type) []Type // len(src) and len(dst). func copy(dst, src []Type) int +// The delete built-in function deletes the element with the specified key +// (m[key]) from the map. If there is no such element, delete is a no-op. +// If m is nil, delete panics. +func delete(m map[Type]Type1, key Type) + // The len built-in function returns the length of v, according to its type: // Array: the number of elements in v. // Pointer to array: the number of elements in *v (even if v is nil). @@ -171,7 +181,7 @@ func complex(r, i FloatType) ComplexType // The return value will be floating point type corresponding to the type of c. func real(c ComplexType) FloatType -// The imaginary built-in function returns the imaginary part of the complex +// The imag built-in function returns the imaginary part of the complex // number c. The return value will be floating point type corresponding to // the type of c. func imag(c ComplexType) FloatType diff --git a/libgo/go/bytes/bytes_test.go b/libgo/go/bytes/bytes_test.go index 9256b18..21a1a4f 100644 --- a/libgo/go/bytes/bytes_test.go +++ b/libgo/go/bytes/bytes_test.go @@ -662,48 +662,49 @@ func TestRunes(t *testing.T) { } type TrimTest struct { - f func([]byte, string) []byte + f string in, cutset, out string } var trimTests = []TrimTest{ - {Trim, "abba", "a", "bb"}, - {Trim, "abba", "ab", ""}, - {TrimLeft, "abba", "ab", ""}, - {TrimRight, "abba", "ab", ""}, - {TrimLeft, "abba", "a", "bba"}, - {TrimRight, "abba", "a", "abb"}, - {Trim, "<tag>", "<>", "tag"}, - {Trim, "* listitem", " *", "listitem"}, - {Trim, `"quote"`, `"`, "quote"}, - {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, + {"Trim", "abba", "a", "bb"}, + {"Trim", "abba", "ab", ""}, + {"TrimLeft", "abba", "ab", ""}, + {"TrimRight", "abba", "ab", ""}, + {"TrimLeft", "abba", "a", "bba"}, + {"TrimRight", "abba", "a", "abb"}, + {"Trim", "<tag>", "<>", "tag"}, + {"Trim", "* listitem", " *", "listitem"}, + {"Trim", `"quote"`, `"`, "quote"}, + {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, //empty string tests - {Trim, "abba", "", "abba"}, - {Trim, "", "123", ""}, - {Trim, "", "", ""}, - {TrimLeft, "abba", "", "abba"}, - {TrimLeft, "", "123", ""}, - {TrimLeft, "", "", ""}, - {TrimRight, "abba", "", "abba"}, - {TrimRight, "", "123", ""}, - {TrimRight, "", "", ""}, - {TrimRight, "☺\xc0", "☺", "☺\xc0"}, + {"Trim", "abba", "", "abba"}, + {"Trim", "", "123", ""}, + {"Trim", "", "", ""}, + {"TrimLeft", "abba", "", "abba"}, + {"TrimLeft", "", "123", ""}, + {"TrimLeft", "", "", ""}, + {"TrimRight", "abba", "", "abba"}, + {"TrimRight", "", "123", ""}, + {"TrimRight", "", "", ""}, + {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, } func TestTrim(t *testing.T) { for _, tc := range trimTests { - actual := string(tc.f([]byte(tc.in), tc.cutset)) - var name string - switch tc.f { - case Trim: - name = "Trim" - case TrimLeft: - name = "TrimLeft" - case TrimRight: - name = "TrimRight" + name := tc.f + var f func([]byte, string) []byte + switch name { + case "Trim": + f = Trim + case "TrimLeft": + f = TrimLeft + case "TrimRight": + f = TrimRight default: - t.Error("Undefined trim function") + t.Error("Undefined trim function %s", name) } + actual := string(f([]byte(tc.in), tc.cutset)) if actual != tc.out { t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) } diff --git a/libgo/go/compress/lzw/reader.go b/libgo/go/compress/lzw/reader.go index c787a95..0ed742c 100644 --- a/libgo/go/compress/lzw/reader.go +++ b/libgo/go/compress/lzw/reader.go @@ -19,7 +19,6 @@ import ( "errors" "fmt" "io" - "os" ) // Order specifies the bit ordering in an LZW data stream. @@ -212,8 +211,10 @@ func (d *decoder) flush() { d.o = 0 } +var errClosed = errors.New("compress/lzw: reader/writer is closed") + func (d *decoder) Close() error { - d.err = os.EINVAL // in case any Reads come along + d.err = errClosed // in case any Reads come along return nil } diff --git a/libgo/go/compress/lzw/writer.go b/libgo/go/compress/lzw/writer.go index 3f380fa..488ba64 100644 --- a/libgo/go/compress/lzw/writer.go +++ b/libgo/go/compress/lzw/writer.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "os" ) // A writer is a buffered, flushable writer. @@ -49,8 +48,9 @@ const ( type encoder struct { // w is the writer that compressed bytes are written to. w writer - // write, bits, nBits and width are the state for converting a code stream - // into a byte stream. + // order, write, bits, nBits and width are the state for + // converting a code stream into a byte stream. + order Order write func(*encoder, uint32) error bits uint32 nBits uint @@ -64,7 +64,7 @@ type encoder struct { // call. It is equal to invalidCode if there was no such call. savedCode uint32 // err is the first error encountered during writing. Closing the encoder - // will make any future Write calls return os.EINVAL. + // will make any future Write calls return errClosed err error // table is the hash table from 20-bit keys to 12-bit values. Each table // entry contains key<<12|val and collisions resolve by linear probing. @@ -191,13 +191,13 @@ loop: // flush e's underlying writer. func (e *encoder) Close() error { if e.err != nil { - if e.err == os.EINVAL { + if e.err == errClosed { return nil } return e.err } - // Make any future calls to Write return os.EINVAL. - e.err = os.EINVAL + // Make any future calls to Write return errClosed. + e.err = errClosed // Write the savedCode if valid. if e.savedCode != invalidCode { if err := e.write(e, e.savedCode); err != nil { @@ -214,7 +214,7 @@ func (e *encoder) Close() error { } // Write the final bits. if e.nBits > 0 { - if e.write == (*encoder).writeMSB { + if e.order == MSB { e.bits >>= 24 } if err := e.w.WriteByte(uint8(e.bits)); err != nil { @@ -250,6 +250,7 @@ func NewWriter(w io.Writer, order Order, litWidth int) io.WriteCloser { lw := uint(litWidth) return &encoder{ w: bw, + order: order, write: write, width: 1 + lw, litWidth: lw, diff --git a/libgo/go/compress/lzw/writer_test.go b/libgo/go/compress/lzw/writer_test.go index 154cdf8..d249a09 100644 --- a/libgo/go/compress/lzw/writer_test.go +++ b/libgo/go/compress/lzw/writer_test.go @@ -50,10 +50,6 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) { return } _, err1 := lzww.Write(b[:n]) - if err1 == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reportable) error broke the pipe. - return - } if err1 != nil { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1) return diff --git a/libgo/go/compress/zlib/writer_test.go b/libgo/go/compress/zlib/writer_test.go index 32f05ab..a71894d 100644 --- a/libgo/go/compress/zlib/writer_test.go +++ b/libgo/go/compress/zlib/writer_test.go @@ -59,10 +59,6 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) { } defer zlibw.Close() _, err = zlibw.Write(b0) - if err == os.EPIPE { - // Fail, but do not report the error, as some other (presumably reported) error broke the pipe. - return - } if err != nil { t.Errorf("%s (level=%d, dict=%q): %v", fn, level, d, err) return diff --git a/libgo/go/crypto/aes/cipher.go b/libgo/go/crypto/aes/cipher.go index 5ad75ec..28752e7 100644 --- a/libgo/go/crypto/aes/cipher.go +++ b/libgo/go/crypto/aes/cipher.go @@ -41,7 +41,7 @@ func NewCipher(key []byte) (*Cipher, error) { } // BlockSize returns the AES block size, 16 bytes. -// It is necessary to satisfy the Cipher interface in the +// It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } diff --git a/libgo/go/crypto/blowfish/cipher.go b/libgo/go/crypto/blowfish/cipher.go index a5d56d2..94e10f0 100644 --- a/libgo/go/crypto/blowfish/cipher.go +++ b/libgo/go/crypto/blowfish/cipher.go @@ -54,7 +54,7 @@ func NewSaltedCipher(key, salt []byte) (*Cipher, error) { } // BlockSize returns the Blowfish block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the +// It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } diff --git a/libgo/go/crypto/rand/rand_windows.go b/libgo/go/crypto/rand/rand_windows.go index 590571d..2b2bd4bb 100644 --- a/libgo/go/crypto/rand/rand_windows.go +++ b/libgo/go/crypto/rand/rand_windows.go @@ -28,16 +28,16 @@ func (r *rngReader) Read(b []byte) (n int, err error) { if r.prov == 0 { const provType = syscall.PROV_RSA_FULL const flags = syscall.CRYPT_VERIFYCONTEXT | syscall.CRYPT_SILENT - errno := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) - if errno != 0 { + err := syscall.CryptAcquireContext(&r.prov, nil, nil, provType, flags) + if err != nil { r.mu.Unlock() - return 0, os.NewSyscallError("CryptAcquireContext", errno) + return 0, os.NewSyscallError("CryptAcquireContext", err) } } r.mu.Unlock() - errno := syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) - if errno != 0 { - return 0, os.NewSyscallError("CryptGenRandom", errno) + err = syscall.CryptGenRandom(r.prov, uint32(len(b)), &b[0]) + if err != nil { + return 0, os.NewSyscallError("CryptGenRandom", err) } return len(b), nil } diff --git a/libgo/go/crypto/rand/util.go b/libgo/go/crypto/rand/util.go index b44ae98..fc5fe6c 100644 --- a/libgo/go/crypto/rand/util.go +++ b/libgo/go/crypto/rand/util.go @@ -5,16 +5,16 @@ package rand import ( + "errors" "io" "math/big" - "os" ) // Prime returns a number, p, of the given size, such that p is prime // with high probability. func Prime(rand io.Reader, bits int) (p *big.Int, err error) { if bits < 1 { - err = os.EINVAL + err = errors.New("crypto/rand: prime size must be positive") } b := uint(bits % 8) diff --git a/libgo/go/crypto/tls/conn.go b/libgo/go/crypto/tls/conn.go index f4178e3..b8fa273 100644 --- a/libgo/go/crypto/tls/conn.go +++ b/libgo/go/crypto/tls/conn.go @@ -93,7 +93,8 @@ func (c *Conn) SetTimeout(nsec int64) error { } // SetReadTimeout sets the time (in nanoseconds) that -// Read will wait for data before returning os.EAGAIN. +// Read will wait for data before returning a net.Error +// with Timeout() == true. // Setting nsec == 0 (the default) disables the deadline. func (c *Conn) SetReadTimeout(nsec int64) error { return c.conn.SetReadTimeout(nsec) @@ -737,7 +738,7 @@ func (c *Conn) Write(b []byte) (n int, err error) { return c.writeRecord(recordTypeApplicationData, b) } -// Read can be made to time out and return err == os.EAGAIN +// Read can be made to time out and return a net.Error with Timeout() == true // after a fixed time limit; see SetTimeout and SetReadTimeout. func (c *Conn) Read(b []byte) (n int, err error) { if err = c.Handshake(); err != nil { diff --git a/libgo/go/crypto/tls/handshake_messages.go b/libgo/go/crypto/tls/handshake_messages.go index f11232d..5438e74 100644 --- a/libgo/go/crypto/tls/handshake_messages.go +++ b/libgo/go/crypto/tls/handshake_messages.go @@ -4,6 +4,8 @@ package tls +import "bytes" + type clientHelloMsg struct { raw []byte vers uint16 @@ -18,6 +20,25 @@ type clientHelloMsg struct { supportedPoints []uint8 } +func (m *clientHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*clientHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + eqUint16s(m.cipherSuites, m1.cipherSuites) && + bytes.Equal(m.compressionMethods, m1.compressionMethods) && + m.nextProtoNeg == m1.nextProtoNeg && + m.serverName == m1.serverName && + m.ocspStapling == m1.ocspStapling && + eqUint16s(m.supportedCurves, m1.supportedCurves) && + bytes.Equal(m.supportedPoints, m1.supportedPoints) +} + func (m *clientHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -309,6 +330,23 @@ type serverHelloMsg struct { ocspStapling bool } +func (m *serverHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*serverHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + m.cipherSuite == m1.cipherSuite && + m.compressionMethod == m1.compressionMethod && + m.nextProtoNeg == m1.nextProtoNeg && + eqStrings(m.nextProtos, m1.nextProtos) && + m.ocspStapling == m1.ocspStapling +} + func (m *serverHelloMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -463,6 +501,16 @@ type certificateMsg struct { certificates [][]byte } +func (m *certificateMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + eqByteSlices(m.certificates, m1.certificates) +} + func (m *certificateMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -540,6 +588,16 @@ type serverKeyExchangeMsg struct { key []byte } +func (m *serverKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*serverKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.key, m1.key) +} + func (m *serverKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -571,6 +629,17 @@ type certificateStatusMsg struct { response []byte } +func (m *certificateStatusMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateStatusMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.statusType == m1.statusType && + bytes.Equal(m.response, m1.response) +} + func (m *certificateStatusMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -622,6 +691,11 @@ func (m *certificateStatusMsg) unmarshal(data []byte) bool { type serverHelloDoneMsg struct{} +func (m *serverHelloDoneMsg) equal(i interface{}) bool { + _, ok := i.(*serverHelloDoneMsg) + return ok +} + func (m *serverHelloDoneMsg) marshal() []byte { x := make([]byte, 4) x[0] = typeServerHelloDone @@ -637,6 +711,16 @@ type clientKeyExchangeMsg struct { ciphertext []byte } +func (m *clientKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*clientKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.ciphertext, m1.ciphertext) +} + func (m *clientKeyExchangeMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -671,6 +755,16 @@ type finishedMsg struct { verifyData []byte } +func (m *finishedMsg) equal(i interface{}) bool { + m1, ok := i.(*finishedMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.verifyData, m1.verifyData) +} + func (m *finishedMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -698,6 +792,16 @@ type nextProtoMsg struct { proto string } +func (m *nextProtoMsg) equal(i interface{}) bool { + m1, ok := i.(*nextProtoMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.proto == m1.proto +} + func (m *nextProtoMsg) marshal() []byte { if m.raw != nil { return m.raw @@ -759,6 +863,17 @@ type certificateRequestMsg struct { certificateAuthorities [][]byte } +func (m *certificateRequestMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateRequestMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.certificateTypes, m1.certificateTypes) && + eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) +} + func (m *certificateRequestMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -859,6 +974,16 @@ type certificateVerifyMsg struct { signature []byte } +func (m *certificateVerifyMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateVerifyMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.signature, m1.signature) +} + func (m *certificateVerifyMsg) marshal() (x []byte) { if m.raw != nil { return m.raw @@ -902,3 +1027,39 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool { return true } + +func eqUint16s(x, y []uint16) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqStrings(x, y []string) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqByteSlices(x, y [][]byte) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if !bytes.Equal(v, y[i]) { + return false + } + } + return true +} diff --git a/libgo/go/crypto/tls/handshake_messages_test.go b/libgo/go/crypto/tls/handshake_messages_test.go index 87e8f7e..e62a9d5 100644 --- a/libgo/go/crypto/tls/handshake_messages_test.go +++ b/libgo/go/crypto/tls/handshake_messages_test.go @@ -27,10 +27,12 @@ var tests = []interface{}{ type testMessage interface { marshal() []byte unmarshal([]byte) bool + equal(interface{}) bool } func TestMarshalUnmarshal(t *testing.T) { rand := rand.New(rand.NewSource(0)) + for i, iface := range tests { ty := reflect.ValueOf(iface).Type() @@ -54,7 +56,7 @@ func TestMarshalUnmarshal(t *testing.T) { } m2.marshal() // to fill any marshal cache in the message - if !reflect.DeepEqual(m1, m2) { + if !m1.equal(m2) { t.Errorf("#%d got:%#v want:%#v %x", i, m2, m1, marshaled) break } diff --git a/libgo/go/crypto/tls/root_windows.go b/libgo/go/crypto/tls/root_windows.go index b8e27a9..13073dc 100644 --- a/libgo/go/crypto/tls/root_windows.go +++ b/libgo/go/crypto/tls/root_windows.go @@ -12,8 +12,8 @@ import ( ) func loadStore(roots *x509.CertPool, name string) { - store, errno := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name)) - if errno != 0 { + store, err := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name)) + if err != nil { return } diff --git a/libgo/go/crypto/xtea/cipher.go b/libgo/go/crypto/xtea/cipher.go index 64d933c..3ed0581 100644 --- a/libgo/go/crypto/xtea/cipher.go +++ b/libgo/go/crypto/xtea/cipher.go @@ -44,7 +44,7 @@ func NewCipher(key []byte) (*Cipher, error) { } // BlockSize returns the XTEA block size, 8 bytes. -// It is necessary to satisfy the Cipher interface in the +// It is necessary to satisfy the Block interface in the // package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } diff --git a/libgo/go/encoding/json/bench_test.go b/libgo/go/encoding/json/bench_test.go new file mode 100644 index 0000000..f0c5201 --- /dev/null +++ b/libgo/go/encoding/json/bench_test.go @@ -0,0 +1,157 @@ +// Copyright 2011 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. + +// Large data benchmark. +// The JSON data is a summary of agl's changes in the +// go, webkit, and chromium open source projects. +// We benchmark converting between the JSON form +// and in-memory data structures. + +package json + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "os" + "testing" +) + +type codeResponse struct { + Tree *codeNode `json:"tree"` + Username string `json:"username"` +} + +type codeNode struct { + Name string `json:"name"` + Kids []*codeNode `json:"kids"` + CLWeight float64 `json:"cl_weight"` + Touches int `json:"touches"` + MinT int64 `json:"min_t"` + MaxT int64 `json:"max_t"` + MeanT int64 `json:"mean_t"` +} + +var codeJSON []byte +var codeStruct codeResponse + +func codeInit() { + f, err := os.Open("testdata/code.json.gz") + if err != nil { + panic(err) + } + defer f.Close() + gz, err := gzip.NewReader(f) + if err != nil { + panic(err) + } + data, err := ioutil.ReadAll(gz) + if err != nil { + panic(err) + } + + codeJSON = data + + if err := Unmarshal(codeJSON, &codeStruct); err != nil { + panic("unmarshal code.json: " + err.Error()) + } + + if data, err = Marshal(&codeStruct); err != nil { + panic("marshal code.json: " + err.Error()) + } + + if !bytes.Equal(data, codeJSON) { + println("different lengths", len(data), len(codeJSON)) + for i := 0; i < len(data) && i < len(codeJSON); i++ { + if data[i] != codeJSON[i] { + println("re-marshal: changed at byte", i) + println("orig: ", string(codeJSON[i-10:i+10])) + println("new: ", string(data[i-10:i+10])) + break + } + } + panic("re-marshal code.json: different result") + } +} + +func BenchmarkCodeEncoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + enc := NewEncoder(ioutil.Discard) + for i := 0; i < b.N; i++ { + if err := enc.Encode(&codeStruct); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeMarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + if _, err := Marshal(&codeStruct); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeDecoder(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var buf bytes.Buffer + dec := NewDecoder(&buf) + var r codeResponse + for i := 0; i < b.N; i++ { + buf.Write(codeJSON) + // hide EOF + buf.WriteByte('\n') + buf.WriteByte('\n') + buf.WriteByte('\n') + if err := dec.Decode(&r); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshal(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + for i := 0; i < b.N; i++ { + var r codeResponse + if err := Unmarshal(codeJSON, &r); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeUnmarshalReuse(b *testing.B) { + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + var r codeResponse + for i := 0; i < b.N; i++ { + if err := Unmarshal(codeJSON, &r); err != nil { + panic(err) + } + } + b.SetBytes(int64(len(codeJSON))) +} diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 41295d2..2ea06c5 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -227,7 +227,7 @@ func (d *decodeState) value(v reflect.Value) { // d.scan thinks we're still at the beginning of the item. // Feed in an empty string - the shortest, simplest value - // so that it knows we got to the end of the value. - if d.scan.step == stateRedo { + if d.scan.redo { panic("redo") } d.scan.step(&d.scan, '"') @@ -381,6 +381,7 @@ func (d *decodeState) array(v reflect.Value) { d.error(errPhase) } } + if i < av.Len() { if !sv.IsValid() { // Array. Zero the rest. @@ -392,6 +393,9 @@ func (d *decodeState) array(v reflect.Value) { sv.SetLen(i) } } + if i == 0 && av.Kind() == reflect.Slice && sv.IsNil() { + sv.Set(reflect.MakeSlice(sv.Type(), 0, 0)) + } } // object consumes an object from d.data[d.off-1:], decoding into the value v. diff --git a/libgo/go/encoding/json/scanner.go b/libgo/go/encoding/json/scanner.go index 1796904..2661f41 100644 --- a/libgo/go/encoding/json/scanner.go +++ b/libgo/go/encoding/json/scanner.go @@ -80,6 +80,9 @@ type scanner struct { // on a 64-bit Mac Mini, and it's nicer to read. step func(*scanner, int) int + // Reached end of top-level value. + endTop bool + // Stack of what we're in the middle of - array values, object keys, object values. parseState []int @@ -87,6 +90,7 @@ type scanner struct { err error // 1-byte redo (see undo method) + redo bool redoCode int redoState func(*scanner, int) int @@ -135,6 +139,8 @@ func (s *scanner) reset() { s.step = stateBeginValue s.parseState = s.parseState[0:0] s.err = nil + s.redo = false + s.endTop = false } // eof tells the scanner that the end of input has been reached. @@ -143,11 +149,11 @@ func (s *scanner) eof() int { if s.err != nil { return scanError } - if s.step == stateEndTop { + if s.endTop { return scanEnd } s.step(s, ' ') - if s.step == stateEndTop { + if s.endTop { return scanEnd } if s.err == nil { @@ -166,8 +172,10 @@ func (s *scanner) pushParseState(p int) { func (s *scanner) popParseState() { n := len(s.parseState) - 1 s.parseState = s.parseState[0:n] + s.redo = false if n == 0 { s.step = stateEndTop + s.endTop = true } else { s.step = stateEndValue } @@ -269,6 +277,7 @@ func stateEndValue(s *scanner, c int) int { if n == 0 { // Completed top-level before the current byte. s.step = stateEndTop + s.endTop = true return stateEndTop(s, c) } if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') { @@ -606,16 +615,18 @@ func quoteChar(c int) string { // undo causes the scanner to return scanCode from the next state transition. // This gives callers a simple 1-byte undo mechanism. func (s *scanner) undo(scanCode int) { - if s.step == stateRedo { - panic("invalid use of scanner") + if s.redo { + panic("json: invalid use of scanner") } s.redoCode = scanCode s.redoState = s.step s.step = stateRedo + s.redo = true } // stateRedo helps implement the scanner's 1-byte undo. func stateRedo(s *scanner, c int) int { + s.redo = false s.step = s.redoState return s.redoCode } diff --git a/libgo/go/encoding/json/scanner_test.go b/libgo/go/encoding/json/scanner_test.go index a0a5995..14d8508 100644 --- a/libgo/go/encoding/json/scanner_test.go +++ b/libgo/go/encoding/json/scanner_test.go @@ -186,11 +186,12 @@ func TestNextValueBig(t *testing.T) { } } +var benchScan scanner + func BenchmarkSkipValue(b *testing.B) { initBig() - var scan scanner for i := 0; i < b.N; i++ { - nextValue(jsonBig, &scan) + nextValue(jsonBig, &benchScan) } b.SetBytes(int64(len(jsonBig))) } diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go index 6c874fa..bcb22af 100644 --- a/libgo/go/encoding/xml/xml_test.go +++ b/libgo/go/encoding/xml/xml_test.go @@ -7,7 +7,6 @@ package xml import ( "bytes" "io" - "os" "reflect" "strings" "testing" @@ -43,17 +42,17 @@ var rawTokens = []Token{ CharData([]byte("World <>'\" 白鵬翔")), EndElement{Name{"", "hello"}}, CharData([]byte("\n ")), - StartElement{Name{"", "goodbye"}, nil}, + StartElement{Name{"", "goodbye"}, []Attr{}}, EndElement{Name{"", "goodbye"}}, CharData([]byte("\n ")), StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, CharData([]byte("\n ")), - StartElement{Name{"", "inner"}, nil}, + StartElement{Name{"", "inner"}, []Attr{}}, EndElement{Name{"", "inner"}}, CharData([]byte("\n ")), EndElement{Name{"", "outer"}}, CharData([]byte("\n ")), - StartElement{Name{"tag", "name"}, nil}, + StartElement{Name{"tag", "name"}, []Attr{}}, CharData([]byte("\n ")), CharData([]byte("Some text here.")), CharData([]byte("\n ")), @@ -77,17 +76,17 @@ var cookedTokens = []Token{ CharData([]byte("World <>'\" 白鵬翔")), EndElement{Name{"ns2", "hello"}}, CharData([]byte("\n ")), - StartElement{Name{"ns2", "goodbye"}, nil}, + StartElement{Name{"ns2", "goodbye"}, []Attr{}}, EndElement{Name{"ns2", "goodbye"}}, CharData([]byte("\n ")), StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, CharData([]byte("\n ")), - StartElement{Name{"ns2", "inner"}, nil}, + StartElement{Name{"ns2", "inner"}, []Attr{}}, EndElement{Name{"ns2", "inner"}}, CharData([]byte("\n ")), EndElement{Name{"ns2", "outer"}}, CharData([]byte("\n ")), - StartElement{Name{"ns3", "name"}, nil}, + StartElement{Name{"ns3", "name"}, []Attr{}}, CharData([]byte("\n ")), CharData([]byte("Some text here.")), CharData([]byte("\n ")), @@ -105,7 +104,7 @@ var rawTokensAltEncoding = []Token{ CharData([]byte("\n")), ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)}, CharData([]byte("\n")), - StartElement{Name{"", "tag"}, nil}, + StartElement{Name{"", "tag"}, []Attr{}}, CharData([]byte("value")), EndElement{Name{"", "tag"}}, } @@ -205,7 +204,7 @@ func (d *downCaser) ReadByte() (c byte, err error) { func (d *downCaser) Read(p []byte) (int, error) { d.t.Fatalf("unexpected Read call on downCaser reader") - return 0, os.EINVAL + panic("unreachable") } func TestRawTokenAltEncoding(t *testing.T) { diff --git a/libgo/go/exp/inotify/inotify_linux.go b/libgo/go/exp/inotify/inotify_linux.go index d6b7e85..f124366 100644 --- a/libgo/go/exp/inotify/inotify_linux.go +++ b/libgo/go/exp/inotify/inotify_linux.go @@ -105,9 +105,9 @@ func (w *Watcher) AddWatch(path string, flags uint32) error { watchEntry.flags |= flags flags |= syscall.IN_MASK_ADD } - wd, errno := syscall.InotifyAddWatch(w.fd, path, flags) - if wd == -1 { - return &os.PathError{"inotify_add_watch", path, os.Errno(errno)} + wd, err := syscall.InotifyAddWatch(w.fd, path, flags) + if err != nil { + return &os.PathError{"inotify_add_watch", path, err} } if !found { @@ -139,14 +139,10 @@ func (w *Watcher) RemoveWatch(path string) error { // readEvents reads from the inotify file descriptor, converts the // received events into Event objects and sends them via the Event channel func (w *Watcher) readEvents() { - var ( - buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events - n int // Number of bytes read with read() - errno int // Syscall errno - ) + var buf [syscall.SizeofInotifyEvent * 4096]byte for { - n, errno = syscall.Read(w.fd, buf[0:]) + n, err := syscall.Read(w.fd, buf[0:]) // See if there is a message on the "done" channel var done bool select { @@ -156,16 +152,16 @@ func (w *Watcher) readEvents() { // If EOF or a "done" message is received if n == 0 || done { - errno := syscall.Close(w.fd) - if errno == -1 { - w.Error <- os.NewSyscallError("close", errno) + err := syscall.Close(w.fd) + if err != nil { + w.Error <- os.NewSyscallError("close", err) } close(w.Event) close(w.Error) return } if n < 0 { - w.Error <- os.NewSyscallError("read", errno) + w.Error <- os.NewSyscallError("read", err) continue } if n < syscall.SizeofInotifyEvent { diff --git a/libgo/go/exp/sql/convert.go b/libgo/go/exp/sql/convert.go index e46cebe..48e2812 100644 --- a/libgo/go/exp/sql/convert.go +++ b/libgo/go/exp/sql/convert.go @@ -14,6 +14,21 @@ import ( "strconv" ) +// subsetTypeArgs takes a slice of arguments from callers of the sql +// package and converts them into a slice of the driver package's +// "subset types". +func subsetTypeArgs(args []interface{}) ([]interface{}, error) { + out := make([]interface{}, len(args)) + for n, arg := range args { + var err error + out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) + } + } + return out, nil +} + // convertAssign copies to dest the value in src, converting it if possible. // An error is returned if the copy would result in loss of information. // dest should be a pointer type. diff --git a/libgo/go/exp/sql/driver/driver.go b/libgo/go/exp/sql/driver/driver.go index 6a51c34..91a3884 100644 --- a/libgo/go/exp/sql/driver/driver.go +++ b/libgo/go/exp/sql/driver/driver.go @@ -36,19 +36,22 @@ type Driver interface { Open(name string) (Conn, error) } -// Execer is an optional interface that may be implemented by a Driver -// or a Conn. -// -// If a Driver does not implement Execer, the sql package's DB.Exec -// method first obtains a free connection from its free pool or from -// the driver's Open method. Execer should only be implemented by -// drivers that can provide a more efficient implementation. +// ErrSkip may be returned by some optional interfaces' methods to +// indicate at runtime that the fast path is unavailable and the sql +// package should continue as if the optional interface was not +// implemented. ErrSkip is only supported where explicitly +// documented. +var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented") + +// Execer is an optional interface that may be implemented by a Conn. // // If a Conn does not implement Execer, the db package's DB.Exec will // first prepare a query, execute the statement, and then close the // statement. // // All arguments are of a subset type as defined in the package docs. +// +// Exec may return ErrSkip. type Execer interface { Exec(query string, args []interface{}) (Result, error) } @@ -94,6 +97,9 @@ type Stmt interface { Close() error // NumInput returns the number of placeholder parameters. + // -1 means the driver doesn't know how to count the number of + // placeholders, so we won't sanity check input here and instead let the + // driver deal with errors. NumInput() int // Exec executes a query that doesn't return rows, such @@ -135,6 +141,8 @@ type Rows interface { // The dest slice may be populated with only with values // of subset types defined above, but excluding string. // All string values must be converted to []byte. + // + // Next should return io.EOF when there are no more rows. Next(dest []interface{}) error } diff --git a/libgo/go/exp/sql/fakedb_test.go b/libgo/go/exp/sql/fakedb_test.go index c8a1997..17028e2 100644 --- a/libgo/go/exp/sql/fakedb_test.go +++ b/libgo/go/exp/sql/fakedb_test.go @@ -195,6 +195,29 @@ func (c *fakeConn) Close() error { return nil } +func checkSubsetTypes(args []interface{}) error { + for n, arg := range args { + switch arg.(type) { + case int64, float64, bool, nil, []byte, string: + default: + return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg) + } + } + return nil +} + +func (c *fakeConn) Exec(query string, args []interface{}) (driver.Result, error) { + // This is an optional interface, but it's implemented here + // just to check that all the args of of the proper types. + // ErrSkip is returned so the caller acts as if we didn't + // implement this at all. + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + return nil, driver.ErrSkip +} + func errf(msg string, args ...interface{}) error { return errors.New("fakedb: " + fmt.Sprintf(msg, args...)) } @@ -323,6 +346,11 @@ func (s *fakeStmt) Close() error { } func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) { + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + db := s.c.db switch s.cmd { case "WIPE": @@ -377,6 +405,11 @@ func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) { } func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) { + err := checkSubsetTypes(args) + if err != nil { + return nil, err + } + db := s.c.db if len(args) != s.placeholders { panic("error in pkg db; should only get here if size is correct") diff --git a/libgo/go/exp/sql/sql.go b/libgo/go/exp/sql/sql.go index 291af7f..c055fdd 100644 --- a/libgo/go/exp/sql/sql.go +++ b/libgo/go/exp/sql/sql.go @@ -88,8 +88,9 @@ type DB struct { driver driver.Driver dsn string - mu sync.Mutex + mu sync.Mutex // protects freeConn and closed freeConn []driver.Conn + closed bool } // Open opens a database specified by its database driver name and a @@ -106,6 +107,22 @@ func Open(driverName, dataSourceName string) (*DB, error) { return &DB{driver: driver, dsn: dataSourceName}, nil } +// Close closes the database, releasing any open resources. +func (db *DB) Close() error { + db.mu.Lock() + defer db.mu.Unlock() + var err error + for _, c := range db.freeConn { + err1 := c.Close() + if err1 != nil { + err = err1 + } + } + db.freeConn = nil + db.closed = true + return err +} + func (db *DB) maxIdleConns() int { const defaultMaxIdleConns = 2 // TODO(bradfitz): ask driver, if supported, for its default preference @@ -116,6 +133,9 @@ func (db *DB) maxIdleConns() int { // conn returns a newly-opened or cached driver.Conn func (db *DB) conn() (driver.Conn, error) { db.mu.Lock() + if db.closed { + return nil, errors.New("sql: database is closed") + } if n := len(db.freeConn); n > 0 { conn := db.freeConn[n-1] db.freeConn = db.freeConn[:n-1] @@ -140,11 +160,13 @@ func (db *DB) connIfFree(wanted driver.Conn) (conn driver.Conn, ok bool) { } func (db *DB) putConn(c driver.Conn) { - if n := len(db.freeConn); n < db.maxIdleConns() { + db.mu.Lock() + defer db.mu.Unlock() + if n := len(db.freeConn); !db.closed && n < db.maxIdleConns() { db.freeConn = append(db.freeConn, c) return } - db.closeConn(c) + db.closeConn(c) // TODO(bradfitz): release lock before calling this? } func (db *DB) closeConn(c driver.Conn) { @@ -180,17 +202,11 @@ func (db *DB) Prepare(query string) (*Stmt, error) { // Exec executes a query without returning any rows. func (db *DB) Exec(query string, args ...interface{}) (Result, error) { - // Optional fast path, if the driver implements driver.Execer. - if execer, ok := db.driver.(driver.Execer); ok { - resi, err := execer.Exec(query, args) - if err != nil { - return nil, err - } - return result{resi}, nil + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err } - // If the driver does not implement driver.Execer, we need - // a connection. ci, err := db.conn() if err != nil { return nil, err @@ -198,11 +214,13 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) { defer db.putConn(ci) if execer, ok := ci.(driver.Execer); ok { - resi, err := execer.Exec(query, args) - if err != nil { - return nil, err + resi, err := execer.Exec(query, sargs) + if err != driver.ErrSkip { + if err != nil { + return nil, err + } + return result{resi}, nil } - return result{resi}, nil } sti, err := ci.Prepare(query) @@ -210,7 +228,8 @@ func (db *DB) Exec(query string, args ...interface{}) (Result, error) { return nil, err } defer sti.Close() - resi, err := sti.Exec(args) + + resi, err := sti.Exec(sargs) if err != nil { return nil, err } @@ -386,7 +405,13 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) { return nil, err } defer sti.Close() - resi, err := sti.Exec(args) + + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err + } + + resi, err := sti.Exec(sargs) if err != nil { return nil, err } @@ -449,7 +474,10 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) { } defer releaseConn() - if want := si.NumInput(); len(args) != want { + // -1 means the driver doesn't know how to count the number of + // placeholders, so we won't sanity check input here and instead let the + // driver deal with errors. + if want := si.NumInput(); want != -1 && len(args) != want { return nil, fmt.Errorf("db: expected %d arguments, got %d", want, len(args)) } @@ -545,10 +573,18 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { if err != nil { return nil, err } - if len(args) != si.NumInput() { + + // -1 means the driver doesn't know how to count the number of + // placeholders, so we won't sanity check input here and instead let the + // driver deal with errors. + if want := si.NumInput(); want != -1 && len(args) != want { return nil, fmt.Errorf("db: statement expects %d inputs; got %d", si.NumInput(), len(args)) } - rowsi, err := si.Query(args) + sargs, err := subsetTypeArgs(args) + if err != nil { + return nil, err + } + rowsi, err := si.Query(sargs) if err != nil { s.db.putConn(ci) return nil, err diff --git a/libgo/go/exp/sql/sql_test.go b/libgo/go/exp/sql/sql_test.go index eb1bb58..d365f6b 100644 --- a/libgo/go/exp/sql/sql_test.go +++ b/libgo/go/exp/sql/sql_test.go @@ -34,8 +34,16 @@ func exec(t *testing.T, db *DB, query string, args ...interface{}) { } } +func closeDB(t *testing.T, db *DB) { + err := db.Close() + if err != nil { + t.Fatalf("error closing DB: %v", err) + } +} + func TestQuery(t *testing.T) { db := newTestDB(t, "people") + defer closeDB(t, db) var name string var age int @@ -69,6 +77,7 @@ func TestQuery(t *testing.T) { func TestStatementQueryRow(t *testing.T) { db := newTestDB(t, "people") + defer closeDB(t, db) stmt, err := db.Prepare("SELECT|people|age|name=?") if err != nil { t.Fatalf("Prepare: %v", err) @@ -94,6 +103,7 @@ func TestStatementQueryRow(t *testing.T) { // just a test of fakedb itself func TestBogusPreboundParameters(t *testing.T) { db := newTestDB(t, "foo") + defer closeDB(t, db) exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") _, err := db.Prepare("INSERT|t1|name=?,age=bogusconversion") if err == nil { @@ -106,6 +116,7 @@ func TestBogusPreboundParameters(t *testing.T) { func TestDb(t *testing.T) { db := newTestDB(t, "foo") + defer closeDB(t, db) exec(t, db, "CREATE|t1|name=string,age=int32,dead=bool") stmt, err := db.Prepare("INSERT|t1|name=?,age=?") if err != nil { diff --git a/libgo/go/exp/ssh/cipher.go b/libgo/go/exp/ssh/cipher.go new file mode 100644 index 0000000..de4926d --- /dev/null +++ b/libgo/go/exp/ssh/cipher.go @@ -0,0 +1,88 @@ +// Copyright 2011 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 ssh + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rc4" +) + +// streamDump is used to dump the initial keystream for stream ciphers. It is a +// a write-only buffer, and not intended for reading so do not require a mutex. +var streamDump [512]byte + +// noneCipher implements cipher.Stream and provides no encryption. It is used +// by the transport before the first key-exchange. +type noneCipher struct{} + +func (c noneCipher) XORKeyStream(dst, src []byte) { + copy(dst, src) +} + +func newAESCTR(key, iv []byte) (cipher.Stream, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewCTR(c, iv), nil +} + +func newRC4(key, iv []byte) (cipher.Stream, error) { + return rc4.NewCipher(key) +} + +type cipherMode struct { + keySize int + ivSize int + skip int + createFn func(key, iv []byte) (cipher.Stream, error) +} + +func (c *cipherMode) createCipher(key, iv []byte) (cipher.Stream, error) { + if len(key) < c.keySize { + panic("ssh: key length too small for cipher") + } + if len(iv) < c.ivSize { + panic("ssh: iv too small for cipher") + } + + stream, err := c.createFn(key[:c.keySize], iv[:c.ivSize]) + if err != nil { + return nil, err + } + + for remainingToDump := c.skip; remainingToDump > 0; { + dumpThisTime := remainingToDump + if dumpThisTime > len(streamDump) { + dumpThisTime = len(streamDump) + } + stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime]) + remainingToDump -= dumpThisTime + } + + return stream, nil +} + +// Specifies a default set of ciphers and a preference order. This is based on +// OpenSSH's default client preference order, minus algorithms that are not +// implemented. +var DefaultCipherOrder = []string{ + "aes128-ctr", "aes192-ctr", "aes256-ctr", + "arcfour256", "arcfour128", +} + +var cipherModes = map[string]*cipherMode{ + // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms + // are defined in the order specified in the RFC. + "aes128-ctr": &cipherMode{16, aes.BlockSize, 0, newAESCTR}, + "aes192-ctr": &cipherMode{24, aes.BlockSize, 0, newAESCTR}, + "aes256-ctr": &cipherMode{32, aes.BlockSize, 0, newAESCTR}, + + // Ciphers from RFC4345, which introduces security-improved arcfour ciphers. + // They are defined in the order specified in the RFC. + "arcfour128": &cipherMode{16, 0, 1536, newRC4}, + "arcfour256": &cipherMode{32, 0, 1536, newRC4}, +} diff --git a/libgo/go/exp/ssh/cipher_test.go b/libgo/go/exp/ssh/cipher_test.go new file mode 100644 index 0000000..ea27bd8 --- /dev/null +++ b/libgo/go/exp/ssh/cipher_test.go @@ -0,0 +1,62 @@ +// Copyright 2011 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 ssh + +import ( + "bytes" + "testing" +) + +// TestCipherReversal tests that each cipher factory produces ciphers that can +// encrypt and decrypt some data successfully. +func TestCipherReversal(t *testing.T) { + testData := []byte("abcdefghijklmnopqrstuvwxyz012345") + testKey := []byte("AbCdEfGhIjKlMnOpQrStUvWxYz012345") + testIv := []byte("sdflkjhsadflkjhasdflkjhsadfklhsa") + + cryptBuffer := make([]byte, 32) + + for name, cipherMode := range cipherModes { + encrypter, err := cipherMode.createCipher(testKey, testIv) + if err != nil { + t.Errorf("failed to create encrypter for %q: %s", name, err) + continue + } + decrypter, err := cipherMode.createCipher(testKey, testIv) + if err != nil { + t.Errorf("failed to create decrypter for %q: %s", name, err) + continue + } + + copy(cryptBuffer, testData) + + encrypter.XORKeyStream(cryptBuffer, cryptBuffer) + if name == "none" { + if !bytes.Equal(cryptBuffer, testData) { + t.Errorf("encryption made change with 'none' cipher") + continue + } + } else { + if bytes.Equal(cryptBuffer, testData) { + t.Errorf("encryption made no change with %q", name) + continue + } + } + + decrypter.XORKeyStream(cryptBuffer, cryptBuffer) + if !bytes.Equal(cryptBuffer, testData) { + t.Errorf("decrypted bytes not equal to input with %q", name) + continue + } + } +} + +func TestDefaultCiphersExist(t *testing.T) { + for _, cipherAlgo := range DefaultCipherOrder { + if _, ok := cipherModes[cipherAlgo]; !ok { + t.Errorf("default cipher %q is unknown", cipherAlgo) + } + } +} diff --git a/libgo/go/exp/ssh/client.go b/libgo/go/exp/ssh/client.go index da45688..24569ad 100644 --- a/libgo/go/exp/ssh/client.go +++ b/libgo/go/exp/ssh/client.go @@ -35,10 +35,6 @@ func Client(c net.Conn, config *ClientConfig) (*ClientConn, error) { conn.Close() return nil, err } - if err := conn.authenticate(); err != nil { - conn.Close() - return nil, err - } go conn.mainLoop() return conn, nil } @@ -64,8 +60,8 @@ func (c *ClientConn) handshake() error { clientKexInit := kexInitMsg{ KexAlgos: supportedKexAlgos, ServerHostKeyAlgos: supportedHostKeyAlgos, - CiphersClientServer: supportedCiphers, - CiphersServerClient: supportedCiphers, + CiphersClientServer: c.config.Crypto.ciphers(), + CiphersServerClient: c.config.Crypto.ciphers(), MACsClientServer: supportedMACs, MACsServerClient: supportedMACs, CompressionClientServer: supportedCompressions, @@ -128,7 +124,10 @@ func (c *ClientConn) handshake() error { if packet[0] != msgNewKeys { return UnexpectedMessageError{msgNewKeys, packet[0]} } - return c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc) + if err := c.transport.reader.setupKeys(serverKeys, K, H, H, hashFunc); err != nil { + return err + } + return c.authenticate(H) } // kexDH performs Diffie-Hellman key agreement on a ClientConn. The @@ -195,6 +194,7 @@ func (c *ClientConn) openChan(typ string) (*clientChan, error) { switch msg := (<-ch.msg).(type) { case *channelOpenConfirmMsg: ch.peersId = msg.MyId + ch.win <- int(msg.MyWindow) case *channelOpenFailureMsg: c.chanlist.remove(ch.id) return nil, errors.New(msg.Message) @@ -301,6 +301,9 @@ type ClientConfig struct { // A slice of ClientAuth methods. Only the first instance // of a particular RFC 4252 method will be used during authentication. Auth []ClientAuth + + // Cryptographic-related configuration. + Crypto CryptoConfig } func (c *ClientConfig) rand() io.Reader { diff --git a/libgo/go/exp/ssh/client_auth.go b/libgo/go/exp/ssh/client_auth.go index 0089d0c..25f9e21 100644 --- a/libgo/go/exp/ssh/client_auth.go +++ b/libgo/go/exp/ssh/client_auth.go @@ -6,10 +6,11 @@ package ssh import ( "errors" + "io" ) // authenticate authenticates with the remote server. See RFC 4252. -func (c *ClientConn) authenticate() error { +func (c *ClientConn) authenticate(session []byte) error { // initiate user auth session if err := c.writePacket(marshal(msgServiceRequest, serviceRequestMsg{serviceUserAuth})); err != nil { return err @@ -26,7 +27,7 @@ func (c *ClientConn) authenticate() error { // then any untried methods suggested by the server. tried, remain := make(map[string]bool), make(map[string]bool) for auth := ClientAuth(new(noneAuth)); auth != nil; { - ok, methods, err := auth.auth(c.config.User, c.transport) + ok, methods, err := auth.auth(session, c.config.User, c.transport, c.config.rand()) if err != nil { return err } @@ -60,7 +61,7 @@ type ClientAuth interface { // Returns true if authentication is successful. // If authentication is not successful, a []string of alternative // method names is returned. - auth(user string, t *transport) (bool, []string, error) + auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) // method returns the RFC 4252 method name. method() string @@ -69,7 +70,7 @@ type ClientAuth interface { // "none" authentication, RFC 4252 section 5.2. type noneAuth int -func (n *noneAuth) auth(user string, t *transport) (bool, []string, error) { +func (n *noneAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { if err := t.writePacket(marshal(msgUserAuthRequest, userAuthRequestMsg{ User: user, Service: serviceSSH, @@ -102,7 +103,7 @@ type passwordAuth struct { ClientPassword } -func (p *passwordAuth) auth(user string, t *transport) (bool, []string, error) { +func (p *passwordAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { type passwordAuthMsg struct { User string Service string @@ -155,3 +156,140 @@ type ClientPassword interface { func ClientAuthPassword(impl ClientPassword) ClientAuth { return &passwordAuth{impl} } + +// ClientKeyring implements access to a client key ring. +type ClientKeyring interface { + // Key returns the i'th rsa.Publickey or dsa.Publickey, or nil if + // no key exists at i. + Key(i int) (key interface{}, err error) + + // Sign returns a signature of the given data using the i'th key + // and the supplied random source. + Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) +} + +// "publickey" authentication, RFC 4252 Section 7. +type publickeyAuth struct { + ClientKeyring +} + +func (p *publickeyAuth) auth(session []byte, user string, t *transport, rand io.Reader) (bool, []string, error) { + type publickeyAuthMsg struct { + User string + Service string + Method string + // HasSig indicates to the reciver packet that the auth request is signed and + // should be used for authentication of the request. + HasSig bool + Algoname string + Pubkey string + // Sig is defined as []byte so marshal will exclude it during the query phase + Sig []byte `ssh:"rest"` + } + + // Authentication is performed in two stages. The first stage sends an + // enquiry to test if each key is acceptable to the remote. The second + // stage attempts to authenticate with the valid keys obtained in the + // first stage. + + var index int + // a map of public keys to their index in the keyring + validKeys := make(map[int]interface{}) + for { + key, err := p.Key(index) + if err != nil { + return false, nil, err + } + if key == nil { + // no more keys in the keyring + break + } + pubkey := serializePublickey(key) + algoname := algoName(key) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + HasSig: false, + Algoname: algoname, + Pubkey: string(pubkey), + } + if err := t.writePacket(marshal(msgUserAuthRequest, msg)); err != nil { + return false, nil, err + } + packet, err := t.readPacket() + if err != nil { + return false, nil, err + } + switch packet[0] { + case msgUserAuthPubKeyOk: + msg := decode(packet).(*userAuthPubKeyOkMsg) + if msg.Algo != algoname || msg.PubKey != string(pubkey) { + continue + } + validKeys[index] = key + case msgUserAuthFailure: + default: + return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} + } + index++ + } + + // methods that may continue if this auth is not successful. + var methods []string + for i, key := range validKeys { + pubkey := serializePublickey(key) + algoname := algoName(key) + sign, err := p.Sign(i, rand, buildDataSignedForAuth(session, userAuthRequestMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + }, []byte(algoname), pubkey)) + if err != nil { + return false, nil, err + } + // manually wrap the serialized signature in a string + s := serializeSignature(algoname, sign) + sig := make([]byte, stringLength(s)) + marshalString(sig, s) + msg := publickeyAuthMsg{ + User: user, + Service: serviceSSH, + Method: p.method(), + HasSig: true, + Algoname: algoname, + Pubkey: string(pubkey), + Sig: sig, + } + p := marshal(msgUserAuthRequest, msg) + if err := t.writePacket(p); err != nil { + return false, nil, err + } + packet, err := t.readPacket() + if err != nil { + return false, nil, err + } + switch packet[0] { + case msgUserAuthSuccess: + return true, nil, nil + case msgUserAuthFailure: + msg := decode(packet).(*userAuthFailureMsg) + methods = msg.Methods + continue + case msgDisconnect: + return false, nil, io.EOF + default: + return false, nil, UnexpectedMessageError{msgUserAuthSuccess, packet[0]} + } + } + return false, methods, nil +} + +func (p *publickeyAuth) method() string { + return "publickey" +} + +// ClientAuthPublickey returns a ClientAuth using public key authentication. +func ClientAuthPublickey(impl ClientKeyring) ClientAuth { + return &publickeyAuth{impl} +} diff --git a/libgo/go/exp/ssh/client_auth_test.go b/libgo/go/exp/ssh/client_auth_test.go new file mode 100644 index 0000000..6467f57 --- /dev/null +++ b/libgo/go/exp/ssh/client_auth_test.go @@ -0,0 +1,248 @@ +// Copyright 2011 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 ssh + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "io" + "io/ioutil" + "testing" +) + +const _pem = `-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU +70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx +9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF +tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z +s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc +qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT ++IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea +riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH +D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh +atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT +b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN +ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M +MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4 +KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8 +e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1 +D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+ +3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj +orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw +64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc +XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc +QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g +/SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ +I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk +gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl +NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw== +-----END RSA PRIVATE KEY-----` + +// reused internally by tests +var serverConfig = new(ServerConfig) + +func init() { + if err := serverConfig.SetRSAPrivateKey([]byte(_pem)); err != nil { + panic("unable to set private key: " + err.Error()) + } +} + +// keychain implements the ClientPublickey interface +type keychain struct { + keys []*rsa.PrivateKey +} + +func (k *keychain) Key(i int) (interface{}, error) { + if i < 0 || i >= len(k.keys) { + return nil, nil + } + return k.keys[i].PublicKey, nil +} + +func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) { + hashFunc := crypto.SHA1 + h := hashFunc.New() + h.Write(data) + digest := h.Sum() + return rsa.SignPKCS1v15(rand, k.keys[i], hashFunc, digest) +} + +func (k *keychain) loadPEM(file string) error { + buf, err := ioutil.ReadFile(file) + if err != nil { + return err + } + block, _ := pem.Decode(buf) + if block == nil { + return errors.New("ssh: no key found") + } + r, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + k.keys = append(k.keys, r) + return nil +} + +var pkey *rsa.PrivateKey + +func init() { + var err error + pkey, err = rsa.GenerateKey(rand.Reader, 512) + if err != nil { + panic("unable to generate public key") + } +} + +func TestClientAuthPublickey(t *testing.T) { + k := new(keychain) + k.keys = append(k.keys, pkey) + + serverConfig.PubKeyCallback = func(user, algo string, pubkey []byte) bool { + expected := []byte(serializePublickey(k.keys[0].PublicKey)) + algoname := algoName(k.keys[0].PublicKey) + return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected) + } + serverConfig.PasswordCallback = nil + + l, err := Listen("tcp", "127.0.0.1:0", serverConfig) + if err != nil { + t.Fatalf("unable to listen: %s", err) + } + defer l.Close() + + done := make(chan bool, 1) + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + defer c.Close() + if err := c.Handshake(); err != nil { + t.Error(err) + } + done <- true + }() + + config := &ClientConfig{ + User: "testuser", + Auth: []ClientAuth{ + ClientAuthPublickey(k), + }, + } + + c, err := Dial("tcp", l.Addr().String(), config) + if err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + defer c.Close() + <-done +} + +// password implements the ClientPassword interface +type password string + +func (p password) Password(user string) (string, error) { + return string(p), nil +} + +func TestClientAuthPassword(t *testing.T) { + pw := password("tiger") + + serverConfig.PasswordCallback = func(user, pass string) bool { + return user == "testuser" && pass == string(pw) + } + serverConfig.PubKeyCallback = nil + + l, err := Listen("tcp", "127.0.0.1:0", serverConfig) + if err != nil { + t.Fatalf("unable to listen: %s", err) + } + defer l.Close() + + done := make(chan bool) + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + if err := c.Handshake(); err != nil { + t.Error(err) + } + defer c.Close() + done <- true + }() + + config := &ClientConfig{ + User: "testuser", + Auth: []ClientAuth{ + ClientAuthPassword(pw), + }, + } + + c, err := Dial("tcp", l.Addr().String(), config) + if err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + defer c.Close() + <-done +} + +func TestClientAuthPasswordAndPublickey(t *testing.T) { + pw := password("tiger") + + serverConfig.PasswordCallback = func(user, pass string) bool { + return user == "testuser" && pass == string(pw) + } + + k := new(keychain) + k.keys = append(k.keys, pkey) + + serverConfig.PubKeyCallback = func(user, algo string, pubkey []byte) bool { + expected := []byte(serializePublickey(k.keys[0].PublicKey)) + algoname := algoName(k.keys[0].PublicKey) + return user == "testuser" && algo == algoname && bytes.Equal(pubkey, expected) + } + + l, err := Listen("tcp", "127.0.0.1:0", serverConfig) + if err != nil { + t.Fatalf("unable to listen: %s", err) + } + defer l.Close() + + done := make(chan bool) + go func() { + c, err := l.Accept() + if err != nil { + t.Fatal(err) + } + if err := c.Handshake(); err != nil { + t.Error(err) + } + defer c.Close() + done <- true + }() + + wrongPw := password("wrong") + config := &ClientConfig{ + User: "testuser", + Auth: []ClientAuth{ + ClientAuthPassword(wrongPw), + ClientAuthPublickey(k), + }, + } + + c, err := Dial("tcp", l.Addr().String(), config) + if err != nil { + t.Fatalf("unable to dial remote side: %s", err) + } + defer c.Close() + <-done +} diff --git a/libgo/go/exp/ssh/client_func_test.go b/libgo/go/exp/ssh/client_func_test.go new file mode 100644 index 0000000..1374560 --- /dev/null +++ b/libgo/go/exp/ssh/client_func_test.go @@ -0,0 +1,61 @@ +// Copyright 2011 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 ssh + +// ClientConn functional tests. +// These tests require a running ssh server listening on port 22 +// on the local host. Functional tests will be skipped unless +// -ssh.user and -ssh.pass must be passed to gotest. + +import ( + "flag" + "testing" +) + +var ( + sshuser = flag.String("ssh.user", "", "ssh username") + sshpass = flag.String("ssh.pass", "", "ssh password") + sshprivkey = flag.String("ssh.privkey", "", "ssh privkey file") +) + +func TestFuncPasswordAuth(t *testing.T) { + if *sshuser == "" { + t.Log("ssh.user not defined, skipping test") + return + } + config := &ClientConfig{ + User: *sshuser, + Auth: []ClientAuth{ + ClientAuthPassword(password(*sshpass)), + }, + } + conn, err := Dial("tcp", "localhost:22", config) + if err != nil { + t.Fatalf("Unable to connect: %s", err) + } + defer conn.Close() +} + +func TestFuncPublickeyAuth(t *testing.T) { + if *sshuser == "" { + t.Log("ssh.user not defined, skipping test") + return + } + kc := new(keychain) + if err := kc.loadPEM(*sshprivkey); err != nil { + t.Fatalf("unable to load private key: %s", err) + } + config := &ClientConfig{ + User: *sshuser, + Auth: []ClientAuth{ + ClientAuthPublickey(kc), + }, + } + conn, err := Dial("tcp", "localhost:22", config) + if err != nil { + t.Fatalf("unable to connect: %s", err) + } + defer conn.Close() +} diff --git a/libgo/go/exp/ssh/common.go b/libgo/go/exp/ssh/common.go index 273820b..01c55219 100644 --- a/libgo/go/exp/ssh/common.go +++ b/libgo/go/exp/ssh/common.go @@ -5,6 +5,8 @@ package ssh import ( + "crypto/dsa" + "crypto/rsa" "math/big" "strconv" "sync" @@ -14,7 +16,6 @@ import ( const ( kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" hostAlgoRSA = "ssh-rsa" - cipherAES128CTR = "aes128-ctr" macSHA196 = "hmac-sha1-96" compressionNone = "none" serviceUserAuth = "ssh-userauth" @@ -23,7 +24,6 @@ const ( var supportedKexAlgos = []string{kexAlgoDH14SHA1} var supportedHostKeyAlgos = []string{hostAlgoRSA} -var supportedCiphers = []string{cipherAES128CTR} var supportedMACs = []string{macSHA196} var supportedCompressions = []string{compressionNone} @@ -127,3 +127,100 @@ func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *ke ok = true return } + +// Cryptographic configuration common to both ServerConfig and ClientConfig. +type CryptoConfig struct { + // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is + // used. + Ciphers []string +} + +func (c *CryptoConfig) ciphers() []string { + if c.Ciphers == nil { + return DefaultCipherOrder + } + return c.Ciphers +} + +// serialize a signed slice according to RFC 4254 6.6. +func serializeSignature(algoname string, sig []byte) []byte { + length := stringLength([]byte(algoname)) + length += stringLength(sig) + + ret := make([]byte, length) + r := marshalString(ret, []byte(algoname)) + r = marshalString(r, sig) + + return ret +} + +// serialize an rsa.PublicKey or dsa.PublicKey according to RFC 4253 6.6. +func serializePublickey(key interface{}) []byte { + algoname := algoName(key) + switch key := key.(type) { + case rsa.PublicKey: + e := new(big.Int).SetInt64(int64(key.E)) + length := stringLength([]byte(algoname)) + length += intLength(e) + length += intLength(key.N) + ret := make([]byte, length) + r := marshalString(ret, []byte(algoname)) + r = marshalInt(r, e) + marshalInt(r, key.N) + return ret + case dsa.PublicKey: + length := stringLength([]byte(algoname)) + length += intLength(key.P) + length += intLength(key.Q) + length += intLength(key.G) + length += intLength(key.Y) + ret := make([]byte, length) + r := marshalString(ret, []byte(algoname)) + r = marshalInt(r, key.P) + r = marshalInt(r, key.Q) + r = marshalInt(r, key.G) + marshalInt(r, key.Y) + return ret + } + panic("unexpected key type") +} + +func algoName(key interface{}) string { + switch key.(type) { + case rsa.PublicKey: + return "ssh-rsa" + case dsa.PublicKey: + return "ssh-dss" + } + panic("unexpected key type") +} + +// buildDataSignedForAuth returns the data that is signed in order to prove +// posession of a private key. See RFC 4252, section 7. +func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { + user := []byte(req.User) + service := []byte(req.Service) + method := []byte(req.Method) + + length := stringLength(sessionId) + length += 1 + length += stringLength(user) + length += stringLength(service) + length += stringLength(method) + length += 1 + length += stringLength(algo) + length += stringLength(pubKey) + + ret := make([]byte, length) + r := marshalString(ret, sessionId) + r[0] = msgUserAuthRequest + r = r[1:] + r = marshalString(r, user) + r = marshalString(r, service) + r = marshalString(r, method) + r[0] = 1 + r = r[1:] + r = marshalString(r, algo) + r = marshalString(r, pubKey) + return ret +} diff --git a/libgo/go/exp/ssh/messages.go b/libgo/go/exp/ssh/messages.go index e24b639..cebb560 100644 --- a/libgo/go/exp/ssh/messages.go +++ b/libgo/go/exp/ssh/messages.go @@ -392,7 +392,10 @@ func parseString(in []byte) (out, rest []byte, ok bool) { return } -var comma = []byte{','} +var ( + comma = []byte{','} + emptyNameList = []string{} +) func parseNameList(in []byte) (out []string, rest []byte, ok bool) { contents, rest, ok := parseString(in) @@ -400,6 +403,7 @@ func parseNameList(in []byte) (out []string, rest []byte, ok bool) { return } if len(contents) == 0 { + out = emptyNameList return } parts := bytes.Split(contents, comma) @@ -444,8 +448,6 @@ func parseUint32(in []byte) (out uint32, rest []byte, ok bool) { return } -const maxPacketSize = 36000 - func nameListLength(namelist []string) int { length := 4 /* uint32 length prefix */ for i, name := range namelist { diff --git a/libgo/go/exp/ssh/server.go b/libgo/go/exp/ssh/server.go index 62035d5..428a747 100644 --- a/libgo/go/exp/ssh/server.go +++ b/libgo/go/exp/ssh/server.go @@ -40,6 +40,9 @@ type ServerConfig struct { // key authentication. It must return true iff the given public key is // valid for the given user. PubKeyCallback func(user, algo string, pubkey []byte) bool + + // Cryptographic-related configuration. + Crypto CryptoConfig } func (c *ServerConfig) rand() io.Reader { @@ -221,7 +224,7 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha return nil, nil, errors.New("internal error") } - serializedSig := serializeRSASignature(sig) + serializedSig := serializeSignature(hostAlgoRSA, sig) kexDHReply := kexDHReplyMsg{ HostKey: serializedHostKey, @@ -234,50 +237,9 @@ func (s *ServerConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha return } -func serializeRSASignature(sig []byte) []byte { - length := stringLength([]byte(hostAlgoRSA)) - length += stringLength(sig) - - ret := make([]byte, length) - r := marshalString(ret, []byte(hostAlgoRSA)) - r = marshalString(r, sig) - - return ret -} - // serverVersion is the fixed identification string that Server will use. var serverVersion = []byte("SSH-2.0-Go\r\n") -// buildDataSignedForAuth returns the data that is signed in order to prove -// posession of a private key. See RFC 4252, section 7. -func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { - user := []byte(req.User) - service := []byte(req.Service) - method := []byte(req.Method) - - length := stringLength(sessionId) - length += 1 - length += stringLength(user) - length += stringLength(service) - length += stringLength(method) - length += 1 - length += stringLength(algo) - length += stringLength(pubKey) - - ret := make([]byte, length) - r := marshalString(ret, sessionId) - r[0] = msgUserAuthRequest - r = r[1:] - r = marshalString(r, user) - r = marshalString(r, service) - r = marshalString(r, method) - r[0] = 1 - r = r[1:] - r = marshalString(r, algo) - r = marshalString(r, pubKey) - return ret -} - // Handshake performs an SSH transport and client authentication on the given ServerConn. func (s *ServerConn) Handshake() error { var magics handshakeMagics @@ -298,8 +260,8 @@ func (s *ServerConn) Handshake() error { serverKexInit := kexInitMsg{ KexAlgos: supportedKexAlgos, ServerHostKeyAlgos: supportedHostKeyAlgos, - CiphersClientServer: supportedCiphers, - CiphersServerClient: supportedCiphers, + CiphersClientServer: s.config.Crypto.ciphers(), + CiphersServerClient: s.config.Crypto.ciphers(), MACsClientServer: supportedMACs, MACsServerClient: supportedMACs, CompressionClientServer: supportedCompressions, @@ -364,7 +326,9 @@ func (s *ServerConn) Handshake() error { if packet[0] != msgNewKeys { return UnexpectedMessageError{msgNewKeys, packet[0]} } - s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc) + if err = s.transport.reader.setupKeys(clientKeys, K, H, H, hashFunc); err != nil { + return err + } if packet, err = s.readPacket(); err != nil { return err } diff --git a/libgo/go/exp/ssh/tcpip.go b/libgo/go/exp/ssh/tcpip.go new file mode 100644 index 0000000..859dedc --- /dev/null +++ b/libgo/go/exp/ssh/tcpip.go @@ -0,0 +1,146 @@ +// Copyright 2011 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 ssh + +import ( + "errors" + "io" + "net" +) +// Dial initiates a connection to the addr from the remote host. +// addr is resolved using net.ResolveTCPAddr before connection. +// This could allow an observer to observe the DNS name of the +// remote host. Consider using ssh.DialTCP to avoid this. +func (c *ClientConn) Dial(n, addr string) (net.Conn, error) { + raddr, err := net.ResolveTCPAddr(n, addr) + if err != nil { + return nil, err + } + return c.DialTCP(n, nil, raddr) +} + +// DialTCP connects to the remote address raddr on the network net, +// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +// as the local address for the connection. +func (c *ClientConn) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) { + if laddr == nil { + laddr = &net.TCPAddr{ + IP: net.IPv4zero, + Port: 0, + } + } + ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port) + if err != nil { + return nil, err + } + return &tcpchanconn{ + tcpchan: ch, + laddr: laddr, + raddr: raddr, + }, nil +} + +// dial opens a direct-tcpip connection to the remote server. laddr and raddr are passed as +// strings and are expected to be resolveable at the remote end. +func (c *ClientConn) dial(laddr string, lport int, raddr string, rport int) (*tcpchan, error) { + // RFC 4254 7.2 + type channelOpenDirectMsg struct { + ChanType string + PeersId uint32 + PeersWindow uint32 + MaxPacketSize uint32 + raddr string + rport uint32 + laddr string + lport uint32 + } + ch := c.newChan(c.transport) + if err := c.writePacket(marshal(msgChannelOpen, channelOpenDirectMsg{ + ChanType: "direct-tcpip", + PeersId: ch.id, + PeersWindow: 1 << 14, + MaxPacketSize: 1 << 15, // RFC 4253 6.1 + raddr: raddr, + rport: uint32(rport), + laddr: laddr, + lport: uint32(lport), + })); err != nil { + c.chanlist.remove(ch.id) + return nil, err + } + // wait for response + switch msg := (<-ch.msg).(type) { + case *channelOpenConfirmMsg: + ch.peersId = msg.MyId + ch.win <- int(msg.MyWindow) + case *channelOpenFailureMsg: + c.chanlist.remove(ch.id) + return nil, errors.New("ssh: error opening remote TCP connection: " + msg.Message) + default: + c.chanlist.remove(ch.id) + return nil, errors.New("ssh: unexpected packet") + } + return &tcpchan{ + clientChan: ch, + Reader: &chanReader{ + packetWriter: ch, + id: ch.id, + data: ch.data, + }, + Writer: &chanWriter{ + packetWriter: ch, + id: ch.id, + win: ch.win, + }, + }, nil +} + +type tcpchan struct { + *clientChan // the backing channel + io.Reader + io.Writer +} + +// tcpchanconn fulfills the net.Conn interface without +// the tcpchan having to hold laddr or raddr directly. +type tcpchanconn struct { + *tcpchan + laddr, raddr net.Addr +} + +// LocalAddr returns the local network address. +func (t *tcpchanconn) LocalAddr() net.Addr { + return t.laddr +} + +// RemoteAddr returns the remote network address. +func (t *tcpchanconn) RemoteAddr() net.Addr { + return t.raddr +} + +// SetTimeout sets the read and write deadlines associated +// with the connection. +func (t *tcpchanconn) SetTimeout(nsec int64) error { + if err := t.SetReadTimeout(nsec); err != nil { + return err + } + return t.SetWriteTimeout(nsec) +} + +// SetReadTimeout sets the time (in nanoseconds) that +// Read will wait for data before returning an error with Timeout() == true. +// Setting nsec == 0 (the default) disables the deadline. +func (t *tcpchanconn) SetReadTimeout(nsec int64) error { + return errors.New("ssh: tcpchan: timeout not supported") +} + +// SetWriteTimeout sets the time (in nanoseconds) that +// Write will wait to send its data before returning an error with Timeout() == true. +// Setting nsec == 0 (the default) disables the deadline. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +func (t *tcpchanconn) SetWriteTimeout(nsec int64) error { + return errors.New("ssh: tcpchan: timeout not supported") +} diff --git a/libgo/go/exp/ssh/transport.go b/libgo/go/exp/ssh/transport.go index 579a9d8..b8cb2c3 100644 --- a/libgo/go/exp/ssh/transport.go +++ b/libgo/go/exp/ssh/transport.go @@ -7,7 +7,6 @@ package ssh import ( "bufio" "crypto" - "crypto/aes" "crypto/cipher" "crypto/hmac" "crypto/subtle" @@ -19,7 +18,10 @@ import ( ) const ( - paddingMultiple = 16 // TODO(dfc) does this need to be configurable? + packetSizeMultiple = 16 // TODO(huin) this should be determined by the cipher. + minPacketSize = 16 + maxPacketSize = 36000 + minPaddingSize = 4 // TODO(huin) should this be configurable? ) // filteredConn reduces the set of methods exposed when embeddeding @@ -61,8 +63,7 @@ type reader struct { type writer struct { *sync.Mutex // protects writer.Writer from concurrent writes *bufio.Writer - paddingMultiple int - rand io.Reader + rand io.Reader common } @@ -82,14 +83,11 @@ type common struct { func (r *reader) readOnePacket() ([]byte, error) { var lengthBytes = make([]byte, 5) var macSize uint32 - if _, err := io.ReadFull(r, lengthBytes); err != nil { return nil, err } - if r.cipher != nil { - r.cipher.XORKeyStream(lengthBytes, lengthBytes) - } + r.cipher.XORKeyStream(lengthBytes, lengthBytes) if r.mac != nil { r.mac.Reset() @@ -153,9 +151,9 @@ func (w *writer) writePacket(packet []byte) error { w.Mutex.Lock() defer w.Mutex.Unlock() - paddingLength := paddingMultiple - (5+len(packet))%paddingMultiple + paddingLength := packetSizeMultiple - (5+len(packet))%packetSizeMultiple if paddingLength < 4 { - paddingLength += paddingMultiple + paddingLength += packetSizeMultiple } length := len(packet) + 1 + paddingLength @@ -188,11 +186,9 @@ func (w *writer) writePacket(packet []byte) error { // TODO(dfc) lengthBytes, packet and padding should be // subslices of a single buffer - if w.cipher != nil { - w.cipher.XORKeyStream(lengthBytes, lengthBytes) - w.cipher.XORKeyStream(packet, packet) - w.cipher.XORKeyStream(padding, padding) - } + w.cipher.XORKeyStream(lengthBytes, lengthBytes) + w.cipher.XORKeyStream(packet, packet) + w.cipher.XORKeyStream(padding, padding) if _, err := w.Write(lengthBytes); err != nil { return err @@ -227,11 +223,17 @@ func newTransport(conn net.Conn, rand io.Reader) *transport { return &transport{ reader: reader{ Reader: bufio.NewReader(conn), + common: common{ + cipher: noneCipher{}, + }, }, writer: writer{ Writer: bufio.NewWriter(conn), rand: rand, Mutex: new(sync.Mutex), + common: common{ + cipher: noneCipher{}, + }, }, filteredConn: conn, } @@ -249,29 +251,32 @@ var ( clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} ) -// setupKeys sets the cipher and MAC keys from K, H and sessionId, as +// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as // described in RFC 4253, section 6.4. direction should either be serverKeys // (to setup server->client keys) or clientKeys (for client->server keys). func (c *common) setupKeys(d direction, K, H, sessionId []byte, hashFunc crypto.Hash) error { - h := hashFunc.New() + cipherMode := cipherModes[c.cipherAlgo] - blockSize := 16 - keySize := 16 macKeySize := 20 - iv := make([]byte, blockSize) - key := make([]byte, keySize) + iv := make([]byte, cipherMode.ivSize) + key := make([]byte, cipherMode.keySize) macKey := make([]byte, macKeySize) + + h := hashFunc.New() generateKeyMaterial(iv, d.ivTag, K, H, sessionId, h) generateKeyMaterial(key, d.keyTag, K, H, sessionId, h) generateKeyMaterial(macKey, d.macKeyTag, K, H, sessionId, h) c.mac = truncatingMAC{12, hmac.NewSHA1(macKey)} - aes, err := aes.NewCipher(key) + + cipher, err := cipherMode.createCipher(key, iv) if err != nil { return err } - c.cipher = cipher.NewCTR(aes, iv) + + c.cipher = cipher + return nil } diff --git a/libgo/go/exp/terminal/shell.go b/libgo/go/exp/terminal/shell.go deleted file mode 100644 index 5c59167..0000000 --- a/libgo/go/exp/terminal/shell.go +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2011 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 terminal - -import "io" - -// Shell contains the state for running a VT100 terminal that is capable of -// reading lines of input. -type Shell struct { - c io.ReadWriter - prompt string - - // line is the current line being entered. - line []byte - // pos is the logical position of the cursor in line - pos int - - // cursorX contains the current X value of the cursor where the left - // edge is 0. cursorY contains the row number where the first row of - // the current line is 0. - cursorX, cursorY int - // maxLine is the greatest value of cursorY so far. - maxLine int - - termWidth, termHeight int - - // outBuf contains the terminal data to be sent. - outBuf []byte - // remainder contains the remainder of any partial key sequences after - // a read. It aliases into inBuf. - remainder []byte - inBuf [256]byte -} - -// NewShell runs a VT100 terminal on the given ReadWriter. If the ReadWriter is -// a local terminal, that terminal must first have been put into raw mode. -// prompt is a string that is written at the start of each input line (i.e. -// "> "). -func NewShell(c io.ReadWriter, prompt string) *Shell { - return &Shell{ - c: c, - prompt: prompt, - termWidth: 80, - termHeight: 24, - } -} - -const ( - keyCtrlD = 4 - keyEnter = '\r' - keyEscape = 27 - keyBackspace = 127 - keyUnknown = 256 + iota - keyUp - keyDown - keyLeft - keyRight - keyAltLeft - keyAltRight -) - -// bytesToKey tries to parse a key sequence from b. If successful, it returns -// the key and the remainder of the input. Otherwise it returns -1. -func bytesToKey(b []byte) (int, []byte) { - if len(b) == 0 { - return -1, nil - } - - if b[0] != keyEscape { - return int(b[0]), b[1:] - } - - if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { - switch b[2] { - case 'A': - return keyUp, b[3:] - case 'B': - return keyDown, b[3:] - case 'C': - return keyRight, b[3:] - case 'D': - return keyLeft, b[3:] - } - } - - if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { - switch b[5] { - case 'C': - return keyAltRight, b[6:] - case 'D': - return keyAltLeft, b[6:] - } - } - - // If we get here then we have a key that we don't recognise, or a - // partial sequence. It's not clear how one should find the end of a - // sequence without knowing them all, but it seems that [a-zA-Z] only - // appears at the end of a sequence. - for i, c := range b[0:] { - if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { - return keyUnknown, b[i+1:] - } - } - - return -1, b -} - -// queue appends data to the end of ss.outBuf -func (ss *Shell) queue(data []byte) { - if len(ss.outBuf)+len(data) > cap(ss.outBuf) { - newOutBuf := make([]byte, len(ss.outBuf), 2*(len(ss.outBuf)+len(data))) - copy(newOutBuf, ss.outBuf) - ss.outBuf = newOutBuf - } - - oldLen := len(ss.outBuf) - ss.outBuf = ss.outBuf[:len(ss.outBuf)+len(data)] - copy(ss.outBuf[oldLen:], data) -} - -var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'} - -func isPrintable(key int) bool { - return key >= 32 && key < 127 -} - -// moveCursorToPos appends data to ss.outBuf which will move the cursor to the -// given, logical position in the text. -func (ss *Shell) moveCursorToPos(pos int) { - x := len(ss.prompt) + pos - y := x / ss.termWidth - x = x % ss.termWidth - - up := 0 - if y < ss.cursorY { - up = ss.cursorY - y - } - - down := 0 - if y > ss.cursorY { - down = y - ss.cursorY - } - - left := 0 - if x < ss.cursorX { - left = ss.cursorX - x - } - - right := 0 - if x > ss.cursorX { - right = x - ss.cursorX - } - - movement := make([]byte, 3*(up+down+left+right)) - m := movement - for i := 0; i < up; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'A' - m = m[3:] - } - for i := 0; i < down; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'B' - m = m[3:] - } - for i := 0; i < left; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'D' - m = m[3:] - } - for i := 0; i < right; i++ { - m[0] = keyEscape - m[1] = '[' - m[2] = 'C' - m = m[3:] - } - - ss.cursorX = x - ss.cursorY = y - ss.queue(movement) -} - -const maxLineLength = 4096 - -// handleKey processes the given key and, optionally, returns a line of text -// that the user has entered. -func (ss *Shell) handleKey(key int) (line string, ok bool) { - switch key { - case keyBackspace: - if ss.pos == 0 { - return - } - ss.pos-- - - copy(ss.line[ss.pos:], ss.line[1+ss.pos:]) - ss.line = ss.line[:len(ss.line)-1] - ss.writeLine(ss.line[ss.pos:]) - ss.moveCursorToPos(ss.pos) - ss.queue(eraseUnderCursor) - case keyAltLeft: - // move left by a word. - if ss.pos == 0 { - return - } - ss.pos-- - for ss.pos > 0 { - if ss.line[ss.pos] != ' ' { - break - } - ss.pos-- - } - for ss.pos > 0 { - if ss.line[ss.pos] == ' ' { - ss.pos++ - break - } - ss.pos-- - } - ss.moveCursorToPos(ss.pos) - case keyAltRight: - // move right by a word. - for ss.pos < len(ss.line) { - if ss.line[ss.pos] == ' ' { - break - } - ss.pos++ - } - for ss.pos < len(ss.line) { - if ss.line[ss.pos] != ' ' { - break - } - ss.pos++ - } - ss.moveCursorToPos(ss.pos) - case keyLeft: - if ss.pos == 0 { - return - } - ss.pos-- - ss.moveCursorToPos(ss.pos) - case keyRight: - if ss.pos == len(ss.line) { - return - } - ss.pos++ - ss.moveCursorToPos(ss.pos) - case keyEnter: - ss.moveCursorToPos(len(ss.line)) - ss.queue([]byte("\r\n")) - line = string(ss.line) - ok = true - ss.line = ss.line[:0] - ss.pos = 0 - ss.cursorX = 0 - ss.cursorY = 0 - ss.maxLine = 0 - default: - if !isPrintable(key) { - return - } - if len(ss.line) == maxLineLength { - return - } - if len(ss.line) == cap(ss.line) { - newLine := make([]byte, len(ss.line), 2*(1+len(ss.line))) - copy(newLine, ss.line) - ss.line = newLine - } - ss.line = ss.line[:len(ss.line)+1] - copy(ss.line[ss.pos+1:], ss.line[ss.pos:]) - ss.line[ss.pos] = byte(key) - ss.writeLine(ss.line[ss.pos:]) - ss.pos++ - ss.moveCursorToPos(ss.pos) - } - return -} - -func (ss *Shell) writeLine(line []byte) { - for len(line) != 0 { - if ss.cursorX == ss.termWidth { - ss.queue([]byte("\r\n")) - ss.cursorX = 0 - ss.cursorY++ - if ss.cursorY > ss.maxLine { - ss.maxLine = ss.cursorY - } - } - - remainingOnLine := ss.termWidth - ss.cursorX - todo := len(line) - if todo > remainingOnLine { - todo = remainingOnLine - } - ss.queue(line[:todo]) - ss.cursorX += todo - line = line[todo:] - } -} - -func (ss *Shell) Write(buf []byte) (n int, err error) { - return ss.c.Write(buf) -} - -// ReadLine returns a line of input from the terminal. -func (ss *Shell) ReadLine() (line string, err error) { - ss.writeLine([]byte(ss.prompt)) - ss.c.Write(ss.outBuf) - ss.outBuf = ss.outBuf[:0] - - for { - // ss.remainder is a slice at the beginning of ss.inBuf - // containing a partial key sequence - readBuf := ss.inBuf[len(ss.remainder):] - var n int - n, err = ss.c.Read(readBuf) - if err != nil { - return - } - - if err == nil { - ss.remainder = ss.inBuf[:n+len(ss.remainder)] - rest := ss.remainder - lineOk := false - for !lineOk { - var key int - key, rest = bytesToKey(rest) - if key < 0 { - break - } - if key == keyCtrlD { - return "", io.EOF - } - line, lineOk = ss.handleKey(key) - } - if len(rest) > 0 { - n := copy(ss.inBuf[:], rest) - ss.remainder = ss.inBuf[:n] - } else { - ss.remainder = nil - } - ss.c.Write(ss.outBuf) - ss.outBuf = ss.outBuf[:0] - if lineOk { - return - } - continue - } - } - panic("unreachable") -} diff --git a/libgo/go/exp/terminal/terminal.go b/libgo/go/exp/terminal/terminal.go index 5732543..18d76cd 100644 --- a/libgo/go/exp/terminal/terminal.go +++ b/libgo/go/exp/terminal/terminal.go @@ -2,102 +2,361 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package terminal provides support functions for dealing with terminals, as -// commonly found on UNIX systems. -// -// Putting a terminal into raw mode is the most common requirement: -// -// oldState, err := terminal.MakeRaw(0) -// if err != nil { -// panic(err.String()) -// } -// defer terminal.Restore(0, oldState) package terminal -import ( - "io" - "os" - "syscall" -) +import "io" + +// Terminal contains the state for running a VT100 terminal that is capable of +// reading lines of input. +type Terminal struct { + c io.ReadWriter + prompt string + + // line is the current line being entered. + line []byte + // pos is the logical position of the cursor in line + pos int + + // cursorX contains the current X value of the cursor where the left + // edge is 0. cursorY contains the row number where the first row of + // the current line is 0. + cursorX, cursorY int + // maxLine is the greatest value of cursorY so far. + maxLine int + + termWidth, termHeight int -// State contains the state of a terminal. -type State struct { - termios syscall.Termios + // outBuf contains the terminal data to be sent. + outBuf []byte + // remainder contains the remainder of any partial key sequences after + // a read. It aliases into inBuf. + remainder []byte + inBuf [256]byte } -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd int) bool { - var termios syscall.Termios - e := syscall.Tcgetattr(fd, &termios) - return e == 0 +// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is +// a local terminal, that terminal must first have been put into raw mode. +// prompt is a string that is written at the start of each input line (i.e. +// "> "). +func NewTerminal(c io.ReadWriter, prompt string) *Terminal { + return &Terminal{ + c: c, + prompt: prompt, + termWidth: 80, + termHeight: 24, + } } -// MakeRaw put the terminal connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be -// restored. -func MakeRaw(fd int) (*State, error) { - var oldState State - if e := syscall.Tcgetattr(fd, &oldState.termios); e != 0 { - return nil, os.Errno(e) +const ( + keyCtrlD = 4 + keyEnter = '\r' + keyEscape = 27 + keyBackspace = 127 + keyUnknown = 256 + iota + keyUp + keyDown + keyLeft + keyRight + keyAltLeft + keyAltRight +) + +// bytesToKey tries to parse a key sequence from b. If successful, it returns +// the key and the remainder of the input. Otherwise it returns -1. +func bytesToKey(b []byte) (int, []byte) { + if len(b) == 0 { + return -1, nil + } + + if b[0] != keyEscape { + return int(b[0]), b[1:] + } + + if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' { + switch b[2] { + case 'A': + return keyUp, b[3:] + case 'B': + return keyDown, b[3:] + case 'C': + return keyRight, b[3:] + case 'D': + return keyLeft, b[3:] + } + } + + if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' { + switch b[5] { + case 'C': + return keyAltRight, b[6:] + case 'D': + return keyAltLeft, b[6:] + } + } + + // If we get here then we have a key that we don't recognise, or a + // partial sequence. It's not clear how one should find the end of a + // sequence without knowing them all, but it seems that [a-zA-Z] only + // appears at the end of a sequence. + for i, c := range b[0:] { + if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' { + return keyUnknown, b[i+1:] + } } - newState := oldState.termios - newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF - newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG - if e := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); e != 0 { - return nil, os.Errno(e) + return -1, b +} + +// queue appends data to the end of t.outBuf +func (t *Terminal) queue(data []byte) { + if len(t.outBuf)+len(data) > cap(t.outBuf) { + newOutBuf := make([]byte, len(t.outBuf), 2*(len(t.outBuf)+len(data))) + copy(newOutBuf, t.outBuf) + t.outBuf = newOutBuf } - return &oldState, nil + oldLen := len(t.outBuf) + t.outBuf = t.outBuf[:len(t.outBuf)+len(data)] + copy(t.outBuf[oldLen:], data) } -// Restore restores the terminal connected to the given file descriptor to a -// previous state. -func Restore(fd int, state *State) error { - e := syscall.Tcsetattr(fd, syscall.TCSANOW, &state.termios) - return os.Errno(e) +var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'} + +func isPrintable(key int) bool { + return key >= 32 && key < 127 } -// ReadPassword reads a line of input from a terminal without local echo. This -// is commonly used for inputting passwords and other sensitive data. The slice -// returned does not include the \n. -func ReadPassword(fd int) ([]byte, error) { - var oldState syscall.Termios - if e := syscall.Tcgetattr(fd, &oldState); e != 0 { - return nil, os.Errno(e) +// moveCursorToPos appends data to t.outBuf which will move the cursor to the +// given, logical position in the text. +func (t *Terminal) moveCursorToPos(pos int) { + x := len(t.prompt) + pos + y := x / t.termWidth + x = x % t.termWidth + + up := 0 + if y < t.cursorY { + up = t.cursorY - y } - newState := oldState - newState.Lflag &^= syscall.ECHO - if e := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); e != 0 { - return nil, os.Errno(e) + down := 0 + if y > t.cursorY { + down = y - t.cursorY } - defer func() { - syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState) - }() + left := 0 + if x < t.cursorX { + left = t.cursorX - x + } - var buf [16]byte - var ret []byte - for { - n, errno := syscall.Read(fd, buf[:]) - if errno != 0 { - return nil, os.Errno(errno) + right := 0 + if x > t.cursorX { + right = x - t.cursorX + } + + movement := make([]byte, 3*(up+down+left+right)) + m := movement + for i := 0; i < up; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'A' + m = m[3:] + } + for i := 0; i < down; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'B' + m = m[3:] + } + for i := 0; i < left; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'D' + m = m[3:] + } + for i := 0; i < right; i++ { + m[0] = keyEscape + m[1] = '[' + m[2] = 'C' + m = m[3:] + } + + t.cursorX = x + t.cursorY = y + t.queue(movement) +} + +const maxLineLength = 4096 + +// handleKey processes the given key and, optionally, returns a line of text +// that the user has entered. +func (t *Terminal) handleKey(key int) (line string, ok bool) { + switch key { + case keyBackspace: + if t.pos == 0 { + return + } + t.pos-- + + copy(t.line[t.pos:], t.line[1+t.pos:]) + t.line = t.line[:len(t.line)-1] + t.writeLine(t.line[t.pos:]) + t.moveCursorToPos(t.pos) + t.queue(eraseUnderCursor) + case keyAltLeft: + // move left by a word. + if t.pos == 0 { + return + } + t.pos-- + for t.pos > 0 { + if t.line[t.pos] != ' ' { + break + } + t.pos-- + } + for t.pos > 0 { + if t.line[t.pos] == ' ' { + t.pos++ + break + } + t.pos-- + } + t.moveCursorToPos(t.pos) + case keyAltRight: + // move right by a word. + for t.pos < len(t.line) { + if t.line[t.pos] == ' ' { + break + } + t.pos++ } - if n == 0 { - if len(ret) == 0 { - return nil, io.EOF + for t.pos < len(t.line) { + if t.line[t.pos] != ' ' { + break + } + t.pos++ + } + t.moveCursorToPos(t.pos) + case keyLeft: + if t.pos == 0 { + return + } + t.pos-- + t.moveCursorToPos(t.pos) + case keyRight: + if t.pos == len(t.line) { + return + } + t.pos++ + t.moveCursorToPos(t.pos) + case keyEnter: + t.moveCursorToPos(len(t.line)) + t.queue([]byte("\r\n")) + line = string(t.line) + ok = true + t.line = t.line[:0] + t.pos = 0 + t.cursorX = 0 + t.cursorY = 0 + t.maxLine = 0 + default: + if !isPrintable(key) { + return + } + if len(t.line) == maxLineLength { + return + } + if len(t.line) == cap(t.line) { + newLine := make([]byte, len(t.line), 2*(1+len(t.line))) + copy(newLine, t.line) + t.line = newLine + } + t.line = t.line[:len(t.line)+1] + copy(t.line[t.pos+1:], t.line[t.pos:]) + t.line[t.pos] = byte(key) + t.writeLine(t.line[t.pos:]) + t.pos++ + t.moveCursorToPos(t.pos) + } + return +} + +func (t *Terminal) writeLine(line []byte) { + for len(line) != 0 { + if t.cursorX == t.termWidth { + t.queue([]byte("\r\n")) + t.cursorX = 0 + t.cursorY++ + if t.cursorY > t.maxLine { + t.maxLine = t.cursorY } - break } - if buf[n-1] == '\n' { - n-- + + remainingOnLine := t.termWidth - t.cursorX + todo := len(line) + if todo > remainingOnLine { + todo = remainingOnLine } - ret = append(ret, buf[:n]...) - if n < len(buf) { - break + t.queue(line[:todo]) + t.cursorX += todo + line = line[todo:] + } +} + +func (t *Terminal) Write(buf []byte) (n int, err error) { + return t.c.Write(buf) +} + +// ReadLine returns a line of input from the terminal. +func (t *Terminal) ReadLine() (line string, err error) { + if t.cursorX == 0 { + t.writeLine([]byte(t.prompt)) + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + } + + for { + // t.remainder is a slice at the beginning of t.inBuf + // containing a partial key sequence + readBuf := t.inBuf[len(t.remainder):] + var n int + n, err = t.c.Read(readBuf) + if err != nil { + return + } + + if err == nil { + t.remainder = t.inBuf[:n+len(t.remainder)] + rest := t.remainder + lineOk := false + for !lineOk { + var key int + key, rest = bytesToKey(rest) + if key < 0 { + break + } + if key == keyCtrlD { + return "", io.EOF + } + line, lineOk = t.handleKey(key) + } + if len(rest) > 0 { + n := copy(t.inBuf[:], rest) + t.remainder = t.inBuf[:n] + } else { + t.remainder = nil + } + t.c.Write(t.outBuf) + t.outBuf = t.outBuf[:0] + if lineOk { + return + } + continue } } + panic("unreachable") +} - return ret, nil +func (t *Terminal) SetSize(width, height int) { + t.termWidth, t.termHeight = width, height } diff --git a/libgo/go/exp/terminal/shell_test.go b/libgo/go/exp/terminal/terminal_test.go index 8a76a85..a219721 100644 --- a/libgo/go/exp/terminal/shell_test.go +++ b/libgo/go/exp/terminal/terminal_test.go @@ -41,7 +41,7 @@ func (c *MockTerminal) Write(data []byte) (n int, err error) { func TestClose(t *testing.T) { c := &MockTerminal{} - ss := NewShell(c, "> ") + ss := NewTerminal(c, "> ") line, err := ss.ReadLine() if line != "" { t.Errorf("Expected empty line but got: %s", line) @@ -95,7 +95,7 @@ func TestKeyPresses(t *testing.T) { toSend: []byte(test.in), bytesPerRead: j, } - ss := NewShell(c, "> ") + ss := NewTerminal(c, "> ") line, err := ss.ReadLine() if line != test.line { t.Errorf("Line resulting from test %d (%d bytes per read) was '%s', expected '%s'", i, j, line, test.line) diff --git a/libgo/go/exp/terminal/util.go b/libgo/go/exp/terminal/util.go new file mode 100644 index 0000000..0303567 --- /dev/null +++ b/libgo/go/exp/terminal/util.go @@ -0,0 +1,102 @@ +// Copyright 2011 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 terminal provides support functions for dealing with terminals, as +// commonly found on UNIX systems. +// +// Putting a terminal into raw mode is the most common requirement: +// +// oldState, err := terminal.MakeRaw(0) +// if err != nil { +// panic(err.String()) +// } +// defer terminal.Restore(0, oldState) +package terminal + +import ( + "io" + "syscall" +) + +// State contains the state of a terminal. +type State struct { + termios syscall.Termios +} + +// IsTerminal returns true if the given file descriptor is a terminal. +func IsTerminal(fd int) bool { + var termios syscall.Termios + err := syscall.Tcgetattr(fd, &termios) + return err == nil +} + +// MakeRaw put the terminal connected to the given file descriptor into raw +// mode and returns the previous state of the terminal so that it can be +// restored. +func MakeRaw(fd int) (*State, error) { + var oldState State + if err := syscall.Tcgetattr(fd, &oldState.termios); err != nil { + return nil, err + } + + newState := oldState.termios + newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF + newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG + if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil { + return nil, err + } + + return &oldState, nil +} + +// Restore restores the terminal connected to the given file descriptor to a +// previous state. +func Restore(fd int, state *State) error { + err := syscall.Tcsetattr(fd, syscall.TCSANOW, &state.termios) + return err +} + +// ReadPassword reads a line of input from a terminal without local echo. This +// is commonly used for inputting passwords and other sensitive data. The slice +// returned does not include the \n. +func ReadPassword(fd int) ([]byte, error) { + var oldState syscall.Termios + if err := syscall.Tcgetattr(fd, &oldState); err != nil { + return nil, err + } + + newState := oldState + newState.Lflag &^= syscall.ECHO + if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil { + return nil, err + } + + defer func() { + syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState) + }() + + var buf [16]byte + var ret []byte + for { + n, err := syscall.Read(fd, buf[:]) + if err != nil { + return nil, err + } + if n == 0 { + if len(ret) == 0 { + return nil, io.EOF + } + break + } + if buf[n-1] == '\n' { + n-- + } + ret = append(ret, buf[:n]...) + if n < len(buf) { + break + } + } + + return ret, nil +} diff --git a/libgo/go/fmt/fmt_test.go b/libgo/go/fmt/fmt_test.go index db83f85..6370560 100644 --- a/libgo/go/fmt/fmt_test.go +++ b/libgo/go/fmt/fmt_test.go @@ -357,6 +357,10 @@ var fmttests = []struct { {"%#v", map[string]B{"a": {1, 2}}, `map[string] fmt_test.B{"a":fmt_test.B{I:1, j:2}}`}, {"%#v", []string{"a", "b"}, `[]string{"a", "b"}`}, {"%#v", SI{}, `fmt_test.SI{I:interface {}(nil)}`}, + {"%#v", []int(nil), `[]int(nil)`}, + {"%#v", []int{}, `[]int{}`}, + {"%#v", map[int]byte(nil), `map[int] uint8(nil)`}, + {"%#v", map[int]byte{}, `map[int] uint8{}`}, // slices with other formats {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`}, diff --git a/libgo/go/fmt/print.go b/libgo/go/fmt/print.go index bfa88d1..7143e07 100644 --- a/libgo/go/fmt/print.go +++ b/libgo/go/fmt/print.go @@ -795,6 +795,10 @@ BigSwitch: case reflect.Map: if goSyntax { p.buf.WriteString(f.Type().String()) + if f.IsNil() { + p.buf.WriteString("(nil)") + break + } p.buf.WriteByte('{') } else { p.buf.Write(mapBytes) @@ -873,6 +877,10 @@ BigSwitch: } if goSyntax { p.buf.WriteString(value.Type().String()) + if f.IsNil() { + p.buf.WriteString("(nil)") + break + } p.buf.WriteByte('{') } else { p.buf.WriteByte('[') diff --git a/libgo/go/fmt/scan_test.go b/libgo/go/fmt/scan_test.go index d3c39be..0689bf3 100644 --- a/libgo/go/fmt/scan_test.go +++ b/libgo/go/fmt/scan_test.go @@ -324,7 +324,7 @@ var x, y Xs var z IntString var multiTests = []ScanfMultiTest{ - {"", "", nil, nil, ""}, + {"", "", []interface{}{}, []interface{}{}, ""}, {"%d", "23", args(&i), args(23), ""}, {"%2s%3s", "22333", args(&s, &t), args("22", "333"), ""}, {"%2d%3d", "44555", args(&i, &j), args(44, 555), ""}, @@ -378,7 +378,7 @@ func testScan(name string, t *testing.T, scan func(r io.Reader, a ...interface{} } val := v.Interface() if !reflect.DeepEqual(val, test.out) { - t.Errorf("%s scanning %q: expected %v got %v, type %T", name, test.text, test.out, val, val) + t.Errorf("%s scanning %q: expected %#v got %#v, type %T", name, test.text, test.out, val, val) } } } @@ -417,7 +417,7 @@ func TestScanf(t *testing.T) { } val := v.Interface() if !reflect.DeepEqual(val, test.out) { - t.Errorf("scanning (%q, %q): expected %v got %v, type %T", test.format, test.text, test.out, val, val) + t.Errorf("scanning (%q, %q): expected %#v got %#v, type %T", test.format, test.text, test.out, val, val) } } } @@ -520,7 +520,7 @@ func testScanfMulti(name string, t *testing.T) { } result := resultVal.Interface() if !reflect.DeepEqual(result, test.out) { - t.Errorf("scanning (%q, %q): expected %v got %v", test.format, test.text, test.out, result) + t.Errorf("scanning (%q, %q): expected %#v got %#v", test.format, test.text, test.out, result) } } } diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go index a0aa5ff..1485f35 100644 --- a/libgo/go/go/ast/ast.go +++ b/libgo/go/go/ast/ast.go @@ -412,29 +412,29 @@ func (x *ChanType) End() token.Pos { return x.Value.End() } // exprNode() ensures that only expression/type nodes can be // assigned to an ExprNode. // -func (x *BadExpr) exprNode() {} -func (x *Ident) exprNode() {} -func (x *Ellipsis) exprNode() {} -func (x *BasicLit) exprNode() {} -func (x *FuncLit) exprNode() {} -func (x *CompositeLit) exprNode() {} -func (x *ParenExpr) exprNode() {} -func (x *SelectorExpr) exprNode() {} -func (x *IndexExpr) exprNode() {} -func (x *SliceExpr) exprNode() {} -func (x *TypeAssertExpr) exprNode() {} -func (x *CallExpr) exprNode() {} -func (x *StarExpr) exprNode() {} -func (x *UnaryExpr) exprNode() {} -func (x *BinaryExpr) exprNode() {} -func (x *KeyValueExpr) exprNode() {} - -func (x *ArrayType) exprNode() {} -func (x *StructType) exprNode() {} -func (x *FuncType) exprNode() {} -func (x *InterfaceType) exprNode() {} -func (x *MapType) exprNode() {} -func (x *ChanType) exprNode() {} +func (*BadExpr) exprNode() {} +func (*Ident) exprNode() {} +func (*Ellipsis) exprNode() {} +func (*BasicLit) exprNode() {} +func (*FuncLit) exprNode() {} +func (*CompositeLit) exprNode() {} +func (*ParenExpr) exprNode() {} +func (*SelectorExpr) exprNode() {} +func (*IndexExpr) exprNode() {} +func (*SliceExpr) exprNode() {} +func (*TypeAssertExpr) exprNode() {} +func (*CallExpr) exprNode() {} +func (*StarExpr) exprNode() {} +func (*UnaryExpr) exprNode() {} +func (*BinaryExpr) exprNode() {} +func (*KeyValueExpr) exprNode() {} + +func (*ArrayType) exprNode() {} +func (*StructType) exprNode() {} +func (*FuncType) exprNode() {} +func (*InterfaceType) exprNode() {} +func (*MapType) exprNode() {} +func (*ChanType) exprNode() {} // ---------------------------------------------------------------------------- // Convenience functions for Idents @@ -711,27 +711,27 @@ func (s *RangeStmt) End() token.Pos { return s.Body.End() } // stmtNode() ensures that only statement nodes can be // assigned to a StmtNode. // -func (s *BadStmt) stmtNode() {} -func (s *DeclStmt) stmtNode() {} -func (s *EmptyStmt) stmtNode() {} -func (s *LabeledStmt) stmtNode() {} -func (s *ExprStmt) stmtNode() {} -func (s *SendStmt) stmtNode() {} -func (s *IncDecStmt) stmtNode() {} -func (s *AssignStmt) stmtNode() {} -func (s *GoStmt) stmtNode() {} -func (s *DeferStmt) stmtNode() {} -func (s *ReturnStmt) stmtNode() {} -func (s *BranchStmt) stmtNode() {} -func (s *BlockStmt) stmtNode() {} -func (s *IfStmt) stmtNode() {} -func (s *CaseClause) stmtNode() {} -func (s *SwitchStmt) stmtNode() {} -func (s *TypeSwitchStmt) stmtNode() {} -func (s *CommClause) stmtNode() {} -func (s *SelectStmt) stmtNode() {} -func (s *ForStmt) stmtNode() {} -func (s *RangeStmt) stmtNode() {} +func (*BadStmt) stmtNode() {} +func (*DeclStmt) stmtNode() {} +func (*EmptyStmt) stmtNode() {} +func (*LabeledStmt) stmtNode() {} +func (*ExprStmt) stmtNode() {} +func (*SendStmt) stmtNode() {} +func (*IncDecStmt) stmtNode() {} +func (*AssignStmt) stmtNode() {} +func (*GoStmt) stmtNode() {} +func (*DeferStmt) stmtNode() {} +func (*ReturnStmt) stmtNode() {} +func (*BranchStmt) stmtNode() {} +func (*BlockStmt) stmtNode() {} +func (*IfStmt) stmtNode() {} +func (*CaseClause) stmtNode() {} +func (*SwitchStmt) stmtNode() {} +func (*TypeSwitchStmt) stmtNode() {} +func (*CommClause) stmtNode() {} +func (*SelectStmt) stmtNode() {} +func (*ForStmt) stmtNode() {} +func (*RangeStmt) stmtNode() {} // ---------------------------------------------------------------------------- // Declarations @@ -807,9 +807,9 @@ func (s *TypeSpec) End() token.Pos { return s.Type.End() } // specNode() ensures that only spec nodes can be // assigned to a Spec. // -func (s *ImportSpec) specNode() {} -func (s *ValueSpec) specNode() {} -func (s *TypeSpec) specNode() {} +func (*ImportSpec) specNode() {} +func (*ValueSpec) specNode() {} +func (*TypeSpec) specNode() {} // A declaration is represented by one of the following declaration nodes. // @@ -875,9 +875,9 @@ func (d *FuncDecl) End() token.Pos { // declNode() ensures that only declaration nodes can be // assigned to a DeclNode. // -func (d *BadDecl) declNode() {} -func (d *GenDecl) declNode() {} -func (d *FuncDecl) declNode() {} +func (*BadDecl) declNode() {} +func (*GenDecl) declNode() {} +func (*FuncDecl) declNode() {} // ---------------------------------------------------------------------------- // Files and packages diff --git a/libgo/go/go/ast/filter.go b/libgo/go/go/ast/filter.go index d7d4b4b..bec235e 100644 --- a/libgo/go/go/ast/filter.go +++ b/libgo/go/go/ast/filter.go @@ -24,7 +24,7 @@ func exportFilter(name string) bool { // it returns false otherwise. // func FileExports(src *File) bool { - return FilterFile(src, exportFilter) + return filterFile(src, exportFilter, true) } // PackageExports trims the AST for a Go package in place such that @@ -35,7 +35,7 @@ func FileExports(src *File) bool { // it returns false otherwise. // func PackageExports(pkg *Package) bool { - return FilterPackage(pkg, exportFilter) + return filterPackage(pkg, exportFilter, true) } // ---------------------------------------------------------------------------- @@ -72,7 +72,7 @@ func fieldName(x Expr) *Ident { return nil } -func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) { +func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) { if fields == nil { return false } @@ -93,8 +93,8 @@ func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) { keepField = len(f.Names) > 0 } if keepField { - if filter == exportFilter { - filterType(f.Type, filter) + if export { + filterType(f.Type, filter, export) } list[j] = f j++ @@ -107,84 +107,84 @@ func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) { return } -func filterParamList(fields *FieldList, filter Filter) bool { +func filterParamList(fields *FieldList, filter Filter, export bool) bool { if fields == nil { return false } var b bool for _, f := range fields.List { - if filterType(f.Type, filter) { + if filterType(f.Type, filter, export) { b = true } } return b } -func filterType(typ Expr, f Filter) bool { +func filterType(typ Expr, f Filter, export bool) bool { switch t := typ.(type) { case *Ident: return f(t.Name) case *ParenExpr: - return filterType(t.X, f) + return filterType(t.X, f, export) case *ArrayType: - return filterType(t.Elt, f) + return filterType(t.Elt, f, export) case *StructType: - if filterFieldList(t.Fields, f) { + if filterFieldList(t.Fields, f, export) { t.Incomplete = true } return len(t.Fields.List) > 0 case *FuncType: - b1 := filterParamList(t.Params, f) - b2 := filterParamList(t.Results, f) + b1 := filterParamList(t.Params, f, export) + b2 := filterParamList(t.Results, f, export) return b1 || b2 case *InterfaceType: - if filterFieldList(t.Methods, f) { + if filterFieldList(t.Methods, f, export) { t.Incomplete = true } return len(t.Methods.List) > 0 case *MapType: - b1 := filterType(t.Key, f) - b2 := filterType(t.Value, f) + b1 := filterType(t.Key, f, export) + b2 := filterType(t.Value, f, export) return b1 || b2 case *ChanType: - return filterType(t.Value, f) + return filterType(t.Value, f, export) } return false } -func filterSpec(spec Spec, f Filter) bool { +func filterSpec(spec Spec, f Filter, export bool) bool { switch s := spec.(type) { case *ValueSpec: s.Names = filterIdentList(s.Names, f) if len(s.Names) > 0 { - if f == exportFilter { - filterType(s.Type, f) + if export { + filterType(s.Type, f, export) } return true } case *TypeSpec: if f(s.Name.Name) { - if f == exportFilter { - filterType(s.Type, f) + if export { + filterType(s.Type, f, export) } return true } - if f != exportFilter { + if !export { // For general filtering (not just exports), // filter type even if name is not filtered // out. // If the type contains filtered elements, // keep the declaration. - return filterType(s.Type, f) + return filterType(s.Type, f, export) } } return false } -func filterSpecList(list []Spec, f Filter) []Spec { +func filterSpecList(list []Spec, f Filter, export bool) []Spec { j := 0 for _, s := range list { - if filterSpec(s, f) { + if filterSpec(s, f, export) { list[j] = s j++ } @@ -200,9 +200,13 @@ func filterSpecList(list []Spec, f Filter) []Spec { // filtering; it returns false otherwise. // func FilterDecl(decl Decl, f Filter) bool { + return filterDecl(decl, f, false) +} + +func filterDecl(decl Decl, f Filter, export bool) bool { switch d := decl.(type) { case *GenDecl: - d.Specs = filterSpecList(d.Specs, f) + d.Specs = filterSpecList(d.Specs, f, export) return len(d.Specs) > 0 case *FuncDecl: return f(d.Name.Name) @@ -221,9 +225,13 @@ func FilterDecl(decl Decl, f Filter) bool { // left after filtering; it returns false otherwise. // func FilterFile(src *File, f Filter) bool { + return filterFile(src, f, false) +} + +func filterFile(src *File, f Filter, export bool) bool { j := 0 for _, d := range src.Decls { - if FilterDecl(d, f) { + if filterDecl(d, f, export) { src.Decls[j] = d j++ } @@ -244,9 +252,13 @@ func FilterFile(src *File, f Filter) bool { // left after filtering; it returns false otherwise. // func FilterPackage(pkg *Package, f Filter) bool { + return filterPackage(pkg, f, false) +} + +func filterPackage(pkg *Package, f Filter, export bool) bool { hasDecls := false for _, src := range pkg.Files { - if FilterFile(src, f) { + if filterFile(src, f, export) { hasDecls = true } } diff --git a/libgo/go/go/build/build_test.go b/libgo/go/go/build/build_test.go index db8bc6c..e22a49a 100644 --- a/libgo/go/go/build/build_test.go +++ b/libgo/go/go/build/build_test.go @@ -37,18 +37,20 @@ var buildPkgs = []struct { { "go/build/cmdtest", &DirInfo{ - GoFiles: []string{"main.go"}, - Package: "main", - Imports: []string{"go/build/pkgtest"}, + GoFiles: []string{"main.go"}, + Package: "main", + Imports: []string{"go/build/pkgtest"}, + TestImports: []string{}, }, }, { "go/build/cgotest", &DirInfo{ - CgoFiles: []string{"cgotest.go"}, - CFiles: []string{"cgotest.c"}, - Imports: []string{"C", "unsafe"}, - Package: "cgotest", + CgoFiles: []string{"cgotest.go"}, + CFiles: []string{"cgotest.c"}, + Imports: []string{"C", "unsafe"}, + TestImports: []string{}, + Package: "cgotest", }, }, } diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index aba7d93..6104c32 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -13,6 +13,8 @@ import ( "io" "os" "path/filepath" + "strconv" + "strings" "text/tabwriter" ) @@ -244,6 +246,8 @@ func (p *printer) writeItem(pos token.Position, data string) { p.last = p.pos } +const linePrefix = "//line " + // writeCommentPrefix writes the whitespace before a comment. // If there is any pending whitespace, it consumes as much of // it as is likely to help position the comment nicely. @@ -252,7 +256,7 @@ func (p *printer) writeItem(pos token.Position, data string) { // a group of comments (or nil), and isKeyword indicates if the // next item is a keyword. // -func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, isKeyword bool) { +func (p *printer) writeCommentPrefix(pos, next token.Position, prev, comment *ast.Comment, isKeyword bool) { if p.written == 0 { // the comment is the first item to be printed - don't write any whitespace return @@ -337,6 +341,13 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment } p.writeWhitespace(j) } + + // turn off indent if we're about to print a line directive. + indent := p.indent + if strings.HasPrefix(comment.Text, linePrefix) { + p.indent = 0 + } + // use formfeeds to break columns before a comment; // this is analogous to using formfeeds to separate // individual lines of /*-style comments - but make @@ -347,6 +358,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment n = 1 } p.writeNewlines(n, true) + p.indent = indent } } @@ -526,6 +538,26 @@ func stripCommonPrefix(lines [][]byte) { func (p *printer) writeComment(comment *ast.Comment) { text := comment.Text + if strings.HasPrefix(text, linePrefix) { + pos := strings.TrimSpace(text[len(linePrefix):]) + i := strings.LastIndex(pos, ":") + if i >= 0 { + // The line directive we are about to print changed + // the Filename and Line number used by go/token + // as it was reading the input originally. + // In order to match the original input, we have to + // update our own idea of the file and line number + // accordingly, after printing the directive. + file := pos[:i] + line, _ := strconv.Atoi(string(pos[i+1:])) + defer func() { + p.pos.Filename = string(file) + p.pos.Line = line + p.pos.Column = 1 + }() + } + } + // shortcut common case of //-style comments if text[1] == '/' { p.writeItem(p.fset.Position(comment.Pos()), p.escape(text)) @@ -599,7 +631,7 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (dro var last *ast.Comment for ; p.commentBefore(next); p.cindex++ { for _, c := range p.comments[p.cindex].List { - p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, tok.IsKeyword()) + p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, c, tok.IsKeyword()) p.writeComment(c) last = c } diff --git a/libgo/go/html/doc.go b/libgo/go/html/doc.go index 1bea690..56b194f 100644 --- a/libgo/go/html/doc.go +++ b/libgo/go/html/doc.go @@ -37,7 +37,7 @@ lower-cased, and attributes are collected into a []Attribute. For example: for { if z.Next() == html.ErrorToken { // Returning io.EOF indicates success. - return z.Error() + return z.Err() } emitToken(z.Token()) } @@ -51,7 +51,7 @@ call to Next. For example, to extract an HTML page's anchor text: tt := z.Next() switch tt { case ErrorToken: - return z.Error() + return z.Err() case TextToken: if depth > 0 { // emitBytes should copy the []byte it receives, diff --git a/libgo/go/html/parse.go b/libgo/go/html/parse.go index f47d4ea..9b7e934 100644 --- a/libgo/go/html/parse.go +++ b/libgo/go/html/parse.go @@ -29,6 +29,8 @@ type parser struct { head, form *Node // Other parsing state flags (section 11.2.3.5). scripting, framesetOK bool + // im is the current insertion mode. + im insertionMode // originalIM is the insertion mode to go back to after completing a text // or inTableText insertion mode. originalIM insertionMode @@ -265,37 +267,22 @@ func (p *parser) acknowledgeSelfClosingTag() { // An insertion mode (section 11.2.3.1) is the state transition function from // a particular state in the HTML5 parser's state machine. It updates the -// parser's fields depending on parser.token (where ErrorToken means EOF). In -// addition to returning the next insertionMode state, it also returns whether -// the token was consumed. -type insertionMode func(*parser) (insertionMode, bool) - -// useTheRulesFor runs the delegate insertionMode over p, returning the actual -// insertionMode unless the delegate caused a state transition. -// Section 11.2.3.1, "using the rules for". -func useTheRulesFor(p *parser, actual, delegate insertionMode) (insertionMode, bool) { - im, consumed := delegate(p) - if p.originalIM == delegate { - p.originalIM = actual - } - if im != delegate { - return im, consumed - } - return actual, consumed -} +// parser's fields depending on parser.tok (where ErrorToken means EOF). +// It returns whether the token was consumed. +type insertionMode func(*parser) bool // setOriginalIM sets the insertion mode to return to after completing a text or // inTableText insertion mode. // Section 11.2.3.1, "using the rules for". -func (p *parser) setOriginalIM(im insertionMode) { +func (p *parser) setOriginalIM() { if p.originalIM != nil { panic("html: bad parser state: originalIM was set twice") } - p.originalIM = im + p.originalIM = p.im } // Section 11.2.3.1, "reset the insertion mode". -func (p *parser) resetInsertionMode() insertionMode { +func (p *parser) resetInsertionMode() { for i := len(p.oe) - 1; i >= 0; i-- { n := p.oe[i] if i == 0 { @@ -303,95 +290,90 @@ func (p *parser) resetInsertionMode() insertionMode { } switch n.Data { case "select": - return inSelectIM + p.im = inSelectIM case "td", "th": - return inCellIM + p.im = inCellIM case "tr": - return inRowIM + p.im = inRowIM case "tbody", "thead", "tfoot": - return inTableBodyIM + p.im = inTableBodyIM case "caption": - // TODO: return inCaptionIM + p.im = inCaptionIM case "colgroup": - // TODO: return inColumnGroupIM + p.im = inColumnGroupIM case "table": - return inTableIM + p.im = inTableIM case "head": - return inBodyIM + p.im = inBodyIM case "body": - return inBodyIM + p.im = inBodyIM case "frameset": - // TODO: return inFramesetIM + p.im = inFramesetIM case "html": - return beforeHeadIM + p.im = beforeHeadIM + default: + continue } + return } - return inBodyIM + p.im = inBodyIM } // Section 11.2.5.4.1. -func initialIM(p *parser) (insertionMode, bool) { +func initialIM(p *parser) bool { switch p.tok.Type { case CommentToken: p.doc.Add(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return initialIM, true + return true case DoctypeToken: p.doc.Add(&Node{ Type: DoctypeNode, Data: p.tok.Data, }) - return beforeHTMLIM, true + p.im = beforeHTMLIM + return true } // TODO: set "quirks mode"? It's defined in the DOM spec instead of HTML5 proper, // and so switching on "quirks mode" might belong in a different package. - return beforeHTMLIM, false + p.im = beforeHTMLIM + return false } // Section 11.2.5.4.2. -func beforeHTMLIM(p *parser) (insertionMode, bool) { - var ( - add bool - attr []Attribute - implied bool - ) +func beforeHTMLIM(p *parser) bool { switch p.tok.Type { - case ErrorToken: - implied = true - case TextToken: - // TODO: distinguish whitespace text from others. - implied = true case StartTagToken: if p.tok.Data == "html" { - add = true - attr = p.tok.Attr - } else { - implied = true + p.addElement(p.tok.Data, p.tok.Attr) + p.im = beforeHeadIM + return true } case EndTagToken: switch p.tok.Data { case "head", "body", "html", "br": - implied = true + // Drop down to creating an implied <html> tag. default: // Ignore the token. + return true } case CommentToken: p.doc.Add(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return beforeHTMLIM, true - } - if add || implied { - p.addElement("html", attr) + return true } - return beforeHeadIM, !implied + // Create an implied <html> tag. + p.addElement("html", nil) + p.im = beforeHeadIM + return false } // Section 11.2.5.4.3. -func beforeHeadIM(p *parser) (insertionMode, bool) { +func beforeHeadIM(p *parser) bool { var ( add bool attr []Attribute @@ -409,7 +391,7 @@ func beforeHeadIM(p *parser) (insertionMode, bool) { add = true attr = p.tok.Attr case "html": - return useTheRulesFor(p, beforeHeadIM, inBodyIM) + return inBodyIM(p) default: implied = true } @@ -425,19 +407,20 @@ func beforeHeadIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return beforeHeadIM, true + return true } if add || implied { p.addElement("head", attr) p.head = p.top() } - return inHeadIM, !implied + p.im = inHeadIM + return !implied } const whitespace = " \t\r\n\f" // Section 11.2.5.4.4. -func inHeadIM(p *parser) (insertionMode, bool) { +func inHeadIM(p *parser) bool { var ( pop bool implied bool @@ -451,7 +434,7 @@ func inHeadIM(p *parser) (insertionMode, bool) { // Add the initial whitespace to the current node. p.addText(p.tok.Data[:len(p.tok.Data)-len(s)]) if s == "" { - return inHeadIM, true + return true } p.tok.Data = s } @@ -464,35 +447,42 @@ func inHeadIM(p *parser) (insertionMode, bool) { p.acknowledgeSelfClosingTag() case "script", "title", "noscript", "noframes", "style": p.addElement(p.tok.Data, p.tok.Attr) - p.setOriginalIM(inHeadIM) - return textIM, true + p.setOriginalIM() + p.im = textIM + return true default: implied = true } case EndTagToken: - if p.tok.Data == "head" { + switch p.tok.Data { + case "head": pop = true + case "body", "html", "br": + implied = true + default: + // Ignore the token. + return true } - // TODO. case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return inHeadIM, true + return true } if pop || implied { n := p.oe.pop() if n.Data != "head" { panic("html: bad parser state: <head> element not found, in the in-head insertion mode") } - return afterHeadIM, !implied + p.im = afterHeadIM + return !implied } - return inHeadIM, true + return true } // Section 11.2.5.4.6. -func afterHeadIM(p *parser) (insertionMode, bool) { +func afterHeadIM(p *parser) bool { var ( add bool attr []Attribute @@ -512,11 +502,13 @@ func afterHeadIM(p *parser) (insertionMode, bool) { attr = p.tok.Attr framesetOK = false case "frameset": - // TODO. + p.addElement(p.tok.Data, p.tok.Attr) + p.im = inFramesetIM + return true case "base", "basefont", "bgsound", "link", "meta", "noframes", "script", "style", "title": p.oe = append(p.oe, p.head) defer p.oe.pop() - return useTheRulesFor(p, afterHeadIM, inHeadIM) + return inHeadIM(p) case "head": // TODO. default: @@ -524,19 +516,27 @@ func afterHeadIM(p *parser) (insertionMode, bool) { framesetOK = true } case EndTagToken: - // TODO. + switch p.tok.Data { + case "body", "html", "br": + implied = true + framesetOK = true + default: + // Ignore the token. + return true + } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return afterHeadIM, true + return true } if add || implied { p.addElement("body", attr) p.framesetOK = framesetOK } - return inBodyIM, !implied + p.im = inBodyIM + return !implied } // copyAttributes copies attributes of src not found on dst to dst. @@ -557,7 +557,7 @@ func copyAttributes(dst *Node, src Token) { } // Section 11.2.5.4.7. -func inBodyIM(p *parser) (insertionMode, bool) { +func inBodyIM(p *parser) bool { switch p.tok.Type { case TextToken: p.reconstructActiveFormattingElements() @@ -604,7 +604,8 @@ func inBodyIM(p *parser) (insertionMode, bool) { p.popUntil(buttonScopeStopTags, "p") // TODO: skip this step in quirks mode. p.addElement(p.tok.Data, p.tok.Attr) p.framesetOK = false - return inTableIM, true + p.im = inTableIM + return true case "hr": p.popUntil(buttonScopeStopTags, "p") p.addElement(p.tok.Data, p.tok.Attr) @@ -616,7 +617,14 @@ func inBodyIM(p *parser) (insertionMode, bool) { p.addElement(p.tok.Data, p.tok.Attr) p.framesetOK = false // TODO: detect <select> inside a table. - return inSelectIM, true + p.im = inSelectIM + return true + case "form": + if p.form == nil { + p.popUntil(buttonScopeStopTags, "p") + p.addElement(p.tok.Data, p.tok.Attr) + p.form = p.top() + } case "li": p.framesetOK = false for i := len(p.oe) - 1; i >= 0; i-- { @@ -634,7 +642,28 @@ func inBodyIM(p *parser) (insertionMode, bool) { break } p.popUntil(buttonScopeStopTags, "p") - p.addElement("li", p.tok.Attr) + p.addElement(p.tok.Data, p.tok.Attr) + case "dd", "dt": + p.framesetOK = false + for i := len(p.oe) - 1; i >= 0; i-- { + node := p.oe[i] + switch node.Data { + case "dd", "dt": + p.oe = p.oe[:i] + case "address", "div", "p": + continue + default: + if !isSpecialElement[node.Data] { + continue + } + } + break + } + p.popUntil(buttonScopeStopTags, "p") + p.addElement(p.tok.Data, p.tok.Attr) + case "plaintext": + p.popUntil(buttonScopeStopTags, "p") + p.addElement(p.tok.Data, p.tok.Attr) case "optgroup", "option": if p.top().Data == "option" { p.oe.pop() @@ -650,10 +679,50 @@ func inBodyIM(p *parser) (insertionMode, bool) { } } case "base", "basefont", "bgsound", "command", "link", "meta", "noframes", "script", "style", "title": - return useTheRulesFor(p, inBodyIM, inHeadIM) + return inHeadIM(p) case "image": p.tok.Data = "img" - return inBodyIM, false + return false + case "isindex": + if p.form != nil { + // Ignore the token. + return true + } + action := "" + prompt := "This is a searchable index. Enter search keywords: " + attr := []Attribute{{Key: "name", Val: "isindex"}} + for _, a := range p.tok.Attr { + switch a.Key { + case "action": + action = a.Val + case "name": + // Ignore the attribute. + case "prompt": + prompt = a.Val + default: + attr = append(attr, a) + } + } + p.acknowledgeSelfClosingTag() + p.popUntil(buttonScopeStopTags, "p") + p.addElement("form", nil) + p.form = p.top() + if action != "" { + p.form.Attr = []Attribute{{Key: "action", Val: action}} + } + p.addElement("hr", nil) + p.oe.pop() + p.addElement("label", nil) + p.addText(prompt) + p.addElement("input", attr) + p.oe.pop() + p.oe.pop() + p.addElement("hr", nil) + p.oe.pop() + p.oe.pop() + p.form = nil + case "caption", "col", "colgroup", "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr": + // Ignore the token. default: // TODO. p.addElement(p.tok.Data, p.tok.Attr) @@ -662,7 +731,8 @@ func inBodyIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "body": // TODO: autoclose the stack of open elements. - return afterBodyIM, true + p.im = afterBodyIM + return true case "p": if !p.elementInScope(buttonScopeStopTags, "p") { p.addElement("p", nil) @@ -676,6 +746,9 @@ func inBodyIM(p *parser) (insertionMode, bool) { if p.popUntil(defaultScopeStopTags, p.tok.Data) { p.clearActiveFormattingElements() } + case "br": + p.tok.Type = StartTagToken + return false default: p.inBodyEndTagOther(p.tok.Data) } @@ -686,7 +759,7 @@ func inBodyIM(p *parser) (insertionMode, bool) { }) } - return inBodyIM, true + return true } func (p *parser) inBodyEndTagFormatting(tag string) { @@ -827,45 +900,64 @@ func (p *parser) inBodyEndTagOther(tag string) { } // Section 11.2.5.4.8. -func textIM(p *parser) (insertionMode, bool) { +func textIM(p *parser) bool { switch p.tok.Type { case ErrorToken: p.oe.pop() case TextToken: p.addText(p.tok.Data) - return textIM, true + return true case EndTagToken: p.oe.pop() } - o := p.originalIM + p.im = p.originalIM p.originalIM = nil - return o, p.tok.Type == EndTagToken + return p.tok.Type == EndTagToken } // Section 11.2.5.4.9. -func inTableIM(p *parser) (insertionMode, bool) { +func inTableIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. - return nil, true + return true case TextToken: // TODO. case StartTagToken: switch p.tok.Data { + case "caption": + p.clearStackToContext(tableScopeStopTags) + p.afe = append(p.afe, &scopeMarker) + p.addElement(p.tok.Data, p.tok.Attr) + p.im = inCaptionIM + return true case "tbody", "tfoot", "thead": p.clearStackToContext(tableScopeStopTags) p.addElement(p.tok.Data, p.tok.Attr) - return inTableBodyIM, true + p.im = inTableBodyIM + return true case "td", "th", "tr": p.clearStackToContext(tableScopeStopTags) p.addElement("tbody", nil) - return inTableBodyIM, false + p.im = inTableBodyIM + return false case "table": if p.popUntil(tableScopeStopTags, "table") { - return p.resetInsertionMode(), false + p.resetInsertionMode() + return false } // Ignore the token. - return inTableIM, true + return true + case "colgroup": + p.clearStackToContext(tableScopeStopTags) + p.addElement(p.tok.Data, p.tok.Attr) + p.im = inColumnGroupIM + return true + case "col": + p.clearStackToContext(tableScopeStopTags) + p.addElement("colgroup", p.tok.Attr) + p.im = inColumnGroupIM + return false default: // TODO. } @@ -873,20 +965,21 @@ func inTableIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "table": if p.popUntil(tableScopeStopTags, "table") { - return p.resetInsertionMode(), true + p.resetInsertionMode() + return true } // Ignore the token. - return inTableIM, true + return true case "body", "caption", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr": // Ignore the token. - return inTableIM, true + return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return inTableIM, true + return true } switch p.top().Data { @@ -895,7 +988,7 @@ func inTableIM(p *parser) (insertionMode, bool) { defer func() { p.fosterParenting = false }() } - return useTheRulesFor(p, inTableIM, inBodyIM) + return inBodyIM(p) } // clearStackToContext pops elements off the stack of open elements @@ -911,8 +1004,90 @@ func (p *parser) clearStackToContext(stopTags []string) { } } +// Section 11.2.5.4.11. +func inCaptionIM(p *parser) bool { + switch p.tok.Type { + case StartTagToken: + switch p.tok.Data { + case "caption", "col", "colgroup", "tbody", "td", "tfoot", "thead", "tr": + if p.popUntil(tableScopeStopTags, "caption") { + p.clearActiveFormattingElements() + p.im = inTableIM + return false + } else { + // Ignore the token. + return true + } + } + case EndTagToken: + switch p.tok.Data { + case "caption": + if p.popUntil(tableScopeStopTags, "caption") { + p.clearActiveFormattingElements() + p.im = inTableIM + } + return true + case "table": + if p.popUntil(tableScopeStopTags, "caption") { + p.clearActiveFormattingElements() + p.im = inTableIM + return false + } else { + // Ignore the token. + return true + } + case "body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr": + // Ignore the token. + return true + } + } + return inBodyIM(p) +} + +// Section 11.2.5.4.12. +func inColumnGroupIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + return true + case DoctypeToken: + // Ignore the token. + return true + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "col": + p.addElement(p.tok.Data, p.tok.Attr) + p.oe.pop() + p.acknowledgeSelfClosingTag() + return true + } + case EndTagToken: + switch p.tok.Data { + case "colgroup": + if p.oe.top().Data != "html" { + p.oe.pop() + } + p.im = inTableIM + return true + case "col": + // Ignore the token. + return true + } + } + if p.oe.top().Data != "html" { + p.oe.pop() + } + p.im = inTableIM + return false +} + // Section 11.2.5.4.13. -func inTableBodyIM(p *parser) (insertionMode, bool) { +func inTableBodyIM(p *parser) bool { var ( add bool data string @@ -942,31 +1117,33 @@ func inTableBodyIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "table": if p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") { - return inTableIM, false + p.im = inTableIM + return false } // Ignore the token. - return inTableBodyIM, true + return true case "body", "caption", "col", "colgroup", "html", "td", "th", "tr": // Ignore the token. - return inTableBodyIM, true + return true } case CommentToken: p.addChild(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return inTableBodyIM, true + return true } if add { // TODO: clear the stack back to a table body context. p.addElement(data, attr) - return inRowIM, consumed + p.im = inRowIM + return consumed } - return useTheRulesFor(p, inTableBodyIM, inTableIM) + return inTableIM(p) } // Section 11.2.5.4.14. -func inRowIM(p *parser) (insertionMode, bool) { +func inRowIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // TODO. @@ -978,13 +1155,15 @@ func inRowIM(p *parser) (insertionMode, bool) { p.clearStackToContext(tableRowContextStopTags) p.addElement(p.tok.Data, p.tok.Attr) p.afe = append(p.afe, &scopeMarker) - return inCellIM, true + p.im = inCellIM + return true case "caption", "col", "colgroup", "tbody", "tfoot", "thead", "tr": if p.popUntil(tableScopeStopTags, "tr") { - return inTableBodyIM, false + p.im = inTableBodyIM + return false } // Ignore the token. - return inRowIM, true + return true default: // TODO. } @@ -992,21 +1171,23 @@ func inRowIM(p *parser) (insertionMode, bool) { switch p.tok.Data { case "tr": if p.popUntil(tableScopeStopTags, "tr") { - return inTableBodyIM, true + p.im = inTableBodyIM + return true } // Ignore the token. - return inRowIM, true + return true case "table": if p.popUntil(tableScopeStopTags, "tr") { - return inTableBodyIM, false + p.im = inTableBodyIM + return false } // Ignore the token. - return inRowIM, true + return true case "tbody", "tfoot", "thead": // TODO. case "body", "caption", "col", "colgroup", "html", "td", "th": // Ignore the token. - return inRowIM, true + return true default: // TODO. } @@ -1015,13 +1196,13 @@ func inRowIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return inRowIM, true + return true } - return useTheRulesFor(p, inRowIM, inTableIM) + return inTableIM(p) } // Section 11.2.5.4.15. -func inCellIM(p *parser) (insertionMode, bool) { +func inCellIM(p *parser) bool { var ( closeTheCellAndReprocess bool ) @@ -1037,10 +1218,11 @@ func inCellIM(p *parser) (insertionMode, bool) { case "td", "th": if !p.popUntil(tableScopeStopTags, p.tok.Data) { // Ignore the token. - return inCellIM, true + return true } p.clearActiveFormattingElements() - return inRowIM, true + p.im = inRowIM + return true case "body", "caption", "col", "colgroup", "html": // TODO. case "table", "tbody", "tfoot", "thead", "tr": @@ -1052,19 +1234,20 @@ func inCellIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return inCellIM, true + return true } if closeTheCellAndReprocess { if p.popUntil(tableScopeStopTags, "td") || p.popUntil(tableScopeStopTags, "th") { p.clearActiveFormattingElements() - return inRowIM, false + p.im = inRowIM + return false } } - return useTheRulesFor(p, inCellIM, inBodyIM) + return inBodyIM(p) } // Section 11.2.5.4.16. -func inSelectIM(p *parser) (insertionMode, bool) { +func inSelectIM(p *parser) bool { endSelect := false switch p.tok.Type { case ErrorToken: @@ -1081,7 +1264,13 @@ func inSelectIM(p *parser) (insertionMode, bool) { } p.addElement(p.tok.Data, p.tok.Attr) case "optgroup": - // TODO. + if p.top().Data == "option" { + p.oe.pop() + } + if p.top().Data == "optgroup" { + p.oe.pop() + } + p.addElement(p.tok.Data, p.tok.Attr) case "select": endSelect = true case "input", "keygen", "textarea": @@ -1094,9 +1283,17 @@ func inSelectIM(p *parser) (insertionMode, bool) { case EndTagToken: switch p.tok.Data { case "option": - // TODO. + if p.top().Data == "option" { + p.oe.pop() + } case "optgroup": - // TODO. + i := len(p.oe) - 1 + if p.oe[i].Data == "option" { + i-- + } + if p.oe[i].Data == "optgroup" { + p.oe = p.oe[:i] + } case "select": endSelect = true default: @@ -1113,34 +1310,33 @@ func inSelectIM(p *parser) (insertionMode, bool) { switch p.oe[i].Data { case "select": p.oe = p.oe[:i] - return p.resetInsertionMode(), true + p.resetInsertionMode() + return true case "option", "optgroup": continue default: // Ignore the token. - return inSelectIM, true + return true } } } - return inSelectIM, true + return true } // Section 11.2.5.4.18. -func afterBodyIM(p *parser) (insertionMode, bool) { +func afterBodyIM(p *parser) bool { switch p.tok.Type { case ErrorToken: - // TODO. - case TextToken: - // TODO. + // Stop parsing. + return true case StartTagToken: - // TODO. + if p.tok.Data == "html" { + return inBodyIM(p) + } case EndTagToken: - switch p.tok.Data { - case "html": - // TODO: autoclose the stack of open elements. - return afterAfterBodyIM, true - default: - // TODO. + if p.tok.Data == "html" { + p.im = afterAfterBodyIM + return true } case CommentToken: // The comment is attached to the <html> element. @@ -1151,32 +1347,119 @@ func afterBodyIM(p *parser) (insertionMode, bool) { Type: CommentNode, Data: p.tok.Data, }) - return afterBodyIM, true + return true + } + p.im = inBodyIM + return false +} + +// Section 11.2.5.4.19. +func inFramesetIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "frameset": + p.addElement(p.tok.Data, p.tok.Attr) + case "frame": + p.addElement(p.tok.Data, p.tok.Attr) + p.oe.pop() + p.acknowledgeSelfClosingTag() + case "noframes": + return inHeadIM(p) + } + case EndTagToken: + switch p.tok.Data { + case "frameset": + if p.oe.top().Data != "html" { + p.oe.pop() + if p.oe.top().Data != "frameset" { + p.im = afterFramesetIM + return true + } + } + } + default: + // Ignore the token. + } + return true +} + +// Section 11.2.5.4.20. +func afterFramesetIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "noframes": + return inHeadIM(p) + } + case EndTagToken: + switch p.tok.Data { + case "html": + p.im = afterAfterFramesetIM + return true + } + default: + // Ignore the token. } - // TODO: should this be "return inBodyIM, true"? - return afterBodyIM, true + return true } // Section 11.2.5.4.21. -func afterAfterBodyIM(p *parser) (insertionMode, bool) { +func afterAfterBodyIM(p *parser) bool { switch p.tok.Type { case ErrorToken: // Stop parsing. - return nil, true + return true case TextToken: // TODO. case StartTagToken: if p.tok.Data == "html" { - return useTheRulesFor(p, afterAfterBodyIM, inBodyIM) + return inBodyIM(p) } case CommentToken: p.doc.Add(&Node{ Type: CommentNode, Data: p.tok.Data, }) - return afterAfterBodyIM, true + return true + } + p.im = inBodyIM + return false +} + +// Section 11.2.5.4.22. +func afterAfterFramesetIM(p *parser) bool { + switch p.tok.Type { + case CommentToken: + p.addChild(&Node{ + Type: CommentNode, + Data: p.tok.Data, + }) + case StartTagToken: + switch p.tok.Data { + case "html": + return inBodyIM(p) + case "noframes": + return inHeadIM(p) + } + default: + // Ignore the token. } - return inBodyIM, false + return true } // Parse returns the parse tree for the HTML from the given Reader. @@ -1189,9 +1472,10 @@ func Parse(r io.Reader) (*Node, error) { }, scripting: true, framesetOK: true, + im: initialIM, } // Iterate until EOF. Any other error will cause an early return. - im, consumed := initialIM, true + consumed := true for { if consumed { if err := p.read(); err != nil { @@ -1201,11 +1485,11 @@ func Parse(r io.Reader) (*Node, error) { return nil, err } } - im, consumed = im(p) + consumed = p.im(p) } // Loop until the final token (the ErrorToken signifying EOF) is consumed. for { - if im, consumed = im(p); consumed { + if consumed = p.im(p); consumed { break } } diff --git a/libgo/go/html/parse_test.go b/libgo/go/html/parse_test.go index 2797922..4f15ae1 100644 --- a/libgo/go/html/parse_test.go +++ b/libgo/go/html/parse_test.go @@ -133,8 +133,8 @@ func TestParser(t *testing.T) { n int }{ // TODO(nigeltao): Process all the test cases from all the .dat files. - {"tests1.dat", 92}, - {"tests2.dat", 0}, + {"tests1.dat", -1}, + {"tests2.dat", 43}, {"tests3.dat", 0}, } for _, tf := range testFiles { @@ -213,4 +213,8 @@ var renderTestBlacklist = map[string]bool{ // More cases of <a> being reparented: `<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe`: true, `<a><table><a></table><p><a><div><a>`: true, + `<a><table><td><a><table></table><a></tr><a></table><a>`: true, + // A <plaintext> element is reparented, putting it before a table. + // A <plaintext> element can't have anything after it in HTML. + `<table><plaintext><td>`: true, } diff --git a/libgo/go/html/render.go b/libgo/go/html/render.go index c815f35..92c349f 100644 --- a/libgo/go/html/render.go +++ b/libgo/go/html/render.go @@ -52,7 +52,19 @@ func Render(w io.Writer, n *Node) error { return buf.Flush() } +// plaintextAbort is returned from render1 when a <plaintext> element +// has been rendered. No more end tags should be rendered after that. +var plaintextAbort = errors.New("html: internal error (plaintext abort)") + func render(w writer, n *Node) error { + err := render1(w, n) + if err == plaintextAbort { + err = nil + } + return err +} + +func render1(w writer, n *Node) error { // Render non-element nodes; these are the easy cases. switch n.Type { case ErrorNode: @@ -61,7 +73,7 @@ func render(w writer, n *Node) error { return escape(w, n.Data) case DocumentNode: for _, c := range n.Child { - if err := render(w, c); err != nil { + if err := render1(w, c); err != nil { return err } } @@ -128,7 +140,7 @@ func render(w writer, n *Node) error { // Render any child nodes. switch n.Data { - case "noembed", "noframes", "noscript", "script", "style": + case "noembed", "noframes", "noscript", "plaintext", "script", "style": for _, c := range n.Child { if c.Type != TextNode { return fmt.Errorf("html: raw text element <%s> has non-text child node", n.Data) @@ -137,18 +149,23 @@ func render(w writer, n *Node) error { return err } } + if n.Data == "plaintext" { + // Don't render anything else. <plaintext> must be the + // last element in the file, with no closing tag. + return plaintextAbort + } case "textarea", "title": for _, c := range n.Child { if c.Type != TextNode { return fmt.Errorf("html: RCDATA element <%s> has non-text child node", n.Data) } - if err := render(w, c); err != nil { + if err := render1(w, c); err != nil { return err } } default: for _, c := range n.Child { - if err := render(w, c); err != nil { + if err := render1(w, c); err != nil { return err } } diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go index d720d4b..3fb15a6 100644 --- a/libgo/go/html/template/content.go +++ b/libgo/go/html/template/content.go @@ -6,6 +6,7 @@ package template import ( "fmt" + "reflect" ) // Strings of content from a trusted source. @@ -70,10 +71,25 @@ const ( contentTypeUnsafe ) +// indirect returns the value, after dereferencing as many times +// as necessary to reach the base type (or nil). +func indirect(a interface{}) interface{} { + if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr { + // Avoid creating a reflect.Value if it's not a pointer. + return a + } + v := reflect.ValueOf(a) + for v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + return v.Interface() +} + // stringify converts its arguments to a string and the type of the content. +// All pointers are dereferenced, as in the text/template package. func stringify(args ...interface{}) (string, contentType) { if len(args) == 1 { - switch s := args[0].(type) { + switch s := indirect(args[0]).(type) { case string: return s, contentTypePlain case CSS: @@ -90,5 +106,8 @@ func stringify(args ...interface{}) (string, contentType) { return string(s), contentTypeURL } } + for i, arg := range args { + args[i] = indirect(arg) + } return fmt.Sprint(args...), contentTypePlain } diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go index d8bfa32..4af58309 100644 --- a/libgo/go/html/template/escape_test.go +++ b/libgo/go/html/template/escape_test.go @@ -28,7 +28,7 @@ func (x *goodMarshaler) MarshalJSON() ([]byte, error) { } func TestEscape(t *testing.T) { - var data = struct { + data := struct { F, T bool C, G, H string A, E []string @@ -50,6 +50,7 @@ func TestEscape(t *testing.T) { Z: nil, W: HTML(`¡<b class="foo">Hello</b>, <textarea>O'World</textarea>!`), } + pdata := &data tests := []struct { name string @@ -668,6 +669,15 @@ func TestEscape(t *testing.T) { t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g) continue } + b.Reset() + if err := tmpl.Execute(b, pdata); err != nil { + t.Errorf("%s: template execution failed for pointer: %s", test.name, err) + continue + } + if w, g := test.output, b.String(); w != g { + t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g) + continue + } } } @@ -1605,6 +1615,29 @@ func TestRedundantFuncs(t *testing.T) { } } +func TestIndirectPrint(t *testing.T) { + a := 3 + ap := &a + b := "hello" + bp := &b + bpp := &bp + tmpl := Must(New("t").Parse(`{{.}}`)) + var buf bytes.Buffer + err := tmpl.Execute(&buf, ap) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } else if buf.String() != "3" { + t.Errorf(`Expected "3"; got %q`, buf.String()) + } + buf.Reset() + err = tmpl.Execute(&buf, bpp) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } else if buf.String() != "hello" { + t.Errorf(`Expected "hello"; got %q`, buf.String()) + } +} + func BenchmarkEscapedExecute(b *testing.B) { tmpl := Must(New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`)) var buf bytes.Buffer diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go index 68c53e5..0e632df 100644 --- a/libgo/go/html/template/js.go +++ b/libgo/go/html/template/js.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/json" "fmt" + "reflect" "strings" "unicode/utf8" ) @@ -117,12 +118,24 @@ var regexpPrecederKeywords = map[string]bool{ "void": true, } +var jsonMarshalType = reflect.TypeOf((*json.Marshaler)(nil)).Elem() + +// indirectToJSONMarshaler returns the value, after dereferencing as many times +// as necessary to reach the base type (or nil) or an implementation of json.Marshal. +func indirectToJSONMarshaler(a interface{}) interface{} { + v := reflect.ValueOf(a) + for !v.Type().Implements(jsonMarshalType) && v.Kind() == reflect.Ptr && !v.IsNil() { + v = v.Elem() + } + return v.Interface() +} + // jsValEscaper escapes its inputs to a JS Expression (section 11.14) that has -// nether side-effects nor free variables outside (NaN, Infinity). +// neither side-effects nor free variables outside (NaN, Infinity). func jsValEscaper(args ...interface{}) string { var a interface{} if len(args) == 1 { - a = args[0] + a = indirectToJSONMarshaler(args[0]) switch t := a.(type) { case JS: return string(t) @@ -135,6 +148,9 @@ func jsValEscaper(args ...interface{}) string { a = t.String() } } else { + for i, arg := range args { + args[i] = indirectToJSONMarshaler(arg) + } a = fmt.Sprint(args...) } // TODO: detect cycles before calling Marshal which loops infinitely on diff --git a/libgo/go/html/token.go b/libgo/go/html/token.go index 2c13822..9400873 100644 --- a/libgo/go/html/token.go +++ b/libgo/go/html/token.go @@ -401,14 +401,14 @@ func (z *Tokenizer) readStartTag() TokenType { break } } - // Any "<noembed>", "<noframes>", "<noscript>", "<script>", "<style>", + // Any "<noembed>", "<noframes>", "<noscript>", "<plaintext", "<script>", "<style>", // "<textarea>" or "<title>" tag flags the tokenizer's next token as raw. - // The tag name lengths of these special cases ranges in [5, 8]. - if x := z.data.end - z.data.start; 5 <= x && x <= 8 { + // The tag name lengths of these special cases ranges in [5, 9]. + if x := z.data.end - z.data.start; 5 <= x && x <= 9 { switch z.buf[z.data.start] { - case 'n', 's', 't', 'N', 'S', 'T': + case 'n', 'p', 's', 't', 'N', 'P', 'S', 'T': switch s := strings.ToLower(string(z.buf[z.data.start:z.data.end])); s { - case "noembed", "noframes", "noscript", "script", "style", "textarea", "title": + case "noembed", "noframes", "noscript", "plaintext", "script", "style", "textarea", "title": z.rawTag = s } } @@ -551,9 +551,19 @@ func (z *Tokenizer) Next() TokenType { z.data.start = z.raw.end z.data.end = z.raw.end if z.rawTag != "" { - z.readRawOrRCDATA() - z.tt = TextToken - return z.tt + if z.rawTag == "plaintext" { + // Read everything up to EOF. + for z.err == nil { + z.readByte() + } + z.textIsRaw = true + } else { + z.readRawOrRCDATA() + } + if z.data.end > z.data.start { + z.tt = TextToken + return z.tt + } } z.textIsRaw = false diff --git a/libgo/go/image/tiff/buffer.go b/libgo/go/image/tiff/buffer.go index ce35073..27533c6 100644 --- a/libgo/go/image/tiff/buffer.go +++ b/libgo/go/image/tiff/buffer.go @@ -4,10 +4,7 @@ package tiff -import ( - "io" - "os" -) +import "io" // buffer buffers an io.Reader to satisfy io.ReaderAt. type buffer struct { @@ -19,7 +16,7 @@ func (b *buffer) ReadAt(p []byte, off int64) (int, error) { o := int(off) end := o + len(p) if int64(end) != off+int64(len(p)) { - return 0, os.EINVAL + return 0, io.ErrUnexpectedEOF } m := len(b.buf) diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go index 658ea78..71028e2 100644 --- a/libgo/go/io/ioutil/tempfile.go +++ b/libgo/go/io/ioutil/tempfile.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "strconv" + "time" ) // Random number state, accessed without lock; racy but harmless. @@ -17,8 +18,7 @@ import ( var rand uint32 func reseed() uint32 { - sec, nsec, _ := os.Time() - return uint32(sec*1e9 + nsec + int64(os.Getpid())) + return uint32(time.Nanoseconds() + int64(os.Getpid())) } func nextSuffix() string { diff --git a/libgo/go/log/syslog/syslog.go b/libgo/go/log/syslog/syslog.go index 26a2f73..546bc29 100644 --- a/libgo/go/log/syslog/syslog.go +++ b/libgo/go/log/syslog/syslog.go @@ -8,6 +8,7 @@ package syslog import ( + "errors" "fmt" "log" "net" @@ -75,7 +76,7 @@ func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, e // Write sends a log message to the syslog daemon. func (w *Writer) Write(b []byte) (int, error) { if w.priority > LOG_DEBUG || w.priority < LOG_EMERG { - return 0, os.EINVAL + return 0, errors.New("log/syslog: invalid priority") } return w.conn.writeBytes(w.priority, w.prefix, b) } diff --git a/libgo/go/math/big/int.go b/libgo/go/math/big/int.go index 533a97f..35e2e29 100644 --- a/libgo/go/math/big/int.go +++ b/libgo/go/math/big/int.go @@ -176,7 +176,7 @@ func (z *Int) Quo(x, y *Int) *Int { // If y == 0, a division-by-zero run-time panic occurs. // Rem implements truncated modulus (like Go); see QuoRem for more details. func (z *Int) Rem(x, y *Int) *Int { - _, z.abs = nat{}.div(z.abs, x.abs, y.abs) + _, z.abs = nat(nil).div(z.abs, x.abs, y.abs) z.neg = len(z.abs) > 0 && x.neg // 0 has no sign return z } @@ -678,14 +678,14 @@ func (z *Int) Bit(i int) uint { panic("negative bit index") } if z.neg { - t := nat{}.sub(z.abs, natOne) + t := nat(nil).sub(z.abs, natOne) return t.bit(uint(i)) ^ 1 } return z.abs.bit(uint(i)) } -// SetBit sets the i'th bit of z to bit and returns z. +// SetBit sets z to x, with x's i'th bit set to b (0 or 1). // That is, if bit is 1 SetBit sets z = x | (1 << i); // if bit is 0 it sets z = x &^ (1 << i). If bit is not 0 or 1, // SetBit will panic. @@ -710,8 +710,8 @@ func (z *Int) And(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.or(x1, y1), natOne) z.neg = true // z cannot be zero if x and y are negative return z @@ -729,7 +729,7 @@ func (z *Int) And(x, y *Int) *Int { } // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := nat{}.sub(y.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.andNot(x.abs, y1) z.neg = false return z @@ -740,8 +740,8 @@ func (z *Int) AndNot(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) &^ (-y) == ^(x-1) &^ ^(y-1) == ^(x-1) & (y-1) == (y-1) &^ (x-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.andNot(y1, x1) z.neg = false return z @@ -755,14 +755,14 @@ func (z *Int) AndNot(x, y *Int) *Int { if x.neg { // (-x) &^ y == ^(x-1) &^ y == ^(x-1) & ^y == ^((x-1) | y) == -(((x-1) | y) + 1) - x1 := nat{}.sub(x.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) z.abs = z.abs.add(z.abs.or(x1, y.abs), natOne) z.neg = true // z cannot be zero if x is negative and y is positive return z } // x &^ (-y) == x &^ ^(y-1) == x & (y-1) - y1 := nat{}.add(y.abs, natOne) + y1 := nat(nil).add(y.abs, natOne) z.abs = z.abs.and(x.abs, y1) z.neg = false return z @@ -773,8 +773,8 @@ func (z *Int) Or(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.and(x1, y1), natOne) z.neg = true // z cannot be zero if x and y are negative return z @@ -792,7 +792,7 @@ func (z *Int) Or(x, y *Int) *Int { } // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := nat{}.sub(y.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.andNot(y1, x.abs), natOne) z.neg = true // z cannot be zero if one of x or y is negative return z @@ -803,8 +803,8 @@ func (z *Int) Xor(x, y *Int) *Int { if x.neg == y.neg { if x.neg { // (-x) ^ (-y) == ^(x-1) ^ ^(y-1) == (x-1) ^ (y-1) - x1 := nat{}.sub(x.abs, natOne) - y1 := nat{}.sub(y.abs, natOne) + x1 := nat(nil).sub(x.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.xor(x1, y1) z.neg = false return z @@ -822,7 +822,7 @@ func (z *Int) Xor(x, y *Int) *Int { } // x ^ (-y) == x ^ ^(y-1) == ^(x ^ (y-1)) == -((x ^ (y-1)) + 1) - y1 := nat{}.sub(y.abs, natOne) + y1 := nat(nil).sub(y.abs, natOne) z.abs = z.abs.add(z.abs.xor(x.abs, y1), natOne) z.neg = true // z cannot be zero if only one of x or y is negative return z diff --git a/libgo/go/math/big/nat.go b/libgo/go/math/big/nat.go index 3fa41e7..eee8ee3 100644 --- a/libgo/go/math/big/nat.go +++ b/libgo/go/math/big/nat.go @@ -447,10 +447,10 @@ func (z nat) mulRange(a, b uint64) nat { case a == b: return z.setUint64(a) case a+1 == b: - return z.mul(nat{}.setUint64(a), nat{}.setUint64(b)) + return z.mul(nat(nil).setUint64(a), nat(nil).setUint64(b)) } m := (a + b) / 2 - return z.mul(nat{}.mulRange(a, m), nat{}.mulRange(m+1, b)) + return z.mul(nat(nil).mulRange(a, m), nat(nil).mulRange(m+1, b)) } // q = (x-r)/y, with 0 <= r < y @@ -785,7 +785,7 @@ func (x nat) string(charset string) string { } // preserve x, create local copy for use in repeated divisions - q := nat{}.set(x) + q := nat(nil).set(x) var r Word // convert @@ -1191,11 +1191,11 @@ func (n nat) probablyPrime(reps int) bool { return false } - nm1 := nat{}.sub(n, natOne) + nm1 := nat(nil).sub(n, natOne) // 1<<k * q = nm1; q, k := nm1.powersOfTwoDecompose() - nm3 := nat{}.sub(nm1, natTwo) + nm3 := nat(nil).sub(nm1, natTwo) rand := rand.New(rand.NewSource(int64(n[0]))) var x, y, quotient nat diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go index 041a6c4..b208646 100644 --- a/libgo/go/math/big/nat_test.go +++ b/libgo/go/math/big/nat_test.go @@ -16,9 +16,9 @@ var cmpTests = []struct { r int }{ {nil, nil, 0}, - {nil, nat{}, 0}, - {nat{}, nil, 0}, - {nat{}, nat{}, 0}, + {nil, nat(nil), 0}, + {nat(nil), nil, 0}, + {nat(nil), nat(nil), 0}, {nat{0}, nat{0}, 0}, {nat{0}, nat{1}, -1}, {nat{1}, nat{0}, 1}, @@ -67,7 +67,7 @@ var prodNN = []argNN{ func TestSet(t *testing.T) { for _, a := range sumNN { - z := nat{}.set(a.z) + z := nat(nil).set(a.z) if z.cmp(a.z) != 0 { t.Errorf("got z = %v; want %v", z, a.z) } @@ -129,7 +129,7 @@ var mulRangesN = []struct { func TestMulRangeN(t *testing.T) { for i, r := range mulRangesN { - prod := nat{}.mulRange(r.a, r.b).decimalString() + prod := nat(nil).mulRange(r.a, r.b).decimalString() if prod != r.prod { t.Errorf("#%d: got %s; want %s", i, prod, r.prod) } @@ -175,7 +175,7 @@ func toString(x nat, charset string) string { s := make([]byte, i) // don't destroy x - q := nat{}.set(x) + q := nat(nil).set(x) // convert for len(q) > 0 { @@ -212,7 +212,7 @@ func TestString(t *testing.T) { t.Errorf("string%+v\n\tgot s = %s; want %s", a, s, a.s) } - x, b, err := nat{}.scan(strings.NewReader(a.s), len(a.c)) + x, b, err := nat(nil).scan(strings.NewReader(a.s), len(a.c)) if x.cmp(a.x) != 0 { t.Errorf("scan%+v\n\tgot z = %v; want %v", a, x, a.x) } @@ -271,7 +271,7 @@ var natScanTests = []struct { func TestScanBase(t *testing.T) { for _, a := range natScanTests { r := strings.NewReader(a.s) - x, b, err := nat{}.scan(r, a.base) + x, b, err := nat(nil).scan(r, a.base) if err == nil && !a.ok { t.Errorf("scan%+v\n\texpected error", a) } @@ -651,17 +651,17 @@ var expNNTests = []struct { func TestExpNN(t *testing.T) { for i, test := range expNNTests { - x, _, _ := nat{}.scan(strings.NewReader(test.x), 0) - y, _, _ := nat{}.scan(strings.NewReader(test.y), 0) - out, _, _ := nat{}.scan(strings.NewReader(test.out), 0) + x, _, _ := nat(nil).scan(strings.NewReader(test.x), 0) + y, _, _ := nat(nil).scan(strings.NewReader(test.y), 0) + out, _, _ := nat(nil).scan(strings.NewReader(test.out), 0) var m nat if len(test.m) > 0 { - m, _, _ = nat{}.scan(strings.NewReader(test.m), 0) + m, _, _ = nat(nil).scan(strings.NewReader(test.m), 0) } - z := nat{}.expNN(x, y, m) + z := nat(nil).expNN(x, y, m) if z.cmp(out) != 0 { t.Errorf("#%d got %v want %v", i, z, out) } diff --git a/libgo/go/math/big/rat.go b/libgo/go/math/big/rat.go index 3a0add3..adf4124 100644 --- a/libgo/go/math/big/rat.go +++ b/libgo/go/math/big/rat.go @@ -33,7 +33,7 @@ func (z *Rat) SetFrac(a, b *Int) *Rat { panic("division by zero") } if &z.a == b || alias(z.a.abs, babs) { - babs = nat{}.set(babs) // make a copy + babs = nat(nil).set(babs) // make a copy } z.a.abs = z.a.abs.set(a.abs) z.b = z.b.set(babs) @@ -315,7 +315,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) { if _, ok := z.a.SetString(s, 10); !ok { return nil, false } - powTen := nat{}.expNN(natTen, exp.abs, nil) + powTen := nat(nil).expNN(natTen, exp.abs, nil) if exp.neg { z.b = powTen z.norm() @@ -357,23 +357,23 @@ func (z *Rat) FloatString(prec int) string { } // z.b != 0 - q, r := nat{}.div(nat{}, z.a.abs, z.b) + q, r := nat(nil).div(nat(nil), z.a.abs, z.b) p := natOne if prec > 0 { - p = nat{}.expNN(natTen, nat{}.setUint64(uint64(prec)), nil) + p = nat(nil).expNN(natTen, nat(nil).setUint64(uint64(prec)), nil) } r = r.mul(r, p) - r, r2 := r.div(nat{}, r, z.b) + r, r2 := r.div(nat(nil), r, z.b) // see if we need to round up r2 = r2.add(r2, r2) if z.b.cmp(r2) <= 0 { r = r.add(r, natOne) if r.cmp(p) >= 0 { - q = nat{}.add(q, natOne) - r = nat{}.sub(r, p) + q = nat(nil).add(q, natOne) + r = nat(nil).sub(r, p) } } diff --git a/libgo/go/math/gamma.go b/libgo/go/math/gamma.go index e117158..ae2c0c4 100644 --- a/libgo/go/math/gamma.go +++ b/libgo/go/math/gamma.go @@ -63,7 +63,7 @@ package math // Stephen L. Moshier // moshier@na-net.ornl.gov -var _P = [...]float64{ +var _gamP = [...]float64{ 1.60119522476751861407e-04, 1.19135147006586384913e-03, 1.04213797561761569935e-02, @@ -72,7 +72,7 @@ var _P = [...]float64{ 4.94214826801497100753e-01, 9.99999999999999996796e-01, } -var _Q = [...]float64{ +var _gamQ = [...]float64{ -2.31581873324120129819e-05, 5.39605580493303397842e-04, -4.45641913851797240494e-03, @@ -82,7 +82,7 @@ var _Q = [...]float64{ 7.14304917030273074085e-02, 1.00000000000000000320e+00, } -var _S = [...]float64{ +var _gamS = [...]float64{ 7.87311395793093628397e-04, -2.29549961613378126380e-04, -2.68132617805781232825e-03, @@ -98,7 +98,7 @@ func stirling(x float64) float64 { MaxStirling = 143.01608 ) w := 1 / x - w = 1 + w*((((_S[0]*w+_S[1])*w+_S[2])*w+_S[3])*w+_S[4]) + w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4]) y := Exp(x) if x > MaxStirling { // avoid Pow() overflow v := Pow(x, 0.5*x-0.25) @@ -176,8 +176,8 @@ func Gamma(x float64) float64 { } x = x - 2 - p = (((((x*_P[0]+_P[1])*x+_P[2])*x+_P[3])*x+_P[4])*x+_P[5])*x + _P[6] - q = ((((((x*_Q[0]+_Q[1])*x+_Q[2])*x+_Q[3])*x+_Q[4])*x+_Q[5])*x+_Q[6])*x + _Q[7] + p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6] + q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7] return z * p / q small: diff --git a/libgo/go/math/lgamma.go b/libgo/go/math/lgamma.go index 8f6d7b9..e2bad69 100644 --- a/libgo/go/math/lgamma.go +++ b/libgo/go/math/lgamma.go @@ -88,6 +88,81 @@ package math // // +var _lgamA = [...]float64{ + 7.72156649015328655494e-02, // 0x3FB3C467E37DB0C8 + 3.22467033424113591611e-01, // 0x3FD4A34CC4A60FAD + 6.73523010531292681824e-02, // 0x3FB13E001A5562A7 + 2.05808084325167332806e-02, // 0x3F951322AC92547B + 7.38555086081402883957e-03, // 0x3F7E404FB68FEFE8 + 2.89051383673415629091e-03, // 0x3F67ADD8CCB7926B + 1.19270763183362067845e-03, // 0x3F538A94116F3F5D + 5.10069792153511336608e-04, // 0x3F40B6C689B99C00 + 2.20862790713908385557e-04, // 0x3F2CF2ECED10E54D + 1.08011567247583939954e-04, // 0x3F1C5088987DFB07 + 2.52144565451257326939e-05, // 0x3EFA7074428CFA52 + 4.48640949618915160150e-05, // 0x3F07858E90A45837 +} +var _lgamR = [...]float64{ + 1.0, // placeholder + 1.39200533467621045958e+00, // 0x3FF645A762C4AB74 + 7.21935547567138069525e-01, // 0x3FE71A1893D3DCDC + 1.71933865632803078993e-01, // 0x3FC601EDCCFBDF27 + 1.86459191715652901344e-02, // 0x3F9317EA742ED475 + 7.77942496381893596434e-04, // 0x3F497DDACA41A95B + 7.32668430744625636189e-06, // 0x3EDEBAF7A5B38140 +} +var _lgamS = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 2.14982415960608852501e-01, // 0x3FCB848B36E20878 + 3.25778796408930981787e-01, // 0x3FD4D98F4F139F59 + 1.46350472652464452805e-01, // 0x3FC2BB9CBEE5F2F7 + 2.66422703033638609560e-02, // 0x3F9B481C7E939961 + 1.84028451407337715652e-03, // 0x3F5E26B67368F239 + 3.19475326584100867617e-05, // 0x3F00BFECDD17E945 +} +var _lgamT = [...]float64{ + 4.83836122723810047042e-01, // 0x3FDEF72BC8EE38A2 + -1.47587722994593911752e-01, // 0xBFC2E4278DC6C509 + 6.46249402391333854778e-02, // 0x3FB08B4294D5419B + -3.27885410759859649565e-02, // 0xBFA0C9A8DF35B713 + 1.79706750811820387126e-02, // 0x3F9266E7970AF9EC + -1.03142241298341437450e-02, // 0xBF851F9FBA91EC6A + 6.10053870246291332635e-03, // 0x3F78FCE0E370E344 + -3.68452016781138256760e-03, // 0xBF6E2EFFB3E914D7 + 2.25964780900612472250e-03, // 0x3F6282D32E15C915 + -1.40346469989232843813e-03, // 0xBF56FE8EBF2D1AF1 + 8.81081882437654011382e-04, // 0x3F4CDF0CEF61A8E9 + -5.38595305356740546715e-04, // 0xBF41A6109C73E0EC + 3.15632070903625950361e-04, // 0x3F34AF6D6C0EBBF7 + -3.12754168375120860518e-04, // 0xBF347F24ECC38C38 + 3.35529192635519073543e-04, // 0x3F35FD3EE8C2D3F4 +} +var _lgamU = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 6.32827064025093366517e-01, // 0x3FE4401E8B005DFF + 1.45492250137234768737e+00, // 0x3FF7475CD119BD6F + 9.77717527963372745603e-01, // 0x3FEF497644EA8450 + 2.28963728064692451092e-01, // 0x3FCD4EAEF6010924 + 1.33810918536787660377e-02, // 0x3F8B678BBF2BAB09 +} +var _lgamV = [...]float64{ + 1.0, + 2.45597793713041134822e+00, // 0x4003A5D7C2BD619C + 2.12848976379893395361e+00, // 0x40010725A42B18F5 + 7.69285150456672783825e-01, // 0x3FE89DFBE45050AF + 1.04222645593369134254e-01, // 0x3FBAAE55D6537C88 + 3.21709242282423911810e-03, // 0x3F6A5ABB57D0CF61 +} +var _lgamW = [...]float64{ + 4.18938533204672725052e-01, // 0x3FDACFE390C97D69 + 8.33333333333329678849e-02, // 0x3FB555555555553B + -2.77777777728775536470e-03, // 0xBF66C16C16B02E5C + 7.93650558643019558500e-04, // 0x3F4A019F98CF38B6 + -5.95187557450339963135e-04, // 0xBF4380CB8C0FE741 + 8.36339918996282139126e-04, // 0x3F4B67BA4CDAD5D1 + -1.63092934096575273989e-03, // 0xBF5AB89D0B9E43E4 +} + // Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x). // // Special cases are: @@ -103,68 +178,10 @@ func Lgamma(x float64) (lgamma float64, sign int) { Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15 Two58 = 1 << 58 // 0x4390000000000000 ~2.8823e+17 Tiny = 1.0 / (1 << 70) // 0x3b90000000000000 ~8.47033e-22 - A0 = 7.72156649015328655494e-02 // 0x3FB3C467E37DB0C8 - A1 = 3.22467033424113591611e-01 // 0x3FD4A34CC4A60FAD - A2 = 6.73523010531292681824e-02 // 0x3FB13E001A5562A7 - A3 = 2.05808084325167332806e-02 // 0x3F951322AC92547B - A4 = 7.38555086081402883957e-03 // 0x3F7E404FB68FEFE8 - A5 = 2.89051383673415629091e-03 // 0x3F67ADD8CCB7926B - A6 = 1.19270763183362067845e-03 // 0x3F538A94116F3F5D - A7 = 5.10069792153511336608e-04 // 0x3F40B6C689B99C00 - A8 = 2.20862790713908385557e-04 // 0x3F2CF2ECED10E54D - A9 = 1.08011567247583939954e-04 // 0x3F1C5088987DFB07 - A10 = 2.52144565451257326939e-05 // 0x3EFA7074428CFA52 - A11 = 4.48640949618915160150e-05 // 0x3F07858E90A45837 Tc = 1.46163214496836224576e+00 // 0x3FF762D86356BE3F Tf = -1.21486290535849611461e-01 // 0xBFBF19B9BCC38A42 // Tt = -(tail of Tf) - Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F - T0 = 4.83836122723810047042e-01 // 0x3FDEF72BC8EE38A2 - T1 = -1.47587722994593911752e-01 // 0xBFC2E4278DC6C509 - T2 = 6.46249402391333854778e-02 // 0x3FB08B4294D5419B - T3 = -3.27885410759859649565e-02 // 0xBFA0C9A8DF35B713 - T4 = 1.79706750811820387126e-02 // 0x3F9266E7970AF9EC - T5 = -1.03142241298341437450e-02 // 0xBF851F9FBA91EC6A - T6 = 6.10053870246291332635e-03 // 0x3F78FCE0E370E344 - T7 = -3.68452016781138256760e-03 // 0xBF6E2EFFB3E914D7 - T8 = 2.25964780900612472250e-03 // 0x3F6282D32E15C915 - T9 = -1.40346469989232843813e-03 // 0xBF56FE8EBF2D1AF1 - T10 = 8.81081882437654011382e-04 // 0x3F4CDF0CEF61A8E9 - T11 = -5.38595305356740546715e-04 // 0xBF41A6109C73E0EC - T12 = 3.15632070903625950361e-04 // 0x3F34AF6D6C0EBBF7 - T13 = -3.12754168375120860518e-04 // 0xBF347F24ECC38C38 - T14 = 3.35529192635519073543e-04 // 0x3F35FD3EE8C2D3F4 - U0 = -7.72156649015328655494e-02 // 0xBFB3C467E37DB0C8 - U1 = 6.32827064025093366517e-01 // 0x3FE4401E8B005DFF - U2 = 1.45492250137234768737e+00 // 0x3FF7475CD119BD6F - U3 = 9.77717527963372745603e-01 // 0x3FEF497644EA8450 - U4 = 2.28963728064692451092e-01 // 0x3FCD4EAEF6010924 - U5 = 1.33810918536787660377e-02 // 0x3F8B678BBF2BAB09 - V1 = 2.45597793713041134822e+00 // 0x4003A5D7C2BD619C - V2 = 2.12848976379893395361e+00 // 0x40010725A42B18F5 - V3 = 7.69285150456672783825e-01 // 0x3FE89DFBE45050AF - V4 = 1.04222645593369134254e-01 // 0x3FBAAE55D6537C88 - V5 = 3.21709242282423911810e-03 // 0x3F6A5ABB57D0CF61 - S0 = -7.72156649015328655494e-02 // 0xBFB3C467E37DB0C8 - S1 = 2.14982415960608852501e-01 // 0x3FCB848B36E20878 - S2 = 3.25778796408930981787e-01 // 0x3FD4D98F4F139F59 - S3 = 1.46350472652464452805e-01 // 0x3FC2BB9CBEE5F2F7 - S4 = 2.66422703033638609560e-02 // 0x3F9B481C7E939961 - S5 = 1.84028451407337715652e-03 // 0x3F5E26B67368F239 - S6 = 3.19475326584100867617e-05 // 0x3F00BFECDD17E945 - R1 = 1.39200533467621045958e+00 // 0x3FF645A762C4AB74 - R2 = 7.21935547567138069525e-01 // 0x3FE71A1893D3DCDC - R3 = 1.71933865632803078993e-01 // 0x3FC601EDCCFBDF27 - R4 = 1.86459191715652901344e-02 // 0x3F9317EA742ED475 - R5 = 7.77942496381893596434e-04 // 0x3F497DDACA41A95B - R6 = 7.32668430744625636189e-06 // 0x3EDEBAF7A5B38140 - W0 = 4.18938533204672725052e-01 // 0x3FDACFE390C97D69 - W1 = 8.33333333333329678849e-02 // 0x3FB555555555553B - W2 = -2.77777777728775536470e-03 // 0xBF66C16C16B02E5C - W3 = 7.93650558643019558500e-04 // 0x3F4A019F98CF38B6 - W4 = -5.95187557450339963135e-04 // 0xBF4380CB8C0FE741 - W5 = 8.36339918996282139126e-04 // 0x3F4B67BA4CDAD5D1 - W6 = -1.63092934096575273989e-03 // 0xBF5AB89D0B9E43E4 + Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F ) // TODO(rsc): Remove manual inlining of IsNaN, IsInf // when compiler does it for us @@ -249,28 +266,28 @@ func Lgamma(x float64) (lgamma float64, sign int) { switch i { case 0: z := y * y - p1 := A0 + z*(A2+z*(A4+z*(A6+z*(A8+z*A10)))) - p2 := z * (A1 + z*(A3+z*(A5+z*(A7+z*(A9+z*A11))))) + p1 := _lgamA[0] + z*(_lgamA[2]+z*(_lgamA[4]+z*(_lgamA[6]+z*(_lgamA[8]+z*_lgamA[10])))) + p2 := z * (_lgamA[1] + z*(+_lgamA[3]+z*(_lgamA[5]+z*(_lgamA[7]+z*(_lgamA[9]+z*_lgamA[11]))))) p := y*p1 + p2 lgamma += (p - 0.5*y) case 1: z := y * y w := z * y - p1 := T0 + w*(T3+w*(T6+w*(T9+w*T12))) // parallel comp - p2 := T1 + w*(T4+w*(T7+w*(T10+w*T13))) - p3 := T2 + w*(T5+w*(T8+w*(T11+w*T14))) + p1 := _lgamT[0] + w*(_lgamT[3]+w*(_lgamT[6]+w*(_lgamT[9]+w*_lgamT[12]))) // parallel comp + p2 := _lgamT[1] + w*(_lgamT[4]+w*(_lgamT[7]+w*(_lgamT[10]+w*_lgamT[13]))) + p3 := _lgamT[2] + w*(_lgamT[5]+w*(_lgamT[8]+w*(_lgamT[11]+w*_lgamT[14]))) p := z*p1 - (Tt - w*(p2+y*p3)) lgamma += (Tf + p) case 2: - p1 := y * (U0 + y*(U1+y*(U2+y*(U3+y*(U4+y*U5))))) - p2 := 1 + y*(V1+y*(V2+y*(V3+y*(V4+y*V5)))) + p1 := y * (_lgamU[0] + y*(_lgamU[1]+y*(_lgamU[2]+y*(_lgamU[3]+y*(_lgamU[4]+y*_lgamU[5]))))) + p2 := 1 + y*(_lgamV[1]+y*(_lgamV[2]+y*(_lgamV[3]+y*(_lgamV[4]+y*_lgamV[5])))) lgamma += (-0.5*y + p1/p2) } case x < 8: // 2 <= x < 8 i := int(x) y := x - float64(i) - p := y * (S0 + y*(S1+y*(S2+y*(S3+y*(S4+y*(S5+y*S6)))))) - q := 1 + y*(R1+y*(R2+y*(R3+y*(R4+y*(R5+y*R6))))) + p := y * (_lgamS[0] + y*(_lgamS[1]+y*(_lgamS[2]+y*(_lgamS[3]+y*(_lgamS[4]+y*(_lgamS[5]+y*_lgamS[6])))))) + q := 1 + y*(_lgamR[1]+y*(_lgamR[2]+y*(_lgamR[3]+y*(_lgamR[4]+y*(_lgamR[5]+y*_lgamR[6]))))) lgamma = 0.5*y + p/q z := 1.0 // Lgamma(1+s) = Log(s) + Lgamma(s) switch i { @@ -294,7 +311,7 @@ func Lgamma(x float64) (lgamma float64, sign int) { t := Log(x) z := 1 / x y := z * z - w := W0 + z*(W1+y*(W2+y*(W3+y*(W4+y*(W5+y*W6))))) + w := _lgamW[0] + z*(_lgamW[1]+y*(_lgamW[2]+y*(_lgamW[3]+y*(_lgamW[4]+y*(_lgamW[5]+y*_lgamW[6]))))) lgamma = (x-0.5)*(t-1) + w default: // 2**58 <= x <= Inf lgamma = x * (Log(x) - 1) diff --git a/libgo/go/mime/multipart/formdata.go b/libgo/go/mime/multipart/formdata.go index d9982e5..ec643c1 100644 --- a/libgo/go/mime/multipart/formdata.go +++ b/libgo/go/mime/multipart/formdata.go @@ -160,7 +160,7 @@ type sliceReaderAt []byte func (r sliceReaderAt) ReadAt(b []byte, off int64) (int, error) { if int(off) >= len(r) || off < 0 { - return 0, os.EINVAL + return 0, io.ErrUnexpectedEOF } n := copy(b, r[int(off):]) return n, nil diff --git a/libgo/go/mime/type.go b/libgo/go/mime/type.go index ce72bb5f..e3d968f 100644 --- a/libgo/go/mime/type.go +++ b/libgo/go/mime/type.go @@ -6,19 +6,11 @@ package mime import ( - "bufio" "fmt" - "os" "strings" "sync" ) -var typeFiles = []string{ - "/etc/mime.types", - "/etc/apache2/mime.types", - "/etc/apache/mime.types", -} - var mimeTypes = map[string]string{ ".css": "text/css; charset=utf-8", ".gif": "image/gif", @@ -33,46 +25,13 @@ var mimeTypes = map[string]string{ var mimeLock sync.RWMutex -func loadMimeFile(filename string) { - f, err := os.Open(filename) - if err != nil { - return - } - - reader := bufio.NewReader(f) - for { - line, err := reader.ReadString('\n') - if err != nil { - f.Close() - return - } - fields := strings.Fields(line) - if len(fields) <= 1 || fields[0][0] == '#' { - continue - } - mimeType := fields[0] - for _, ext := range fields[1:] { - if ext[0] == '#' { - break - } - setExtensionType("."+ext, mimeType) - } - } -} - -func initMime() { - for _, filename := range typeFiles { - loadMimeFile(filename) - } -} - var once sync.Once // TypeByExtension returns the MIME type associated with the file extension ext. // The extension ext should begin with a leading dot, as in ".html". // When ext has no associated type, TypeByExtension returns "". // -// The built-in table is small but is is augmented by the local +// The built-in table is small but on unix it is augmented by the local // system's mime.types file(s) if available under one or more of these // names: // @@ -80,6 +39,8 @@ var once sync.Once // /etc/apache2/mime.types // /etc/apache/mime.types // +// Windows system mime types are extracted from registry. +// // Text types have the charset parameter set to "utf-8" by default. func TypeByExtension(ext string) string { once.Do(initMime) diff --git a/libgo/go/mime/type_test.go b/libgo/go/mime/type_test.go index 976f853..07e1cd5 100644 --- a/libgo/go/mime/type_test.go +++ b/libgo/go/mime/type_test.go @@ -6,15 +6,9 @@ package mime import "testing" -var typeTests = map[string]string{ - ".t1": "application/test", - ".t2": "text/test; charset=utf-8", - ".png": "image/png", -} +var typeTests = initMimeForTests() func TestTypeByExtension(t *testing.T) { - typeFiles = []string{"test.types"} - for ext, want := range typeTests { val := TypeByExtension(ext) if val != want { diff --git a/libgo/go/mime/type_unix.go b/libgo/go/mime/type_unix.go new file mode 100644 index 0000000..45127ba --- /dev/null +++ b/libgo/go/mime/type_unix.go @@ -0,0 +1,59 @@ +// Copyright 2010 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 mime + +import ( + "bufio" + "os" + "strings" +) + +var typeFiles = []string{ + "/etc/mime.types", + "/etc/apache2/mime.types", + "/etc/apache/mime.types", +} + +func loadMimeFile(filename string) { + f, err := os.Open(filename) + if err != nil { + return + } + + reader := bufio.NewReader(f) + for { + line, err := reader.ReadString('\n') + if err != nil { + f.Close() + return + } + fields := strings.Fields(line) + if len(fields) <= 1 || fields[0][0] == '#' { + continue + } + mimeType := fields[0] + for _, ext := range fields[1:] { + if ext[0] == '#' { + break + } + setExtensionType("."+ext, mimeType) + } + } +} + +func initMime() { + for _, filename := range typeFiles { + loadMimeFile(filename) + } +} + +func initMimeForTests() map[string]string { + typeFiles = []string{"test.types"} + return map[string]string{ + ".t1": "application/test", + ".t2": "text/test; charset=utf-8", + ".png": "image/png", + } +} diff --git a/libgo/go/mime/type_windows.go b/libgo/go/mime/type_windows.go new file mode 100644 index 0000000..7cf2d39 --- /dev/null +++ b/libgo/go/mime/type_windows.go @@ -0,0 +1,61 @@ +// Copyright 2010 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 mime + +import ( + "syscall" + "unsafe" +) + +func initMime() { + var root syscall.Handle + if syscall.RegOpenKeyEx(syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`), + 0, syscall.KEY_READ, &root) != 0 { + return + } + defer syscall.RegCloseKey(root) + var count uint32 + if syscall.RegQueryInfoKey(root, nil, nil, nil, &count, nil, nil, nil, nil, nil, nil, nil) != 0 { + return + } + var buf [1 << 10]uint16 + for i := uint32(0); i < count; i++ { + n := uint32(len(buf)) + if syscall.RegEnumKeyEx(root, i, &buf[0], &n, nil, nil, nil, nil) != 0 { + continue + } + ext := syscall.UTF16ToString(buf[:]) + if len(ext) < 2 || ext[0] != '.' { // looking for extensions only + continue + } + var h syscall.Handle + if syscall.RegOpenKeyEx( + syscall.HKEY_CLASSES_ROOT, syscall.StringToUTF16Ptr(`\`+ext), + 0, syscall.KEY_READ, &h) != 0 { + continue + } + var typ uint32 + n = uint32(len(buf) * 2) // api expects array of bytes, not uint16 + if syscall.RegQueryValueEx( + h, syscall.StringToUTF16Ptr("Content Type"), + nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n) != 0 { + syscall.RegCloseKey(h) + continue + } + syscall.RegCloseKey(h) + if typ != syscall.REG_SZ { // null terminated strings only + continue + } + mimeType := syscall.UTF16ToString(buf[:]) + setExtensionType(ext, mimeType) + } +} + +func initMimeForTests() map[string]string { + return map[string]string{ + ".bmp": "image/bmp", + ".png": "image/png", + } +} diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go index 680d3e7..1a0f406 100644 --- a/libgo/go/net/cgo_unix.go +++ b/libgo/go/net/cgo_unix.go @@ -109,7 +109,7 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet if gerrno == syscall.EAI_NONAME { str = noSuchHost } else if gerrno == syscall.EAI_SYSTEM { - str = syscall.Errstr(syscall.GetErrno()) + str = syscall.GetErrno().Error() } else { str = bytePtrToString(libc_gai_strerror(gerrno)) } diff --git a/libgo/go/net/fd.go b/libgo/go/net/fd.go index 025075d..70e04a2 100644 --- a/libgo/go/net/fd.go +++ b/libgo/go/net/fd.go @@ -278,8 +278,8 @@ func startServer() { func newFD(fd, family, proto int, net string) (f *netFD, err error) { onceStartServer.Do(startServer) - if e := syscall.SetNonblock(fd, true); e != 0 { - return nil, os.Errno(e) + if e := syscall.SetNonblock(fd, true); e != nil { + return nil, e } f = &netFD{ sysfd: fd, @@ -306,19 +306,19 @@ func (fd *netFD) setAddr(laddr, raddr Addr) { } func (fd *netFD) connect(ra syscall.Sockaddr) (err error) { - e := syscall.Connect(fd.sysfd, ra) - if e == syscall.EINPROGRESS { - var errno int + err = syscall.Connect(fd.sysfd, ra) + if err == syscall.EINPROGRESS { pollserver.WaitWrite(fd) - e, errno = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) - if errno != 0 { - return os.NewSyscallError("getsockopt", errno) + var e int + e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) + if err != nil { + return os.NewSyscallError("getsockopt", err) + } + if e != 0 { + err = syscall.Errno(e) } } - if e != 0 { - return os.Errno(e) - } - return nil + return err } // Add a reference to this fd. @@ -362,9 +362,9 @@ func (fd *netFD) shutdown(how int) error { if fd == nil || fd.sysfile == nil { return os.EINVAL } - errno := syscall.Shutdown(fd.sysfd, how) - if errno != 0 { - return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)} + err := syscall.Shutdown(fd.sysfd, how) + if err != nil { + return &OpError{"shutdown", fd.net, fd.laddr, err} } return nil } @@ -377,6 +377,14 @@ func (fd *netFD) CloseWrite() error { return fd.shutdown(syscall.SHUT_WR) } +type timeoutError struct{} + +func (e *timeoutError) Error() string { return "i/o timeout" } +func (e *timeoutError) Timeout() bool { return true } +func (e *timeoutError) Temporary() bool { return true } + +var errTimeout error = &timeoutError{} + func (fd *netFD) Read(p []byte) (n int, err error) { if fd == nil { return 0, os.EINVAL @@ -393,24 +401,24 @@ func (fd *netFD) Read(p []byte) (n int, err error) { } else { fd.rdeadline = 0 } - var oserr error for { - var errno int - n, errno = syscall.Read(fd.sysfile.Fd(), p) - if errno == syscall.EAGAIN && fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + n, err = syscall.Read(fd.sysfile.Fd(), p) + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout } - if errno != 0 { + if err != nil { n = 0 - oserr = os.Errno(errno) - } else if n == 0 && errno == 0 && fd.proto != syscall.SOCK_DGRAM { + } else if n == 0 && err == nil && fd.proto != syscall.SOCK_DGRAM { err = io.EOF } break } - if oserr != nil { - err = &OpError{"read", fd.net, fd.raddr, oserr} + if err != nil && err != io.EOF { + err = &OpError{"read", fd.net, fd.raddr, err} } return } @@ -428,22 +436,22 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { } else { fd.rdeadline = 0 } - var oserr error for { - var errno int - n, sa, errno = syscall.Recvfrom(fd.sysfd, p, 0) - if errno == syscall.EAGAIN && fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue + n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout } - if errno != 0 { + if err != nil { n = 0 - oserr = os.Errno(errno) } break } - if oserr != nil { - err = &OpError{"read", fd.net, fd.laddr, oserr} + if err != nil { + err = &OpError{"read", fd.net, fd.laddr, err} } return } @@ -461,24 +469,22 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S } else { fd.rdeadline = 0 } - var oserr error for { - var errno int - n, oobn, flags, sa, errno = syscall.Recvmsg(fd.sysfd, p, oob, 0) - if errno == syscall.EAGAIN && fd.rdeadline >= 0 { - pollserver.WaitRead(fd) - continue - } - if errno != 0 { - oserr = os.Errno(errno) + n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout } - if n == 0 { - oserr = io.EOF + if err == nil && n == 0 { + err = io.EOF } break } - if oserr != nil { - err = &OpError{"read", fd.net, fd.laddr, oserr} + if err != nil && err != io.EOF { + err = &OpError{"read", fd.net, fd.laddr, err} return } return @@ -501,32 +507,34 @@ func (fd *netFD) Write(p []byte) (n int, err error) { fd.wdeadline = 0 } nn := 0 - var oserr error for { - n, errno := syscall.Write(fd.sysfile.Fd(), p[nn:]) + var n int + n, err = syscall.Write(fd.sysfile.Fd(), p[nn:]) if n > 0 { nn += n } if nn == len(p) { break } - if errno == syscall.EAGAIN && fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue + if err == syscall.EAGAIN { + if fd.wdeadline >= 0 { + pollserver.WaitWrite(fd) + continue + } + err = errTimeout } - if errno != 0 { + if err != nil { n = 0 - oserr = os.Errno(errno) break } if n == 0 { - oserr = io.ErrUnexpectedEOF + err = io.ErrUnexpectedEOF break } } - if oserr != nil { - err = &OpError{"write", fd.net, fd.raddr, oserr} + if err != nil { + err = &OpError{"write", fd.net, fd.raddr, err} } return nn, err } @@ -544,22 +552,21 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { } else { fd.wdeadline = 0 } - var oserr error for { - errno := syscall.Sendto(fd.sysfd, p, 0, sa) - if errno == syscall.EAGAIN && fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue - } - if errno != 0 { - oserr = os.Errno(errno) + err = syscall.Sendto(fd.sysfd, p, 0, sa) + if err == syscall.EAGAIN { + if fd.wdeadline >= 0 { + pollserver.WaitWrite(fd) + continue + } + err = errTimeout } break } - if oserr == nil { + if err == nil { n = len(p) } else { - err = &OpError{"write", fd.net, fd.raddr, oserr} + err = &OpError{"write", fd.net, fd.raddr, err} } return } @@ -577,24 +584,22 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob } else { fd.wdeadline = 0 } - var oserr error for { - var errno int - errno = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) - if errno == syscall.EAGAIN && fd.wdeadline >= 0 { - pollserver.WaitWrite(fd) - continue - } - if errno != 0 { - oserr = os.Errno(errno) + err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) + if err == syscall.EAGAIN { + if fd.wdeadline >= 0 { + pollserver.WaitWrite(fd) + continue + } + err = errTimeout } break } - if oserr == nil { + if err == nil { n = len(p) oobn = len(oob) } else { - err = &OpError{"write", fd.net, fd.raddr, oserr} + err = &OpError{"write", fd.net, fd.raddr, err} } return } @@ -615,25 +620,26 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err // See ../syscall/exec.go for description of ForkLock. // It is okay to hold the lock across syscall.Accept // because we have put fd.sysfd into non-blocking mode. - syscall.ForkLock.RLock() - var s, e int + var s int var rsa syscall.Sockaddr for { if fd.closing { - syscall.ForkLock.RUnlock() return nil, os.EINVAL } - s, rsa, e = syscall.Accept(fd.sysfd) - if e != syscall.EAGAIN || fd.rdeadline < 0 { - break - } - syscall.ForkLock.RUnlock() - pollserver.WaitRead(fd) syscall.ForkLock.RLock() - } - if e != 0 { - syscall.ForkLock.RUnlock() - return nil, &OpError{"accept", fd.net, fd.laddr, os.Errno(e)} + s, rsa, err = syscall.Accept(fd.sysfd) + if err != nil { + syscall.ForkLock.RUnlock() + if err == syscall.EAGAIN { + if fd.rdeadline >= 0 { + pollserver.WaitRead(fd) + continue + } + err = errTimeout + } + return nil, &OpError{"accept", fd.net, fd.laddr, err} + } + break } syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() @@ -648,19 +654,19 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err } func (fd *netFD) dup() (f *os.File, err error) { - ns, e := syscall.Dup(fd.sysfd) - if e != 0 { - return nil, &OpError{"dup", fd.net, fd.laddr, os.Errno(e)} + ns, err := syscall.Dup(fd.sysfd) + if err != nil { + return nil, &OpError{"dup", fd.net, fd.laddr, err} } // We want blocking mode for the new fd, hence the double negative. - if e = syscall.SetNonblock(ns, false); e != 0 { - return nil, &OpError{"setnonblock", fd.net, fd.laddr, os.Errno(e)} + if err = syscall.SetNonblock(ns, false); err != nil { + return nil, &OpError{"setnonblock", fd.net, fd.laddr, err} } return os.NewFile(ns, fd.sysfile.Name()), nil } -func closesocket(s int) (errno int) { +func closesocket(s int) error { return syscall.Close(s) } diff --git a/libgo/go/net/fd_linux.go b/libgo/go/net/fd_linux.go index cce74cd..8e07833 100644 --- a/libgo/go/net/fd_linux.go +++ b/libgo/go/net/fd_linux.go @@ -35,12 +35,12 @@ type pollster struct { func newpollster() (p *pollster, err error) { p = new(pollster) - var e int + var e error // The arg to epoll_create is a hint to the kernel // about the number of FDs we will care about. // We don't know, and since 2.6.8 the kernel ignores it anyhow. - if p.epfd, e = syscall.EpollCreate(16); e != 0 { + if p.epfd, e = syscall.EpollCreate(16); e != nil { return nil, os.NewSyscallError("epoll_create", e) } p.events = make(map[int]uint32) @@ -68,7 +68,7 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { } else { op = syscall.EPOLL_CTL_ADD } - if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != 0 { + if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != nil { return false, os.NewSyscallError("epoll_ctl", e) } p.events[fd] = p.ctlEvent.Events @@ -97,13 +97,13 @@ func (p *pollster) StopWaiting(fd int, bits uint) { if int32(events)&^syscall.EPOLLONESHOT != 0 { p.ctlEvent.Fd = int32(fd) p.ctlEvent.Events = events - if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != 0 { - print("Epoll modify fd=", fd, ": ", os.Errno(e).Error(), "\n") + if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != nil { + print("Epoll modify fd=", fd, ": ", e.Error(), "\n") } p.events[fd] = events } else { - if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 { - print("Epoll delete fd=", fd, ": ", os.Errno(e).Error(), "\n") + if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != nil { + print("Epoll delete fd=", fd, ": ", e.Error(), "\n") } delete(p.events, fd) } @@ -141,7 +141,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro n, e := syscall.EpollWait(p.epfd, p.waitEventBuf[0:], msec) s.Lock() - if e != 0 { + if e != nil { if e == syscall.EAGAIN || e == syscall.EINTR { continue } diff --git a/libgo/go/net/fd_openbsd.go b/libgo/go/net/fd_openbsd.go index f61008a..e52ac35 100644 --- a/libgo/go/net/fd_openbsd.go +++ b/libgo/go/net/fd_openbsd.go @@ -23,9 +23,8 @@ type pollster struct { func newpollster() (p *pollster, err error) { p = new(pollster) - var e int - if p.kq, e = syscall.Kqueue(); e != 0 { - return nil, os.NewSyscallError("kqueue", e) + if p.kq, err = syscall.Kqueue(); err != nil { + return nil, os.NewSyscallError("kqueue", err) } p.events = p.eventbuf[0:0] return p, nil @@ -50,14 +49,14 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { syscall.SetKevent(ev, fd, kmode, flags) n, e := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) - if e != 0 { + if e != nil { return false, os.NewSyscallError("kevent", e) } if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { return false, os.NewSyscallError("kqueue phase error", e) } if ev.Data != 0 { - return false, os.Errno(int(ev.Data)) + return false, syscall.Errno(int(ev.Data)) } return false, nil } @@ -91,7 +90,7 @@ func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err erro nn, e := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) s.Lock() - if e != 0 { + if e != nil { if e == syscall.EINTR { continue } diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index ce228e9..7a16023 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -26,11 +26,11 @@ func init() { var d syscall.WSAData e := syscall.WSAStartup(uint32(0x202), &d) if e != 0 { - initErr = os.NewSyscallError("WSAStartup", e) + initErr = os.NewSyscallError("WSAStartup", syscall.Errno(e)) } } -func closesocket(s syscall.Handle) (errno int) { +func closesocket(s syscall.Handle) (err error) { return syscall.Closesocket(s) } @@ -38,13 +38,13 @@ func closesocket(s syscall.Handle) (errno int) { type anOpIface interface { Op() *anOp Name() string - Submit() (errno int) + Submit() (err error) } // IO completion result parameters. type ioResult struct { qty uint32 - err int + err error } // anOp implements functionality common to all io operations. @@ -54,7 +54,7 @@ type anOp struct { o syscall.Overlapped resultc chan ioResult - errnoc chan int + errnoc chan error fd *netFD } @@ -71,7 +71,7 @@ func (o *anOp) Init(fd *netFD, mode int) { } o.resultc = fd.resultc[i] if fd.errnoc[i] == nil { - fd.errnoc[i] = make(chan int) + fd.errnoc[i] = make(chan error) } o.errnoc = fd.errnoc[i] } @@ -111,14 +111,14 @@ func (s *resultSrv) Run() { for { r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE) switch { - case r.err == 0: + case r.err == nil: // Dequeued successfully completed io packet. - case r.err == syscall.WAIT_TIMEOUT && o == nil: + case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil: // Wait has timed out (should not happen now, but might be used in the future). panic("GetQueuedCompletionStatus timed out") case o == nil: // Failed to dequeue anything -> report the error. - panic("GetQueuedCompletionStatus failed " + syscall.Errstr(r.err)) + panic("GetQueuedCompletionStatus failed " + r.err.Error()) default: // Dequeued failed io packet. } @@ -153,7 +153,7 @@ func (s *ioSrv) ProcessRemoteIO() { // inline, or, if timeouts are employed, passes the request onto // a special goroutine and waits for completion or cancels request. func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { - var e int + var e error o := oi.Op() if deadline_delta > 0 { // Send request to a special dedicated thread, @@ -164,12 +164,12 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { e = oi.Submit() } switch e { - case 0: + case nil: // IO completed immediately, but we need to get our completion message anyway. case syscall.ERROR_IO_PENDING: // IO started, and we have to wait for its completion. default: - return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(e)} + return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e} } // Wait for our request to complete. var r ioResult @@ -187,8 +187,8 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) { } else { r = <-o.resultc } - if r.err != 0 { - err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(r.err)} + if r.err != nil { + err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err} } return int(r.qty), err } @@ -200,10 +200,10 @@ var onceStartServer sync.Once func startServer() { resultsrv = new(resultSrv) - var errno int - resultsrv.iocp, errno = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) - if errno != 0 { - panic("CreateIoCompletionPort failed " + syscall.Errstr(errno)) + var err error + resultsrv.iocp, err = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) + if err != nil { + panic("CreateIoCompletionPort: " + err.Error()) } go resultsrv.Run() @@ -228,7 +228,7 @@ type netFD struct { laddr Addr raddr Addr resultc [2]chan ioResult // read/write completion results - errnoc [2]chan int // read/write submit or cancel operation errors + errnoc [2]chan error // read/write submit or cancel operation errors // owned by client rdeadline_delta int64 @@ -256,8 +256,8 @@ func newFD(fd syscall.Handle, family, proto int, net string) (f *netFD, err erro } onceStartServer.Do(startServer) // Associate our socket with resultsrv.iocp. - if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != 0 { - return nil, os.Errno(e) + if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != nil { + return nil, e } return allocFD(fd, family, proto, net), nil } @@ -268,11 +268,7 @@ func (fd *netFD) setAddr(laddr, raddr Addr) { } func (fd *netFD) connect(ra syscall.Sockaddr) (err error) { - e := syscall.Connect(fd.sysfd, ra) - if e != 0 { - return os.Errno(e) - } - return nil + return syscall.Connect(fd.sysfd, ra) } // Add a reference to this fd. @@ -317,9 +313,9 @@ func (fd *netFD) shutdown(how int) error { if fd == nil || fd.sysfd == syscall.InvalidHandle { return os.EINVAL } - errno := syscall.Shutdown(fd.sysfd, how) - if errno != 0 { - return &OpError{"shutdown", fd.net, fd.laddr, os.Errno(errno)} + err := syscall.Shutdown(fd.sysfd, how) + if err != nil { + return &OpError{"shutdown", fd.net, fd.laddr, err} } return nil } @@ -338,7 +334,7 @@ type readOp struct { bufOp } -func (o *readOp) Submit() (errno int) { +func (o *readOp) Submit() (err error) { var d, f uint32 return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) } @@ -375,7 +371,7 @@ type readFromOp struct { rsan int32 } -func (o *readFromOp) Submit() (errno int) { +func (o *readFromOp) Submit() (err error) { var d, f uint32 return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &o.rsan, &o.o, nil) } @@ -415,7 +411,7 @@ type writeOp struct { bufOp } -func (o *writeOp) Submit() (errno int) { +func (o *writeOp) Submit() (err error) { var d uint32 return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil) } @@ -447,7 +443,7 @@ type writeToOp struct { sa syscall.Sockaddr } -func (o *writeToOp) Submit() (errno int) { +func (o *writeToOp) Submit() (err error) { var d uint32 return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil) } @@ -484,7 +480,7 @@ type acceptOp struct { attrs [2]syscall.RawSockaddrAny // space for local and remote address only } -func (o *acceptOp) Submit() (errno int) { +func (o *acceptOp) Submit() (err error) { var d uint32 l := uint32(unsafe.Sizeof(o.attrs[0])) return syscall.AcceptEx(o.fd.sysfd, o.newsock, @@ -506,17 +502,17 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock() s, e := syscall.Socket(fd.family, fd.proto, 0) - if e != 0 { + if e != nil { syscall.ForkLock.RUnlock() - return nil, os.Errno(e) + return nil, e } syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() // Associate our new socket with IOCP. onceStartServer.Do(startServer) - if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != 0 { - return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)} + if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != nil { + return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, e} } // Submit accept request. @@ -531,9 +527,9 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err // Inherit properties of the listening socket. e = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) - if e != 0 { + if e != nil { closesocket(s) - return nil, err + return nil, e } // Get local and peer addr out of AcceptEx buffer. diff --git a/libgo/go/net/file.go b/libgo/go/net/file.go index 0ad869d..bf8cd9d 100644 --- a/libgo/go/net/file.go +++ b/libgo/go/net/file.go @@ -13,12 +13,12 @@ import ( func newFileFD(f *os.File) (nfd *netFD, err error) { fd, errno := syscall.Dup(f.Fd()) - if errno != 0 { + if errno != nil { return nil, os.NewSyscallError("dup", errno) } proto, errno := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE) - if errno != 0 { + if errno != nil { return nil, os.NewSyscallError("getsockopt", errno) } diff --git a/libgo/go/net/hosts.go b/libgo/go/net/hosts.go index d75e9e0..ddfb074 100644 --- a/libgo/go/net/hosts.go +++ b/libgo/go/net/hosts.go @@ -7,8 +7,8 @@ package net import ( - "os" "sync" + "time" ) const cacheMaxAge = int64(300) // 5 minutes. @@ -26,7 +26,7 @@ var hosts struct { } func readHosts() { - now, _, _ := os.Time() + now := time.Seconds() hp := hostsPath if len(hosts.byName) == 0 || hosts.time+cacheMaxAge <= now || hosts.path != hp { hs := make(map[string][]string) @@ -51,7 +51,7 @@ func readHosts() { } } // Update the data cache. - hosts.time, _, _ = os.Time() + hosts.time = time.Seconds() hosts.path = hp hosts.byName = hs hosts.byAddr = is diff --git a/libgo/go/net/http/cgi/host_test.go b/libgo/go/net/http/cgi/host_test.go index 2bc913a..e6e85e8 100644 --- a/libgo/go/net/http/cgi/host_test.go +++ b/libgo/go/net/http/cgi/host_test.go @@ -363,14 +363,13 @@ func TestCopyError(t *testing.T) { } conn.Close() - if tries := 0; childRunning() { - for tries < 15 && childRunning() { - time.Sleep(50e6 * int64(tries)) - tries++ - } - if childRunning() { - t.Fatalf("post-conn.Close, expected child to be gone") - } + tries := 0 + for tries < 15 && childRunning() { + time.Sleep(50e6 * int64(tries)) + tries++ + } + if childRunning() { + t.Fatalf("post-conn.Close, expected child to be gone") } } diff --git a/libgo/go/net/http/chunked.go b/libgo/go/net/http/chunked.go index b012dd1..74c41aa 100644 --- a/libgo/go/net/http/chunked.go +++ b/libgo/go/net/http/chunked.go @@ -2,20 +2,137 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The wire protocol for HTTP's "chunked" Transfer-Encoding. + +// This code is duplicated in httputil/chunked.go. +// Please make any changes in both files. + package http import ( "bufio" + "bytes" + "errors" "io" "strconv" ) +const maxLineLength = 4096 // assumed <= bufio.defaultBufSize + +var ErrLineTooLong = errors.New("header line too long") + +// newChunkedReader returns a new chunkedReader that translates the data read from r +// out of HTTP "chunked" format before returning it. +// The chunkedReader returns io.EOF when the final 0-length chunk is read. +// +// newChunkedReader is not needed by normal applications. The http package +// automatically decodes chunking when reading response bodies. +func newChunkedReader(r io.Reader) io.Reader { + br, ok := r.(*bufio.Reader) + if !ok { + br = bufio.NewReader(r) + } + return &chunkedReader{r: br} +} + +type chunkedReader struct { + r *bufio.Reader + n uint64 // unread bytes in chunk + err error +} + +func (cr *chunkedReader) beginChunk() { + // chunk-size CRLF + var line string + line, cr.err = readLine(cr.r) + if cr.err != nil { + return + } + cr.n, cr.err = strconv.Btoui64(line, 16) + if cr.err != nil { + return + } + if cr.n == 0 { + cr.err = io.EOF + } +} + +func (cr *chunkedReader) Read(b []uint8) (n int, err error) { + if cr.err != nil { + return 0, cr.err + } + if cr.n == 0 { + cr.beginChunk() + if cr.err != nil { + return 0, cr.err + } + } + if uint64(len(b)) > cr.n { + b = b[0:cr.n] + } + n, cr.err = cr.r.Read(b) + cr.n -= uint64(n) + if cr.n == 0 && cr.err == nil { + // end of chunk (CRLF) + b := make([]byte, 2) + if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { + if b[0] != '\r' || b[1] != '\n' { + cr.err = errors.New("malformed chunked encoding") + } + } + } + return n, cr.err +} + +// Read a line of bytes (up to \n) from b. +// Give up if the line exceeds maxLineLength. +// The returned bytes are a pointer into storage in +// the bufio, so they are only valid until the next bufio read. +func readLineBytes(b *bufio.Reader) (p []byte, err error) { + if p, err = b.ReadSlice('\n'); err != nil { + // We always know when EOF is coming. + // If the caller asked for a line, there should be a line. + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if err == bufio.ErrBufferFull { + err = ErrLineTooLong + } + return nil, err + } + if len(p) >= maxLineLength { + return nil, ErrLineTooLong + } + + // Chop off trailing white space. + p = bytes.TrimRight(p, " \r\t\n") + + return p, nil +} + +// readLineBytes, but convert the bytes into a string. +func readLine(b *bufio.Reader) (s string, err error) { + p, e := readLineBytes(b) + if e != nil { + return "", e + } + return string(p), nil +} + +// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP +// "chunked" format before writing them to w. Closing the returned chunkedWriter +// sends the final 0-length chunk that marks the end of the stream. +// +// newChunkedWriter is not needed by normal applications. The http +// package adds chunking automatically if handlers don't set a +// Content-Length header. Using newChunkedWriter inside a handler +// would result in double chunking or chunking with a Content-Length +// length, both of which are wrong. func newChunkedWriter(w io.Writer) io.WriteCloser { return &chunkedWriter{w} } -// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer -// Encoding wire format to the underlying Wire writer. +// Writing to chunkedWriter translates to writing in HTTP chunked Transfer +// Encoding wire format to the underlying Wire chunkedWriter. type chunkedWriter struct { Wire io.Writer } @@ -51,7 +168,3 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } - -func newChunkedReader(r *bufio.Reader) io.Reader { - return &chunkedReader{r: r} -} diff --git a/libgo/go/net/http/chunked_test.go b/libgo/go/net/http/chunked_test.go new file mode 100644 index 0000000..b77ee2f --- /dev/null +++ b/libgo/go/net/http/chunked_test.go @@ -0,0 +1,39 @@ +// Copyright 2011 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. + +// This code is duplicated in httputil/chunked_test.go. +// Please make any changes in both files. + +package http + +import ( + "bytes" + "io/ioutil" + "testing" +) + +func TestChunk(t *testing.T) { + var b bytes.Buffer + + w := newChunkedWriter(&b) + const chunk1 = "hello, " + const chunk2 = "world! 0123456789abcdef" + w.Write([]byte(chunk1)) + w.Write([]byte(chunk2)) + w.Close() + + if g, e := b.String(), "7\r\nhello, \r\n17\r\nworld! 0123456789abcdef\r\n0\r\n"; g != e { + t.Fatalf("chunk writer wrote %q; want %q", g, e) + } + + r := newChunkedReader(&b) + data, err := ioutil.ReadAll(r) + if err != nil { + t.Logf(`data: "%s"`, data) + t.Fatalf("ReadAll from reader: %v", err) + } + if g, e := string(data), chunk1+chunk2; g != e { + t.Errorf("chunk reader read %q; want %q", g, e) + } +} diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index d224380..57a9dd9 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -26,6 +26,31 @@ var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "User-agent: go\nDisallow: /something/") }) +// pedanticReadAll works like ioutil.ReadAll but additionally +// verifies that r obeys the documented io.Reader contract. +func pedanticReadAll(r io.Reader) (b []byte, err error) { + var bufa [64]byte + buf := bufa[:] + for { + n, err := r.Read(buf) + if n == 0 && err == nil { + return nil, fmt.Errorf("Read: n=0 with err=nil") + } + b = append(b, buf[:n]...) + if err == io.EOF { + n, err := r.Read(buf) + if n != 0 || err != io.EOF { + return nil, fmt.Errorf("Read: n=%d err=%#v after EOF", n, err) + } + return b, nil + } + if err != nil { + return b, err + } + } + panic("unreachable") +} + func TestClient(t *testing.T) { ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() @@ -33,7 +58,7 @@ func TestClient(t *testing.T) { r, err := Get(ts.URL) var b []byte if err == nil { - b, err = ioutil.ReadAll(r.Body) + b, err = pedanticReadAll(r.Body) r.Body.Close() } if err != nil { diff --git a/libgo/go/net/http/fcgi/child.go b/libgo/go/net/http/fcgi/child.go index 7b56395..529440c 100644 --- a/libgo/go/net/http/fcgi/child.go +++ b/libgo/go/net/http/fcgi/child.go @@ -7,6 +7,7 @@ package fcgi // This file implements FastCGI from the perspective of a child process. import ( + "errors" "fmt" "io" "net" @@ -123,89 +124,101 @@ func (r *response) Close() error { } type child struct { - conn *conn - handler http.Handler + conn *conn + handler http.Handler + requests map[uint16]*request // keyed by request ID } -func newChild(rwc net.Conn, handler http.Handler) *child { - return &child{newConn(rwc), handler} +func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child { + return &child{ + conn: newConn(rwc), + handler: handler, + requests: make(map[uint16]*request), + } } func (c *child) serve() { - requests := map[uint16]*request{} defer c.conn.Close() var rec record - var br beginRequest for { if err := rec.read(c.conn.rwc); err != nil { return } - - req, ok := requests[rec.h.Id] - if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { - // The spec says to ignore unknown request IDs. - continue - } - if ok && rec.h.Type == typeBeginRequest { - // The server is trying to begin a request with the same ID - // as an in-progress request. This is an error. + if err := c.handleRecord(&rec); err != nil { return } + } +} - switch rec.h.Type { - case typeBeginRequest: - if err := br.read(rec.content()); err != nil { - return - } - if br.role != roleResponder { - c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) - break - } - requests[rec.h.Id] = newRequest(rec.h.Id, br.flags) - case typeParams: - // NOTE(eds): Technically a key-value pair can straddle the boundary - // between two packets. We buffer until we've received all parameters. - if len(rec.content()) > 0 { - req.rawParams = append(req.rawParams, rec.content()...) - break - } - req.parseParams() - case typeStdin: - content := rec.content() - if req.pw == nil { - var body io.ReadCloser - if len(content) > 0 { - // body could be an io.LimitReader, but it shouldn't matter - // as long as both sides are behaving. - body, req.pw = io.Pipe() - } - go c.serveRequest(req, body) - } +var errCloseConn = errors.New("fcgi: connection should be closed") + +func (c *child) handleRecord(rec *record) error { + req, ok := c.requests[rec.h.Id] + if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues { + // The spec says to ignore unknown request IDs. + return nil + } + if ok && rec.h.Type == typeBeginRequest { + // The server is trying to begin a request with the same ID + // as an in-progress request. This is an error. + return errors.New("fcgi: received ID that is already in-flight") + } + + switch rec.h.Type { + case typeBeginRequest: + var br beginRequest + if err := br.read(rec.content()); err != nil { + return err + } + if br.role != roleResponder { + c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole) + return nil + } + c.requests[rec.h.Id] = newRequest(rec.h.Id, br.flags) + case typeParams: + // NOTE(eds): Technically a key-value pair can straddle the boundary + // between two packets. We buffer until we've received all parameters. + if len(rec.content()) > 0 { + req.rawParams = append(req.rawParams, rec.content()...) + return nil + } + req.parseParams() + case typeStdin: + content := rec.content() + if req.pw == nil { + var body io.ReadCloser if len(content) > 0 { - // TODO(eds): This blocks until the handler reads from the pipe. - // If the handler takes a long time, it might be a problem. - req.pw.Write(content) - } else if req.pw != nil { - req.pw.Close() - } - case typeGetValues: - values := map[string]string{"FCGI_MPXS_CONNS": "1"} - c.conn.writePairs(0, typeGetValuesResult, values) - case typeData: - // If the filter role is implemented, read the data stream here. - case typeAbortRequest: - delete(requests, rec.h.Id) - c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) - if !req.keepConn { - // connection will close upon return - return + // body could be an io.LimitReader, but it shouldn't matter + // as long as both sides are behaving. + body, req.pw = io.Pipe() } - default: - b := make([]byte, 8) - b[0] = rec.h.Type - c.conn.writeRecord(typeUnknownType, 0, b) + go c.serveRequest(req, body) + } + if len(content) > 0 { + // TODO(eds): This blocks until the handler reads from the pipe. + // If the handler takes a long time, it might be a problem. + req.pw.Write(content) + } else if req.pw != nil { + req.pw.Close() + } + case typeGetValues: + values := map[string]string{"FCGI_MPXS_CONNS": "1"} + c.conn.writePairs(typeGetValuesResult, 0, values) + case typeData: + // If the filter role is implemented, read the data stream here. + case typeAbortRequest: + delete(c.requests, rec.h.Id) + c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete) + if !req.keepConn { + // connection will close upon return + return errCloseConn } + default: + b := make([]byte, 8) + b[0] = byte(rec.h.Type) + c.conn.writeRecord(typeUnknownType, 0, b) } + return nil } func (c *child) serveRequest(req *request, body io.ReadCloser) { diff --git a/libgo/go/net/http/fcgi/fcgi.go b/libgo/go/net/http/fcgi/fcgi.go index 70cf781..d35aa84 100644 --- a/libgo/go/net/http/fcgi/fcgi.go +++ b/libgo/go/net/http/fcgi/fcgi.go @@ -19,19 +19,22 @@ import ( "sync" ) +// recType is a record type, as defined by +// http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S8 +type recType uint8 + const ( - // Packet Types - typeBeginRequest = iota + 1 - typeAbortRequest - typeEndRequest - typeParams - typeStdin - typeStdout - typeStderr - typeData - typeGetValues - typeGetValuesResult - typeUnknownType + typeBeginRequest recType = 1 + typeAbortRequest recType = 2 + typeEndRequest recType = 3 + typeParams recType = 4 + typeStdin recType = 5 + typeStdout recType = 6 + typeStderr recType = 7 + typeData recType = 8 + typeGetValues recType = 9 + typeGetValuesResult recType = 10 + typeUnknownType recType = 11 ) // keep the connection between web-server and responder open after request @@ -59,7 +62,7 @@ const headerLen = 8 type header struct { Version uint8 - Type uint8 + Type recType Id uint16 ContentLength uint16 PaddingLength uint8 @@ -85,7 +88,7 @@ func (br *beginRequest) read(content []byte) error { // not synchronized because we don't care what the contents are var pad [maxPad]byte -func (h *header) init(recType uint8, reqId uint16, contentLength int) { +func (h *header) init(recType recType, reqId uint16, contentLength int) { h.Version = 1 h.Type = recType h.Id = reqId @@ -137,7 +140,7 @@ func (r *record) content() []byte { } // writeRecord writes and sends a single record. -func (c *conn) writeRecord(recType uint8, reqId uint16, b []byte) error { +func (c *conn) writeRecord(recType recType, reqId uint16, b []byte) error { c.mutex.Lock() defer c.mutex.Unlock() c.buf.Reset() @@ -167,12 +170,12 @@ func (c *conn) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8 return c.writeRecord(typeEndRequest, reqId, b) } -func (c *conn) writePairs(recType uint8, reqId uint16, pairs map[string]string) error { +func (c *conn) writePairs(recType recType, reqId uint16, pairs map[string]string) error { w := newWriter(c, recType, reqId) b := make([]byte, 8) for k, v := range pairs { n := encodeSize(b, uint32(len(k))) - n += encodeSize(b[n:], uint32(len(k))) + n += encodeSize(b[n:], uint32(len(v))) if _, err := w.Write(b[:n]); err != nil { return err } @@ -235,7 +238,7 @@ func (w *bufWriter) Close() error { return w.closer.Close() } -func newWriter(c *conn, recType uint8, reqId uint16) *bufWriter { +func newWriter(c *conn, recType recType, reqId uint16) *bufWriter { s := &streamWriter{c: c, recType: recType, reqId: reqId} w, _ := bufio.NewWriterSize(s, maxWrite) return &bufWriter{s, w} @@ -245,7 +248,7 @@ func newWriter(c *conn, recType uint8, reqId uint16) *bufWriter { // It only writes maxWrite bytes at a time. type streamWriter struct { c *conn - recType uint8 + recType recType reqId uint16 } diff --git a/libgo/go/net/http/fcgi/fcgi_test.go b/libgo/go/net/http/fcgi/fcgi_test.go index e42f8ef..6c7e1a9 100644 --- a/libgo/go/net/http/fcgi/fcgi_test.go +++ b/libgo/go/net/http/fcgi/fcgi_test.go @@ -6,6 +6,7 @@ package fcgi import ( "bytes" + "errors" "io" "testing" ) @@ -40,25 +41,25 @@ func TestSize(t *testing.T) { var streamTests = []struct { desc string - recType uint8 + recType recType reqId uint16 content []byte raw []byte }{ {"single record", typeStdout, 1, nil, - []byte{1, typeStdout, 0, 1, 0, 0, 0, 0}, + []byte{1, byte(typeStdout), 0, 1, 0, 0, 0, 0}, }, // this data will have to be split into two records {"two records", typeStdin, 300, make([]byte, 66000), bytes.Join([][]byte{ // header for the first record - {1, typeStdin, 0x01, 0x2C, 0xFF, 0xFF, 1, 0}, + {1, byte(typeStdin), 0x01, 0x2C, 0xFF, 0xFF, 1, 0}, make([]byte, 65536), // header for the second - {1, typeStdin, 0x01, 0x2C, 0x01, 0xD1, 7, 0}, + {1, byte(typeStdin), 0x01, 0x2C, 0x01, 0xD1, 7, 0}, make([]byte, 472), // header for the empty record - {1, typeStdin, 0x01, 0x2C, 0, 0, 0, 0}, + {1, byte(typeStdin), 0x01, 0x2C, 0, 0, 0, 0}, }, nil), }, @@ -111,3 +112,39 @@ outer: } } } + +type writeOnlyConn struct { + buf []byte +} + +func (c *writeOnlyConn) Write(p []byte) (int, error) { + c.buf = append(c.buf, p...) + return len(p), nil +} + +func (c *writeOnlyConn) Read(p []byte) (int, error) { + return 0, errors.New("conn is write-only") +} + +func (c *writeOnlyConn) Close() error { + return nil +} + +func TestGetValues(t *testing.T) { + var rec record + rec.h.Type = typeGetValues + + wc := new(writeOnlyConn) + c := newChild(wc, nil) + err := c.handleRecord(&rec) + if err != nil { + t.Fatalf("handleRecord: %v", err) + } + + const want = "\x01\n\x00\x00\x00\x12\x06\x00" + + "\x0f\x01FCGI_MPXS_CONNS1" + + "\x00\x00\x00\x00\x00\x00\x01\n\x00\x00\x00\x00\x00\x00" + if got := string(wc.buf); got != want { + t.Errorf(" got: %q\nwant: %q\n", got, want) + } +} diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go index 5f91ff5..5aadac1 100644 --- a/libgo/go/net/http/fs.go +++ b/libgo/go/net/http/fs.go @@ -22,13 +22,19 @@ import ( // A Dir implements http.FileSystem using the native file // system restricted to a specific directory tree. +// +// An empty Dir is treated as ".". type Dir string func (d Dir) Open(name string) (File, error) { if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 { return nil, errors.New("http: invalid character in file path") } - f, err := os.Open(filepath.Join(string(d), filepath.FromSlash(path.Clean("/"+name)))) + dir := string(d) + if dir == "" { + dir = "." + } + f, err := os.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))) if err != nil { return nil, err } diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go index e1a784c..6697189 100644 --- a/libgo/go/net/http/fs_test.go +++ b/libgo/go/net/http/fs_test.go @@ -208,6 +208,20 @@ func TestDirJoin(t *testing.T) { test(Dir("/etc/hosts"), "../") } +func TestEmptyDirOpenCWD(t *testing.T) { + test := func(d Dir) { + name := "fs_test.go" + f, err := d.Open(name) + if err != nil { + t.Fatalf("open of %s: %v", name, err) + } + defer f.Close() + } + test(Dir("")) + test(Dir(".")) + test(Dir("./")) +} + func TestServeFileContentType(t *testing.T) { const ctype = "icecream/chocolate" override := false @@ -247,6 +261,20 @@ func TestServeFileMimeType(t *testing.T) { } } +func TestServeFileFromCWD(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + ServeFile(w, r, "fs_test.go") + })) + defer ts.Close() + r, err := Get(ts.URL) + if err != nil { + t.Fatal(err) + } + if r.StatusCode != 200 { + t.Fatalf("expected 200 OK, got %s", r.Status) + } +} + func TestServeFileWithContentEncoding(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Encoding", "foo") diff --git a/libgo/go/net/http/httputil/chunked.go b/libgo/go/net/http/httputil/chunked.go index 34e47c7..69bcc0e 100644 --- a/libgo/go/net/http/httputil/chunked.go +++ b/libgo/go/net/http/httputil/chunked.go @@ -2,18 +2,126 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// The wire protocol for HTTP's "chunked" Transfer-Encoding. + +// This code is a duplicate of ../chunked.go with these edits: +// s/newChunked/NewChunked/g +// s/package http/package httputil/ +// Please make any changes in both files. + package httputil import ( "bufio" + "bytes" + "errors" "io" - "net/http" "strconv" - "strings" ) -// NewChunkedWriter returns a new writer that translates writes into HTTP -// "chunked" format before writing them to w. Closing the returned writer +const maxLineLength = 4096 // assumed <= bufio.defaultBufSize + +var ErrLineTooLong = errors.New("header line too long") + +// NewChunkedReader returns a new chunkedReader that translates the data read from r +// out of HTTP "chunked" format before returning it. +// The chunkedReader returns io.EOF when the final 0-length chunk is read. +// +// NewChunkedReader is not needed by normal applications. The http package +// automatically decodes chunking when reading response bodies. +func NewChunkedReader(r io.Reader) io.Reader { + br, ok := r.(*bufio.Reader) + if !ok { + br = bufio.NewReader(r) + } + return &chunkedReader{r: br} +} + +type chunkedReader struct { + r *bufio.Reader + n uint64 // unread bytes in chunk + err error +} + +func (cr *chunkedReader) beginChunk() { + // chunk-size CRLF + var line string + line, cr.err = readLine(cr.r) + if cr.err != nil { + return + } + cr.n, cr.err = strconv.Btoui64(line, 16) + if cr.err != nil { + return + } + if cr.n == 0 { + cr.err = io.EOF + } +} + +func (cr *chunkedReader) Read(b []uint8) (n int, err error) { + if cr.err != nil { + return 0, cr.err + } + if cr.n == 0 { + cr.beginChunk() + if cr.err != nil { + return 0, cr.err + } + } + if uint64(len(b)) > cr.n { + b = b[0:cr.n] + } + n, cr.err = cr.r.Read(b) + cr.n -= uint64(n) + if cr.n == 0 && cr.err == nil { + // end of chunk (CRLF) + b := make([]byte, 2) + if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { + if b[0] != '\r' || b[1] != '\n' { + cr.err = errors.New("malformed chunked encoding") + } + } + } + return n, cr.err +} + +// Read a line of bytes (up to \n) from b. +// Give up if the line exceeds maxLineLength. +// The returned bytes are a pointer into storage in +// the bufio, so they are only valid until the next bufio read. +func readLineBytes(b *bufio.Reader) (p []byte, err error) { + if p, err = b.ReadSlice('\n'); err != nil { + // We always know when EOF is coming. + // If the caller asked for a line, there should be a line. + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if err == bufio.ErrBufferFull { + err = ErrLineTooLong + } + return nil, err + } + if len(p) >= maxLineLength { + return nil, ErrLineTooLong + } + + // Chop off trailing white space. + p = bytes.TrimRight(p, " \r\t\n") + + return p, nil +} + +// readLineBytes, but convert the bytes into a string. +func readLine(b *bufio.Reader) (s string, err error) { + p, e := readLineBytes(b) + if e != nil { + return "", e + } + return string(p), nil +} + +// NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP +// "chunked" format before writing them to w. Closing the returned chunkedWriter // sends the final 0-length chunk that marks the end of the stream. // // NewChunkedWriter is not needed by normal applications. The http @@ -25,8 +133,8 @@ func NewChunkedWriter(w io.Writer) io.WriteCloser { return &chunkedWriter{w} } -// Writing to ChunkedWriter translates to writing in HTTP chunked Transfer -// Encoding wire format to the underlying Wire writer. +// Writing to chunkedWriter translates to writing in HTTP chunked Transfer +// Encoding wire format to the underlying Wire chunkedWriter. type chunkedWriter struct { Wire io.Writer } @@ -62,23 +170,3 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } - -// NewChunkedReader returns a new reader that translates the data read from r -// out of HTTP "chunked" format before returning it. -// The reader returns io.EOF when the final 0-length chunk is read. -// -// NewChunkedReader is not needed by normal applications. The http package -// automatically decodes chunking when reading response bodies. -func NewChunkedReader(r io.Reader) io.Reader { - // This is a bit of a hack so we don't have to copy chunkedReader into - // httputil. It's a bit more complex than chunkedWriter, which is copied - // above. - req, err := http.ReadRequest(bufio.NewReader(io.MultiReader( - strings.NewReader("POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"), - r, - strings.NewReader("\r\n")))) - if err != nil { - panic("bad fake request: " + err.Error()) - } - return req.Body -} diff --git a/libgo/go/net/http/httputil/chunked_test.go b/libgo/go/net/http/httputil/chunked_test.go index 258d39b9..155a32b 100644 --- a/libgo/go/net/http/httputil/chunked_test.go +++ b/libgo/go/net/http/httputil/chunked_test.go @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// This code is a duplicate of ../chunked_test.go with these edits: +// s/newChunked/NewChunked/g +// s/package http/package httputil/ +// Please make any changes in both files. + package httputil import ( @@ -27,7 +32,8 @@ func TestChunk(t *testing.T) { r := NewChunkedReader(&b) data, err := ioutil.ReadAll(r) if err != nil { - t.Fatalf("ReadAll from NewChunkedReader: %v", err) + t.Logf(`data: "%s"`, data) + t.Fatalf("ReadAll from reader: %v", err) } if g, e := string(data), chunk1+chunk2; g != e { t.Errorf("chunk reader read %q; want %q", g, e) diff --git a/libgo/go/net/http/httputil/persist.go b/libgo/go/net/http/httputil/persist.go index d7b67011..1266bd3 100644 --- a/libgo/go/net/http/httputil/persist.go +++ b/libgo/go/net/http/httputil/persist.go @@ -22,6 +22,10 @@ var ( ErrPipeline = &http.ProtocolError{"pipeline error"} ) +// This is an API usage error - the local side is closed. +// ErrPersistEOF (above) reports that the remote side is closed. +var errClosed = errors.New("i/o operation on closed connection") + // A ServerConn reads requests and sends responses over an underlying // connection, until the HTTP keepalive logic commands an end. ServerConn // also allows hijacking the underlying connection by calling Hijack @@ -108,7 +112,7 @@ func (sc *ServerConn) Read() (req *http.Request, err error) { } if sc.r == nil { // connection closed by user in the meantime defer sc.lk.Unlock() - return nil, os.EBADF + return nil, errClosed } r := sc.r lastbody := sc.lastbody @@ -313,7 +317,7 @@ func (cc *ClientConn) Write(req *http.Request) (err error) { } if cc.c == nil { // connection closed by user in the meantime defer cc.lk.Unlock() - return os.EBADF + return errClosed } c := cc.c if req.Close { @@ -369,7 +373,7 @@ func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) { } if cc.r == nil { // connection closed by user in the meantime defer cc.lk.Unlock() - return nil, os.EBADF + return nil, errClosed } r := cc.r lastbody := cc.lastbody diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go index 2219d43..c64fff6 100644 --- a/libgo/go/net/http/readrequest_test.go +++ b/libgo/go/net/http/readrequest_test.go @@ -70,7 +70,6 @@ var reqTests = []reqTest{ Close: false, ContentLength: 7, Host: "www.techcrunch.com", - Form: url.Values{}, }, "abcdef\n", @@ -94,10 +93,10 @@ var reqTests = []reqTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, Close: false, ContentLength: 0, Host: "foo.com", - Form: url.Values{}, }, noBody, @@ -131,7 +130,6 @@ var reqTests = []reqTest{ Close: false, ContentLength: 0, Host: "test", - Form: url.Values{}, }, noBody, @@ -180,9 +178,9 @@ var reqTests = []reqTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, ContentLength: -1, Host: "foo.com", - Form: url.Values{}, }, "foobar", diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 4410ca1..6617849 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -19,12 +19,10 @@ import ( "mime/multipart" "net/textproto" "net/url" - "strconv" "strings" ) const ( - maxLineLength = 4096 // assumed <= bufio.defaultBufSize maxValueLength = 4096 maxHeaderLines = 1024 chunkSize = 4 << 10 // 4 KB chunks @@ -43,7 +41,6 @@ type ProtocolError struct { func (err *ProtocolError) Error() string { return err.ErrorString } var ( - ErrLineTooLong = &ProtocolError{"header line too long"} ErrHeaderTooLong = &ProtocolError{"header too long"} ErrShortBody = &ProtocolError{"entity body too short"} ErrNotSupported = &ProtocolError{"feature not supported"} @@ -375,44 +372,6 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err return nil } -// Read a line of bytes (up to \n) from b. -// Give up if the line exceeds maxLineLength. -// The returned bytes are a pointer into storage in -// the bufio, so they are only valid until the next bufio read. -func readLineBytes(b *bufio.Reader) (p []byte, err error) { - if p, err = b.ReadSlice('\n'); err != nil { - // We always know when EOF is coming. - // If the caller asked for a line, there should be a line. - if err == io.EOF { - err = io.ErrUnexpectedEOF - } else if err == bufio.ErrBufferFull { - err = ErrLineTooLong - } - return nil, err - } - if len(p) >= maxLineLength { - return nil, ErrLineTooLong - } - - // Chop off trailing white space. - var i int - for i = len(p); i > 0; i-- { - if c := p[i-1]; c != ' ' && c != '\r' && c != '\t' && c != '\n' { - break - } - } - return p[0:i], nil -} - -// readLineBytes, but convert the bytes into a string. -func readLine(b *bufio.Reader) (s string, err error) { - p, e := readLineBytes(b) - if e != nil { - return "", e - } - return string(p), nil -} - // Convert decimal at s[i:len(s)] to integer, // returning value, string position where the digits stopped, // and whether there was a valid number (digits, not too big). @@ -448,55 +407,6 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) { return major, minor, true } -type chunkedReader struct { - r *bufio.Reader - n uint64 // unread bytes in chunk - err error -} - -func (cr *chunkedReader) beginChunk() { - // chunk-size CRLF - var line string - line, cr.err = readLine(cr.r) - if cr.err != nil { - return - } - cr.n, cr.err = strconv.Btoui64(line, 16) - if cr.err != nil { - return - } - if cr.n == 0 { - cr.err = io.EOF - } -} - -func (cr *chunkedReader) Read(b []uint8) (n int, err error) { - if cr.err != nil { - return 0, cr.err - } - if cr.n == 0 { - cr.beginChunk() - if cr.err != nil { - return 0, cr.err - } - } - if uint64(len(b)) > cr.n { - b = b[0:cr.n] - } - n, cr.err = cr.r.Read(b) - cr.n -= uint64(n) - if cr.n == 0 && cr.err == nil { - // end of chunk (CRLF) - b := make([]byte, 2) - if _, cr.err = io.ReadFull(cr.r, b); cr.err == nil { - if b[0] != '\r' || b[1] != '\n' { - cr.err = errors.New("malformed chunked encoding") - } - } - } - return n, cr.err -} - // NewRequest returns a new Request given a method, URL, and optional body. func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { u, err := url.Parse(urlStr) diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go index be717aa..e5d0169 100644 --- a/libgo/go/net/http/response_test.go +++ b/libgo/go/net/http/response_test.go @@ -65,6 +65,7 @@ var respTests = []respTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, Request: dummyReq("GET"), Close: true, ContentLength: -1, @@ -85,6 +86,7 @@ var respTests = []respTest{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, + Header: Header{}, Request: dummyReq("GET"), Close: false, ContentLength: 0, @@ -315,7 +317,7 @@ func TestReadResponseCloseInMiddle(t *testing.T) { } var wr io.Writer = &buf if test.chunked { - wr = &chunkedWriter{wr} + wr = newChunkedWriter(wr) } if test.compressed { buf.WriteString("Content-Encoding: gzip\r\n") diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index e278396..97a0b13 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -1077,6 +1077,31 @@ func TestClientWriteShutdown(t *testing.T) { } } +// Tests that chunked server responses that write 1 byte at a time are +// buffered before chunk headers are added, not after chunk headers. +func TestServerBufferedChunking(t *testing.T) { + if true { + t.Logf("Skipping known broken test; see Issue 2357") + return + } + conn := new(testConn) + conn.readBuf.Write([]byte("GET / HTTP/1.1\r\n\r\n")) + done := make(chan bool) + ls := &oneConnListener{conn} + go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) { + defer close(done) + rw.Header().Set("Content-Type", "text/plain") // prevent sniffing, which buffers + rw.Write([]byte{'x'}) + rw.Write([]byte{'y'}) + rw.Write([]byte{'z'}) + })) + <-done + if !bytes.HasSuffix(conn.writeBuf.Bytes(), []byte("\r\n\r\n3\r\nxyz\r\n0\r\n\r\n")) { + t.Errorf("response didn't end with a single 3 byte 'xyz' chunk; got:\n%q", + conn.writeBuf.Bytes()) + } +} + // goTimeout runs f, failing t if f takes more than ns to complete. func goTimeout(t *testing.T, ns int64, f func()) { ch := make(chan bool, 2) @@ -1120,7 +1145,7 @@ func TestAcceptMaxFds(t *testing.T) { ln := &errorListener{[]error{ &net.OpError{ Op: "accept", - Err: os.Errno(syscall.EMFILE), + Err: syscall.EMFILE, }}} err := Serve(ln, HandlerFunc(HandlerFunc(func(ResponseWriter, *Request) {}))) if err != io.EOF { diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index 8c48894..7221d25 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -149,11 +149,13 @@ type writerOnly struct { } func (w *response) ReadFrom(src io.Reader) (n int64, err error) { - // Flush before checking w.chunking, as Flush will call - // WriteHeader if it hasn't been called yet, and WriteHeader - // is what sets w.chunking. - w.Flush() + // Call WriteHeader before checking w.chunking if it hasn't + // been called yet, since WriteHeader is what sets w.chunking. + if !w.wroteHeader { + w.WriteHeader(StatusOK) + } if !w.chunking && w.bodyAllowed() && !w.needSniff { + w.Flush() if rf, ok := w.conn.rwc.(io.ReaderFrom); ok { n, err = rf.ReadFrom(src) w.written += n diff --git a/libgo/go/net/http/sniff_test.go b/libgo/go/net/http/sniff_test.go index a414e64..86744ee 100644 --- a/libgo/go/net/http/sniff_test.go +++ b/libgo/go/net/http/sniff_test.go @@ -6,6 +6,7 @@ package http_test import ( "bytes" + "io" "io/ioutil" "log" . "net/http" @@ -79,3 +80,35 @@ func TestServerContentType(t *testing.T) { resp.Body.Close() } } + +func TestContentTypeWithCopy(t *testing.T) { + const ( + input = "\n<html>\n\t<head>\n" + expected = "text/html; charset=utf-8" + ) + + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + // Use io.Copy from a bytes.Buffer to trigger ReadFrom. + buf := bytes.NewBuffer([]byte(input)) + n, err := io.Copy(w, buf) + if int(n) != len(input) || err != nil { + t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) + } + })) + defer ts.Close() + + resp, err := Get(ts.URL) + if err != nil { + t.Fatalf("Get: %v", err) + } + if ct := resp.Header.Get("Content-Type"); ct != expected { + t.Errorf("Content-Type = %q, want %q", ct, expected) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Errorf("reading body: %v", err) + } else if !bytes.Equal(data, []byte(input)) { + t.Errorf("data is %q, want %q", data, input) + } + resp.Body.Close() +} diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go index 2670d77..d25c8fc 100644 --- a/libgo/go/net/http/transfer.go +++ b/libgo/go/net/http/transfer.go @@ -537,7 +537,9 @@ func (b *body) Read(p []byte) (n int, err error) { // Read the final trailer once we hit EOF. if err == io.EOF && b.hdr != nil { - err = b.readTrailer() + if e := b.readTrailer(); e != nil { + err = e + } b.hdr = nil } return n, err diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index da5244b..e622e41 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -504,7 +504,7 @@ func (pc *persistConn) expectingResponse() bool { var remoteSideClosedFunc func(error) bool // or nil to use default func remoteSideClosed(err error) bool { - if err == io.EOF || err == os.EINVAL { + if err == io.EOF { return true } if remoteSideClosedFunc != nil { diff --git a/libgo/go/net/http/transport_windows.go b/libgo/go/net/http/transport_windows.go index 2a20d22..c9ef2c2 100644 --- a/libgo/go/net/http/transport_windows.go +++ b/libgo/go/net/http/transport_windows.go @@ -6,14 +6,14 @@ package http import ( "net" - "os" + "syscall" ) func init() { remoteSideClosedFunc = func(err error) (out bool) { op, ok := err.(*net.OpError) - if ok && op.Op == "WSARecv" && op.Net == "tcp" && op.Err == os.Errno(10058) { - // TODO(bradfitz): find the symbol for 10058 + if ok && op.Op == "WSARecv" && op.Net == "tcp" && op.Err == syscall.Errno(10058) { + // TODO(brainman,rsc): Fix whatever is generating this. return true } return false diff --git a/libgo/go/net/interface_bsd.go b/libgo/go/net/interface_bsd.go index b026e01..e896d43 100644 --- a/libgo/go/net/interface_bsd.go +++ b/libgo/go/net/interface_bsd.go @@ -20,18 +20,18 @@ import ( func interfaceTable(ifindex int) ([]Interface, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ift []Interface ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -55,7 +55,7 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, error) { var ift []Interface sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } @@ -110,18 +110,18 @@ func linkFlags(rawFlags int32) Flags { func interfaceAddrTable(ifindex int) ([]Addr, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ifat []Addr ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -145,7 +145,7 @@ func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, error) { var ifat []Addr sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } diff --git a/libgo/go/net/interface_darwin.go b/libgo/go/net/interface_darwin.go index 1472afb..2da447a 100644 --- a/libgo/go/net/interface_darwin.go +++ b/libgo/go/net/interface_darwin.go @@ -17,18 +17,18 @@ import ( func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ifmat []Addr ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST2, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -52,7 +52,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) var ifmat []Addr sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } diff --git a/libgo/go/net/interface_freebsd.go b/libgo/go/net/interface_freebsd.go index b0274f6..a12877e 100644 --- a/libgo/go/net/interface_freebsd.go +++ b/libgo/go/net/interface_freebsd.go @@ -17,18 +17,18 @@ import ( func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) { var ( tab []byte - e int + e error msgs []syscall.RoutingMessage ifmat []Addr ) tab, e = syscall.RouteRIB(syscall.NET_RT_IFMALIST, ifindex) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route rib", e) } msgs, e = syscall.ParseRoutingMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route message", e) } @@ -52,7 +52,7 @@ func newMulticastAddr(m *syscall.InterfaceMulticastAddrMessage) ([]Addr, error) var ifmat []Addr sas, e := syscall.ParseRoutingSockaddr(m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("route sockaddr", e) } diff --git a/libgo/go/net/interface_linux.go b/libgo/go/net/interface_linux.go index cd0339d..96db718 100644 --- a/libgo/go/net/interface_linux.go +++ b/libgo/go/net/interface_linux.go @@ -21,16 +21,16 @@ func interfaceTable(ifindex int) ([]Interface, error) { ift []Interface tab []byte msgs []syscall.NetlinkMessage - e int + e error ) tab, e = syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink rib", e) } msgs, e = syscall.ParseNetlinkMessage(tab) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink message", e) } @@ -42,7 +42,7 @@ func interfaceTable(ifindex int) ([]Interface, error) { ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) if ifindex == 0 || ifindex == int(ifim.Index) { attrs, e := syscall.ParseNetlinkRouteAttr(&m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink routeattr", e) } ifi := newLink(attrs, ifim) @@ -102,27 +102,19 @@ func linkFlags(rawFlags uint32) Flags { // for all network interfaces. Otherwise it returns addresses // for a specific interface. func interfaceAddrTable(ifindex int) ([]Addr, error) { - var ( - tab []byte - e int - err error - ifat []Addr - msgs []syscall.NetlinkMessage - ) - - tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) - if e != 0 { + tab, e := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC) + if e != nil { return nil, os.NewSyscallError("netlink rib", e) } - msgs, e = syscall.ParseNetlinkMessage(tab) - if e != 0 { + msgs, e := syscall.ParseNetlinkMessage(tab) + if e != nil { return nil, os.NewSyscallError("netlink message", e) } - ifat, err = addrTable(msgs, ifindex) - if err != nil { - return nil, err + ifat, e := addrTable(msgs, ifindex) + if e != nil { + return nil, e } return ifat, nil @@ -139,7 +131,7 @@ func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, error) { ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) if ifindex == 0 || ifindex == int(ifam.Index) { attrs, e := syscall.ParseNetlinkRouteAttr(&m) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("netlink routeattr", e) } ifat = append(ifat, newAddr(attrs, int(ifam.Family))...) diff --git a/libgo/go/net/interface_windows.go b/libgo/go/net/interface_windows.go index a1c8d95..2ed66cd 100644 --- a/libgo/go/net/interface_windows.go +++ b/libgo/go/net/interface_windows.go @@ -39,7 +39,7 @@ func getAdapterList() (*syscall.IpAdapterInfo, error) { func getInterfaceList() ([]syscall.InterfaceInfo, error) { s, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("Socket", e) } defer syscall.Closesocket(s) @@ -48,7 +48,7 @@ func getInterfaceList() ([]syscall.InterfaceInfo, error) { ret := uint32(0) size := uint32(unsafe.Sizeof(ii)) e = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("WSAIoctl", e) } c := ret / uint32(unsafe.Sizeof(ii[0])) diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index 716454d..9234f5a 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -9,7 +9,7 @@ package net var supportsIPv6, supportsIPv4map = probeIPv6Stack() func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { - if filter == anyaddr { + if filter == nil { // We'll take any IP address, but since the dialing code // does not yet try multiple addresses, prefer to use // an IPv4 address if possible. This is especially relevant @@ -113,7 +113,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err error) { // Try as an IP address. addr = ParseIP(host) if addr == nil { - filter := anyaddr + var filter func(IP) IP if net != "" && net[len(net)-1] == '4' { filter = ipv4only } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index d5b8f21..f0ca7da 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -33,8 +33,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { } for i := range probes { - s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) - if errno != 0 { + s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if err != nil { continue } defer closesocket(s) @@ -42,8 +42,8 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { if err != nil { continue } - errno = syscall.Bind(s, sa) - if errno != 0 { + err = syscall.Bind(s, sa) + if err != nil { continue } probes[i].ok = true diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go index 61d8a88..020871b 100644 --- a/libgo/go/net/lookup_windows.go +++ b/libgo/go/net/lookup_windows.go @@ -22,7 +22,7 @@ func lookupProtocol(name string) (proto int, err error) { protoentLock.Lock() defer protoentLock.Unlock() p, e := syscall.GetProtoByName(name) - if e != 0 { + if e != nil { return 0, os.NewSyscallError("GetProtoByName", e) } return int(p.Proto), nil @@ -44,7 +44,7 @@ func LookupIP(name string) (addrs []IP, err error) { hostentLock.Lock() defer hostentLock.Unlock() h, e := syscall.GetHostByName(name) - if e != 0 { + if e != nil { return nil, os.NewSyscallError("GetHostByName", e) } switch h.AddrType { @@ -71,7 +71,7 @@ func LookupPort(network, service string) (port int, err error) { serventLock.Lock() defer serventLock.Unlock() s, e := syscall.GetServByName(service, network) - if e != 0 { + if e != nil { return 0, os.NewSyscallError("GetServByName", e) } return int(syscall.Ntohs(s.Port)), nil @@ -81,7 +81,7 @@ func LookupCNAME(name string) (cname string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) if e != 0 { - return "", os.NewSyscallError("LookupCNAME", int(e)) + return "", os.NewSyscallError("LookupCNAME", e) } defer syscall.DnsRecordListFree(r, 1) if r != nil && r.Type == syscall.DNS_TYPE_CNAME { @@ -110,7 +110,7 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err var r *syscall.DNSRecord e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &r, nil) if e != 0 { - return "", nil, os.NewSyscallError("LookupSRV", int(e)) + return "", nil, os.NewSyscallError("LookupSRV", e) } defer syscall.DnsRecordListFree(r, 1) addrs = make([]*SRV, 0, 10) @@ -126,7 +126,7 @@ func LookupMX(name string) (mx []*MX, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil) if e != 0 { - return nil, os.NewSyscallError("LookupMX", int(e)) + return nil, os.NewSyscallError("LookupMX", e) } defer syscall.DnsRecordListFree(r, 1) mx = make([]*MX, 0, 10) @@ -142,7 +142,7 @@ func LookupTXT(name string) (txt []string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil) if e != 0 { - return nil, os.NewSyscallError("LookupTXT", int(e)) + return nil, os.NewSyscallError("LookupTXT", e) } defer syscall.DnsRecordListFree(r, 1) txt = make([]string, 0, 10) @@ -164,7 +164,7 @@ func LookupAddr(addr string) (name []string, err error) { var r *syscall.DNSRecord e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &r, nil) if e != 0 { - return nil, os.NewSyscallError("LookupAddr", int(e)) + return nil, os.NewSyscallError("LookupAddr", e) } defer syscall.DnsRecordListFree(r, 1) name = make([]string, 0, 10) diff --git a/libgo/go/net/newpollserver.go b/libgo/go/net/newpollserver.go index 9ad6f7b..035df4a 100644 --- a/libgo/go/net/newpollserver.go +++ b/libgo/go/net/newpollserver.go @@ -18,11 +18,10 @@ func newPollServer() (s *pollServer, err error) { if s.pr, s.pw, err = os.Pipe(); err != nil { return nil, err } - var e int - if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 { + if err = syscall.SetNonblock(s.pr.Fd(), true); err != nil { goto Errno } - if e = syscall.SetNonblock(s.pw.Fd(), true); e != 0 { + if err = syscall.SetNonblock(s.pw.Fd(), true); err != nil { goto Errno } if s.poll, err = newpollster(); err != nil { @@ -37,7 +36,7 @@ func newPollServer() (s *pollServer, err error) { return s, nil Errno: - err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)} + err = &os.PathError{"setnonblock", s.pr.Name(), err} Error: s.pr.Close() s.pw.Close() diff --git a/libgo/go/net/pipe.go b/libgo/go/net/pipe.go index b99e6e6..0ce7ccb 100644 --- a/libgo/go/net/pipe.go +++ b/libgo/go/net/pipe.go @@ -1,3 +1,7 @@ +// Copyright 2010 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 net import ( diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go index 36c7578..350abe4 100644 --- a/libgo/go/net/sendfile_linux.go +++ b/libgo/go/net/sendfile_linux.go @@ -62,18 +62,18 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { written += int64(n) remain -= int64(n) } - if n == 0 && errno == 0 { + if n == 0 && errno == nil { break } if errno == syscall.EAGAIN && c.wdeadline >= 0 { pollserver.WaitWrite(c) continue } - if errno != 0 { + if errno != nil { // This includes syscall.ENOSYS (no kernel // support) and syscall.EINVAL (fd types which // don't implement sendfile together) - err = &OpError{"sendfile", c.net, c.raddr, os.Errno(errno)} + err = &OpError{"sendfile", c.net, c.raddr, errno} break } } diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go index 0b31572..ee7ff8b 100644 --- a/libgo/go/net/sendfile_windows.go +++ b/libgo/go/net/sendfile_windows.go @@ -16,7 +16,7 @@ type sendfileOp struct { n uint32 } -func (o *sendfileOp) Submit() (errno int) { +func (o *sendfileOp) Submit() (err error) { return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) } diff --git a/libgo/go/net/sock.go b/libgo/go/net/sock.go index d9df02c..33f11f2 100644 --- a/libgo/go/net/sock.go +++ b/libgo/go/net/sock.go @@ -28,9 +28,9 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal // See ../syscall/exec.go for description of ForkLock. syscall.ForkLock.RLock() s, e := syscall.Socket(f, p, t) - if e != 0 { + if err != nil { syscall.ForkLock.RUnlock() - return nil, os.Errno(e) + return nil, err } syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() @@ -39,9 +39,9 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal if la != nil { e = syscall.Bind(s, la) - if e != 0 { + if e != nil { closesocket(s) - return nil, os.Errno(e) + return nil, e } } diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index a726b45..44890ba 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -250,9 +250,9 @@ func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err error) { return nil, err } errno := syscall.Listen(fd.sysfd, listenBacklog()) - if errno != 0 { + if errno != nil { closesocket(fd.sysfd) - return nil, &OpError{"listen", "tcp", laddr, os.Errno(errno)} + return nil, &OpError{"listen", "tcp", laddr, errno} } l = new(TCPListener) l.fd = fd diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 6ba692e..929f640 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -327,9 +327,9 @@ func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) { return nil, err } e1 := syscall.Listen(fd.sysfd, 8) // listenBacklog()); - if e1 != 0 { + if e1 != nil { closesocket(fd.sysfd) - return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Err: os.Errno(e1)} + return nil, &OpError{Op: "listen", Net: "unix", Addr: laddr, Err: e1} } return &UnixListener{fd, laddr.Name}, nil } diff --git a/libgo/go/os/dir_largefile.go b/libgo/go/os/dir_largefile.go index c723ec9..d6b4239 100644 --- a/libgo/go/os/dir_largefile.go +++ b/libgo/go/os/dir_largefile.go @@ -9,4 +9,4 @@ package os import "syscall" -func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir64_r") +func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno __asm__ ("readdir64_r") diff --git a/libgo/go/os/dir_regfile.go b/libgo/go/os/dir_regfile.go index 22fb5fe..7effdf7 100644 --- a/libgo/go/os/dir_regfile.go +++ b/libgo/go/os/dir_regfile.go @@ -9,4 +9,4 @@ package os import "syscall" -func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) int __asm__ ("readdir_r") +func libc_readdir_r(*syscall.DIR, *syscall.Dirent, **syscall.Dirent) syscall.Errno __asm__ ("readdir_r") diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go index a16bcf6..e4dff83 100644 --- a/libgo/go/os/dir_unix.go +++ b/libgo/go/os/dir_unix.go @@ -47,9 +47,9 @@ func (f *File) Readdirnames(n int) (names []string, err error) { // Refill the buffer if necessary if d.bufp >= d.nbuf { d.bufp = 0 - var errno int + var errno error d.nbuf, errno = syscall.ReadDirent(f.fd, d.buf) - if errno != 0 { + if errno != nil { return names, NewSyscallError("readdirent", errno) } if d.nbuf <= 0 { diff --git a/libgo/go/os/env.go b/libgo/go/os/env.go index 4844fa3..7e3f525 100644 --- a/libgo/go/os/env.go +++ b/libgo/go/os/env.go @@ -6,7 +6,10 @@ package os -func setenv_c(k, v string) +import ( + "errors" + "syscall" +) // Expand replaces ${var} or $var in the string based on the mapping function. // Invocations of undefined variables are replaced with the empty string. @@ -73,3 +76,47 @@ func getShellName(s string) (string, int) { } return s[:i], i } + +// ENOENV is the error indicating that an environment variable does not exist. +var ENOENV = errors.New("no such environment variable") + +// Getenverror retrieves the value of the environment variable named by the key. +// It returns the value and an error, if any. +func Getenverror(key string) (value string, err error) { + if len(key) == 0 { + return "", EINVAL + } + val, found := syscall.Getenv(key) + if !found { + return "", ENOENV + } + return val, nil +} + +// Getenv retrieves the value of the environment variable named by the key. +// It returns the value, which will be empty if the variable is not present. +func Getenv(key string) string { + v, _ := Getenverror(key) + return v +} + +// Setenv sets the value of the environment variable named by the key. +// It returns an error, if any. +func Setenv(key, value string) error { + err := syscall.Setenv(key, value) + if err != nil { + return NewSyscallError("setenv", err) + } + return nil +} + +// Clearenv deletes all environment variables. +func Clearenv() { + syscall.Clearenv() +} + +// Environ returns an array of strings representing the environment, +// in the form "key=value". +func Environ() []string { + return syscall.Environ() +} diff --git a/libgo/go/os/env_plan9.go b/libgo/go/os/env_plan9.go deleted file mode 100644 index 9757aa9..0000000 --- a/libgo/go/os/env_plan9.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2011 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. - -// Plan 9 environment variables. - -package os - -import ( - "errors" - "syscall" -) - -// ENOENV is the error indicating that an environment variable does not exist. -var ENOENV = errors.New("no such environment variable") - -// Getenverror retrieves the value of the environment variable named by the key. -// It returns the value and an error, if any. -func Getenverror(key string) (value string, err error) { - if len(key) == 0 { - return "", EINVAL - } - f, e := Open("/env/" + key) - if iserror(e) { - return "", ENOENV - } - defer f.Close() - - l, _ := f.Seek(0, 2) - f.Seek(0, 0) - buf := make([]byte, l) - n, e := f.Read(buf) - if iserror(e) { - return "", ENOENV - } - - if n > 0 && buf[n-1] == 0 { - buf = buf[:n-1] - } - return string(buf), nil -} - -// Getenv retrieves the value of the environment variable named by the key. -// It returns the value, which will be empty if the variable is not present. -func Getenv(key string) string { - v, _ := Getenverror(key) - return v -} - -// Setenv sets the value of the environment variable named by the key. -// It returns an error, if any. -func Setenv(key, value string) error { - if len(key) == 0 { - return EINVAL - } - - f, e := Create("/env/" + key) - if iserror(e) { - return e - } - defer f.Close() - - _, e = f.Write([]byte(value)) - return nil -} - -// Clearenv deletes all environment variables. -func Clearenv() { - syscall.RawSyscall(syscall.SYS_RFORK, syscall.RFCENVG, 0, 0) -} - -// Environ returns an array of strings representing the environment, -// in the form "key=value". -func Environ() []string { - env := make([]string, 0, 100) - - f, e := Open("/env") - if iserror(e) { - panic(e) - } - defer f.Close() - - names, e := f.Readdirnames(-1) - if iserror(e) { - panic(e) - } - - for _, k := range names { - if v, e := Getenverror(k); !iserror(e) { - env = append(env, k+"="+v) - } - } - return env[0:len(env)] -} - -// TempDir returns the default directory to use for temporary files. -func TempDir() string { - return "/tmp" -} diff --git a/libgo/go/os/env_unix.go b/libgo/go/os/env_unix.go deleted file mode 100644 index 01fd9d4..0000000 --- a/libgo/go/os/env_unix.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2010 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 darwin freebsd linux openbsd - -// Unix environment variables. - -package os - -import ( - "errors" - "sync" -) - -// ENOENV is the error indicating that an environment variable does not exist. -var ENOENV = errors.New("no such environment variable") - -var env map[string]string -var once sync.Once - -func copyenv() { - env = make(map[string]string) - for _, s := range Envs { - for j := 0; j < len(s); j++ { - if s[j] == '=' { - env[s[0:j]] = s[j+1:] - break - } - } - } -} - -var envLock sync.RWMutex - -// Getenverror retrieves the value of the environment variable named by the key. -// It returns the value and an error, if any. -func Getenverror(key string) (value string, err error) { - once.Do(copyenv) - - if len(key) == 0 { - return "", EINVAL - } - - envLock.RLock() - defer envLock.RUnlock() - - v, ok := env[key] - if !ok { - return "", ENOENV - } - return v, nil -} - -// Getenv retrieves the value of the environment variable named by the key. -// It returns the value, which will be empty if the variable is not present. -func Getenv(key string) string { - v, _ := Getenverror(key) - return v -} - -// Setenv sets the value of the environment variable named by the key. -// It returns an error, if any. -func Setenv(key, value string) error { - once.Do(copyenv) - if len(key) == 0 { - return EINVAL - } - - envLock.Lock() - defer envLock.Unlock() - - env[key] = value - setenv_c(key, value) // is a no-op if cgo isn't loaded - return nil -} - -// Clearenv deletes all environment variables. -func Clearenv() { - once.Do(copyenv) // prevent copyenv in Getenv/Setenv - - envLock.Lock() - defer envLock.Unlock() - - env = make(map[string]string) - - // TODO(bradfitz): pass through to C -} - -// Environ returns an array of strings representing the environment, -// in the form "key=value". -func Environ() []string { - once.Do(copyenv) - envLock.RLock() - defer envLock.RUnlock() - a := make([]string, len(env)) - i := 0 - for k, v := range env { - a[i] = k + "=" + v - i++ - } - return a -} - -// TempDir returns the default directory to use for temporary files. -func TempDir() string { - dir := Getenv("TMPDIR") - if dir == "" { - dir = "/tmp" - } - return dir -} diff --git a/libgo/go/os/env_windows.go b/libgo/go/os/env_windows.go deleted file mode 100644 index 4e90385..0000000 --- a/libgo/go/os/env_windows.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2010 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. - -// Windows environment variables. - -package os - -import ( - "errors" - "syscall" - "unicode/utf16" - "unsafe" -) - -// ENOENV is the error indicating that an environment variable does not exist. -var ENOENV = errors.New("no such environment variable") - -// Getenverror retrieves the value of the environment variable named by the key. -// It returns the value and an error, if any. -func Getenverror(key string) (value string, err error) { - b := make([]uint16, 100) - n, e := syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b))) - if n == 0 && e == syscall.ERROR_ENVVAR_NOT_FOUND { - return "", ENOENV - } - if n > uint32(len(b)) { - b = make([]uint16, n) - n, e = syscall.GetEnvironmentVariable(syscall.StringToUTF16Ptr(key), &b[0], uint32(len(b))) - if n > uint32(len(b)) { - n = 0 - } - } - if n == 0 { - return "", NewSyscallError("GetEnvironmentVariable", e) - } - return string(utf16.Decode(b[0:n])), nil -} - -// Getenv retrieves the value of the environment variable named by the key. -// It returns the value, which will be empty if the variable is not present. -func Getenv(key string) string { - v, _ := Getenverror(key) - return v -} - -// Setenv sets the value of the environment variable named by the key. -// It returns an error, if any. -func Setenv(key, value string) error { - var v *uint16 - if len(value) > 0 { - v = syscall.StringToUTF16Ptr(value) - } - e := syscall.SetEnvironmentVariable(syscall.StringToUTF16Ptr(key), v) - if e != 0 { - return NewSyscallError("SetEnvironmentVariable", e) - } - return nil -} - -// Clearenv deletes all environment variables. -func Clearenv() { - for _, s := range Environ() { - // Environment variables can begin with = - // so start looking for the separator = at j=1. - // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx - for j := 1; j < len(s); j++ { - if s[j] == '=' { - Setenv(s[0:j], "") - break - } - } - } -} - -// Environ returns an array of strings representing the environment, -// in the form "key=value". -func Environ() []string { - s, e := syscall.GetEnvironmentStrings() - if e != 0 { - return nil - } - defer syscall.FreeEnvironmentStrings(s) - r := make([]string, 0, 50) // Empty with room to grow. - for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(s)); true; i++ { - if p[i] == 0 { - // empty string marks the end - if i <= from { - break - } - r = append(r, string(utf16.Decode(p[from:i]))) - from = i + 1 - } - } - return r -} - -// TempDir returns the default directory to use for temporary files. -func TempDir() string { - const pathSep = '\\' - dirw := make([]uint16, syscall.MAX_PATH) - n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) - if n > uint32(len(dirw)) { - dirw = make([]uint16, n) - n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0]) - if n > uint32(len(dirw)) { - n = 0 - } - } - if n > 0 && dirw[n-1] == pathSep { - n-- - } - return string(utf16.Decode(dirw[0:n])) -} - -func init() { - var argc int32 - cmd := syscall.GetCommandLine() - argv, e := syscall.CommandLineToArgv(cmd, &argc) - if e != 0 { - return - } - defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv)))) - Args = make([]string, argc) - for i, v := range (*argv)[:argc] { - Args[i] = string(syscall.UTF16ToString((*v)[:])) - } -} diff --git a/libgo/go/os/error_plan9.go b/libgo/go/os/error_plan9.go index e087070..8f005ef 100644 --- a/libgo/go/os/error_plan9.go +++ b/libgo/go/os/error_plan9.go @@ -24,7 +24,7 @@ func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err } // NewSyscallError returns, as an error, a new SyscallError // with the given system call name and error details. // As a convenience, if err is nil, NewSyscallError returns nil. -func NewSyscallError(syscall string, err syscall.Error) error { +func NewSyscallError(syscall string, err error) error { if err == nil { return nil } @@ -57,9 +57,3 @@ var ( EPIPE = errors.New("Broken Pipe") EPLAN9 = errors.New("not supported by plan 9") ) - -func iserror(err syscall.Error) bool { - return err != nil -} - -func Errno(e syscall.Error) syscall.Error { return e } diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_posix.go index c3d7942..dbe1b9a 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_posix.go @@ -8,67 +8,53 @@ package os import syscall "syscall" -// Errno is the Unix error number. Names such as EINVAL are simple -// wrappers to convert the error number into an error. -type Errno int64 - -func (e Errno) Error() string { return syscall.Errstr(int(e)) } - -func (e Errno) Temporary() bool { - return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) || e.Timeout() -} - -func (e Errno) Timeout() bool { - return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT) -} - // Commonly known Unix errors. var ( - EPERM error = Errno(syscall.EPERM) - ENOENT error = Errno(syscall.ENOENT) - ESRCH error = Errno(syscall.ESRCH) - EINTR error = Errno(syscall.EINTR) - EIO error = Errno(syscall.EIO) - ENXIO error = Errno(syscall.ENXIO) - E2BIG error = Errno(syscall.E2BIG) - ENOEXEC error = Errno(syscall.ENOEXEC) - EBADF error = Errno(syscall.EBADF) - ECHILD error = Errno(syscall.ECHILD) - EDEADLK error = Errno(syscall.EDEADLK) - ENOMEM error = Errno(syscall.ENOMEM) - EACCES error = Errno(syscall.EACCES) - EFAULT error = Errno(syscall.EFAULT) - EBUSY error = Errno(syscall.EBUSY) - EEXIST error = Errno(syscall.EEXIST) - EXDEV error = Errno(syscall.EXDEV) - ENODEV error = Errno(syscall.ENODEV) - ENOTDIR error = Errno(syscall.ENOTDIR) - EISDIR error = Errno(syscall.EISDIR) - EINVAL error = Errno(syscall.EINVAL) - ENFILE error = Errno(syscall.ENFILE) - EMFILE error = Errno(syscall.EMFILE) - ENOTTY error = Errno(syscall.ENOTTY) - EFBIG error = Errno(syscall.EFBIG) - ENOSPC error = Errno(syscall.ENOSPC) - ESPIPE error = Errno(syscall.ESPIPE) - EROFS error = Errno(syscall.EROFS) - EMLINK error = Errno(syscall.EMLINK) - EPIPE error = Errno(syscall.EPIPE) - EAGAIN error = Errno(syscall.EAGAIN) - EDOM error = Errno(syscall.EDOM) - ERANGE error = Errno(syscall.ERANGE) - EADDRINUSE error = Errno(syscall.EADDRINUSE) - ECONNREFUSED error = Errno(syscall.ECONNREFUSED) - ENAMETOOLONG error = Errno(syscall.ENAMETOOLONG) - EAFNOSUPPORT error = Errno(syscall.EAFNOSUPPORT) - ETIMEDOUT error = Errno(syscall.ETIMEDOUT) - ENOTCONN error = Errno(syscall.ENOTCONN) + EPERM error = syscall.EPERM + ENOENT error = syscall.ENOENT + ESRCH error = syscall.ESRCH + EINTR error = syscall.EINTR + EIO error = syscall.EIO + ENXIO error = syscall.ENXIO + E2BIG error = syscall.E2BIG + ENOEXEC error = syscall.ENOEXEC + EBADF error = syscall.EBADF + ECHILD error = syscall.ECHILD + EDEADLK error = syscall.EDEADLK + ENOMEM error = syscall.ENOMEM + EACCES error = syscall.EACCES + EFAULT error = syscall.EFAULT + EBUSY error = syscall.EBUSY + EEXIST error = syscall.EEXIST + EXDEV error = syscall.EXDEV + ENODEV error = syscall.ENODEV + ENOTDIR error = syscall.ENOTDIR + EISDIR error = syscall.EISDIR + EINVAL error = syscall.EINVAL + ENFILE error = syscall.ENFILE + EMFILE error = syscall.EMFILE + ENOTTY error = syscall.ENOTTY + EFBIG error = syscall.EFBIG + ENOSPC error = syscall.ENOSPC + ESPIPE error = syscall.ESPIPE + EROFS error = syscall.EROFS + EMLINK error = syscall.EMLINK + EPIPE error = syscall.EPIPE + EAGAIN error = syscall.EAGAIN + EDOM error = syscall.EDOM + ERANGE error = syscall.ERANGE + EADDRINUSE error = syscall.EADDRINUSE + ECONNREFUSED error = syscall.ECONNREFUSED + ENAMETOOLONG error = syscall.ENAMETOOLONG + EAFNOSUPPORT error = syscall.EAFNOSUPPORT + ETIMEDOUT error = syscall.ETIMEDOUT + ENOTCONN error = syscall.ENOTCONN ) // SyscallError records an error from a specific system call. type SyscallError struct { Syscall string - Errno Errno + Errno error } func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Errno.Error() } @@ -79,14 +65,10 @@ func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Errno.Error( // NewSyscallError returns, as an error, a new SyscallError // with the given system call name and error details. -// As a convenience, if errno is 0, NewSyscallError returns nil. -func NewSyscallError(syscall string, errno int) error { - if errno == 0 { +// As a convenience, if err is nil, NewSyscallError returns nil. +func NewSyscallError(syscall string, err error) error { + if err == nil { return nil } - return &SyscallError{syscall, Errno(errno)} -} - -func iserror(errno int) bool { - return errno != 0 + return &SyscallError{syscall, err} } diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index ebdfd54..4c95c1b 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -50,14 +50,14 @@ type Cmd struct { // calling process's current directory. Dir string - // Stdin specifies the process's standard input. - // If Stdin is nil, the process reads from DevNull. + // Stdin specifies the process's standard input. If Stdin is + // nil, the process reads from the null device (os.DevNull). Stdin io.Reader // Stdout and Stderr specify the process's standard output and error. // - // If either is nil, Run connects the - // corresponding file descriptor to /dev/null. + // If either is nil, Run connects the corresponding file descriptor + // to the null device (os.DevNull). // // If Stdout and Stderr are are the same writer, at most one // goroutine at a time will call Write. diff --git a/libgo/go/os/exec_plan9.go b/libgo/go/os/exec_plan9.go index a1a3353..9a0db6d 100644 --- a/libgo/go/os/exec_plan9.go +++ b/libgo/go/os/exec_plan9.go @@ -32,7 +32,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e sysattr.Files = intfd pid, h, e := syscall.StartProcess(name, argv, sysattr) - if iserror(e) { + if e != nil { return nil, &PathError{"fork/exec", name, e} } @@ -52,7 +52,7 @@ func (p *Process) Signal(sig Signal) error { } f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0) - if iserror(e) { + if e != nil { return NewSyscallError("signal", e) } defer f.Close() @@ -63,7 +63,7 @@ func (p *Process) Signal(sig Signal) error { // Kill causes the Process to exit immediately. func (p *Process) Kill() error { f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0) - if iserror(e) { + if e != nil { return NewSyscallError("kill", e) } defer f.Close() @@ -77,7 +77,7 @@ func (p *Process) Kill() error { // ForkExec is almost always a better way to execute a program. func Exec(name string, argv []string, envv []string) error { e := syscall.Exec(name, argv, envv) - if iserror(e) { + if e != nil { return &PathError{"exec", name, e} } @@ -102,7 +102,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { for true { err = syscall.Await(&waitmsg) - if iserror(err) { + if err != nil { return nil, NewSyscallError("wait", err) } diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index 12b44e5..8b08eeb 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -40,8 +40,8 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e } pid, h, e := syscall.StartProcess(name, argv, sysattr) - if iserror(e) { - return nil, &PathError{"fork/exec", name, Errno(e)} + if e != nil { + return nil, &PathError{"fork/exec", name, e} } return newProcess(pid, h), nil } @@ -62,8 +62,8 @@ func Exec(name string, argv []string, envv []string) error { envv = Environ() } e := syscall.Exec(name, argv, envv) - if iserror(e) { - return &PathError{"exec", name, Errno(e)} + if e != nil { + return &PathError{"exec", name, e} } return nil } diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index 242bda7..3dcac41 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -38,7 +38,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { options ^= WRUSAGE } pid1, e := syscall.Wait4(p.Pid, &status, options, rusage) - if e != 0 { + if e != nil { return nil, NewSyscallError("wait", e) } // With WNOHANG pid is 0 if child has not exited. @@ -57,8 +57,8 @@ func (p *Process) Signal(sig Signal) error { if p.done { return errors.New("os: process already finished") } - if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 { - return Errno(e) + if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != nil { + return e } return nil } diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go index 866757e..c4c9dcf 100644 --- a/libgo/go/os/exec_windows.go +++ b/libgo/go/os/exec_windows.go @@ -8,6 +8,7 @@ import ( "errors" "runtime" "syscall" + "unsafe" ) func (p *Process) Wait(options int) (w *Waitmsg, err error) { @@ -22,7 +23,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) { } var ec uint32 e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec) - if e != 0 { + if e != nil { return nil, NewSyscallError("GetExitCodeProcess", e) } p.done = true @@ -39,7 +40,7 @@ func (p *Process) Signal(sig Signal) error { e := syscall.TerminateProcess(syscall.Handle(p.handle), 1) return NewSyscallError("TerminateProcess", e) } - return Errno(syscall.EWINDOWS) + return syscall.Errno(syscall.EWINDOWS) } func (p *Process) Release() error { @@ -47,7 +48,7 @@ func (p *Process) Release() error { return EINVAL } e := syscall.CloseHandle(syscall.Handle(p.handle)) - if e != 0 { + if e != nil { return NewSyscallError("CloseHandle", e) } p.handle = -1 @@ -60,8 +61,22 @@ func FindProcess(pid int) (p *Process, err error) { const da = syscall.STANDARD_RIGHTS_READ | syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE h, e := syscall.OpenProcess(da, false, uint32(pid)) - if e != 0 { + if e != nil { return nil, NewSyscallError("OpenProcess", e) } return newProcess(pid, int(h)), nil } + +func init() { + var argc int32 + cmd := syscall.GetCommandLine() + argv, e := syscall.CommandLineToArgv(cmd, &argc) + if e != nil { + return + } + defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv)))) + Args = make([]string, argc) + for i, v := range (*argv)[:argc] { + Args[i] = string(syscall.UTF16ToString((*v)[:])) + } +} diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index 0f3b2db..386afb8 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -59,11 +59,11 @@ func (file *File) Read(b []byte) (n int, err error) { if n < 0 { n = 0 } - if n == 0 && len(b) > 0 && !iserror(e) { + if n == 0 && len(b) > 0 && e == nil { return 0, io.EOF } - if iserror(e) { - err = &PathError{"read", file.name, Errno(e)} + if e != nil { + err = &PathError{"read", file.name, e} } return n, err } @@ -78,11 +78,11 @@ func (file *File) ReadAt(b []byte, off int64) (n int, err error) { } for len(b) > 0 { m, e := file.pread(b, off) - if m == 0 && !iserror(e) { + if m == 0 && e == nil { return n, io.EOF } - if iserror(e) { - err = &PathError{"read", file.name, Errno(e)} + if e != nil { + err = &PathError{"read", file.name, e} break } n += m @@ -106,8 +106,8 @@ func (file *File) Write(b []byte) (n int, err error) { epipecheck(file, e) - if iserror(e) { - err = &PathError{"write", file.name, Errno(e)} + if e != nil { + err = &PathError{"write", file.name, e} } return n, err } @@ -121,8 +121,8 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err error) { } for len(b) > 0 { m, e := file.pwrite(b, off) - if iserror(e) { - err = &PathError{"write", file.name, Errno(e)} + if e != nil { + err = &PathError{"write", file.name, e} break } n += m @@ -138,11 +138,11 @@ func (file *File) WriteAt(b []byte, off int64) (n int, err error) { // It returns the new offset and an error, if any. func (file *File) Seek(offset int64, whence int) (ret int64, err error) { r, e := file.seek(offset, whence) - if !iserror(e) && file.dirinfo != nil && r != 0 { + if e == nil && file.dirinfo != nil && r != 0 { e = syscall.EISDIR } - if iserror(e) { - return 0, &PathError{"seek", file.name, Errno(e)} + if e != nil { + return 0, &PathError{"seek", file.name, e} } return r, nil } @@ -160,16 +160,16 @@ func (file *File) WriteString(s string) (ret int, err error) { // It returns an error, if any. func Mkdir(name string, perm uint32) error { e := syscall.Mkdir(name, perm) - if iserror(e) { - return &PathError{"mkdir", name, Errno(e)} + if e != nil { + return &PathError{"mkdir", name, e} } return nil } // Chdir changes the current working directory to the named directory. func Chdir(dir string) error { - if e := syscall.Chdir(dir); iserror(e) { - return &PathError{"chdir", dir, Errno(e)} + if e := syscall.Chdir(dir); e != nil { + return &PathError{"chdir", dir, e} } return nil } @@ -177,8 +177,8 @@ func Chdir(dir string) error { // Chdir changes the current working directory to the file, // which must be a directory. func (f *File) Chdir() error { - if e := syscall.Fchdir(f.fd); iserror(e) { - return &PathError{"chdir", f.name, Errno(e)} + if e := syscall.Fchdir(f.fd); e != nil { + return &PathError{"chdir", f.name, e} } return nil } diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index 42332e1..42fefa9 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -11,6 +11,14 @@ import ( // File represents an open file descriptor. type File struct { + *file +} + +// file is the real representation of *File. +// The extra level of indirection ensures that no clients of os +// can overwrite this data, which could cause the finalizer +// to close the wrong file descriptor. +type file struct { fd int name string dirinfo *dirInfo // nil unless directory being read @@ -29,8 +37,8 @@ func NewFile(fd int, name string) *File { if fd < 0 { return nil } - f := &File{fd: fd, name: name} - runtime.SetFinalizer(f, (*File).Close) + f := &File{&file{fd: fd, name: name}} + runtime.SetFinalizer(f.file, (*file).close) return f } @@ -41,7 +49,7 @@ type dirInfo struct { bufp int // location of next record in buf. } -func epipecheck(file *File, e syscall.Error) { +func epipecheck(file *File, e error) { } // DevNull is the name of the operating system's ``null device.'' @@ -56,7 +64,7 @@ const DevNull = "/dev/null" func OpenFile(name string, flag int, perm uint32) (file *File, err error) { var ( fd int - e syscall.Error + e error create bool excl bool trunc bool @@ -85,7 +93,7 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) { } else { fd, e = syscall.Open(name, flag) if e != nil && create { - var e1 syscall.Error + var e1 error fd, e1 = syscall.Create(name, flag, perm) if e1 == nil { e = nil @@ -110,6 +118,10 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) { // Close closes the File, rendering it unusable for I/O. // It returns an error, if any. func (file *File) Close() error { + return file.file.close() +} + +func (file *file) close() error { if file == nil || file.fd < 0 { return Ebadfd } @@ -130,7 +142,7 @@ func (file *File) Close() error { // It returns the FileInfo and an error, if any. func (f *File) Stat() (fi *FileInfo, err error) { d, err := dirstat(f) - if iserror(err) { + if err != nil { return nil, err } return fileInfoFromStat(new(FileInfo), d), err @@ -144,7 +156,7 @@ func (f *File) Truncate(size int64) error { d.Length = uint64(size) - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { return &PathError{"truncate", f.name, e} } return nil @@ -157,12 +169,12 @@ func (f *File) Chmod(mode uint32) error { d.Null() odir, e := dirstat(f) - if iserror(e) { + if e != nil { return &PathError{"chmod", f.name, e} } d.Mode = (odir.Mode & mask) | (mode &^ mask) - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { return &PathError{"chmod", f.name, e} } return nil @@ -179,7 +191,7 @@ func (f *File) Sync() (err error) { var d Dir d.Null() - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { + if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { return NewSyscallError("fsync", e) } return nil @@ -187,26 +199,26 @@ func (f *File) Sync() (err error) { // read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. -func (f *File) read(b []byte) (n int, err syscall.Error) { +func (f *File) read(b []byte) (n int, err error) { return syscall.Read(f.fd, b) } // pread reads len(b) bytes from the File starting at byte offset off. // It returns the number of bytes read and the error, if any. // EOF is signaled by a zero count with err set to nil. -func (f *File) pread(b []byte, off int64) (n int, err syscall.Error) { +func (f *File) pread(b []byte, off int64) (n int, err error) { return syscall.Pread(f.fd, b, off) } // write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. -func (f *File) write(b []byte) (n int, err syscall.Error) { +func (f *File) write(b []byte) (n int, err error) { return syscall.Write(f.fd, b) } // pwrite writes len(b) bytes to the File starting at byte offset off. // It returns the number of bytes written and an error, if any. -func (f *File) pwrite(b []byte, off int64) (n int, err syscall.Error) { +func (f *File) pwrite(b []byte, off int64) (n int, err error) { return syscall.Pwrite(f.fd, b, off) } @@ -214,7 +226,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err syscall.Error) { // according to whence: 0 means relative to the origin of the file, 1 means // relative to the current offset, and 2 means relative to the end. // It returns the new offset and an error, if any. -func (f *File) seek(offset int64, whence int) (ret int64, err syscall.Error) { +func (f *File) seek(offset int64, whence int) (ret int64, err error) { return syscall.Seek(f.fd, offset, whence) } @@ -226,7 +238,7 @@ func Truncate(name string, size int64) error { d.Length = uint64(size) - if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { return &PathError{"truncate", name, e} } return nil @@ -234,7 +246,7 @@ func Truncate(name string, size int64) error { // Remove removes the named file or directory. func Remove(name string) error { - if e := syscall.Remove(name); iserror(e) { + if e := syscall.Remove(name); e != nil { return &PathError{"remove", name, e} } return nil @@ -247,7 +259,7 @@ func Rename(oldname, newname string) error { d.Name = newname - if e := syscall.Wstat(oldname, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(oldname, pdir(nil, &d)); e != nil { return &PathError{"rename", oldname, e} } return nil @@ -260,12 +272,12 @@ func Chmod(name string, mode uint32) error { d.Null() odir, e := dirstat(name) - if iserror(e) { + if e != nil { return &PathError{"chmod", name, e} } d.Mode = (odir.Mode & mask) | (mode &^ mask) - if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { return &PathError{"chmod", name, e} } return nil @@ -284,7 +296,7 @@ func Chtimes(name string, atimeNs int64, mtimeNs int64) error { d.Atime = uint32(atimeNs / 1e9) d.Mtime = uint32(mtimeNs / 1e9) - if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { + if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { return &PathError{"chtimes", name, e} } return nil @@ -294,7 +306,7 @@ func Pipe() (r *File, w *File, err error) { var p [2]int syscall.ForkLock.RLock() - if e := syscall.Pipe(p[0:]); iserror(e) { + if e := syscall.Pipe(p[0:]); e != nil { syscall.ForkLock.RUnlock() return nil, nil, NewSyscallError("pipe", e) } @@ -329,3 +341,8 @@ func Lchown(name string, uid, gid int) error { func (f *File) Chown(uid, gid int) error { return EPLAN9 } + +// TempDir returns the default directory to use for temporary files. +func TempDir() string { + return "/tmp" +} diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index c937b6d..c80d3df 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -12,7 +12,7 @@ import ( func sigpipe() // implemented in package runtime -func epipecheck(file *File, e int) { +func epipecheck(file *File, e error) { if e == syscall.EPIPE { file.nepipe++ if file.nepipe >= 10 { @@ -30,11 +30,11 @@ func Remove(name string) error { // Try both: it is cheaper on average than // doing a Stat plus the right one. e := syscall.Unlink(name) - if !iserror(e) { + if e == nil { return nil } e1 := syscall.Rmdir(name) - if !iserror(e1) { + if e1 == nil { return nil } @@ -53,7 +53,7 @@ func Remove(name string) error { if e1 != syscall.ENOTDIR { e = e1 } - return &PathError{"remove", name, Errno(e)} + return &PathError{"remove", name, e} } // LinkError records an error during a link or symlink or rename @@ -72,8 +72,8 @@ func (e *LinkError) Error() string { // Link creates a hard link. func Link(oldname, newname string) error { e := syscall.Link(oldname, newname) - if iserror(e) { - return &LinkError{"link", oldname, newname, Errno(e)} + if e != nil { + return &LinkError{"link", oldname, newname, e} } return nil } @@ -81,8 +81,8 @@ func Link(oldname, newname string) error { // Symlink creates a symbolic link. func Symlink(oldname, newname string) error { e := syscall.Symlink(oldname, newname) - if iserror(e) { - return &LinkError{"symlink", oldname, newname, Errno(e)} + if e != nil { + return &LinkError{"symlink", oldname, newname, e} } return nil } @@ -93,8 +93,8 @@ func Readlink(name string) (string, error) { for len := 128; ; len *= 2 { b := make([]byte, len) n, e := syscall.Readlink(name, b) - if iserror(e) { - return "", &PathError{"readlink", name, Errno(e)} + if e != nil { + return "", &PathError{"readlink", name, e} } if n < len { return string(b[0:n]), nil @@ -107,8 +107,8 @@ func Readlink(name string) (string, error) { // Rename renames a file. func Rename(oldname, newname string) error { e := syscall.Rename(oldname, newname) - if iserror(e) { - return &LinkError{"rename", oldname, newname, Errno(e)} + if e != nil { + return &LinkError{"rename", oldname, newname, e} } return nil } @@ -116,16 +116,16 @@ func Rename(oldname, newname string) error { // Chmod changes the mode of the named file to mode. // If the file is a symbolic link, it changes the mode of the link's target. func Chmod(name string, mode uint32) error { - if e := syscall.Chmod(name, mode); iserror(e) { - return &PathError{"chmod", name, Errno(e)} + if e := syscall.Chmod(name, mode); e != nil { + return &PathError{"chmod", name, e} } return nil } // Chmod changes the mode of the file to mode. func (f *File) Chmod(mode uint32) error { - if e := syscall.Fchmod(f.fd, mode); iserror(e) { - return &PathError{"chmod", f.name, Errno(e)} + if e := syscall.Fchmod(f.fd, mode); e != nil { + return &PathError{"chmod", f.name, e} } return nil } @@ -133,8 +133,8 @@ func (f *File) Chmod(mode uint32) error { // Chown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link's target. func Chown(name string, uid, gid int) error { - if e := syscall.Chown(name, uid, gid); iserror(e) { - return &PathError{"chown", name, Errno(e)} + if e := syscall.Chown(name, uid, gid); e != nil { + return &PathError{"chown", name, e} } return nil } @@ -142,16 +142,16 @@ func Chown(name string, uid, gid int) error { // Lchown changes the numeric uid and gid of the named file. // If the file is a symbolic link, it changes the uid and gid of the link itself. func Lchown(name string, uid, gid int) error { - if e := syscall.Lchown(name, uid, gid); iserror(e) { - return &PathError{"lchown", name, Errno(e)} + if e := syscall.Lchown(name, uid, gid); e != nil { + return &PathError{"lchown", name, e} } return nil } // Chown changes the numeric uid and gid of the named file. func (f *File) Chown(uid, gid int) error { - if e := syscall.Fchown(f.fd, uid, gid); iserror(e) { - return &PathError{"chown", f.name, Errno(e)} + if e := syscall.Fchown(f.fd, uid, gid); e != nil { + return &PathError{"chown", f.name, e} } return nil } @@ -159,8 +159,8 @@ func (f *File) Chown(uid, gid int) error { // Truncate changes the size of the file. // It does not change the I/O offset. func (f *File) Truncate(size int64) error { - if e := syscall.Ftruncate(f.fd, size); iserror(e) { - return &PathError{"truncate", f.name, Errno(e)} + if e := syscall.Ftruncate(f.fd, size); e != nil { + return &PathError{"truncate", f.name, e} } return nil } @@ -172,7 +172,7 @@ func (file *File) Sync() (err error) { if file == nil { return EINVAL } - if e := syscall.Fsync(file.fd); iserror(e) { + if e := syscall.Fsync(file.fd); e != nil { return NewSyscallError("fsync", e) } return nil @@ -188,8 +188,8 @@ func Chtimes(name string, atime_ns int64, mtime_ns int64) error { var utimes [2]syscall.Timeval utimes[0] = syscall.NsecToTimeval(atime_ns) utimes[1] = syscall.NsecToTimeval(mtime_ns) - if e := syscall.Utimes(name, utimes[0:]); iserror(e) { - return &PathError{"chtimes", name, Errno(e)} + if e := syscall.Utimes(name, utimes[0:]); e != nil { + return &PathError{"chtimes", name, e} } return nil } diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index a7ccb33..0bf31ec 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -13,6 +13,14 @@ import ( // File represents an open file descriptor. type File struct { + *file +} + +// file is the real representation of *File. +// The extra level of indirection ensures that no clients of os +// can overwrite this data, which could cause the finalizer +// to close the wrong file descriptor. +type file struct { fd int name string dirinfo *dirInfo // nil unless directory being read @@ -32,8 +40,8 @@ func NewFile(fd int, name string) *File { if fd < 0 { return nil } - f := &File{fd: fd, name: name} - runtime.SetFinalizer(f, (*File).Close) + f := &File{&file{fd: fd, name: name}} + runtime.SetFinalizer(f.file, (*file).close) return f } @@ -54,8 +62,8 @@ const DevNull = "/dev/null" // It returns the File and an error, if any. func OpenFile(name string, flag int, perm uint32) (file *File, err error) { r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm) - if e != 0 { - return nil, &PathError{"open", name, Errno(e)} + if e != nil { + return nil, &PathError{"open", name, e} } // There's a race here with fork/exec, which we are @@ -70,17 +78,21 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) { // Close closes the File, rendering it unusable for I/O. // It returns an error, if any. func (file *File) Close() error { + return file.file.close() +} + +func (file *file) close() error { if file == nil || file.fd < 0 { return EINVAL } var err error - if e := syscall.Close(file.fd); e != 0 { - err = &PathError{"close", file.name, Errno(e)} + if e := syscall.Close(file.fd); e != nil { + err = &PathError{"close", file.name, e} } if file.dirinfo != nil { if libc_closedir(file.dirinfo.dir) < 0 && err == nil { - err = &PathError{"closedir", file.name, Errno(syscall.GetErrno())} + err = &PathError{"closedir", file.name, syscall.GetErrno()} } } @@ -96,8 +108,8 @@ func (file *File) Close() error { func (file *File) Stat() (fi *FileInfo, err error) { var stat syscall.Stat_t e := syscall.Fstat(file.fd, &stat) - if e != 0 { - return nil, &PathError{"stat", file.name, Errno(e)} + if e != nil { + return nil, &PathError{"stat", file.name, e} } return fileInfoFromStat(file.name, new(FileInfo), &stat, &stat), nil } @@ -110,13 +122,13 @@ func (file *File) Stat() (fi *FileInfo, err error) { func Stat(name string) (fi *FileInfo, err error) { var lstat, stat syscall.Stat_t e := syscall.Lstat(name, &lstat) - if iserror(e) { - return nil, &PathError{"stat", name, Errno(e)} + if e != nil { + return nil, &PathError{"stat", name, e} } statp := &lstat if lstat.Mode&syscall.S_IFMT == syscall.S_IFLNK { e := syscall.Stat(name, &stat) - if !iserror(e) { + if e == nil { statp = &stat } } @@ -129,8 +141,8 @@ func Stat(name string) (fi *FileInfo, err error) { func Lstat(name string) (fi *FileInfo, err error) { var stat syscall.Stat_t e := syscall.Lstat(name, &stat) - if iserror(e) { - return nil, &PathError{"lstat", name, Errno(e)} + if e != nil { + return nil, &PathError{"lstat", name, e} } return fileInfoFromStat(name, new(FileInfo), &stat, &stat), nil } @@ -171,26 +183,26 @@ func (file *File) Readdir(n int) (fi []FileInfo, err error) { // read reads up to len(b) bytes from the File. // It returns the number of bytes read and an error, if any. -func (f *File) read(b []byte) (n int, err int) { +func (f *File) read(b []byte) (n int, err error) { return syscall.Read(f.fd, b) } // pread reads len(b) bytes from the File starting at byte offset off. // It returns the number of bytes read and the error, if any. // EOF is signaled by a zero count with err set to 0. -func (f *File) pread(b []byte, off int64) (n int, err int) { +func (f *File) pread(b []byte, off int64) (n int, err error) { return syscall.Pread(f.fd, b, off) } // write writes len(b) bytes to the File. // It returns the number of bytes written and an error, if any. -func (f *File) write(b []byte) (n int, err int) { +func (f *File) write(b []byte) (n int, err error) { return syscall.Write(f.fd, b) } // pwrite writes len(b) bytes to the File starting at byte offset off. // It returns the number of bytes written and an error, if any. -func (f *File) pwrite(b []byte, off int64) (n int, err int) { +func (f *File) pwrite(b []byte, off int64) (n int, err error) { return syscall.Pwrite(f.fd, b, off) } @@ -198,15 +210,15 @@ func (f *File) pwrite(b []byte, off int64) (n int, err int) { // according to whence: 0 means relative to the origin of the file, 1 means // relative to the current offset, and 2 means relative to the end. // It returns the new offset and an error, if any. -func (f *File) seek(offset int64, whence int) (ret int64, err int) { +func (f *File) seek(offset int64, whence int) (ret int64, err error) { return syscall.Seek(f.fd, offset, whence) } // Truncate changes the size of the named file. // If the file is a symbolic link, it changes the size of the link's target. func Truncate(name string, size int64) error { - if e := syscall.Truncate(name, size); e != 0 { - return &PathError{"truncate", name, Errno(e)} + if e := syscall.Truncate(name, size); e != nil { + return &PathError{"truncate", name, e} } return nil } @@ -237,7 +249,7 @@ func Pipe() (r *File, w *File, err error) { // See ../syscall/exec.go for description of lock. syscall.ForkLock.RLock() e := syscall.Pipe(p[0:]) - if iserror(e) { + if e != nil { syscall.ForkLock.RUnlock() return nil, nil, NewSyscallError("pipe", e) } @@ -247,3 +259,12 @@ func Pipe() (r *File, w *File, err error) { return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil } + +// TempDir returns the default directory to use for temporary files. +func TempDir() string { + dir := Getenv("TMPDIR") + if dir == "" { + dir = "/tmp" + } + return dir +} diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 1d6d872..170f733 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -18,7 +18,7 @@ import ( var dot = []string{ "dir_unix.go", - "env_unix.go", + "env.go", "error.go", "file.go", "os_test.go", @@ -940,11 +940,6 @@ func TestHostname(t *testing.T) { return } - // TODO(jsing): Fix nametomib() on OpenBSD - if syscall.OS == "openbsd" { - return - } - // Check internal Hostname() against the output of /bin/hostname. // Allow that the internal Hostname returns a Fully Qualified Domain Name // and the /bin/hostname only returns the first component diff --git a/libgo/go/os/proc.go b/libgo/go/os/proc.go index d21f849..61545f4 100644 --- a/libgo/go/os/proc.go +++ b/libgo/go/os/proc.go @@ -8,8 +8,8 @@ package os import "syscall" -var Args []string // provided by runtime -var Envs []string // provided by runtime +// Args hold the command-line arguments, starting with the program name. +var Args []string // Getuid returns the numeric user id of the caller. func Getuid() int { return syscall.Getuid() } diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go index 8df9e58..e4a1dbb 100644 --- a/libgo/go/os/stat_plan9.go +++ b/libgo/go/os/stat_plan9.go @@ -34,7 +34,7 @@ func dirstat(arg interface{}) (d *Dir, err error) { buf := make([]byte, nd) var n int - var e syscall.Error + var e error switch syscallArg := arg.(type) { case *File: @@ -72,7 +72,7 @@ func dirstat(arg interface{}) (d *Dir, err error) { // Stat returns a FileInfo structure describing the named file and an error, if any. func Stat(name string) (fi *FileInfo, err error) { d, err := dirstat(name) - if iserror(err) { + if err != nil { return nil, err } return fileInfoFromStat(new(FileInfo), d), err @@ -83,7 +83,7 @@ func Stat(name string) (fi *FileInfo, err error) { // the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. func Lstat(name string) (fi *FileInfo, err error) { d, err := dirstat(name) - if iserror(err) { + if err != nil { return nil, err } return fileInfoFromStat(new(FileInfo), d), err diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go index 67133a1..c6a6de5 100644 --- a/libgo/go/os/sys_bsd.go +++ b/libgo/go/os/sys_bsd.go @@ -12,10 +12,9 @@ package os import "syscall" func Hostname() (name string, err error) { - var errno int - name, errno = syscall.Sysctl("kern.hostname") - if errno != 0 { - return "", NewSyscallError("sysctl kern.hostname", errno) + name, err = syscall.Sysctl("kern.hostname") + if err != nil { + return "", NewSyscallError("sysctl kern.hostname", err) } return name, nil } diff --git a/libgo/go/os/time.go b/libgo/go/os/time.go index a4678ee..eb564e5 100644 --- a/libgo/go/os/time.go +++ b/libgo/go/os/time.go @@ -12,7 +12,7 @@ import "syscall" // time is the Unix epoch. func Time() (sec int64, nsec int64, err error) { var tv syscall.Timeval - if e := syscall.Gettimeofday(&tv); iserror(e) { + if e := syscall.Gettimeofday(&tv); e != nil { return 0, 0, NewSyscallError("gettimeofday", e) } return int64(tv.Sec), int64(tv.Usec) * 1000, err diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index ba4e1ac..89886cb 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -8,7 +8,6 @@ package user import ( "fmt" - "os" "strings" "syscall" "unsafe" @@ -70,7 +69,7 @@ func lookup(uid int, username string, lookupByName bool) (*User, error) { bufSize, &result) if rv != 0 { - return nil, fmt.Errorf("user: lookup username %s: %s", username, os.Errno(syscall.GetErrno())) + return nil, fmt.Errorf("user: lookup username %s: %s", username, syscall.GetErrno()) } if result == nil { return nil, UnknownUserError(username) @@ -82,7 +81,7 @@ func lookup(uid int, username string, lookupByName bool) (*User, error) { bufSize, &result) if rv != 0 { - return nil, fmt.Errorf("user: lookup userid %d: %s", uid, os.Errno(syscall.GetErrno())) + return nil, fmt.Errorf("user: lookup userid %d: %s", uid, syscall.GetErrno()) } if result == nil { return nil, UnknownUserIdError(uid) diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 7c1022e..cf31d1b 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -16,6 +16,13 @@ import ( "unsafe" ) +func TestBool(t *testing.T) { + v := ValueOf(true) + if v.Bool() != true { + t.Fatal("ValueOf(true).Bool() = false") + } +} + type integer int type T struct { a int @@ -215,7 +222,8 @@ func TestTypes(t *testing.T) { func TestSet(t *testing.T) { for i, tt := range valueTests { - v := ValueOf(tt.i).Elem() + v := ValueOf(tt.i) + v = v.Elem() switch v.Kind() { case Int: v.SetInt(132) @@ -651,6 +659,14 @@ var deepEqualTests = []DeepEqualTest{ {nil, 1, false}, {1, nil, false}, + // Nil vs empty: not the same. + {[]int{}, []int(nil), false}, + {[]int{}, []int{}, true}, + {[]int(nil), []int(nil), true}, + {map[int]int{}, map[int]int(nil), false}, + {map[int]int{}, map[int]int{}, true}, + {map[int]int(nil), map[int]int(nil), true}, + // Mismatched types {1, 1.0, false}, {int32(1), int64(1), false}, @@ -1092,21 +1108,38 @@ func TestMethod(t *testing.T) { } // Curried method of value. - i = ValueOf(p).Method(1).Call([]Value{ValueOf(10)})[0].Int() + tfunc := TypeOf(func(int) int(nil)) + v := ValueOf(p).Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Value Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Value Method returned %d; want 250", i) } - i = ValueOf(p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + v = ValueOf(p).MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Value MethodByName returned %d; want 250", i) } // Curried method of pointer. - i = ValueOf(&p).Method(1).Call([]Value{ValueOf(10)})[0].Int() + v = ValueOf(&p).Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Value Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Pointer Value Method returned %d; want 250", i) } - i = ValueOf(&p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + v = ValueOf(&p).MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Pointer Value MethodByName returned %d; want 250", i) } @@ -1121,11 +1154,19 @@ func TestMethod(t *testing.T) { } }{p} pv := ValueOf(s).Field(0) - i = pv.Method(0).Call([]Value{ValueOf(10)})[0].Int() + v = pv.Method(0) + if tt := v.Type(); tt != tfunc { + t.Errorf("Interface Method Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Interface Method returned %d; want 250", i) } - i = pv.MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + v = pv.MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc) + } + i = v.Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Interface MethodByName returned %d; want 250", i) } diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go index 63c28fe..df5ec0a 100644 --- a/libgo/go/reflect/deepequal.go +++ b/libgo/go/reflect/deepequal.go @@ -69,6 +69,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } return true case Slice: + if v1.IsNil() != v2.IsNil() { + return false + } if v1.Len() != v2.Len() { return false } @@ -93,6 +96,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } return true case Map: + if v1.IsNil() != v2.IsNil() { + return false + } if v1.Len() != v2.Len() { return false } diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index 9be7f83..07cc0f5 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -188,7 +188,7 @@ type Type interface { // A Kind represents the specific kind of type that a Type represents. // The zero Kind is not a valid kind. -type Kind uint8 +type Kind uint const ( Invalid Kind = iota @@ -455,15 +455,16 @@ func (t *uncommonType) Method(i int) (m Method) { if p.name != nil { m.Name = *p.name } - flag := uint32(0) + fl := flag(Func) << flagKindShift if p.pkgPath != nil { m.PkgPath = *p.pkgPath - flag |= flagRO + fl |= flagRO } - m.Type = toType(p.typ) + mt := toCommonType(p.typ) + m.Type = mt.toType() x := new(unsafe.Pointer) *x = p.tfn - m.Func = valueFromIword(flag, m.Type, iword(uintptr(unsafe.Pointer(x)))) + m.Func = Value{mt, unsafe.Pointer(x), fl|flagIndir} m.Index = i return } @@ -769,7 +770,7 @@ func (t *structType) Field(i int) (f StructField) { if i < 0 || i >= len(t.fields) { return } - p := t.fields[i] + p := &t.fields[i] f.Type = toType(p.typ) if p.name != nil { f.Name = *p.name @@ -868,10 +869,12 @@ L: if n == 1 { // Found matching field. - if len(ff.Index) <= depth { + if depth >= len(ff.Index) { ff.Index = make([]int, depth+1) } - ff.Index[depth] = fi + if len(ff.Index) > 1 { + ff.Index[depth] = fi + } } else { // None or more than one matching field found. fd = inf @@ -903,9 +906,6 @@ func toCommonType(p *runtime.Type) *commonType { return nil } x := unsafe.Pointer(p) - if uintptr(x)&reflectFlags != 0 { - panic("reflect: invalid interface value") - } return (*commonType)(x) } @@ -967,10 +967,12 @@ func (t *commonType) runtimeType() *runtime.Type { // PtrTo returns the pointer type with element t. // For example, if t represents type Foo, PtrTo(t) represents *Foo. func PtrTo(t Type) Type { - // If t records its pointer-to type, use it. - ct := t.(*commonType) + return t.(*commonType).ptrTo() +} + +func (ct *commonType) ptrTo() *commonType { if p := ct.ptrToThis; p != nil { - return toType(p) + return toCommonType(p) } // Otherwise, synthesize one. @@ -982,7 +984,7 @@ func PtrTo(t Type) Type { if m := ptrMap.m; m != nil { if p := m[ct]; p != nil { ptrMap.RUnlock() - return p.commonType.toType() + return &p.commonType } } ptrMap.RUnlock() @@ -994,7 +996,7 @@ func PtrTo(t Type) Type { if p != nil { // some other goroutine won the race and created it ptrMap.Unlock() - return p + return &p.commonType } rt := (*runtime.Type)(unsafe.Pointer(ct)) @@ -1024,7 +1026,7 @@ func PtrTo(t Type) Type { ptrMap.m[ct] = p ptrMap.Unlock() - return p.commonType.toType() + return &p.commonType } func (t *commonType) Implements(u Type) bool { diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index dbdfa09..b57ed46 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -11,6 +11,7 @@ import ( "unsafe" ) +const bigEndian = false // can be smarter if we find a big-endian machine const ptrSize = unsafe.Sizeof((*byte)(nil)) const cannotSet = "cannot set value obtained from unexported struct field" @@ -53,14 +54,54 @@ func memmove(adst, asrc unsafe.Pointer, n uintptr) { // its String method returns "<invalid Value>", and all other methods panic. // Most functions and methods never return an invalid value. // If one does, its documentation states the conditions explicitly. -// -// The fields of Value are exported so that clients can copy and -// pass Values around, but they should not be edited or inspected -// directly. A future language change may make it possible not to -// export these fields while still keeping Values usable as values. type Value struct { - Internal interface{} - InternalMethod int + // typ holds the type of the value represented by a Value. + typ *commonType + + // val holds the 1-word representation of the value. + // If flag's flagIndir bit is set, then val is a pointer to the data. + // Otherwise val is a word holding the actual data. + // When the data is smaller than a word, it begins at + // the first byte (in the memory address sense) of val. + // We use unsafe.Pointer so that the garbage collector + // knows that val could be a pointer. + val unsafe.Pointer + + // flag holds metadata about the value. + // The lowest bits are flag bits: + // - flagRO: obtained via unexported field, so read-only + // - flagIndir: val holds a pointer to the data + // - flagAddr: v.CanAddr is true (implies flagIndir) + // - flagMethod: v is a method value. + // The next five bits give the Kind of the value. + // This repeats typ.Kind() except for method values. + // The remaining 23+ bits give a method number for method values. + // If flag.kind() != Func, code can assume that flagMethod is unset. + // If typ.size > ptrSize, code can assume that flagIndir is set. + flag + + // A method value represents a curried method invocation + // like r.Read for some receiver r. The typ+val+flag bits describe + // the receiver r, but the flag's Kind bits say Func (methods are + // functions), and the top bits of the flag give the method number + // in r's type's method table. +} + +type flag uintptr + +const ( + flagRO flag = 1 << iota + flagIndir + flagAddr + flagMethod + flagKindShift = iota + flagKindWidth = 5 // there are 27 kinds + flagKindMask flag = 1<<flagKindWidth - 1 + flagMethodShift = flagKindShift + flagKindWidth +) + +func (f flag) kind() Kind { + return Kind((f >> flagKindShift) & flagKindMask) } // A ValueError occurs when a Value method is invoked on @@ -92,17 +133,30 @@ func methodName() string { // An iword is the word that would be stored in an // interface to represent a given value v. Specifically, if v is // bigger than a pointer, its word is a pointer to v's data. -// Otherwise, its word is a zero uintptr with the data stored -// in the leading bytes. -type iword uintptr +// Otherwise, its word holds the data stored +// in its leading bytes (so is not a pointer). +// Because the value sometimes holds a pointer, we use +// unsafe.Pointer to represent it, so that if iword appears +// in a struct, the garbage collector knows that might be +// a pointer. +type iword unsafe.Pointer + +func (v Value) iword() iword { + if v.flag&flagIndir != 0 && (v.kind() == Ptr || v.kind() == UnsafePointer) { + // Have indirect but want direct word. + return loadIword(v.val, v.typ.size) + } + return iword(v.val) +} -func loadIword(p unsafe.Pointer, size uintptr) iword { +// loadIword loads n bytes at p from memory into an iword. +func loadIword(p unsafe.Pointer, n uintptr) iword { // Run the copy ourselves instead of calling memmove - // to avoid moving v to the heap. - w := iword(0) - switch size { + // to avoid moving w to the heap. + var w iword + switch n { default: - panic("reflect: internal error: loadIword of " + strconv.Itoa(int(size)) + "-byte value") + panic("reflect: internal error: loadIword of " + strconv.Itoa(int(n)) + "-byte value") case 0: case 1: *(*uint8)(unsafe.Pointer(&w)) = *(*uint8)(p) @@ -124,12 +178,13 @@ func loadIword(p unsafe.Pointer, size uintptr) iword { return w } -func storeIword(p unsafe.Pointer, w iword, size uintptr) { +// storeIword stores n bytes from w into p. +func storeIword(p unsafe.Pointer, w iword, n uintptr) { // Run the copy ourselves instead of calling memmove - // to avoid moving v to the heap. - switch size { + // to avoid moving w to the heap. + switch n { default: - panic("reflect: internal error: storeIword of " + strconv.Itoa(int(size)) + "-byte value") + panic("reflect: internal error: storeIword of " + strconv.Itoa(int(n)) + "-byte value") case 0: case 1: *(*uint8)(p) = *(*uint8)(unsafe.Pointer(&w)) @@ -166,237 +221,42 @@ type nonEmptyInterface struct { word iword } -// Regarding the implementation of Value: -// -// The Internal interface is a true interface value in the Go sense, -// but it also serves as a (type, address) pair in which one cannot -// be changed separately from the other. That is, it serves as a way -// to prevent unsafe mutations of the Internal state even though -// we cannot (yet?) hide the field while preserving the ability for -// clients to make copies of Values. -// -// The internal method converts a Value into the expanded internalValue struct. -// If we could avoid exporting fields we'd probably make internalValue the -// definition of Value. -// -// If a Value is addressable (CanAddr returns true), then the Internal -// interface value holds a pointer to the actual field data, and Set stores -// through that pointer. If a Value is not addressable (CanAddr returns false), -// then the Internal interface value holds the actual value. -// -// In addition to whether a value is addressable, we track whether it was -// obtained by using an unexported struct field. Such values are allowed -// to be read, mainly to make fmt.Print more useful, but they are not -// allowed to be written. We call such values read-only. -// -// A Value can be set (via the Set, SetUint, etc. methods) only if it is both -// addressable and not read-only. -// -// The two permission bits - addressable and read-only - are stored in -// the bottom two bits of the type pointer in the interface value. -// -// ordinary value: Internal = value -// addressable value: Internal = value, Internal.typ |= flagAddr -// read-only value: Internal = value, Internal.typ |= flagRO -// addressable, read-only value: Internal = value, Internal.typ |= flagAddr | flagRO -// -// It is important that the read-only values have the extra bit set -// (as opposed to using the bit to mean writable), because client code -// can grab the interface field and try to use it. Having the extra bit -// set makes the type pointer compare not equal to any real type, -// so that a client cannot, say, write through v.Internal.(*int). -// The runtime routines that access interface types reject types with -// low bits set. -// -// If a Value fv = v.Method(i), then fv = v with the InternalMethod -// field set to i+1. Methods are never addressable. -// -// All in all, this is a lot of effort just to avoid making this new API -// depend on a language change we'll probably do anyway, but -// it's helpful to keep the two separate, and much of the logic is -// necessary to implement the Interface method anyway. - -const ( - flagAddr uint32 = 1 << iota // holds address of value - flagRO // read-only - - reflectFlags = 3 -) - -// An internalValue is the unpacked form of a Value. -// The zero Value unpacks to a zero internalValue -type internalValue struct { - typ *commonType // type of value - kind Kind // kind of value - flag uint32 - word iword - addr unsafe.Pointer - rcvr iword - method bool - nilmethod bool -} - -func (v Value) internal() internalValue { - var iv internalValue - eface := *(*emptyInterface)(unsafe.Pointer(&v.Internal)) - p := uintptr(unsafe.Pointer(eface.typ)) - iv.typ = toCommonType((*runtime.Type)(unsafe.Pointer(p &^ reflectFlags))) - if iv.typ == nil { - return iv - } - iv.flag = uint32(p & reflectFlags) - iv.word = eface.word - if iv.flag&flagAddr != 0 { - iv.addr = unsafe.Pointer(uintptr(iv.word)) - iv.typ = iv.typ.Elem().common() - if Kind(iv.typ.kind) == Ptr || Kind(iv.typ.kind) == UnsafePointer { - iv.word = loadIword(iv.addr, iv.typ.size) - } - } else { - if Kind(iv.typ.kind) != Ptr && Kind(iv.typ.kind) != UnsafePointer { - iv.addr = unsafe.Pointer(uintptr(iv.word)) - } +// mustBe panics if f's kind is not expected. +// Making this a method on flag instead of on Value +// (and embedding flag in Value) means that we can write +// the very clear v.mustBe(Bool) and have it compile into +// v.flag.mustBe(Bool), which will only bother to copy the +// single important word for the receiver. +func (f flag) mustBe(expected Kind) { + k := f.kind() + if k != expected { + panic(&ValueError{methodName(), k}) } - iv.kind = iv.typ.Kind() - - // Is this a method? If so, iv describes the receiver. - // Rewrite to describe the method function. - if v.InternalMethod != 0 { - // If this Value is a method value (x.Method(i) for some Value x) - // then we will invoke it using the interface form of the method, - // which always passes the receiver as a single word. - // Record that information. - i := v.InternalMethod - 1 - if iv.kind == Interface { - it := (*interfaceType)(unsafe.Pointer(iv.typ)) - if i < 0 || i >= len(it.methods) { - panic("reflect: broken Value") - } - m := &it.methods[i] - if m.pkgPath != nil { - iv.flag |= flagRO - } - iv.typ = toCommonType(m.typ) - iface := (*nonEmptyInterface)(iv.addr) - if iface.itab == nil { - iv.word = 0 - iv.nilmethod = true - } else { - iv.word = iword(uintptr(iface.itab.fun[i])) - } - iv.rcvr = iface.word - } else { - ut := iv.typ.uncommon() - if ut == nil || i < 0 || i >= len(ut.methods) { - panic("reflect: broken Value") - } - m := &ut.methods[i] - if m.pkgPath != nil { - iv.flag |= flagRO - } - iv.typ = toCommonType(m.mtyp) - iv.rcvr = iv.word - iv.word = iword(uintptr(m.tfn)) - } - if iv.word != 0 { - p := new(iword) - *p = iv.word - iv.word = iword(uintptr(unsafe.Pointer(p))) - } - iv.kind = Func - iv.method = true - iv.flag &^= flagAddr - iv.addr = unsafe.Pointer(uintptr(iv.word)) - } - - return iv } -// packValue returns a Value with the given flag bits, type, and interface word. -func packValue(flag uint32, typ *runtime.Type, word iword) Value { - if typ == nil { - panic("packValue") +// mustBeExported panics if f records that the value was obtained using +// an unexported field. +func (f flag) mustBeExported() { + if f == 0 { + panic(&ValueError{methodName(), 0}) } - t := uintptr(unsafe.Pointer(typ)) - t |= uintptr(flag) - eface := emptyInterface{(*runtime.Type)(unsafe.Pointer(t)), word} - return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))} -} - -var dummy struct { - b bool - x interface{} -} - -// Dummy annotation marking that the value x escapes, -// for use in cases where the reflect code is so clever that -// the compiler cannot follow. -func escapes(x interface{}) { - if dummy.b { - dummy.x = x - } -} - -// valueFromAddr returns a Value using the given type and address. -func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value { - // TODO(rsc): Eliminate this terrible hack. - // The escape analysis knows that addr is a pointer - // but it doesn't see addr get passed to anything - // that keeps it. packValue keeps it, but packValue - // takes a uintptr (iword(addr)), and integers (non-pointers) - // are assumed not to matter. The escapes function works - // because return values always escape (for now). - escapes(addr) - - if flag&flagAddr != 0 { - // Addressable, so the internal value is - // an interface containing a pointer to the real value. - return packValue(flag, PtrTo(typ).runtimeType(), iword(uintptr(addr))) - } - - var w iword - if k := typ.Kind(); k == Ptr || k == UnsafePointer { - // In line, so the interface word is the actual value. - w = loadIword(addr, typ.Size()) - } else { - // Not in line: the interface word is the address. - w = iword(uintptr(addr)) - } - return packValue(flag, typ.runtimeType(), w) -} - -// valueFromIword returns a Value using the given type and interface word. -func valueFromIword(flag uint32, typ Type, w iword) Value { - if flag&flagAddr != 0 { - panic("reflect: internal error: valueFromIword addressable") - } - return packValue(flag, typ.runtimeType(), w) -} - -func (iv internalValue) mustBe(want Kind) { - if iv.kind != want { - panic(&ValueError{methodName(), iv.kind}) - } -} - -func (iv internalValue) mustBeExported() { - if iv.kind == 0 { - panic(&ValueError{methodName(), iv.kind}) - } - if iv.flag&flagRO != 0 { + if f&flagRO != 0 { panic(methodName() + " using value obtained using unexported field") } } -func (iv internalValue) mustBeAssignable() { - if iv.kind == 0 { - panic(&ValueError{methodName(), iv.kind}) +// mustBeAssignable panics if f records that the value is not assignable, +// which is to say that either it was obtained using an unexported field +// or it is not addressable. +func (f flag) mustBeAssignable() { + if f == 0 { + panic(&ValueError{methodName(), Invalid}) } // Assignable if addressable and not read-only. - if iv.flag&flagRO != 0 { + if f&flagRO != 0 { panic(methodName() + " using value obtained using unexported field") } - if iv.flag&flagAddr == 0 { + if f&flagAddr == 0 { panic(methodName() + " using unaddressable value") } } @@ -407,31 +267,31 @@ func (iv internalValue) mustBeAssignable() { // or slice element in order to call a method that requires a // pointer receiver. func (v Value) Addr() Value { - iv := v.internal() - if iv.flag&flagAddr == 0 { + if v.flag&flagAddr == 0 { panic("reflect.Value.Addr of unaddressable value") } - return valueFromIword(iv.flag&flagRO, PtrTo(iv.typ.toType()), iword(uintptr(iv.addr))) + return Value{v.typ.ptrTo(), v.val, (v.flag & flagRO) | flag(Ptr)<<flagKindShift} } // Bool returns v's underlying value. // It panics if v's kind is not Bool. func (v Value) Bool() bool { - iv := v.internal() - iv.mustBe(Bool) - return *(*bool)(unsafe.Pointer(iv.addr)) + v.mustBe(Bool) + if v.flag&flagIndir != 0 { + return *(*bool)(v.val) + } + return *(*bool)(unsafe.Pointer(&v.val)) } // Bytes returns v's underlying value. // It panics if v's underlying value is not a slice of bytes. func (v Value) Bytes() []byte { - iv := v.internal() - iv.mustBe(Slice) - typ := iv.typ.toType() - if typ.Elem().Kind() != Uint8 { + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { panic("reflect.Value.Bytes of non-byte slice") } - return *(*[]byte)(iv.addr) + // Slice is always bigger than a word; assume flagIndir. + return *(*[]byte)(v.val) } // CanAddr returns true if the value's address can be obtained with Addr. @@ -440,8 +300,7 @@ func (v Value) Bytes() []byte { // a field of an addressable struct, or the result of dereferencing a pointer. // If CanAddr returns false, calling Addr will panic. func (v Value) CanAddr() bool { - iv := v.internal() - return iv.flag&flagAddr != 0 + return v.flag&flagAddr != 0 } // CanSet returns true if the value of v can be changed. @@ -450,8 +309,7 @@ func (v Value) CanAddr() bool { // If CanSet returns false, calling Set or any type-specific // setter (e.g., SetBool, SetInt64) will panic. func (v Value) CanSet() bool { - iv := v.internal() - return iv.flag&(flagAddr|flagRO) == flagAddr + return v.flag&(flagAddr|flagRO) == flagAddr } // Call calls the function v with the input arguments in. @@ -463,10 +321,9 @@ func (v Value) CanSet() bool { // If v is a variadic function, Call creates the variadic slice parameter // itself, copying in the corresponding values. func (v Value) Call(in []Value) []Value { - iv := v.internal() - iv.mustBe(Func) - iv.mustBeExported() - return iv.call("Call", in) + v.mustBe(Func) + v.mustBeExported() + return v.call("Call", in) } // CallSlice calls the variadic function v with the input arguments in, @@ -477,22 +334,60 @@ func (v Value) Call(in []Value) []Value { // As in Go, each input argument must be assignable to the // type of the function's corresponding input parameter. func (v Value) CallSlice(in []Value) []Value { - iv := v.internal() - iv.mustBe(Func) - iv.mustBeExported() - return iv.call("CallSlice", in) -} - -func (iv internalValue) call(method string, in []Value) []Value { - if iv.word == 0 { - if iv.nilmethod { - panic("reflect.Value.Call: call of method on nil interface value") + v.mustBe(Func) + v.mustBeExported() + return v.call("CallSlice", in) +} + +func (v Value) call(method string, in []Value) []Value { + // Get function pointer, type. + t := v.typ + var ( + fn unsafe.Pointer + rcvr iword + ) + if v.flag&flagMethod != 0 { + i := int(v.flag) >> flagMethodShift + if v.typ.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: broken Value") + } + m := &tt.methods[i] + if m.pkgPath != nil { + panic(method + " of unexported method") + } + t = toCommonType(m.typ) + iface := (*nonEmptyInterface)(v.val) + if iface.itab == nil { + panic(method + " of method on nil interface value") + } + fn = iface.itab.fun[i] + rcvr = iface.word + } else { + ut := v.typ.uncommon() + if ut == nil || i < 0 || i >= len(ut.methods) { + panic("reflect: broken Value") + } + m := &ut.methods[i] + if m.pkgPath != nil { + panic(method + " of unexported method") + } + fn = m.tfn + t = toCommonType(m.mtyp) + rcvr = v.iword() } + } else if v.flag&flagIndir != 0 { + fn = *(*unsafe.Pointer)(v.val) + } else { + fn = v.val + } + + if fn == nil { panic("reflect.Value.Call: call of nil function") } isSlice := method == "CallSlice" - t := iv.typ n := t.NumIn() if isSlice { if !t.IsVariadic() { @@ -549,34 +444,32 @@ func (iv internalValue) call(method string, in []Value) []Value { } nout := t.NumOut() - if iv.method { + if v.flag&flagMethod != 0 { nin++ } params := make([]unsafe.Pointer, nin) delta := 0 off := 0 - if iv.method { + if v.flag&flagMethod != 0 { // Hard-wired first argument. p := new(iword) - *p = iv.rcvr + *p = rcvr params[0] = unsafe.Pointer(p) off = 1 } - first_pointer := false - for i, v := range in { - siv := v.internal() - siv.mustBeExported() + for i, pv := range in { + pv.mustBeExported() targ := t.In(i).(*commonType) - siv = convertForAssignment("reflect.Value.Call", nil, targ, siv) - if siv.addr == nil { + pv = pv.assignTo("reflect.Value.Call", targ, nil) + if pv.flag&flagIndir == 0 { p := new(unsafe.Pointer) - *p = unsafe.Pointer(uintptr(siv.word)) + *p = pv.val params[off] = unsafe.Pointer(p) } else { - params[off] = siv.addr + params[off] = pv.val } - if i == 0 && Kind(targ.kind) != Ptr && !iv.method && isMethod(iv.typ) { + if i == 0 && Kind(targ.kind) != Ptr && v.flag&flagMethod == 0 && isMethod(v.typ) { p := new(unsafe.Pointer) *p = params[off] params[off] = unsafe.Pointer(p) @@ -602,7 +495,7 @@ func (iv internalValue) call(method string, in []Value) []Value { pr = &results[0] } - call(t, *(*unsafe.Pointer)(iv.addr), iv.method, first_pointer, pp, pr) + call(t, fn, v.flag&flagMethod != 0, first_pointer, pp, pr) return ret } @@ -635,39 +528,42 @@ func isMethod(t *commonType) bool { // Cap returns v's capacity. // It panics if v's Kind is not Array, Chan, or Slice. func (v Value) Cap() int { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Array: - return iv.typ.Len() + return v.typ.Len() case Chan: - return int(chancap(*(*iword)(iv.addr))) + return int(chancap(*(*iword)(v.iword()))) case Slice: - return (*SliceHeader)(iv.addr).Cap + // Slice is always bigger than a word; assume flagIndir. + return (*SliceHeader)(v.val).Cap } - panic(&ValueError{"reflect.Value.Cap", iv.kind}) + panic(&ValueError{"reflect.Value.Cap", k}) } // Close closes the channel v. // It panics if v's Kind is not Chan. func (v Value) Close() { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - ch := *(*iword)(iv.addr) - chanclose(ch) + v.mustBe(Chan) + v.mustBeExported() + chanclose(*(*iword)(v.iword())) } // Complex returns v's underlying value, as a complex128. // It panics if v's Kind is not Complex64 or Complex128 func (v Value) Complex() complex128 { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Complex64: - return complex128(*(*complex64)(iv.addr)) + if v.flag&flagIndir != 0 { + return complex128(*(*complex64)(v.val)) + } + return complex128(*(*complex64)(unsafe.Pointer(&v.val))) case Complex128: - return *(*complex128)(iv.addr) + // complex128 is always bigger than a word; assume flagIndir. + return *(*complex128)(v.val) } - panic(&ValueError{"reflect.Value.Complex", iv.kind}) + panic(&ValueError{"reflect.Value.Complex", k}) } // Elem returns the value that the interface v contains @@ -675,90 +571,94 @@ func (v Value) Complex() complex128 { // It panics if v's Kind is not Interface or Ptr. // It returns the zero Value if v is nil. func (v Value) Elem() Value { - iv := v.internal() - return iv.Elem() -} - -func (iv internalValue) Elem() Value { - switch iv.kind { + k := v.kind() + switch k { case Interface: - // Empty interface and non-empty interface have different layouts. - // Convert to empty interface. - var eface emptyInterface - if iv.typ.NumMethod() == 0 { - eface = *(*emptyInterface)(iv.addr) + var ( + typ *commonType + val unsafe.Pointer + ) + if v.typ.NumMethod() == 0 { + eface := (*emptyInterface)(v.val) + if eface.typ == nil { + // nil interface value + return Value{} + } + typ = toCommonType(eface.typ) + val = unsafe.Pointer(eface.word) } else { - iface := (*nonEmptyInterface)(iv.addr) - if iface.itab != nil { - eface.typ = iface.itab.typ + iface := (*nonEmptyInterface)(v.val) + if iface.itab == nil { + // nil interface value + return Value{} } - eface.word = iface.word + typ = toCommonType(iface.itab.typ) + val = unsafe.Pointer(iface.word) } - if eface.typ == nil { - return Value{} + fl := v.flag & flagRO + fl |= flag(typ.Kind()) << flagKindShift + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir } - return valueFromIword(iv.flag&flagRO, toType(eface.typ), eface.word) + return Value{typ, val, fl} case Ptr: + val := v.val + if v.flag&flagIndir != 0 { + val = *(*unsafe.Pointer)(val) + } // The returned value's address is v's value. - if iv.word == 0 { + if val == nil { return Value{} } - return valueFromAddr(iv.flag&flagRO|flagAddr, iv.typ.Elem(), unsafe.Pointer(uintptr(iv.word))) + tt := (*ptrType)(unsafe.Pointer(v.typ)) + typ := toCommonType(tt.elem) + fl := v.flag&flagRO | flagIndir | flagAddr + fl |= flag(typ.Kind() << flagKindShift) + return Value{typ, val, fl} } - panic(&ValueError{"reflect.Value.Elem", iv.kind}) + panic(&ValueError{"reflect.Value.Elem", k}) } // Field returns the i'th field of the struct v. // It panics if v's Kind is not Struct or i is out of range. func (v Value) Field(i int) Value { - iv := v.internal() - iv.mustBe(Struct) - t := iv.typ.toType() - if i < 0 || i >= t.NumField() { + v.mustBe(Struct) + tt := (*structType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.fields) { panic("reflect: Field index out of range") } - f := t.Field(i) + field := &tt.fields[i] + typ := toCommonType(field.typ) // Inherit permission bits from v. - flag := iv.flag + fl := v.flag & (flagRO | flagIndir | flagAddr) // Using an unexported field forces flagRO. - if f.PkgPath != "" { - flag |= flagRO + if field.pkgPath != nil { + fl |= flagRO } - return valueFromValueOffset(flag, f.Type, iv, f.Offset) -} + fl |= flag(typ.Kind()) << flagKindShift -// valueFromValueOffset returns a sub-value of outer -// (outer is an array or a struct) with the given flag and type -// starting at the given byte offset into outer. -func valueFromValueOffset(flag uint32, typ Type, outer internalValue, offset uintptr) Value { - if outer.addr != nil { - return valueFromAddr(flag, typ, unsafe.Pointer(uintptr(outer.addr)+offset)) + var val unsafe.Pointer + switch { + case fl&flagIndir != 0: + // Indirect. Just bump pointer. + val = unsafe.Pointer(uintptr(v.val) + field.offset) + case bigEndian: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) << (field.offset * 8)) + default: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) >> (field.offset * 8)) } - // outer is so tiny it is in line. - // We have to use outer.word and derive - // the new word (it cannot possibly be bigger). - // In line, so not addressable. - if flag&flagAddr != 0 { - panic("reflect: internal error: misuse of valueFromValueOffset") - } - b := *(*[ptrSize]byte)(unsafe.Pointer(&outer.word)) - for i := uintptr(0); i < typ.Size(); i++ { - b[i] = b[offset+i] - } - for i := typ.Size(); i < ptrSize; i++ { - b[i] = 0 - } - w := *(*iword)(unsafe.Pointer(&b)) - return valueFromIword(flag, typ, w) + return Value{typ, val, fl} } // FieldByIndex returns the nested field corresponding to index. // It panics if v's Kind is not struct. func (v Value) FieldByIndex(index []int) Value { - v.internal().mustBe(Struct) + v.mustBe(Struct) for i, x := range index { if i > 0 { if v.Kind() == Ptr && v.Elem().Kind() == Struct { @@ -774,9 +674,8 @@ func (v Value) FieldByIndex(index []int) Value { // It returns the zero Value if no field was found. // It panics if v's Kind is not struct. func (v Value) FieldByName(name string) Value { - iv := v.internal() - iv.mustBe(Struct) - if f, ok := iv.typ.FieldByName(name); ok { + v.mustBe(Struct) + if f, ok := v.typ.FieldByName(name); ok { return v.FieldByIndex(f.Index) } return Value{} @@ -787,8 +686,8 @@ func (v Value) FieldByName(name string) Value { // It panics if v's Kind is not struct. // It returns the zero Value if no field was found. func (v Value) FieldByNameFunc(match func(string) bool) Value { - v.internal().mustBe(Struct) - if f, ok := v.Type().FieldByNameFunc(match); ok { + v.mustBe(Struct) + if f, ok := v.typ.FieldByNameFunc(match); ok { return v.FieldByIndex(f.Index) } return Value{} @@ -797,74 +696,101 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value { // Float returns v's underlying value, as an float64. // It panics if v's Kind is not Float32 or Float64 func (v Value) Float() float64 { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Float32: - return float64(*(*float32)(iv.addr)) + if v.flag&flagIndir != 0 { + return float64(*(*float32)(v.val)) + } + return float64(*(*float32)(unsafe.Pointer(&v.val))) case Float64: - return *(*float64)(iv.addr) + if v.flag&flagIndir != 0 { + return *(*float64)(v.val) + } + return *(*float64)(unsafe.Pointer(&v.val)) } - panic(&ValueError{"reflect.Value.Float", iv.kind}) + panic(&ValueError{"reflect.Value.Float", k}) } // Index returns v's i'th element. // It panics if v's Kind is not Array or Slice or i is out of range. func (v Value) Index(i int) Value { - iv := v.internal() - switch iv.kind { - default: - panic(&ValueError{"reflect.Value.Index", iv.kind}) + k := v.kind() + switch k { case Array: - flag := iv.flag // element flag same as overall array - t := iv.typ.toType() - if i < 0 || i > t.Len() { + tt := (*arrayType)(unsafe.Pointer(v.typ)) + if i < 0 || i > int(tt.len) { panic("reflect: array index out of range") } - typ := t.Elem() - return valueFromValueOffset(flag, typ, iv, uintptr(i)*typ.Size()) + typ := toCommonType(tt.elem) + fl := v.flag & (flagRO | flagIndir | flagAddr) // bits same as overall array + fl |= flag(typ.Kind()) << flagKindShift + offset := uintptr(i) * typ.size + + var val unsafe.Pointer + switch { + case fl&flagIndir != 0: + // Indirect. Just bump pointer. + val = unsafe.Pointer(uintptr(v.val) + offset) + case bigEndian: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) << (offset * 8)) + default: + // Direct. Discard leading bytes. + val = unsafe.Pointer(uintptr(v.val) >> (offset * 8)) + } + return Value{typ, val, fl} case Slice: // Element flag same as Elem of Ptr. - // Addressable, possibly read-only. - flag := iv.flag&flagRO | flagAddr - s := (*SliceHeader)(iv.addr) + // Addressable, indirect, possibly read-only. + fl := flagAddr | flagIndir | v.flag&flagRO + s := (*SliceHeader)(v.val) if i < 0 || i >= s.Len { panic("reflect: slice index out of range") } - typ := iv.typ.Elem() - addr := unsafe.Pointer(s.Data + uintptr(i)*typ.Size()) - return valueFromAddr(flag, typ, addr) + tt := (*sliceType)(unsafe.Pointer(v.typ)) + typ := toCommonType(tt.elem) + fl |= flag(typ.Kind()) << flagKindShift + val := unsafe.Pointer(s.Data + uintptr(i)*typ.size) + return Value{typ, val, fl} } - - panic("not reached") + panic(&ValueError{"reflect.Value.Index", k}) } // Int returns v's underlying value, as an int64. // It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64. func (v Value) Int() int64 { - iv := v.internal() - switch iv.kind { + k := v.kind() + var p unsafe.Pointer + if v.flag&flagIndir != 0 { + p = v.val + } else { + // The escape analysis is good enough that &v.val + // does not trigger a heap allocation. + p = unsafe.Pointer(&v.val) + } + switch k { case Int: - return int64(*(*int)(iv.addr)) + return int64(*(*int)(p)) case Int8: - return int64(*(*int8)(iv.addr)) + return int64(*(*int8)(p)) case Int16: - return int64(*(*int16)(iv.addr)) + return int64(*(*int16)(p)) case Int32: - return int64(*(*int32)(iv.addr)) + return int64(*(*int32)(p)) case Int64: - return *(*int64)(iv.addr) + return int64(*(*int64)(p)) } - panic(&ValueError{"reflect.Value.Int", iv.kind}) + panic(&ValueError{"reflect.Value.Int", k}) } // CanInterface returns true if Interface can be used without panicking. func (v Value) CanInterface() bool { - iv := v.internal() - if iv.kind == Invalid { - panic(&ValueError{"reflect.Value.CanInterface", iv.kind}) + if v.flag == 0 { + panic(&ValueError{"reflect.Value.CanInterface", Invalid}) } - return v.InternalMethod == 0 && iv.flag&flagRO == 0 + return v.flag&(flagMethod|flagRO) == 0 } // Interface returns v's value as an interface{}. @@ -876,75 +802,72 @@ func (v Value) Interface() interface{} { } func valueInterface(v Value, safe bool) interface{} { - iv := v.internal() - return iv.valueInterface(safe) -} - -func (iv internalValue) valueInterface(safe bool) interface{} { - if iv.kind == 0 { - panic(&ValueError{"reflect.Value.Interface", iv.kind}) + if v.flag == 0 { + panic(&ValueError{"reflect.Value.Interface", 0}) } - if iv.method { + if v.flag&flagMethod != 0 { panic("reflect.Value.Interface: cannot create interface value for method with bound receiver") } - if safe && iv.flag&flagRO != 0 { + if safe && v.flag&flagRO != 0 { // Do not allow access to unexported values via Interface, // because they might be pointers that should not be // writable or methods or function that should not be callable. panic("reflect.Value.Interface: cannot return value obtained from unexported field or method") } - if iv.kind == Interface { + + k := v.kind() + if k == Interface { // Special case: return the element inside the interface. - // Won't recurse further because an interface cannot contain an interface. - if iv.IsNil() { - return nil + // Empty interface has one layout, all interfaces with + // methods have a second layout. + if v.NumMethod() == 0 { + return *(*interface{})(v.val) } - return iv.Elem().Interface() + return *(*interface { + M() + })(v.val) } // Non-interface value. var eface emptyInterface - eface.typ = iv.typ.runtimeType() - eface.word = iv.word + eface.typ = v.typ.runtimeType() + eface.word = v.iword() return *(*interface{})(unsafe.Pointer(&eface)) } // InterfaceData returns the interface v's value as a uintptr pair. // It panics if v's Kind is not Interface. func (v Value) InterfaceData() [2]uintptr { - iv := v.internal() - iv.mustBe(Interface) + v.mustBe(Interface) // We treat this as a read operation, so we allow // it even for unexported data, because the caller // has to import "unsafe" to turn it into something // that can be abused. - return *(*[2]uintptr)(iv.addr) + // Interface value is always bigger than a word; assume flagIndir. + return *(*[2]uintptr)(v.val) } // IsNil returns true if v is a nil value. // It panics if v's Kind is not Chan, Func, Interface, Map, Ptr, or Slice. func (v Value) IsNil() bool { - return v.internal().IsNil() -} - -func (iv internalValue) IsNil() bool { - switch iv.kind { - case Ptr: - if iv.method { + k := v.kind() + switch k { + case Chan, Func, Map, Ptr: + if v.flag&flagMethod != 0 { panic("reflect: IsNil of method Value") } - return iv.word == 0 - case Chan, Func, Map: - if iv.method { - panic("reflect: IsNil of method Value") + ptr := v.val + if v.flag&flagIndir != 0 { + ptr = *(*unsafe.Pointer)(ptr) } - return *(*uintptr)(iv.addr) == 0 + return ptr == nil case Interface, Slice: // Both interface and slice are nil if first word is 0. - return *(*uintptr)(iv.addr) == 0 + // Both are always bigger than a word; assume flagIndir. + return *(*unsafe.Pointer)(v.val) == nil } - panic(&ValueError{"reflect.Value.IsNil", iv.kind}) + panic(&ValueError{"reflect.Value.IsNil", k}) } // IsValid returns true if v represents a value. @@ -953,32 +876,35 @@ func (iv internalValue) IsNil() bool { // Most functions and methods never return an invalid value. // If one does, its documentation states the conditions explicitly. func (v Value) IsValid() bool { - return v.Internal != nil + return v.flag != 0 } // Kind returns v's Kind. // If v is the zero Value (IsValid returns false), Kind returns Invalid. func (v Value) Kind() Kind { - return v.internal().kind + return v.kind() } // Len returns v's length. // It panics if v's Kind is not Array, Chan, Map, Slice, or String. func (v Value) Len() int { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Array: - return iv.typ.Len() + tt := (*arrayType)(unsafe.Pointer(v.typ)) + return int(tt.len) case Chan: - return int(chanlen(*(*iword)(iv.addr))) + return int(chanlen(*(*iword)(v.iword()))) case Map: - return int(maplen(*(*iword)(iv.addr))) + return int(maplen(*(*iword)(v.iword()))) case Slice: - return (*SliceHeader)(iv.addr).Len + // Slice is bigger than a word; assume flagIndir. + return (*SliceHeader)(v.val).Len case String: - return (*StringHeader)(iv.addr).Len + // String is bigger than a word; assume flagIndir. + return (*StringHeader)(v.val).Len } - panic(&ValueError{"reflect.Value.Len", iv.kind}) + panic(&ValueError{"reflect.Value.Len", k}) } // MapIndex returns the value associated with key in the map v. @@ -986,29 +912,29 @@ func (v Value) Len() int { // It returns the zero Value if key is not found in the map or if v represents a nil map. // As in Go, the key's value must be assignable to the map's key type. func (v Value) MapIndex(key Value) Value { - iv := v.internal() - iv.mustBe(Map) - typ := iv.typ.toType() + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ)) - // Do not require ikey to be exported, so that DeepEqual + // Do not require key to be exported, so that DeepEqual // and other programs can use all the keys returned by // MapKeys as arguments to MapIndex. If either the map // or the key is unexported, though, the result will be - // considered unexported. - - ikey := key.internal() - ikey = convertForAssignment("reflect.Value.MapIndex", nil, typ.Key(), ikey) - if iv.word == 0 { - return Value{} - } + // considered unexported. This is consistent with the + // behavior for structs, which allow read but not write + // of unexported fields. + key = key.assignTo("reflect.Value.MapIndex", toCommonType(tt.key), nil) - flag := (iv.flag | ikey.flag) & flagRO - elemType := typ.Elem() - elemWord, ok := mapaccess(typ.runtimeType(), *(*iword)(iv.addr), ikey.word) + word, ok := mapaccess(v.typ.runtimeType(), *(*iword)(v.iword()), key.iword()) if !ok { return Value{} } - return valueFromIword(flag, elemType, elemWord) + typ := toCommonType(tt.elem) + fl := (v.flag | key.flag) & flagRO + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir + } + fl |= flag(typ.Kind()) << flagKindShift + return Value{typ, unsafe.Pointer(word), fl} } // MapKeys returns a slice containing all the keys present in the map, @@ -1016,17 +942,22 @@ func (v Value) MapIndex(key Value) Value { // It panics if v's Kind is not Map. // It returns an empty slice if v represents a nil map. func (v Value) MapKeys() []Value { - iv := v.internal() - iv.mustBe(Map) - keyType := iv.typ.Key() + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ)) + keyType := toCommonType(tt.key) + + fl := v.flag & flagRO + fl |= flag(keyType.Kind()) << flagKindShift + if keyType.Kind() != Ptr && keyType.Kind() != UnsafePointer { + fl |= flagIndir + } - flag := iv.flag & flagRO - m := *(*iword)(iv.addr) + m := *(*iword)(v.iword()) mlen := int32(0) - if m != 0 { + if m != nil { mlen = maplen(m) } - it := mapiterinit(iv.typ.runtimeType(), m) + it := mapiterinit(v.typ.runtimeType(), m) a := make([]Value, mlen) var i int for i = 0; i < len(a); i++ { @@ -1034,7 +965,7 @@ func (v Value) MapKeys() []Value { if !ok { break } - a[i] = valueFromIword(flag, keyType, keyWord) + a[i] = Value{keyType, unsafe.Pointer(keyWord), fl} mapiternext(it) } return a[:i] @@ -1045,23 +976,27 @@ func (v Value) MapKeys() []Value { // a receiver; the returned function will always use v as the receiver. // Method panics if i is out of range. func (v Value) Method(i int) Value { - iv := v.internal() - if iv.kind == Invalid { + if v.typ == nil { panic(&ValueError{"reflect.Value.Method", Invalid}) } - if i < 0 || i >= iv.typ.NumMethod() { + if v.flag&flagMethod != 0 || i < 0 || i >= v.typ.NumMethod() { panic("reflect: Method index out of range") } - return Value{v.Internal, i + 1} + fl := v.flag & (flagRO | flagAddr | flagIndir) + fl |= flag(Func) << flagKindShift + fl |= flag(i)<<flagMethodShift | flagMethod + return Value{v.typ, v.val, fl} } // NumMethod returns the number of methods in the value's method set. func (v Value) NumMethod() int { - iv := v.internal() - if iv.kind == Invalid { + if v.typ == nil { panic(&ValueError{"reflect.Value.NumMethod", Invalid}) } - return iv.typ.NumMethod() + if v.flag&flagMethod != 0 { + return 0 + } + return v.typ.NumMethod() } // MethodByName returns a function value corresponding to the method @@ -1070,49 +1005,51 @@ func (v Value) NumMethod() int { // a receiver; the returned function will always use v as the receiver. // It returns the zero Value if no method was found. func (v Value) MethodByName(name string) Value { - iv := v.internal() - if iv.kind == Invalid { + if v.typ == nil { panic(&ValueError{"reflect.Value.MethodByName", Invalid}) } - m, ok := iv.typ.MethodByName(name) - if ok { - return Value{v.Internal, m.Index + 1} + if v.flag&flagMethod != 0 { + return Value{} } - return Value{} + m, ok := v.typ.MethodByName(name) + if !ok { + return Value{} + } + return v.Method(m.Index) } // NumField returns the number of fields in the struct v. // It panics if v's Kind is not Struct. func (v Value) NumField() int { - iv := v.internal() - iv.mustBe(Struct) - return iv.typ.NumField() + v.mustBe(Struct) + tt := (*structType)(unsafe.Pointer(v.typ)) + return len(tt.fields) } // OverflowComplex returns true if the complex128 x cannot be represented by v's type. // It panics if v's Kind is not Complex64 or Complex128. func (v Value) OverflowComplex(x complex128) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Complex64: return overflowFloat32(real(x)) || overflowFloat32(imag(x)) case Complex128: return false } - panic(&ValueError{"reflect.Value.OverflowComplex", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowComplex", k}) } // OverflowFloat returns true if the float64 x cannot be represented by v's type. // It panics if v's Kind is not Float32 or Float64. func (v Value) OverflowFloat(x float64) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Float32: return overflowFloat32(x) case Float64: return false } - panic(&ValueError{"reflect.Value.OverflowFloat", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowFloat", k}) } func overflowFloat32(x float64) bool { @@ -1125,27 +1062,27 @@ func overflowFloat32(x float64) bool { // OverflowInt returns true if the int64 x cannot be represented by v's type. // It panics if v's Kind is not Int, Int8, int16, Int32, or Int64. func (v Value) OverflowInt(x int64) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Int, Int8, Int16, Int32, Int64: - bitSize := iv.typ.size * 8 + bitSize := v.typ.size * 8 trunc := (x << (64 - bitSize)) >> (64 - bitSize) return x != trunc } - panic(&ValueError{"reflect.Value.OverflowInt", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowInt", k}) } // OverflowUint returns true if the uint64 x cannot be represented by v's type. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. func (v Value) OverflowUint(x uint64) bool { - iv := v.internal() - switch iv.kind { + k := v.kind() + switch k { case Uint, Uintptr, Uint8, Uint16, Uint32, Uint64: - bitSize := iv.typ.size * 8 + bitSize := v.typ.size * 8 trunc := (x << (64 - bitSize)) >> (64 - bitSize) return x != trunc } - panic(&ValueError{"reflect.Value.OverflowUint", iv.kind}) + panic(&ValueError{"reflect.Value.OverflowUint", k}) } // Pointer returns v's value as a uintptr. @@ -1154,22 +1091,21 @@ func (v Value) OverflowUint(x uint64) bool { // without importing the unsafe package explicitly. // It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer. func (v Value) Pointer() uintptr { - iv := v.internal() - switch iv.kind { - case Ptr, UnsafePointer: - if iv.kind == Func && v.InternalMethod != 0 { + k := v.kind() + switch k { + case Chan, Func, Map, Ptr, UnsafePointer: + if k == Func && v.flag&flagMethod != 0 { panic("reflect.Value.Pointer of method Value") } - return uintptr(iv.word) - case Chan, Func, Map: - if iv.kind == Func && v.InternalMethod != 0 { - panic("reflect.Value.Pointer of method Value") + p := v.val + if v.flag&flagIndir != 0 { + p = *(*unsafe.Pointer)(p) } - return *(*uintptr)(iv.addr) + return uintptr(p) case Slice: - return (*SliceHeader)(iv.addr).Data + return (*SliceHeader)(v.val).Data } - panic(&ValueError{"reflect.Value.Pointer", iv.kind}) + panic(&ValueError{"reflect.Value.Pointer", k}) } // Recv receives and returns a value from the channel v. @@ -1178,25 +1114,26 @@ func (v Value) Pointer() uintptr { // The boolean value ok is true if the value x corresponds to a send // on the channel, false if it is a zero value received because the channel is closed. func (v Value) Recv() (x Value, ok bool) { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - return iv.recv(false) + v.mustBe(Chan) + v.mustBeExported() + return v.recv(false) } -// internal recv, possibly non-blocking (nb) -func (iv internalValue) recv(nb bool) (val Value, ok bool) { - t := iv.typ.toType() - if t.ChanDir()&RecvDir == 0 { +// internal recv, possibly non-blocking (nb). +// v is known to be a channel. +func (v Value) recv(nb bool) (val Value, ok bool) { + tt := (*chanType)(unsafe.Pointer(v.typ)) + if ChanDir(tt.dir)&RecvDir == 0 { panic("recv on send-only channel") } - ch := *(*iword)(iv.addr) - if ch == 0 { - panic("recv on nil channel") - } - valWord, selected, ok := chanrecv(iv.typ.runtimeType(), ch, nb) + word, selected, ok := chanrecv(v.typ.runtimeType(), *(*iword)(v.iword()), nb) if selected { - val = valueFromIword(0, t.Elem(), valWord) + typ := toCommonType(tt.elem) + fl := flag(typ.Kind()) << flagKindShift + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir + } + val = Value{typ, unsafe.Pointer(word), fl} } return } @@ -1205,128 +1142,114 @@ func (iv internalValue) recv(nb bool) (val Value, ok bool) { // It panics if v's kind is not Chan or if x's type is not the same type as v's element type. // As in Go, x's value must be assignable to the channel's element type. func (v Value) Send(x Value) { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - iv.send(x, false) + v.mustBe(Chan) + v.mustBeExported() + v.send(x, false) } -// internal send, possibly non-blocking -func (iv internalValue) send(x Value, nb bool) (selected bool) { - t := iv.typ.toType() - if t.ChanDir()&SendDir == 0 { +// internal send, possibly non-blocking. +// v is known to be a channel. +func (v Value) send(x Value, nb bool) (selected bool) { + tt := (*chanType)(unsafe.Pointer(v.typ)) + if ChanDir(tt.dir)&SendDir == 0 { panic("send on recv-only channel") } - ix := x.internal() - ix.mustBeExported() // do not let unexported x leak - ix = convertForAssignment("reflect.Value.Send", nil, t.Elem(), ix) - ch := *(*iword)(iv.addr) - if ch == 0 { - panic("send on nil channel") - } - return chansend(iv.typ.runtimeType(), ch, ix.word, nb) + x.mustBeExported() + x = x.assignTo("reflect.Value.Send", toCommonType(tt.elem), nil) + return chansend(v.typ.runtimeType(), *(*iword)(v.iword()), x.iword(), nb) } // Set assigns x to the value v. // It panics if CanSet returns false. // As in Go, x's value must be assignable to v's type. func (v Value) Set(x Value) { - iv := v.internal() - ix := x.internal() - - iv.mustBeAssignable() - ix.mustBeExported() // do not let unexported x leak - - ix = convertForAssignment("reflect.Set", iv.addr, iv.typ, ix) - - n := ix.typ.size - if Kind(ix.typ.kind) == Ptr || Kind(ix.typ.kind) == UnsafePointer { - storeIword(iv.addr, ix.word, n) + v.mustBeAssignable() + x.mustBeExported() // do not let unexported x leak + var target *interface{} + if v.kind() == Interface { + target = (*interface{})(v.val) + } + x = x.assignTo("reflect.Set", v.typ, target) + if x.flag&flagIndir != 0 { + memmove(v.val, x.val, v.typ.size) } else { - memmove(iv.addr, ix.addr, n) + storeIword(v.val, iword(x.val), v.typ.size) } } // SetBool sets v's underlying value. // It panics if v's Kind is not Bool or if CanSet() is false. func (v Value) SetBool(x bool) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(Bool) - *(*bool)(iv.addr) = x + v.mustBeAssignable() + v.mustBe(Bool) + *(*bool)(v.val) = x } // SetBytes sets v's underlying value. // It panics if v's underlying value is not a slice of bytes. func (v Value) SetBytes(x []byte) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(Slice) - typ := iv.typ.toType() - if typ.Elem().Kind() != Uint8 { + v.mustBeAssignable() + v.mustBe(Slice) + if v.typ.Elem().Kind() != Uint8 { panic("reflect.Value.SetBytes of non-byte slice") } - *(*[]byte)(iv.addr) = x + *(*[]byte)(v.val) = x } // SetComplex sets v's underlying value to x. // It panics if v's Kind is not Complex64 or Complex128, or if CanSet() is false. func (v Value) SetComplex(x complex128) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetComplex", iv.kind}) + panic(&ValueError{"reflect.Value.SetComplex", k}) case Complex64: - *(*complex64)(iv.addr) = complex64(x) + *(*complex64)(v.val) = complex64(x) case Complex128: - *(*complex128)(iv.addr) = x + *(*complex128)(v.val) = x } } // SetFloat sets v's underlying value to x. // It panics if v's Kind is not Float32 or Float64, or if CanSet() is false. func (v Value) SetFloat(x float64) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetFloat", iv.kind}) + panic(&ValueError{"reflect.Value.SetFloat", k}) case Float32: - *(*float32)(iv.addr) = float32(x) + *(*float32)(v.val) = float32(x) case Float64: - *(*float64)(iv.addr) = x + *(*float64)(v.val) = x } } // SetInt sets v's underlying value to x. // It panics if v's Kind is not Int, Int8, Int16, Int32, or Int64, or if CanSet() is false. func (v Value) SetInt(x int64) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetInt", iv.kind}) + panic(&ValueError{"reflect.Value.SetInt", k}) case Int: - *(*int)(iv.addr) = int(x) + *(*int)(v.val) = int(x) case Int8: - *(*int8)(iv.addr) = int8(x) + *(*int8)(v.val) = int8(x) case Int16: - *(*int16)(iv.addr) = int16(x) + *(*int16)(v.val) = int16(x) case Int32: - *(*int32)(iv.addr) = int32(x) + *(*int32)(v.val) = int32(x) case Int64: - *(*int64)(iv.addr) = x + *(*int64)(v.val) = x } } // SetLen sets v's length to n. // It panics if v's Kind is not Slice. func (v Value) SetLen(n int) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(Slice) - s := (*SliceHeader)(iv.addr) + v.mustBeAssignable() + v.mustBe(Slice) + s := (*SliceHeader)(v.val) if n < 0 || n > int(s.Cap) { panic("reflect: slice length out of range in SetLen") } @@ -1339,88 +1262,84 @@ func (v Value) SetLen(n int) { // As in Go, key's value must be assignable to the map's key type, // and val's value must be assignable to the map's value type. func (v Value) SetMapIndex(key, val Value) { - iv := v.internal() - ikey := key.internal() - ival := val.internal() - - iv.mustBe(Map) - iv.mustBeExported() - - ikey.mustBeExported() - ikey = convertForAssignment("reflect.Value.SetMapIndex", nil, iv.typ.Key(), ikey) - - if ival.kind != Invalid { - ival.mustBeExported() - ival = convertForAssignment("reflect.Value.SetMapIndex", nil, iv.typ.Elem(), ival) + v.mustBe(Map) + v.mustBeExported() + key.mustBeExported() + tt := (*mapType)(unsafe.Pointer(v.typ)) + key = key.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.key), nil) + if val.typ != nil { + val.mustBeExported() + val = val.assignTo("reflect.Value.SetMapIndex", toCommonType(tt.elem), nil) } - - mapassign(iv.typ.runtimeType(), *(*iword)(iv.addr), ikey.word, ival.word, ival.kind != Invalid) + mapassign(v.typ.runtimeType(), *(*iword)(v.iword()), key.iword(), val.iword(), val.typ != nil) } // SetUint sets v's underlying value to x. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64, or if CanSet() is false. func (v Value) SetUint(x uint64) { - iv := v.internal() - iv.mustBeAssignable() - switch iv.kind { + v.mustBeAssignable() + switch k := v.kind(); k { default: - panic(&ValueError{"reflect.Value.SetUint", iv.kind}) + panic(&ValueError{"reflect.Value.SetUint", k}) case Uint: - *(*uint)(iv.addr) = uint(x) + *(*uint)(v.val) = uint(x) case Uint8: - *(*uint8)(iv.addr) = uint8(x) + *(*uint8)(v.val) = uint8(x) case Uint16: - *(*uint16)(iv.addr) = uint16(x) + *(*uint16)(v.val) = uint16(x) case Uint32: - *(*uint32)(iv.addr) = uint32(x) + *(*uint32)(v.val) = uint32(x) case Uint64: - *(*uint64)(iv.addr) = x + *(*uint64)(v.val) = x case Uintptr: - *(*uintptr)(iv.addr) = uintptr(x) + *(*uintptr)(v.val) = uintptr(x) } } // SetPointer sets the unsafe.Pointer value v to x. // It panics if v's Kind is not UnsafePointer. func (v Value) SetPointer(x unsafe.Pointer) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(UnsafePointer) - *(*unsafe.Pointer)(iv.addr) = x + v.mustBeAssignable() + v.mustBe(UnsafePointer) + *(*unsafe.Pointer)(v.val) = x } // SetString sets v's underlying value to x. // It panics if v's Kind is not String or if CanSet() is false. func (v Value) SetString(x string) { - iv := v.internal() - iv.mustBeAssignable() - iv.mustBe(String) - *(*string)(iv.addr) = x + v.mustBeAssignable() + v.mustBe(String) + *(*string)(v.val) = x } // Slice returns a slice of v. // It panics if v's Kind is not Array or Slice. func (v Value) Slice(beg, end int) Value { - iv := v.internal() - if iv.kind != Array && iv.kind != Slice { - panic(&ValueError{"reflect.Value.Slice", iv.kind}) - } - cap := v.Cap() - if beg < 0 || end < beg || end > cap { - panic("reflect.Value.Slice: slice index out of bounds") - } - var typ Type - var base uintptr - switch iv.kind { + var ( + cap int + typ *sliceType + base unsafe.Pointer + ) + switch k := v.kind(); k { + default: + panic(&ValueError{"reflect.Value.Slice", k}) case Array: - if iv.flag&flagAddr == 0 { + if v.flag&flagAddr == 0 { panic("reflect.Value.Slice: slice of unaddressable array") } - typ = toType((*arrayType)(unsafe.Pointer(iv.typ)).slice) - base = uintptr(iv.addr) + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = (*sliceType)(unsafe.Pointer(toCommonType(tt.slice))) + base = v.val case Slice: - typ = iv.typ.toType() - base = (*SliceHeader)(iv.addr).Data + typ = (*sliceType)(unsafe.Pointer(v.typ)) + s := (*SliceHeader)(v.val) + base = unsafe.Pointer(s.Data) + cap = s.Cap + + } + if beg < 0 || end < beg || end > cap { + panic("reflect.Value.Slice: slice index out of bounds") } // Declare slice so that gc can see the base pointer in it. @@ -1428,11 +1347,12 @@ func (v Value) Slice(beg, end int) Value { // Reinterpret as *SliceHeader to edit. s := (*SliceHeader)(unsafe.Pointer(&x)) - s.Data = base + uintptr(beg)*typ.Elem().Size() + s.Data = uintptr(base) + uintptr(beg)*toCommonType(typ.elem).Size() s.Len = end - beg s.Cap = end - beg - return valueFromAddr(iv.flag&flagRO, typ, unsafe.Pointer(&x)) + fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift + return Value{typ.common(), unsafe.Pointer(&x), fl} } // String returns the string v's underlying value, as a string. @@ -1440,16 +1360,15 @@ func (v Value) Slice(beg, end int) Value { // Unlike the other getters, it does not panic if v's Kind is not String. // Instead, it returns a string of the form "<T value>" where T is v's type. func (v Value) String() string { - iv := v.internal() - switch iv.kind { + switch k := v.kind(); k { case Invalid: return "<invalid Value>" case String: - return *(*string)(iv.addr) + return *(*string)(v.val) } // If you call String on a reflect.Value of other type, it's better to // print something than to panic. Useful in debugging. - return "<" + iv.typ.String() + " Value>" + return "<" + v.typ.String() + " Value>" } // TryRecv attempts to receive a value from the channel v but will not block. @@ -1458,10 +1377,9 @@ func (v Value) String() string { // The boolean ok is true if the value x corresponds to a send // on the channel, false if it is a zero value received because the channel is closed. func (v Value) TryRecv() (x Value, ok bool) { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - return iv.recv(true) + v.mustBe(Chan) + v.mustBeExported() + return v.recv(true) } // TrySend attempts to send x on the channel v but will not block. @@ -1469,54 +1387,83 @@ func (v Value) TryRecv() (x Value, ok bool) { // It returns true if the value was sent, false otherwise. // As in Go, x's value must be assignable to the channel's element type. func (v Value) TrySend(x Value) bool { - iv := v.internal() - iv.mustBe(Chan) - iv.mustBeExported() - return iv.send(x, true) + v.mustBe(Chan) + v.mustBeExported() + return v.send(x, true) } // Type returns v's type. func (v Value) Type() Type { - t := v.internal().typ - if t == nil { + f := v.flag + if f == 0 { panic(&ValueError{"reflect.Value.Type", Invalid}) } - return t.toType() + if f&flagMethod == 0 { + // Easy case + return v.typ.toType() + } + + // Method value. + // v.typ describes the receiver, not the method type. + i := int(v.flag) >> flagMethodShift + if v.typ.Kind() == Interface { + // Method on interface. + tt := (*interfaceType)(unsafe.Pointer(v.typ)) + if i < 0 || i >= len(tt.methods) { + panic("reflect: broken Value") + } + m := &tt.methods[i] + return toCommonType(m.typ).toType() + } + // Method on concrete type. + ut := v.typ.uncommon() + if ut == nil || i < 0 || i >= len(ut.methods) { + panic("reflect: broken Value") + } + m := &ut.methods[i] + return toCommonType(m.mtyp).toType() } // Uint returns v's underlying value, as a uint64. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. func (v Value) Uint() uint64 { - iv := v.internal() - switch iv.kind { + k := v.kind() + var p unsafe.Pointer + if v.flag&flagIndir != 0 { + p = v.val + } else { + // The escape analysis is good enough that &v.val + // does not trigger a heap allocation. + p = unsafe.Pointer(&v.val) + } + switch k { case Uint: - return uint64(*(*uint)(iv.addr)) + return uint64(*(*uint)(p)) case Uint8: - return uint64(*(*uint8)(iv.addr)) + return uint64(*(*uint8)(p)) case Uint16: - return uint64(*(*uint16)(iv.addr)) + return uint64(*(*uint16)(p)) case Uint32: - return uint64(*(*uint32)(iv.addr)) - case Uintptr: - return uint64(*(*uintptr)(iv.addr)) + return uint64(*(*uint32)(p)) case Uint64: - return *(*uint64)(iv.addr) + return uint64(*(*uint64)(p)) + case Uintptr: + return uint64(*(*uintptr)(p)) } - panic(&ValueError{"reflect.Value.Uint", iv.kind}) + panic(&ValueError{"reflect.Value.Uint", k}) } // UnsafeAddr returns a pointer to v's data. // It is for advanced clients that also import the "unsafe" package. // It panics if v is not addressable. func (v Value) UnsafeAddr() uintptr { - iv := v.internal() - if iv.kind == Invalid { - panic(&ValueError{"reflect.Value.UnsafeAddr", iv.kind}) + if v.typ == nil { + panic(&ValueError{"reflect.Value.UnsafeAddr", Invalid}) } - if iv.flag&flagAddr == 0 { + if v.flag&flagAddr == 0 { panic("reflect.Value.UnsafeAddr of unaddressable value") } - return uintptr(iv.addr) + return uintptr(v.val) } // StringHeader is the runtime representation of a string. @@ -1536,7 +1483,7 @@ type SliceHeader struct { func typesMustMatch(what string, t1, t2 Type) { if t1 != t2 { - panic("reflect: " + what + ": " + t1.String() + " != " + t2.String()) + panic(what + ": " + t1.String() + " != " + t2.String()) } } @@ -1571,7 +1518,7 @@ func grow(s Value, extra int) (Value, int, int) { // Append appends the values x to a slice s and returns the resulting slice. // As in Go, each x's value must be assignable to the slice's element type. func Append(s Value, x ...Value) Value { - s.internal().mustBe(Slice) + s.mustBe(Slice) s, i0, i1 := grow(s, len(x)) for i, j := i0, 0; i < i1; i, j = i+1, j+1 { s.Index(i).Set(x[j]) @@ -1582,8 +1529,8 @@ func Append(s Value, x ...Value) Value { // AppendSlice appends a slice t to a slice s and returns the resulting slice. // The slices s and t must have the same element type. func AppendSlice(s, t Value) Value { - s.internal().mustBe(Slice) - t.internal().mustBe(Slice) + s.mustBe(Slice) + t.mustBe(Slice) typesMustMatch("reflect.AppendSlice", s.Type().Elem(), t.Type().Elem()) s, i0, i1 := grow(s, t.Len()) Copy(s.Slice(i0, i1), t) @@ -1596,23 +1543,23 @@ func AppendSlice(s, t Value) Value { // Dst and src each must have kind Slice or Array, and // dst and src must have the same element type. func Copy(dst, src Value) int { - idst := dst.internal() - isrc := src.internal() - - if idst.kind != Array && idst.kind != Slice { - panic(&ValueError{"reflect.Copy", idst.kind}) + dk := dst.kind() + if dk != Array && dk != Slice { + panic(&ValueError{"reflect.Copy", dk}) } - if idst.kind == Array { - idst.mustBeAssignable() + if dk == Array { + dst.mustBeAssignable() } - idst.mustBeExported() - if isrc.kind != Array && isrc.kind != Slice { - panic(&ValueError{"reflect.Copy", isrc.kind}) + dst.mustBeExported() + + sk := src.kind() + if sk != Array && sk != Slice { + panic(&ValueError{"reflect.Copy", sk}) } - isrc.mustBeExported() + src.mustBeExported() - de := idst.typ.Elem() - se := isrc.typ.Elem() + de := dst.typ.Elem() + se := src.typ.Elem() typesMustMatch("reflect.Copy", de, se) n := dst.Len() @@ -1622,7 +1569,7 @@ func Copy(dst, src Value) int { // If sk is an in-line array, cannot take its address. // Instead, copy element by element. - if isrc.addr == nil { + if src.flag&flagIndir == 0 { for i := 0; i < n; i++ { dst.Index(i).Set(src.Index(i)) } @@ -1631,15 +1578,15 @@ func Copy(dst, src Value) int { // Copy via memmove. var da, sa unsafe.Pointer - if idst.kind == Array { - da = idst.addr + if dk == Array { + da = dst.val } else { - da = unsafe.Pointer((*SliceHeader)(idst.addr).Data) + da = unsafe.Pointer((*SliceHeader)(dst.val).Data) } - if isrc.kind == Array { - sa = isrc.addr + if sk == Array { + sa = src.val } else { - sa = unsafe.Pointer((*SliceHeader)(isrc.addr).Data) + sa = unsafe.Pointer((*SliceHeader)(src.val).Data) } memmove(da, sa, uintptr(n)*de.Size()) return n @@ -1653,7 +1600,7 @@ func Copy(dst, src Value) int { // for the specified slice type, length, and capacity. func MakeSlice(typ Type, len, cap int) Value { if typ.Kind() != Slice { - panic("reflect: MakeSlice of non-slice type") + panic("reflect.MakeSlice of non-slice type") } // Declare slice so that gc can see the base pointer in it. @@ -1665,31 +1612,31 @@ func MakeSlice(typ Type, len, cap int) Value { s.Len = len s.Cap = cap - return valueFromAddr(0, typ, unsafe.Pointer(&x)) + return Value{typ.common(), unsafe.Pointer(&x), flagIndir | flag(Slice)<<flagKindShift} } // MakeChan creates a new channel with the specified type and buffer size. func MakeChan(typ Type, buffer int) Value { if typ.Kind() != Chan { - panic("reflect: MakeChan of non-chan type") + panic("reflect.MakeChan of non-chan type") } if buffer < 0 { - panic("MakeChan: negative buffer size") + panic("reflect.MakeChan: negative buffer size") } if typ.ChanDir() != BothDir { - panic("MakeChan: unidirectional channel type") + panic("reflect.MakeChan: unidirectional channel type") } ch := makechan(typ.runtimeType(), uint32(buffer)) - return valueFromIword(0, typ, ch) + return Value{typ.common(), unsafe.Pointer(ch), flagIndir | (flag(Chan)<<flagKindShift)} } // MakeMap creates a new map of the specified type. func MakeMap(typ Type) Value { if typ.Kind() != Map { - panic("reflect: MakeMap of non-map type") + panic("reflect.MakeMap of non-map type") } m := makemap(typ.runtimeType()) - return valueFromIword(0, typ, m) + return Value{typ.common(), unsafe.Pointer(m), flagIndir | (flag(Map)<<flagKindShift)} } // Indirect returns the value that v points to. @@ -1719,7 +1666,12 @@ func ValueOf(i interface{}) Value { // For an interface value with the noAddr bit set, // the representation is identical to an empty interface. eface := *(*emptyInterface)(unsafe.Pointer(&i)) - return packValue(0, eface.typ, eface.word) + typ := toCommonType(eface.typ) + fl := flag(typ.Kind()) << flagKindShift + if typ.Kind() != Ptr && typ.Kind() != UnsafePointer { + fl |= flagIndir + } + return Value{typ, unsafe.Pointer(eface.word), fl} } // Zero returns a Value representing a zero value for the specified type. @@ -1730,10 +1682,12 @@ func Zero(typ Type) Value { if typ == nil { panic("reflect: Zero(nil)") } - if typ.Kind() == Ptr || typ.Kind() == UnsafePointer { - return valueFromIword(0, typ, 0) + t := typ.common() + fl := flag(t.Kind()) << flagKindShift + if t.Kind() == Ptr || t.Kind() == UnsafePointer { + return Value{t, nil, fl} } - return valueFromAddr(0, typ, unsafe.New(typ)) + return Value{t, unsafe.New(typ), fl | flagIndir} } // New returns a Value representing a pointer to a new zero value @@ -1743,40 +1697,42 @@ func New(typ Type) Value { panic("reflect: New(nil)") } ptr := unsafe.New(typ) - return valueFromIword(0, PtrTo(typ), iword(uintptr(ptr))) + fl := flag(Ptr) << flagKindShift + return Value{typ.common().ptrTo(), ptr, fl} } -// convertForAssignment -func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv internalValue) internalValue { - if iv.method { - panic(what + ": cannot assign method value to type " + dst.String()) +// assignTo returns a value v that can be assigned directly to typ. +// It panics if v is not assignable to typ. +// For a conversion to an interface type, target is a suggested scratch space to use. +func (v Value) assignTo(context string, dst *commonType, target *interface{}) Value { + if v.flag&flagMethod != 0 { + panic(context + ": cannot assign method value to type " + dst.String()) } - dst1 := dst.(*commonType) - if directlyAssignable(dst1, iv.typ) { + switch { + case directlyAssignable(dst, v.typ): // Overwrite type so that they match. // Same memory layout, so no harm done. - iv.typ = dst1 - return iv - } - if implements(dst1, iv.typ) { - if addr == nil { - addr = unsafe.Pointer(new(interface{})) + v.typ = dst + fl := v.flag & (flagRO | flagAddr | flagIndir) + fl |= flag(dst.Kind()) << flagKindShift + return Value{dst, v.val, fl} + + case implements(dst, v.typ): + if target == nil { + target = new(interface{}) } - x := iv.valueInterface(false) + x := valueInterface(v, false) if dst.NumMethod() == 0 { - *(*interface{})(addr) = x + *target = x } else { - ifaceE2I(dst1.runtimeType(), x, addr) + ifaceE2I(dst.runtimeType(), x, unsafe.Pointer(target)) } - iv.addr = addr - iv.word = iword(uintptr(addr)) - iv.typ = dst1 - return iv + return Value{dst, unsafe.Pointer(target), flagIndir | flag(Interface)<<flagKindShift} } // Failed. - panic(what + ": value of type " + iv.typ.String() + " is not assignable to type " + dst.String()) + panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String()) } // implemented in ../pkg/runtime @@ -1787,7 +1743,7 @@ func chanrecv(t *runtime.Type, ch iword, nb bool) (val iword, selected, received func chansend(t *runtime.Type, ch iword, val iword, nb bool) bool func makechan(typ *runtime.Type, size uint32) (ch iword) -func makemap(t *runtime.Type) iword +func makemap(t *runtime.Type) (m iword) func mapaccess(t *runtime.Type, m iword, key iword) (val iword, ok bool) func mapassign(t *runtime.Type, m iword, key, val iword, ok bool) func mapiterinit(t *runtime.Type, m iword) *byte @@ -1797,3 +1753,17 @@ func maplen(m iword) int32 func call(typ *commonType, fnaddr unsafe.Pointer, isInterface bool, isMethod bool, params *unsafe.Pointer, results *unsafe.Pointer) func ifaceE2I(t *runtime.Type, src interface{}, dst unsafe.Pointer) + +// Dummy annotation marking that the value x escapes, +// for use in cases where the reflect code is so clever that +// the compiler cannot follow. +func escapes(x interface{}) { + if dummy.b { + dummy.x = x + } +} + +var dummy struct { + b bool + x interface{} +} diff --git a/libgo/go/regexp/regexp.go b/libgo/go/regexp/regexp.go index b906076..59f3be3 100644 --- a/libgo/go/regexp/regexp.go +++ b/libgo/go/regexp/regexp.go @@ -1,7 +1,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package regexp implements a simple regular expression library. +// Package regexp implements regular expression search. // // The syntax of the regular expressions accepted is the same // general syntax used by Perl, Python, and other languages. diff --git a/libgo/go/strconv/decimal.go b/libgo/go/strconv/decimal.go index f572ea4..5415530 100644 --- a/libgo/go/strconv/decimal.go +++ b/libgo/go/strconv/decimal.go @@ -102,12 +102,6 @@ func (a *decimal) Assign(v uint64) { trim(a) } -func newDecimal(i uint64) *decimal { - a := new(decimal) - a.Assign(i) - return a -} - // Maximum shift that we can do in one pass without overflow. // Signed int has 31 bits, and we have to be able to accommodate 9<<k. const maxShift = 27 @@ -303,32 +297,32 @@ func shouldRoundUp(a *decimal, nd int) bool { // If nd is zero, it means we're rounding // just to the left of the digits, as in // 0.09 -> 0.1. -func (a *decimal) Round(nd int) *decimal { +func (a *decimal) Round(nd int) { if nd < 0 || nd >= a.nd { - return a + return } if shouldRoundUp(a, nd) { - return a.RoundUp(nd) + a.RoundUp(nd) + } else { + a.RoundDown(nd) } - return a.RoundDown(nd) } // Round a down to nd digits (or fewer). // Returns receiver for convenience. -func (a *decimal) RoundDown(nd int) *decimal { +func (a *decimal) RoundDown(nd int) { if nd < 0 || nd >= a.nd { - return a + return } a.nd = nd trim(a) - return a } // Round a up to nd digits (or fewer). // Returns receiver for convenience. -func (a *decimal) RoundUp(nd int) *decimal { +func (a *decimal) RoundUp(nd int) { if nd < 0 || nd >= a.nd { - return a + return } // round up @@ -337,7 +331,7 @@ func (a *decimal) RoundUp(nd int) *decimal { if c < '9' { // can stop after this digit a.d[i]++ a.nd = i + 1 - return a + return } } @@ -346,7 +340,6 @@ func (a *decimal) RoundUp(nd int) *decimal { a.d[0] = '1' a.nd = 1 a.dp++ - return a } // Extract integer part, rounded appropriately. diff --git a/libgo/go/strconv/decimal_test.go b/libgo/go/strconv/decimal_test.go index deb2e02..13a127f 100644 --- a/libgo/go/strconv/decimal_test.go +++ b/libgo/go/strconv/decimal_test.go @@ -70,17 +70,23 @@ var roundtests = []roundTest{ func TestDecimalRound(t *testing.T) { for i := 0; i < len(roundtests); i++ { test := &roundtests[i] - s := NewDecimal(test.i).RoundDown(test.nd).String() + d := NewDecimal(test.i) + d.RoundDown(test.nd) + s := d.String() if s != test.down { t.Errorf("Decimal %v RoundDown %d = %v, want %v", test.i, test.nd, s, test.down) } - s = NewDecimal(test.i).Round(test.nd).String() + d = NewDecimal(test.i) + d.Round(test.nd) + s = d.String() if s != test.round { t.Errorf("Decimal %v Round %d = %v, want %v", test.i, test.nd, s, test.down) } - s = NewDecimal(test.i).RoundUp(test.nd).String() + d = NewDecimal(test.i) + d.RoundUp(test.nd) + s = d.String() if s != test.up { t.Errorf("Decimal %v RoundUp %d = %v, want %v", test.i, test.nd, s, test.up) diff --git a/libgo/go/strconv/ftoa.go b/libgo/go/strconv/ftoa.go index 07fe806..8342b6a 100644 --- a/libgo/go/strconv/ftoa.go +++ b/libgo/go/strconv/ftoa.go @@ -98,7 +98,8 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string { // The shift is exp - flt.mantbits because mant is a 1-bit integer // followed by a flt.mantbits fraction, and we are treating it as // a 1+flt.mantbits-bit integer. - d := newDecimal(mant) + d := new(decimal) + d.Assign(mant) d.Shift(exp - int(flt.mantbits)) // Round appropriately. @@ -184,7 +185,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // d = mant << (exp - mantbits) // Next highest floating point number is mant+1 << exp-mantbits. // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1. - upper := newDecimal(mant*2 + 1) + upper := new(decimal) + upper.Assign(mant*2 + 1) upper.Shift(exp - int(flt.mantbits) - 1) // d = mant << (exp - mantbits) @@ -203,7 +205,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { mantlo = mant*2 - 1 explo = exp - 1 } - lower := newDecimal(mantlo*2 + 1) + lower := new(decimal) + lower.Assign(mantlo*2 + 1) lower.Shift(explo - int(flt.mantbits) - 1) // The upper and lower bounds are possible outputs only if diff --git a/libgo/go/strconv/ftoa_test.go b/libgo/go/strconv/ftoa_test.go index 6d361a1..8bac5da 100644 --- a/libgo/go/strconv/ftoa_test.go +++ b/libgo/go/strconv/ftoa_test.go @@ -148,3 +148,27 @@ func TestFtoa(t *testing.T) { } } } + +func BenchmarkFtoa64Decimal(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(33909, 'g', -1) + } +} + +func BenchmarkFtoa64Float(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(339.7784, 'g', -1) + } +} + +func BenchmarkFtoa64FloatExp(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(-5.09e75, 'g', -1) + } +} + +func BenchmarkFtoa64Big(b *testing.B) { + for i := 0; i < b.N; i++ { + Ftoa64(123456789123456789123456789, 'g', -1) + } +} diff --git a/libgo/go/strconv/internal_test.go b/libgo/go/strconv/internal_test.go index 9a7f4f0..d0fa80e 100644 --- a/libgo/go/strconv/internal_test.go +++ b/libgo/go/strconv/internal_test.go @@ -6,7 +6,11 @@ package strconv -func NewDecimal(i uint64) *decimal { return newDecimal(i) } +func NewDecimal(i uint64) *decimal { + d := new(decimal) + d.Assign(i) + return d +} func SetOptimize(b bool) bool { old := optimize diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go index 304d69a..96207f5 100644 --- a/libgo/go/strings/strings_test.go +++ b/libgo/go/strings/strings_test.go @@ -489,46 +489,47 @@ func TestSpecialCase(t *testing.T) { func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) } var trimTests = []struct { - f func(string, string) string + f string in, cutset, out string }{ - {Trim, "abba", "a", "bb"}, - {Trim, "abba", "ab", ""}, - {TrimLeft, "abba", "ab", ""}, - {TrimRight, "abba", "ab", ""}, - {TrimLeft, "abba", "a", "bba"}, - {TrimRight, "abba", "a", "abb"}, - {Trim, "<tag>", "<>", "tag"}, - {Trim, "* listitem", " *", "listitem"}, - {Trim, `"quote"`, `"`, "quote"}, - {Trim, "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, + {"Trim", "abba", "a", "bb"}, + {"Trim", "abba", "ab", ""}, + {"TrimLeft", "abba", "ab", ""}, + {"TrimRight", "abba", "ab", ""}, + {"TrimLeft", "abba", "a", "bba"}, + {"TrimRight", "abba", "a", "abb"}, + {"Trim", "<tag>", "<>", "tag"}, + {"Trim", "* listitem", " *", "listitem"}, + {"Trim", `"quote"`, `"`, "quote"}, + {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"}, //empty string tests - {Trim, "abba", "", "abba"}, - {Trim, "", "123", ""}, - {Trim, "", "", ""}, - {TrimLeft, "abba", "", "abba"}, - {TrimLeft, "", "123", ""}, - {TrimLeft, "", "", ""}, - {TrimRight, "abba", "", "abba"}, - {TrimRight, "", "123", ""}, - {TrimRight, "", "", ""}, - {TrimRight, "☺\xc0", "☺", "☺\xc0"}, + {"Trim", "abba", "", "abba"}, + {"Trim", "", "123", ""}, + {"Trim", "", "", ""}, + {"TrimLeft", "abba", "", "abba"}, + {"TrimLeft", "", "123", ""}, + {"TrimLeft", "", "", ""}, + {"TrimRight", "abba", "", "abba"}, + {"TrimRight", "", "123", ""}, + {"TrimRight", "", "", ""}, + {"TrimRight", "☺\xc0", "☺", "☺\xc0"}, } func TestTrim(t *testing.T) { for _, tc := range trimTests { - actual := tc.f(tc.in, tc.cutset) - var name string - switch tc.f { - case Trim: - name = "Trim" - case TrimLeft: - name = "TrimLeft" - case TrimRight: - name = "TrimRight" + name := tc.f + var f func(string, string) string + switch name { + case "Trim": + f = Trim + case "TrimLeft": + f = TrimLeft + case "TrimRight": + f = TrimRight default: - t.Error("Undefined trim function") + t.Error("Undefined trim function %s", name) } + actual := f(tc.in, tc.cutset) if actual != tc.out { t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out) } diff --git a/libgo/go/sync/mutex.go b/libgo/go/sync/mutex.go index 2d46c89..4fc0274 100644 --- a/libgo/go/sync/mutex.go +++ b/libgo/go/sync/mutex.go @@ -6,6 +6,8 @@ // exclusion locks. Other than the Once and WaitGroup types, most are intended // for use by low-level library routines. Higher-level synchronization is // better done via channels and communication. +// +// Values containing the types defined in this package should not be copied. package sync import ( diff --git a/libgo/go/syscall/bpf_bsd.go b/libgo/go/syscall/bpf_bsd.go index 06a2953..f94b723 100644 --- a/libgo/go/syscall/bpf_bsd.go +++ b/libgo/go/syscall/bpf_bsd.go @@ -20,54 +20,54 @@ func BpfJump(code, k, jt, jf int) *BpfInsn { return &BpfInsn{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} } -func BpfBuflen(fd int) (int, int) { +func BpfBuflen(fd int) (int, error) { var l int - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l))) - if e := int(ep); e != 0 { - return 0, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l))) + if err != 0 { + return 0, Errno(err) } - return l, 0 + return l, nil } -func SetBpfBuflen(fd, l int) (int, int) { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l))) - if e := int(ep); e != 0 { - return 0, e +func SetBpfBuflen(fd, l int) (int, error) { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l))) + if err != 0 { + return 0, Errno(err) } - return l, 0 + return l, nil } -func BpfDatalink(fd int) (int, int) { +func BpfDatalink(fd int) (int, error) { var t int - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t))) - if e := int(ep); e != 0 { - return 0, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t))) + if err != 0 { + return 0, Errno(err) } - return t, 0 + return t, nil } -func SetBpfDatalink(fd, t int) (int, int) { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t))) - if e := int(ep); e != 0 { - return 0, e +func SetBpfDatalink(fd, t int) (int, error) { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t))) + if err != 0 { + return 0, Errno(err) } - return t, 0 + return t, nil } -func SetBpfPromisc(fd, m int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m))) - if e := int(ep); e != 0 { - return e +func SetBpfPromisc(fd, m int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func FlushBpf(fd int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0) - if e := int(ep); e != 0 { - return e +func FlushBpf(fd int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0) + if err != 0 { + return Errno(err) } - return 0 + return nil } type ivalue struct { @@ -75,95 +75,95 @@ type ivalue struct { value int16 } -func BpfInterface(fd int, name string) (string, int) { +func BpfInterface(fd int, name string) (string, error) { var iv ivalue - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv))) - if e := int(ep); e != 0 { - return "", e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv))) + if err != 0 { + return "", Errno(err) } - return name, 0 + return name, nil } -func SetBpfInterface(fd int, name string) int { +func SetBpfInterface(fd int, name string) error { var iv ivalue copy(iv.name[:], []byte(name)) - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETIF, uintptr(unsafe.Pointer(&iv))) - if e := int(ep); e != 0 { - return e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETIF, uintptr(unsafe.Pointer(&iv))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func BpfTimeout(fd int) (*Timeval, int) { +func BpfTimeout(fd int) (*Timeval, error) { var tv Timeval - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv))) - if e := int(ep); e != 0 { - return nil, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv))) + if err != 0 { + return nil, Errno(err) } - return &tv, 0 + return &tv, nil } -func SetBpfTimeout(fd int, tv *Timeval) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv))) - if e := int(ep); e != 0 { - return e +func SetBpfTimeout(fd int, tv *Timeval) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func BpfStats(fd int) (*BpfStat, int) { +func BpfStats(fd int) (*BpfStat, error) { var s BpfStat - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s))) - if e := int(ep); e != 0 { - return nil, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s))) + if err != 0 { + return nil, Errno(err) } - return &s, 0 + return &s, nil } -func SetBpfImmediate(fd, m int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m))) - if e := int(ep); e != 0 { - return e +func SetBpfImmediate(fd, m int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func SetBpf(fd int, i []BpfInsn) int { +func SetBpf(fd int, i []BpfInsn) error { var p BpfProgram p.Len = uint32(len(i)) p.Insns = (*BpfInsn)(unsafe.Pointer(&i[0])) - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETF, uintptr(unsafe.Pointer(&p))) - if e := int(ep); e != 0 { - return e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETF, uintptr(unsafe.Pointer(&p))) + if err != 0 { + return Errno(err) } - return 0 + return nil } -func CheckBpfVersion(fd int) int { +func CheckBpfVersion(fd int) error { var v BpfVersion - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v))) - if e := int(ep); e != 0 { - return e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v))) + if err != 0 { + return Errno(err) } if v.Major != BPF_MAJOR_VERSION || v.Minor != BPF_MINOR_VERSION { return EINVAL } - return 0 + return nil } -func BpfHeadercmpl(fd int) (int, int) { +func BpfHeadercmpl(fd int) (int, error) { var f int - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f))) - if e := int(ep); e != 0 { - return 0, e + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f))) + if err != 0 { + return 0, Errno(err) } - return f, 0 + return f, nil } -func SetBpfHeadercmpl(fd, f int) int { - _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f))) - if e := int(ep); e != 0 { - return e +func SetBpfHeadercmpl(fd, f int) error { + _, _, err := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f))) + if err != 0 { + return Errno(err) } - return 0 + return nil } diff --git a/libgo/go/syscall/env_plan9.go b/libgo/go/syscall/env_plan9.go new file mode 100644 index 0000000..5185733 --- /dev/null +++ b/libgo/go/syscall/env_plan9.go @@ -0,0 +1,74 @@ +// Copyright 2011 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. + +// Plan 9 environment variables. + +package syscall + +import "errors" + +func Getenv(key string) (value string, found bool) { + if len(key) == 0 { + return "", false + } + f, e := Open("/env/"+key, O_RDONLY) + if e != nil { + return "", false + } + defer Close(f) + + l, _ := Seek(f, 0, 2) + Seek(f, 0, 0) + buf := make([]byte, l) + n, e := Read(f, buf) + if e != nil { + return "", false + } + + if n > 0 && buf[n-1] == 0 { + buf = buf[:n-1] + } + return string(buf), true +} + +func Setenv(key, value string) error { + if len(key) == 0 { + return errors.New("bad arg in system call") + } + + f, e := Create("/env/"+key, O_RDWR, 0666) + if e != nil { + return e + } + defer Close(f) + + _, e = Write(f, []byte(value)) + return nil +} + +func Clearenv() { + RawSyscall(SYS_RFORK, RFCENVG, 0, 0) +} + +func Environ() []string { + env := make([]string, 0, 100) + + f, e := Open("/env", O_RDONLY) + if e != nil { + panic(e) + } + defer Close(f) + + names, e := readdirnames(f) + if e != nil { + panic(e) + } + + for _, k := range names { + if v, ok := Getenv(k); ok { + env = append(env, k+"="+v) + } + } + return env[0:len(env)] +} diff --git a/libgo/go/syscall/env_unix.go b/libgo/go/syscall/env_unix.go new file mode 100644 index 0000000..df25909 --- /dev/null +++ b/libgo/go/syscall/env_unix.go @@ -0,0 +1,85 @@ +// Copyright 2010 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 darwin freebsd linux openbsd + +// Unix environment variables. + +package syscall + +import "sync" + +var env map[string]string +var envOnce sync.Once +var Envs []string // provided by runtime + +func setenv_c(k, v string) + +func copyenv() { + env = make(map[string]string) + for _, s := range Envs { + for j := 0; j < len(s); j++ { + if s[j] == '=' { + env[s[0:j]] = s[j+1:] + break + } + } + } +} + +var envLock sync.RWMutex + +func Getenv(key string) (value string, found bool) { + envOnce.Do(copyenv) + if len(key) == 0 { + return "", false + } + + envLock.RLock() + defer envLock.RUnlock() + + v, ok := env[key] + if !ok { + return "", false + } + return v, true +} + +func Setenv(key, value string) error { + envOnce.Do(copyenv) + if len(key) == 0 { + return EINVAL + } + + envLock.Lock() + defer envLock.Unlock() + + env[key] = value + setenv_c(key, value) // is a no-op if cgo isn't loaded + return nil +} + +func Clearenv() { + envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv + + envLock.Lock() + defer envLock.Unlock() + + env = make(map[string]string) + + // TODO(bradfitz): pass through to C +} + +func Environ() []string { + envOnce.Do(copyenv) + envLock.RLock() + defer envLock.RUnlock() + a := make([]string, len(env)) + i := 0 + for k, v := range env { + a[i] = k + "=" + v + i++ + } + return a +} diff --git a/libgo/go/syscall/env_windows.go b/libgo/go/syscall/env_windows.go new file mode 100644 index 0000000..8308f10 --- /dev/null +++ b/libgo/go/syscall/env_windows.go @@ -0,0 +1,77 @@ +// Copyright 2010 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. + +// Windows environment variables. + +package syscall + +import ( + "unicode/utf16" + "unsafe" +) + +func Getenv(key string) (value string, found bool) { + b := make([]uint16, 100) + n, e := GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b))) + if n == 0 && e == ERROR_ENVVAR_NOT_FOUND { + return "", false + } + if n > uint32(len(b)) { + b = make([]uint16, n) + n, e = GetEnvironmentVariable(StringToUTF16Ptr(key), &b[0], uint32(len(b))) + if n > uint32(len(b)) { + n = 0 + } + } + if n == 0 { + return "", false + } + return string(utf16.Decode(b[0:n])), true +} + +func Setenv(key, value string) error { + var v *uint16 + if len(value) > 0 { + v = StringToUTF16Ptr(value) + } + e := SetEnvironmentVariable(StringToUTF16Ptr(key), v) + if e != nil { + return e + } + return nil +} + +func Clearenv() { + for _, s := range Environ() { + // Environment variables can begin with = + // so start looking for the separator = at j=1. + // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx + for j := 1; j < len(s); j++ { + if s[j] == '=' { + Setenv(s[0:j], "") + break + } + } + } +} + +func Environ() []string { + s, e := GetEnvironmentStrings() + if e != nil { + return nil + } + defer FreeEnvironmentStrings(s) + r := make([]string, 0, 50) // Empty with room to grow. + for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(s)); true; i++ { + if p[i] == 0 { + // empty string marks the end + if i <= from { + break + } + r = append(r, string(utf16.Decode(p[from:i]))) + from = i + 1 + } + } + return r +} diff --git a/libgo/go/syscall/errno.c b/libgo/go/syscall/errno.c index 854b5aa..8e57811 100644 --- a/libgo/go/syscall/errno.c +++ b/libgo/go/syscall/errno.c @@ -5,21 +5,22 @@ license that can be found in the LICENSE file. */ #include <errno.h> +#include <stdint.h> /* errno is typically a macro. These functions set and get errno specific to the libc being used. */ -int GetErrno() asm ("libgo_syscall.syscall.GetErrno"); -void SetErrno(int) asm ("libgo_syscall.syscall.SetErrno"); +uintptr_t GetErrno() asm ("libgo_syscall.syscall.GetErrno"); +void SetErrno(uintptr_t) asm ("libgo_syscall.syscall.SetErrno"); -int +uintptr_t GetErrno() { - return errno; + return (uintptr_t) errno; } void -SetErrno(int value) +SetErrno(uintptr_t value) { - errno = value; + errno = (int) value; } diff --git a/libgo/go/syscall/errstr.go b/libgo/go/syscall/errstr.go index d9f3fe8..5ef10da 100644 --- a/libgo/go/syscall/errstr.go +++ b/libgo/go/syscall/errstr.go @@ -6,14 +6,14 @@ package syscall -//sysnb strerror_r(errnum int, buf []byte) (errno int) +//sysnb strerror_r(errnum int, buf []byte) (err error) //strerror_r(errnum int, buf *byte, buflen Size_t) int func Errstr(errnum int) string { for len := 128; ; len *= 2 { b := make([]byte, len) err := strerror_r(errnum, b) - if err == 0 { + if err == nil { i := 0 for b[i] != 0 { i++ diff --git a/libgo/go/syscall/exec_stubs.go b/libgo/go/syscall/exec_stubs.go index 02b9ec3..74b0af5 100644 --- a/libgo/go/syscall/exec_stubs.go +++ b/libgo/go/syscall/exec_stubs.go @@ -14,10 +14,10 @@ func Exec(argv0 string, argv []string, envv []string) (err int) { return ENOSYS; } -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { return -1, ENOSYS; } -func raw_ptrace(request int, pid int, addr *byte, data *byte) int { +func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno { return ENOSYS } diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go index 60e9770..c9814b7 100644 --- a/libgo/go/syscall/exec_unix.go +++ b/libgo/go/syscall/exec_unix.go @@ -11,39 +11,43 @@ import ( "unsafe" ) -//sysnb raw_fork() (pid Pid_t, errno int) +//sysnb raw_fork() (pid Pid_t, err Errno) //fork() Pid_t -//sysnb raw_setsid() (errno int) +//sysnb raw_setsid() (err Errno) //setsid() Pid_t -//sysnb raw_chroot(path *byte) (errno int) +//sysnb raw_setpgid(pid int, pgid int) (err Errno) +//setpgid(pid Pid_t, pgid Pid_t) int + +//sysnb raw_chroot(path *byte) (err Errno) //chroot(path *byte) int -//sysnb raw_chdir(path *byte) (errno int) +//sysnb raw_chdir(path *byte) (err Errno) //chdir(path *byte) int -//sysnb raw_fcntl(fd int, cmd int, arg int) (val int, errno int) +//sysnb raw_fcntl(fd int, cmd int, arg int) (val int, err Errno) //fcntl(fd int, cmd int, arg int) int -//sysnb raw_close(fd int) (errno int) +//sysnb raw_close(fd int) (err Errno) //close(fd int) int -//sysnb raw_ioctl(fd int, cmd int, val int) (rval int, errno int) +//sysnb raw_ioctl(fd int, cmd int, val int) (rval int, err Errno) //ioctl(fd int, cmd int, val int) int -//sysnb raw_execve(argv0 *byte, argv **byte, envv **byte) (errno int) +//sysnb raw_execve(argv0 *byte, argv **byte, envv **byte) (err Errno) //execve(argv0 *byte, argv **byte, envv **byte) int -//sysnb raw_read(fd int, p *byte, np int) (n int, errno int) -//read(fd int, buf *byte, count Size_t) Ssize_t - -//sysnb raw_write(fd int, buf *byte, count int) int +//sysnb raw_write(fd int, buf *byte, count int) (err Errno) //write(fd int, buf *byte, count Size_t) Ssize_t //sysnb raw_exit(status int) //_exit(status int) +// Note: not raw, returns error rather than Errno. +//sys read(fd int, p *byte, np int) (n int, err error) +//read(fd int, buf *byte, count Size_t) Ssize_t + // Lock synchronizing creation of new file descriptors with fork. // // We want the child in a fork/exec sequence to inherit only the @@ -106,9 +110,9 @@ func StringSlicePtr(ss []string) []*byte { func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) } -func SetNonblock(fd int, nonblocking bool) (errno int) { +func SetNonblock(fd int, nonblocking bool) (err error) { flag, err := fcntl(fd, F_GETFL, 0) - if err != 0 { + if err != nil { return err } if nonblocking { @@ -121,20 +125,22 @@ func SetNonblock(fd int, nonblocking bool) (errno int) { } // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. -// If a dup or exec fails, write the errno int to pipe. +// If a dup or exec fails, write the errno error to pipe. // (Pipe is close-on-exec so if exec succeeds, it will be closed.) // In the child, this function must not acquire any locks, because // they might have been locked at the time of the fork. This means // no rescheduling, no malloc calls, and no new stack segments. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. -func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err int) { +func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). - var r1 Pid_t - var err1 int - var nextfd int - var i int + var ( + r1 Pid_t + err1 Errno + nextfd int + i int + ) // guard against side effects of shuffling fds below. fd := append([]int(nil), attr.Files...) @@ -143,7 +149,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // No more allocation or calls of non-assembly functions. r1, err1 = raw_fork() if err1 != 0 { - return 0, int(err1) + return 0, err1 } if r1 != 0 { @@ -171,7 +177,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Set process group if sys.Setpgid { - err1 = Setpgid(0, 0) + err1 = raw_setpgid(0, 0) if err1 != 0 { goto childerror } @@ -189,23 +195,35 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr if cred := sys.Credential; cred != nil { ngroups := len(cred.Groups) if ngroups == 0 { - err1 = setgroups(0, nil) + err2 := setgroups(0, nil) + if err2 == nil { + err1 = 0 + } else { + err1 = err2.(Errno) + } } else { groups := make([]Gid_t, ngroups) for i, v := range cred.Groups { groups[i] = Gid_t(v) } - err1 = setgroups(ngroups, &groups[0]) + err2 := setgroups(ngroups, &groups[0]) + if err2 == nil { + err1 = 0 + } else { + err1 = err2.(Errno) + } } if err1 != 0 { goto childerror } - err1 = Setgid(int(cred.Gid)) - if err1 != 0 { + err2 := Setgid(int(cred.Gid)) + if err2 != nil { + err1 = err2.(Errno) goto childerror } - err1 = Setuid(int(cred.Uid)) - if err1 != 0 { + err2 = Setuid(int(cred.Uid)) + if err2 != nil { + err1 = err2.(Errno) goto childerror } } @@ -222,8 +240,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // so that pass 2 won't stomp on an fd it needs later. nextfd = int(len(fd)) if pipe < nextfd { - _, err1 = Dup2(pipe, nextfd) - if err1 != 0 { + _, err2 := Dup2(pipe, nextfd) + if err2 != nil { + err1 = err2.(Errno) goto childerror } raw_fcntl(nextfd, F_SETFD, FD_CLOEXEC) @@ -232,8 +251,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } for i = 0; i < len(fd); i++ { if fd[i] >= 0 && fd[i] < int(i) { - _, err1 = Dup2(fd[i], nextfd) - if err1 != 0 { + _, err2 := Dup2(fd[i], nextfd) + if err2 != nil { + err1 = err2.(Errno) goto childerror } raw_fcntl(nextfd, F_SETFD, FD_CLOEXEC) @@ -262,8 +282,9 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // The new fd is created NOT close-on-exec, // which is exactly what we want. - _, err1 = Dup2(fd[i], i) - if err1 != 0 { + _, err2 := Dup2(fd[i], i) + if err2 != nil { + err1 = err2.(Errno) goto childerror } } @@ -338,10 +359,10 @@ type SysProcAttr struct { var zeroProcAttr ProcAttr var zeroSysProcAttr SysProcAttr -func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { +func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) { var p [2]int var n int - var err1 uintptr + var err1 Errno var wstatus WaitStatus if attr == nil { @@ -379,32 +400,32 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { ForkLock.Lock() // Allocate child status pipe close on exec. - if err = Pipe(p[0:]); err != 0 { + if err = Pipe(p[0:]); err != nil { goto error } - if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != 0 { + if _, err = fcntl(p[0], F_SETFD, FD_CLOEXEC); err != nil { goto error } - if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != 0 { + if _, err = fcntl(p[1], F_SETFD, FD_CLOEXEC); err != nil { goto error } // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) - if err != 0 { + pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) + if err1 != 0 { goto error } ForkLock.Unlock() // Read child error status from pipe. Close(p[1]) - n, err = raw_read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) + n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) Close(p[0]) - if err != 0 || n != 0 { + if err != nil || n != 0 { if n == int(unsafe.Sizeof(err1)) { - err = int(err1) + err = Errno(err1) } - if err == 0 { + if err == nil { err = EPIPE } @@ -418,7 +439,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { } // Read got EOF, so pipe closed on exec, so exec succeeded. - return pid, 0 + return pid, nil error: if p[0] >= 0 { @@ -430,20 +451,20 @@ error: } // Combination of fork and exec, careful to be thread safe. -func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { +func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) { return forkExec(argv0, argv, attr) } // StartProcess wraps ForkExec for package os. -func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) { +func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err error) { pid, err = forkExec(argv0, argv, attr) return pid, 0, err } // Ordinary exec. -func Exec(argv0 string, argv []string, envv []string) (err int) { +func Exec(argv0 string, argv []string, envv []string) (err error) { err1 := raw_execve(StringBytePtr(argv0), &StringSlicePtr(argv)[0], &StringSlicePtr(envv)[0]) - return int(err1) + return Errno(err1) } diff --git a/libgo/go/syscall/exec_windows.go b/libgo/go/syscall/exec_windows.go index e8b540a..2826e2f 100644 --- a/libgo/go/syscall/exec_windows.go +++ b/libgo/go/syscall/exec_windows.go @@ -8,8 +8,8 @@ package syscall import ( "sync" + "unicode/utf16" "unsafe" - "utf16" ) var ForkLock sync.RWMutex @@ -100,7 +100,7 @@ func makeCmdLine(args []string) string { // Last bytes are two UCS-2 NULs, or four NUL bytes. func createEnvBlock(envv []string) *uint16 { if len(envv) == 0 { - return &utf16.Encode([]int("\x00\x00"))[0] + return &utf16.Encode([]rune("\x00\x00"))[0] } length := 0 for _, s := range envv { @@ -118,54 +118,54 @@ func createEnvBlock(envv []string) *uint16 { } copy(b[i:i+1], []byte{0}) - return &utf16.Encode([]int(string(b)))[0] + return &utf16.Encode([]rune(string(b)))[0] } func CloseOnExec(fd Handle) { SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) } -func SetNonblock(fd Handle, nonblocking bool) (errno int) { - return 0 +func SetNonblock(fd Handle, nonblocking bool) (err error) { + return nil } // getFullPath retrieves the full path of the specified file. // Just a wrapper for Windows GetFullPathName api. -func getFullPath(name string) (path string, err int) { +func getFullPath(name string) (path string, err error) { p := StringToUTF16Ptr(name) buf := make([]uint16, 100) n, err := GetFullPathName(p, uint32(len(buf)), &buf[0], nil) - if err != 0 { + if err != nil { return "", err } if n > uint32(len(buf)) { // Windows is asking for bigger buffer. buf = make([]uint16, n) n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil) - if err != 0 { + if err != nil { return "", err } if n > uint32(len(buf)) { return "", EINVAL } } - return UTF16ToString(buf[:n]), 0 + return UTF16ToString(buf[:n]), nil } func isSlash(c uint8) bool { return c == '\\' || c == '/' } -func normalizeDir(dir string) (name string, err int) { +func normalizeDir(dir string) (name string, err error) { ndir, err := getFullPath(dir) - if err != 0 { + if err != nil { return "", err } if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) { // dir cannot have \\server\share\path form return "", EINVAL } - return ndir, 0 + return ndir, nil } func volToUpper(ch int) int { @@ -175,13 +175,13 @@ func volToUpper(ch int) int { return ch } -func joinExeDirAndFName(dir, p string) (name string, err int) { +func joinExeDirAndFName(dir, p string) (name string, err error) { if len(p) == 0 { return "", EINVAL } if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) { // \\server\share\path form - return p, 0 + return p, nil } if len(p) > 1 && p[1] == ':' { // has drive letter @@ -189,10 +189,10 @@ func joinExeDirAndFName(dir, p string) (name string, err int) { return "", EINVAL } if isSlash(p[2]) { - return p, 0 + return p, nil } else { d, err := normalizeDir(dir) - if err != 0 { + if err != nil { return "", err } if volToUpper(int(p[0])) == volToUpper(int(d[0])) { @@ -204,7 +204,7 @@ func joinExeDirAndFName(dir, p string) (name string, err int) { } else { // no drive letter d, err := normalizeDir(dir) - if err != 0 { + if err != nil { return "", err } if isSlash(p[0]) { @@ -232,7 +232,7 @@ type SysProcAttr struct { var zeroProcAttr ProcAttr var zeroSysProcAttr SysProcAttr -func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) { +func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err error) { if len(argv0) == 0 { return 0, 0, EWINDOWS } @@ -255,9 +255,9 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, // argv0 relative to the current directory, and, only once the new // process is started, it does Chdir(attr.Dir). We are adjusting // for that difference here by making argv0 absolute. - var err int + var err error argv0, err = joinExeDirAndFName(attr.Dir, argv0) - if err != 0 { + if err != nil { return 0, 0, err } } @@ -294,7 +294,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, for i := range attr.Files { if attr.Files[i] > 0 { err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) - if err != 0 { + if err != nil { return 0, 0, err } defer CloseHandle(Handle(fd[i])) @@ -314,14 +314,14 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, pi := new(ProcessInformation) err = CreateProcess(argv0p, argvp, nil, nil, true, CREATE_UNICODE_ENVIRONMENT, createEnvBlock(attr.Env), dirp, si, pi) - if err != 0 { + if err != nil { return 0, 0, err } defer CloseHandle(Handle(pi.Thread)) - return int(pi.ProcessId), int(pi.Process), 0 + return int(pi.ProcessId), int(pi.Process), nil } -func Exec(argv0 string, argv []string, envv []string) (err int) { +func Exec(argv0 string, argv []string, envv []string) (err error) { return EWINDOWS } diff --git a/libgo/go/syscall/libcall_irix.go b/libgo/go/syscall/libcall_irix.go index ae45355..69e0db2 100644 --- a/libgo/go/syscall/libcall_irix.go +++ b/libgo/go/syscall/libcall_irix.go @@ -4,5 +4,5 @@ package syscall -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long diff --git a/libgo/go/syscall/libcall_linux.go b/libgo/go/syscall/libcall_linux.go index 3948e51..79f5d48 100644 --- a/libgo/go/syscall/libcall_linux.go +++ b/libgo/go/syscall/libcall_linux.go @@ -8,31 +8,31 @@ package syscall import "unsafe" -//sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, errno int) +//sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) //openat(dirfd int, path *byte, flags int, mode Mode_t) int -//sys futimesat(dirfd int, path *byte, times *[2]Timeval) (errno int) +//sys futimesat(dirfd int, path *byte, times *[2]Timeval) (err error) //futimesat(dirfd int, path *byte, times *[2]Timeval) int -func Futimesat(dirfd int, path string, tv []Timeval) (errno int) { +func Futimesat(dirfd int, path string, tv []Timeval) (err error) { if len(tv) != 2 { return EINVAL } return futimesat(dirfd, StringBytePtr(path), (*[2]Timeval)(unsafe.Pointer(&tv[0]))) } -func Futimes(fd int, tv []Timeval) (errno int) { +func Futimes(fd int, tv []Timeval) (err error) { // Believe it or not, this is the best we can do on GNU/Linux // (and is what glibc does). return Utimes("/proc/self/fd/"+itoa(fd), tv) } -//sys ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) +//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long -func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno int) { +func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err error) { // The peek requests are machine-size oriented, so we wrap it // to retrieve arbitrary-length data. @@ -48,9 +48,9 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno in // boundary. n := 0 if addr%sizeofPtr != 0 { - errno = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return 0, errno + err = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return 0, err } n += copy(out, buf[addr%sizeofPtr:]) out = out[n:] @@ -60,27 +60,27 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, errno in for len(out) > 0 { // We use an internal buffer to gaurantee alignment. // It's not documented if this is necessary, but we're paranoid. - errno = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return n, errno + err = ptrace(req, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return n, err } copied := copy(out, buf[0:]) n += copied out = out[copied:] } - return n, 0 + return n, nil } -func PtracePeekText(pid int, addr uintptr, out []byte) (count int, errno int) { +func PtracePeekText(pid int, addr uintptr, out []byte) (count int, err error) { return ptracePeek(PTRACE_PEEKTEXT, pid, addr, out) } -func PtracePeekData(pid int, addr uintptr, out []byte) (count int, errno int) { +func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) { return ptracePeek(PTRACE_PEEKDATA, pid, addr, out) } -func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, errno int) { +func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (count int, err error) { // As for ptracePeek, we need to align our accesses to deal // with the possibility of straddling an invalid page. @@ -88,15 +88,15 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c n := 0 if addr%sizeofPtr != 0 { var buf [sizeofPtr]byte - errno = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return 0, errno + err = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return 0, err } n += copy(buf[addr%sizeofPtr:], data) word := *((*uintptr)(unsafe.Pointer(&buf[0]))) - errno = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word) - if errno != 0 { - return 0, errno + err = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word) + if err != nil { + return 0, err } data = data[n:] } @@ -104,9 +104,9 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c // Interior. for len(data) > int(sizeofPtr) { word := *((*uintptr)(unsafe.Pointer(&data[0]))) - errno = ptrace(pokeReq, pid, addr+uintptr(n), word) - if errno != 0 { - return n, errno + err = ptrace(pokeReq, pid, addr+uintptr(n), word) + if err != nil { + return n, err } n += int(sizeofPtr) data = data[sizeofPtr:] @@ -115,167 +115,167 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c // Trailing edge. if len(data) > 0 { var buf [sizeofPtr]byte - errno = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) - if errno != 0 { - return n, errno + err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return n, err } copy(buf[0:], data) word := *((*uintptr)(unsafe.Pointer(&buf[0]))) - errno = ptrace(pokeReq, pid, addr+uintptr(n), word) - if errno != 0 { - return n, errno + err = ptrace(pokeReq, pid, addr+uintptr(n), word) + if err != nil { + return n, err } n += len(data) } - return n, 0 + return n, nil } -func PtracePokeText(pid int, addr uintptr, data []byte) (count int, errno int) { +func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) { return ptracePoke(PTRACE_POKETEXT, PTRACE_PEEKTEXT, pid, addr, data) } -func PtracePokeData(pid int, addr uintptr, data []byte) (count int, errno int) { +func PtracePokeData(pid int, addr uintptr, data []byte) (count int, err error) { return ptracePoke(PTRACE_POKEDATA, PTRACE_PEEKDATA, pid, addr, data) } -func PtraceGetRegs(pid int, regsout *PtraceRegs) (errno int) { +func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { return ptrace(PTRACE_GETREGS, pid, 0, uintptr(unsafe.Pointer(regsout))) } -func PtraceSetRegs(pid int, regs *PtraceRegs) (errno int) { +func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { return ptrace(PTRACE_SETREGS, pid, 0, uintptr(unsafe.Pointer(regs))) } -func PtraceSetOptions(pid int, options int) (errno int) { +func PtraceSetOptions(pid int, options int) (err error) { return ptrace(PTRACE_SETOPTIONS, pid, 0, uintptr(options)) } -func PtraceGetEventMsg(pid int) (msg uint, errno int) { +func PtraceGetEventMsg(pid int) (msg uint, err error) { var data _C_long - errno = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data))) + err = ptrace(PTRACE_GETEVENTMSG, pid, 0, uintptr(unsafe.Pointer(&data))) msg = uint(data) return } -func PtraceCont(pid int, signal int) (errno int) { +func PtraceCont(pid int, signal int) (err error) { return ptrace(PTRACE_CONT, pid, 0, uintptr(signal)) } -func PtraceSingleStep(pid int) (errno int) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) } +func PtraceSingleStep(pid int) (err error) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) } -func PtraceAttach(pid int) (errno int) { return ptrace(PTRACE_ATTACH, pid, 0, 0) } +func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0) } -func PtraceDetach(pid int) (errno int) { return ptrace(PTRACE_DETACH, pid, 0, 0) } +func PtraceDetach(pid int) (err error) { return ptrace(PTRACE_DETACH, pid, 0, 0) } // FIXME: mksysinfo needs to produce LINUX_REBOOT_MAGIC[12]. -// //sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (errno int) +// //sys reboot(magic1 uint, magic2 uint, cmd int, arg string) (err error) // //reboot(magic1 uint, magic2 uint, cmd int, arg *byte) int -// func Reboot(cmd int) (errno int) { +// func Reboot(cmd int) (err error) { // return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "") // } -//sys Acct(path string) (errno int) +//sys Acct(path string) (err error) //acct(path *byte) int // FIXME: mksysinfo Timex -// //sys Adjtimex(buf *Timex) (state int, errno int) +// //sys Adjtimex(buf *Timex) (state int, err error) // //adjtimex(buf *Timex) int -//sys Faccessat(dirfd int, path string, mode uint32, flags int) (errno int) +//sys Faccessat(dirfd int, path string, mode uint32, flags int) (err error) //faccessat(dirfd int, pathname *byte, mode int, flags int) int // FIXME: Only in glibc 2.10 and later. -// //sys Fallocate(fd int, mode uint32, off int64, len int64) (errno int) +// //sys Fallocate(fd int, mode uint32, off int64, len int64) (err error) // //fallocate(fd int, mode int, offset Offset_t, len Offset_t) int -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (errno int) +//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) //fchmodat(dirfd int, pathname *byte, mode Mode_t, flags int) int -//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (errno int) +//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) //fchownat(dirfd int, path *byte, owner Uid_t, group Gid_t, flags int) int -//sys Flock(fd int, how int) (errno int) +//sys Flock(fd int, how int) (err error) //flock(fd int, how int) int // FIXME: mksysinfo statfs -// //sys Fstatfs(fd int, buf *Statfs_t) (errno int) +// //sys Fstatfs(fd int, buf *Statfs_t) (err error) // //fstatfs(fd int, buf *Statfs_t) int // FIXME: Only available as a syscall. // //sysnb Gettid() (tid int) // //gettid() Pid_t -//sys Ioperm(from int, num int, on int) (errno int) +//sys Ioperm(from int, num int, on int) (err error) //ioperm(from _C_long, num _C_long, on int) int -//sys Iopl(level int) (errno int) +//sys Iopl(level int) (err error) //iopl(level int) int // FIXME: mksysinfo linux_dirent // Or just abandon this function. -// //sys Getdents(fd int, buf []byte) (n int, errno int) +// //sys Getdents(fd int, buf []byte) (n int, err error) // //getdents64(fd int, buf *byte, count uint) -//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, errno int) +//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) //inotify_add_watch(fd int, pathname *byte, mask uint32) int -//sysnb InotifyInit() (fd int, errno int) +//sysnb InotifyInit() (fd int, err error) //inotify_init() int // FIXME: Only in glibc 2.9 and later. -// //sysnb InotifyInit1(flags int) (fd int, errno int) +// //sysnb InotifyInit1(flags int) (fd int, err error) // //inotify_init1(flags int) int -//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, errno int) +//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, err error) //inotify_rm_watch(fd int, wd uint32) int -//sys Klogctl(typ int, buf []byte) (n int, errno int) +//sys Klogctl(typ int, buf []byte) (n int, err error) //klogctl(typ int, bufp *byte, len int) int -//sys Mkdirat(dirfd int, path string, mode uint32) (errno int) +//sys Mkdirat(dirfd int, path string, mode uint32) (err error) //mkdirat(dirfd int, path *byte, mode Mode_t) int -//sys Mknodat(dirfd int, path string, mode uint32, dev int) (errno int) +//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error) //mknodat(dirfd int, path *byte, mode Mode_t, dev _dev_t) int -//sys PivotRoot(newroot string, putold string) (errno int) +//sys PivotRoot(newroot string, putold string) (err error) //pivot_root(newroot *byte, putold *byte) int -//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (errno int) +//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //renameat(olddirfd int, oldpath *byte, newdirfd int, newpath *byte) int -//sys sendfile(outfd int, infd int, offset *Offset_t, count int) (written int, errno int) +//sys sendfile(outfd int, infd int, offset *Offset_t, count int) (written int, err error) //sendfile64(outfd int, infd int, offset *Offset_t, count Size_t) Ssize_t -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno int) { +func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { var soff Offset_t var psoff *Offset_t if offset != nil { psoff = &soff } - written, errno = sendfile(outfd, infd, psoff, count) + written, err = sendfile(outfd, infd, psoff, count) if offset != nil { *offset = int64(soff) } return } -//sys Setfsgid(gid int) (errno int) +//sys Setfsgid(gid int) (err error) //setfsgid(gid Gid_t) int -//sys Setfsuid(uid int) (errno int) +//sys Setfsuid(uid int) (err error) //setfsuid(uid Uid_t) int -//sysnb Setresgid(rgid int, egid int, sgid int) (errno int) +//sysnb Setresgid(rgid int, egid int, sgid int) (err error) //setresgid(rgid Gid_t, egid Gid_t, sgid Gid_t) int -//sysnb Setresuid(ruid int, eguid int, suid int) (errno int) +//sysnb Setresuid(ruid int, eguid int, suid int) (err error) //setresuid(ruid Uid_t, euid Uid_t, suid Uid_t) int -//sys splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len int, flags int) (n int64, errno int) +//sys splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len int, flags int) (n int64, err error) //splice(rfd int, roff *_loff_t, wfd int, woff *_loff_t, len Size_t, flags uint) Ssize_t -func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, errno int) { +func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error) { var lroff _loff_t var plroff *_loff_t if (roff != nil) { @@ -286,7 +286,7 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i if (woff != nil) { plwoff = &lwoff } - n, errno = splice(rfd, plroff, wfd, plwoff, len, flags) + n, err = splice(rfd, plroff, wfd, plwoff, len, flags) if (roff != nil) { *roff = int64(lroff) } @@ -297,37 +297,37 @@ func Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n i } // FIXME: mksysinfo statfs -// //sys Statfs(path string, buf *Statfs_t) (errno int) +// //sys Statfs(path string, buf *Statfs_t) (err error) // //statfs(path *byte, buf *Statfs_t) int // FIXME: Only in glibc 2.6 and later. -// //sys SyncFileRange(fd int, off int64, n int64, flags int) (errno int) +// //sys SyncFileRange(fd int, off int64, n int64, flags int) (err error) // //sync_file_range(fd int, off Offset_t, n Offset_t, flags uint) int // FIXME: mksysinfo Sysinfo_t -// //sysnb Sysinfo(info *Sysinfo_t) (errno int) +// //sysnb Sysinfo(info *Sysinfo_t) (err error) // //sysinfo(info *Sysinfo_t) int -//sys Tee(rfd int, wfd int, len int, flags int) (n int64, errno int) +//sys Tee(rfd int, wfd int, len int, flags int) (n int64, err error) //tee(rfd int, wfd int, len Size_t, flags uint) Ssize_t // FIXME: Only available as a syscall. -// //sysnb Tgkill(tgid int, tid int, sig int) (errno int) +// //sysnb Tgkill(tgid int, tid int, sig int) (err error) // //tgkill(tgid int, tid int, sig int) int -//sys unlinkat(dirfd int, path string, flags int) (errno int) +//sys unlinkat(dirfd int, path string, flags int) (err error) //unlinkat(dirfd int, path *byte, flags int) int -func Unlinkat(dirfd int, path string) (errno int) { +func Unlinkat(dirfd int, path string) (err error) { return unlinkat(dirfd, path, 0) } -//sys Unmount(target string, flags int) (errno int) = SYS_UMOUNT2 +//sys Unmount(target string, flags int) (err error) = SYS_UMOUNT2 //umount2(target *byte, flags int) int -//sys Unshare(flags int) (errno int) +//sys Unshare(flags int) (err error) //unshare(flags int) int // FIXME: mksysinfo Ustat_t -// //sys Ustat(dev int, ubuf *Ustat_t) (errno int) +// //sys Ustat(dev int, ubuf *Ustat_t) (err error) // //ustat(dev _dev_t, ubuf *Ustat_t) int diff --git a/libgo/go/syscall/libcall_posix.go b/libgo/go/syscall/libcall_posix.go index 87ed4e6..d90e595 100644 --- a/libgo/go/syscall/libcall_posix.go +++ b/libgo/go/syscall/libcall_posix.go @@ -17,43 +17,43 @@ import "unsafe" * Wrapped */ -//sysnb pipe(p *[2]int) (errno int) +//sysnb pipe(p *[2]int) (err error) //pipe(p *[2]int) int -func Pipe(p []int) (errno int) { +func Pipe(p []int) (err error) { if len(p) != 2 { return EINVAL } var pp [2]int - errno = pipe(&pp) + err = pipe(&pp) p[0] = pp[0] p[1] = pp[1] return } -//sys utimes(path string, times *[2]Timeval) (errno int) +//sys utimes(path string, times *[2]Timeval) (err error) //utimes(path *byte, times *[2]Timeval) int -func Utimes(path string, tv []Timeval) (errno int) { +func Utimes(path string, tv []Timeval) (err error) { if len(tv) != 2 { return EINVAL } return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0]))) } -//sys getcwd(buf *byte, size Size_t) (errno int) +//sys getcwd(buf *byte, size Size_t) (err error) //getcwd(buf *byte, size Size_t) *byte const ImplementsGetwd = true -func Getwd() (ret string, errno int) { +func Getwd() (ret string, err error) { for len := Size_t(4096); ; len *= 2 { b := make([]byte, len) err := getcwd(&b[0], len) - if err == 0 { - i := 0; + if err == nil { + i := 0 for b[i] != 0 { - i++; + i++ } - return string(b[0:i]), 0; + return string(b[0:i]), nil } if err != ERANGE { return "", err @@ -61,16 +61,16 @@ func Getwd() (ret string, errno int) { } } -//sysnb getgroups(size int, list *Gid_t) (nn int, errno int) +//sysnb getgroups(size int, list *Gid_t) (nn int, err error) //getgroups(size int, list *Gid_t) int -func Getgroups() (gids []int, errno int) { +func Getgroups() (gids []int, err error) { n, err := getgroups(0, nil) - if err != 0 { - return nil, errno + if err != nil { + return nil, err } if n == 0 { - return nil, 0 + return nil, nil } // Sanity check group count. Max is 1<<16 on GNU/Linux. @@ -80,8 +80,8 @@ func Getgroups() (gids []int, errno int) { a := make([]Gid_t, n) n, err = getgroups(n, &a[0]) - if err != 0 { - return nil, errno + if err != nil { + return nil, err } gids = make([]int, n) for i, v := range a[0:n] { @@ -90,10 +90,10 @@ func Getgroups() (gids []int, errno int) { return } -//sysnb setgroups(n int, list *Gid_t) (errno int) +//sysnb setgroups(n int, list *Gid_t) (err error) //setgroups(n Size_t, list *Gid_t) int -func Setgroups(gids []int) (errno int) { +func Setgroups(gids []int) (err error) { if len(gids) == 0 { return setgroups(0, nil) } @@ -120,10 +120,10 @@ func (w WaitStatus) Signal() int func (w WaitStatus) StopSignal() int func (w WaitStatus) TrapCause() int -//sys Mkfifo(path string, mode uint32) (errno int) +//sys Mkfifo(path string, mode uint32) (err error) //mkfifo(path *byte, mode Mode_t) int -//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, errno int) +//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) //select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) int const nfdbits = unsafe.Sizeof(fds_bits_type) * 8 @@ -154,52 +154,52 @@ func FDZero(set *FdSet) { } } -//sys Access(path string, mode uint32) (errno int) +//sys Access(path string, mode uint32) (err error) //access(path *byte, mode int) int -//sys Chdir(path string) (errno int) +//sys Chdir(path string) (err error) //chdir(path *byte) int -//sys Chmod(path string, mode uint32) (errno int) +//sys Chmod(path string, mode uint32) (err error) //chmod(path *byte, mode Mode_t) int -//sys Chown(path string, uid int, gid int) (errno int) +//sys Chown(path string, uid int, gid int) (err error) //chown(path *byte, uid Uid_t, gid Gid_t) int -//sys Chroot(path string) (errno int) +//sys Chroot(path string) (err error) //chroot(path *byte) int -//sys Close(fd int) (errno int) +//sys Close(fd int) (err error) //close(fd int) int -//sys Creat(path string, mode uint32) (fd int, errno int) +//sys Creat(path string, mode uint32) (fd int, err error) //creat(path *byte, mode Mode_t) int -//sysnb Dup(oldfd int) (fd int, errno int) +//sysnb Dup(oldfd int) (fd int, err error) //dup(oldfd int) int -//sysnb Dup2(oldfd int, newfd int) (fd int, errno int) +//sysnb Dup2(oldfd int, newfd int) (fd int, err error) //dup2(oldfd int, newfd int) int //sys Exit(code int) //exit(code int) -//sys Fchdir(fd int) (errno int) +//sys Fchdir(fd int) (err error) //fchdir(fd int) int -//sys Fchmod(fd int, mode uint32) (errno int) +//sys Fchmod(fd int, mode uint32) (err error) //fchmod(fd int, mode Mode_t) int -//sys Fchown(fd int, uid int, gid int) (errno int) +//sys Fchown(fd int, uid int, gid int) (err error) //fchown(fd int, uid Uid_t, gid Gid_t) int -//sys fcntl(fd int, cmd int, arg int) (val int, errno int) +//sys fcntl(fd int, cmd int, arg int) (val int, err error) //fcntl(fd int, cmd int, arg int) int -//sys Fdatasync(fd int) (errno int) +//sys Fdatasync(fd int) (err error) //fdatasync(fd int) int -//sys Fsync(fd int) (errno int) +//sys Fsync(fd int) (err error) //fsync(fd int) int //sysnb Getegid() (egid int) @@ -214,7 +214,7 @@ func FDZero(set *FdSet) { //sysnb Getpagesize() (pagesize int) //getpagesize() int -//sysnb Getpgid(pid int) (pgid int, errno int) +//sysnb Getpgid(pid int) (pgid int, err error) //getpgid(pid Pid_t) Pid_t //sysnb Getpgrp() (pid int) @@ -227,138 +227,138 @@ func FDZero(set *FdSet) { //getppid() Pid_t // FIXME: mksysinfo Rlimit -// //sysnb Getrlimit(resource int, rlim *Rlimit) (errno int) +// //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) // //getrlimit(resource int, rlim *Rlimit) int -//sysnb Getrusage(who int, rusage *Rusage) (errno int) +//sysnb Getrusage(who int, rusage *Rusage) (err error) //getrusage(who int, rusage *Rusage) int -//sysnb gettimeofday(tv *Timeval, tz *byte) (errno int) +//sysnb gettimeofday(tv *Timeval, tz *byte) (err error) //gettimeofday(tv *Timeval, tz *byte) int -func Gettimeofday(tv *Timeval) (errno int) { +func Gettimeofday(tv *Timeval) (err error) { return gettimeofday(tv, nil) } //sysnb Getuid() (uid int) //getuid() Uid_t -//sysnb Kill(pid int, sig int) (errno int) +//sysnb Kill(pid int, sig int) (err error) //kill(pid Pid_t, sig int) int -//sys Lchown(path string, uid int, gid int) (errno int) +//sys Lchown(path string, uid int, gid int) (err error) //lchown(path *byte, uid Uid_t, gid Gid_t) int -//sys Link(oldpath string, newpath string) (errno int) +//sys Link(oldpath string, newpath string) (err error) //link(oldpath *byte, newpath *byte) int -//sys Mkdir(path string, mode uint32) (errno int) +//sys Mkdir(path string, mode uint32) (err error) //mkdir(path *byte, mode Mode_t) int -//sys Mknod(path string, mode uint32, dev int) (errno int) +//sys Mknod(path string, mode uint32, dev int) (err error) //mknod(path *byte, mode Mode_t, dev _dev_t) int -//sys Mount(source string, target string, fstype string, flags int, data string) (errno int) +//sys Mount(source string, target string, fstype string, flags int, data string) (err error) //mount(source *byte, target *byte, fstype *byte, flags _C_long, data *byte) int -//sys Nanosleep(time *Timespec, leftover *Timespec) (errno int) +//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //nanosleep(time *Timespec, leftover *Timespec) int -//sys Pause() (errno int) +//sys Pause() (err error) //pause() int -//sys Read(fd int, p []byte) (n int, errno int) +//sys Read(fd int, p []byte) (n int, err error) //read(fd int, buf *byte, count Size_t) Ssize_t -//sys Readlink(path string, buf []byte) (n int, errno int) +//sys Readlink(path string, buf []byte) (n int, err error) //readlink(path *byte, buf *byte, bufsiz Size_t) Ssize_t -//sys Rename(oldpath string, newpath string) (errno int) +//sys Rename(oldpath string, newpath string) (err error) //rename(oldpath *byte, newpath *byte) int -//sys Rmdir(path string) (errno int) +//sys Rmdir(path string) (err error) //rmdir(path *byte) int -//sys Setdomainname(p []byte) (errno int) +//sys Setdomainname(p []byte) (err error) //setdomainname(name *byte, len Size_t) int -//sys Sethostname(p []byte) (errno int) +//sys Sethostname(p []byte) (err error) //sethostname(name *byte, len Size_t) int -//sysnb Setgid(gid int) (errno int) +//sysnb Setgid(gid int) (err error) //setgid(gid Gid_t) int -//sysnb Setregid(rgid int, egid int) (errno int) +//sysnb Setregid(rgid int, egid int) (err error) //setregid(rgid Gid_t, egid Gid_t) int -//sysnb Setpgid(pid int, pgid int) (errno int) +//sysnb Setpgid(pid int, pgid int) (err error) //setpgid(pid Pid_t, pgid Pid_t) int -//sysnb Setreuid(ruid int, euid int) (errno int) +//sysnb Setreuid(ruid int, euid int) (err error) //setreuid(ruid Uid_t, euid Uid_t) int // FIXME: mksysinfo Rlimit -// //sysnb Setrlimit(resource int, rlim *Rlimit) (errno int) +// //sysnb Setrlimit(resource int, rlim *Rlimit) (err error) // //setrlimit(resource int, rlim *Rlimit) int -//sysnb Setsid() (pid int, errno int) +//sysnb Setsid() (pid int, err error) //setsid() Pid_t -//sysnb settimeofday(tv *Timeval, tz *byte) (errno int) +//sysnb settimeofday(tv *Timeval, tz *byte) (err error) //settimeofday(tv *Timeval, tz *byte) int -func Settimeofday(tv *Timeval) (errno int) { +func Settimeofday(tv *Timeval) (err error) { return settimeofday(tv, nil) } -//sysnb Setuid(uid int) (errno int) +//sysnb Setuid(uid int) (err error) //setuid(uid Uid_t) int -//sys Symlink(oldpath string, newpath string) (errno int) +//sys Symlink(oldpath string, newpath string) (err error) //symlink(oldpath *byte, newpath *byte) int //sys Sync() //sync() // FIXME: mksysinfo Time_t -// //sysnb Time(t *Time_t) (tt Time_t, errno int) +// //sysnb Time(t *Time_t) (tt Time_t, err error) // //time(t *Time_t) Time_t // FIXME: mksysinfo Tms -// //sysnb Times(tms *Tms) (ticks uintptr, errno int) +// //sysnb Times(tms *Tms) (ticks uintptr, err error) // //times(tms *Tms) _clock_t //sysnb Umask(mask int) (oldmask int) //umask(mask Mode_t) Mode_t -//sys Unlink(path string) (errno int) +//sys Unlink(path string) (err error) //unlink(path *byte) int // FIXME: mksysinfo Utimbuf -// //sys Utime(path string, buf *Utimbuf) (errno int) +// //sys Utime(path string, buf *Utimbuf) (err error) // //utime(path *byte, buf *Utimbuf) int -//sys Write(fd int, p []byte) (n int, errno int) +//sys Write(fd int, p []byte) (n int, err error) //write(fd int, buf *byte, count Size_t) Ssize_t -//sys munmap(addr uintptr, length uintptr) (errno int) +//sys munmap(addr uintptr, length uintptr) (err error) //munmap(addr *byte, length Size_t) int -//sys Madvise(b []byte, advice int) (errno int) +//sys Madvise(b []byte, advice int) (err error) //madvise(addr *byte, len Size_t, advice int) int -//sys Mprotect(b []byte, prot int) (errno int) +//sys Mprotect(b []byte, prot int) (err error) //mprotect(addr *byte, len Size_t, prot int) int -//sys Mlock(b []byte) (errno int) +//sys Mlock(b []byte) (err error) //mlock(addr *byte, len Size_t) int -//sys Munlock(b []byte) (errno int) +//sys Munlock(b []byte) (err error) //munlock(addr *byte, len Size_t) int -//sys Mlockall(flags int) (errno int) +//sys Mlockall(flags int) (err error) //mlockall(flags int) int -//sys Munlockall() (errno int) +//sys Munlockall() (err error) //munlockall() int func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } @@ -378,8 +378,8 @@ func NsecToTimeval(nsec int64) (tv Timeval) { return } -//sysnb Tcgetattr(fd int, p *Termios) (errno int) +//sysnb Tcgetattr(fd int, p *Termios) (err error) //tcgetattr(fd int, p *Termios) int -//sys Tcsetattr(fd int, actions int, p *Termios) (errno int) +//sys Tcsetattr(fd int, actions int, p *Termios) (err error) //tcsetattr(fd int, actions int, p *Termios) int diff --git a/libgo/go/syscall/libcall_posix_largefile.go b/libgo/go/syscall/libcall_posix_largefile.go index acfafec..e898648 100644 --- a/libgo/go/syscall/libcall_posix_largefile.go +++ b/libgo/go/syscall/libcall_posix_largefile.go @@ -6,32 +6,32 @@ package syscall -//sys Fstat(fd int, stat *Stat_t) (errno int) +//sys Fstat(fd int, stat *Stat_t) (err error) //fstat64(fd int, stat *Stat_t) int -//sys Ftruncate(fd int, length int64) (errno int) +//sys Ftruncate(fd int, length int64) (err error) //ftruncate64(fd int, length Offset_t) int -//sys Lstat(path string, stat *Stat_t) (errno int) +//sys Lstat(path string, stat *Stat_t) (err error) //lstat64(path *byte, stat *Stat_t) int -//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int) +//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) //mmap64(addr *byte, length Size_t, prot int, flags int, fd int, offset Offset_t) *byte -//sys Open(path string, mode int, perm uint32) (fd int, errno int) +//sys Open(path string, mode int, perm uint32) (fd int, err error) //open64(path *byte, mode int, perm Mode_t) int -//sys Pread(fd int, p []byte, offset int64) (n int, errno int) +//sys Pread(fd int, p []byte, offset int64) (n int, err error) //pread64(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int) +//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) //pwrite64(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Seek(fd int, offset int64, whence int) (off int64, errno int) +//sys Seek(fd int, offset int64, whence int) (off int64, err error) //lseek64(fd int, offset Offset_t, whence int) Offset_t -//sys Stat(path string, stat *Stat_t) (errno int) +//sys Stat(path string, stat *Stat_t) (err error) //stat64(path *byte, stat *Stat_t) int -//sys Truncate(path string, length int64) (errno int) +//sys Truncate(path string, length int64) (err error) //truncate64(path *byte, length Offset_t) int diff --git a/libgo/go/syscall/libcall_posix_regfile.go b/libgo/go/syscall/libcall_posix_regfile.go index b71da0c..9716701 100644 --- a/libgo/go/syscall/libcall_posix_regfile.go +++ b/libgo/go/syscall/libcall_posix_regfile.go @@ -7,32 +7,32 @@ package syscall -//sys Fstat(fd int, stat *Stat_t) (errno int) +//sys Fstat(fd int, stat *Stat_t) (err error) //fstat(fd int, stat *Stat_t) int -//sys Ftruncate(fd int, length int64) (errno int) +//sys Ftruncate(fd int, length int64) (err error) //ftruncate(fd int, length Offset_t) int -//sys Lstat(path string, stat *Stat_t) (errno int) +//sys Lstat(path string, stat *Stat_t) (err error) //lstat(path *byte, stat *Stat_t) int -//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, errno int) +//sys mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) //mmap(addr *byte, length Size_t, prot int, flags int, fd int, offset Offset_t) *byte -//sys Open(path string, mode int, perm uint32) (fd int, errno int) +//sys Open(path string, mode int, perm uint32) (fd int, err error) //open(path *byte, mode int, perm Mode_t) int -//sys Pread(fd int, p []byte, offset int64) (n int, errno int) +//sys Pread(fd int, p []byte, offset int64) (n int, err error) //pread(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Pwrite(fd int, p []byte, offset int64) (n int, errno int) +//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) //pwrite(fd int, buf *byte, count Size_t, offset Offset_t) Ssize_t -//sys Seek(fd int, offset int64, whence int) (off int64, errno int) +//sys Seek(fd int, offset int64, whence int) (off int64, err error) //lseek(fd int, offset Offset_t, whence int) Offset_t -//sys Stat(path string, stat *Stat_t) (errno int) +//sys Stat(path string, stat *Stat_t) (err error) //stat(path *byte, stat *Stat_t) int -//sys Truncate(path string, length int64) (errno int) +//sys Truncate(path string, length int64) (err error) //truncate(path *byte, length Offset_t) int diff --git a/libgo/go/syscall/libcall_solaris_386.go b/libgo/go/syscall/libcall_solaris_386.go index 9c4e966..e94deec 100644 --- a/libgo/go/syscall/libcall_solaris_386.go +++ b/libgo/go/syscall/libcall_solaris_386.go @@ -5,8 +5,8 @@ package syscall // 32-bit Solaris 2/x86 needs to use _nuname internally, cf. <sys/utsname.h>. -//sysnb Uname(buf *Utsname) (errno int) +//sysnb Uname(buf *Utsname) (err error) //_nuname(buf *Utsname) int -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long diff --git a/libgo/go/syscall/libcall_solaris_amd64.go b/libgo/go/syscall/libcall_solaris_amd64.go index f0d335d..69b11ba 100644 --- a/libgo/go/syscall/libcall_solaris_amd64.go +++ b/libgo/go/syscall/libcall_solaris_amd64.go @@ -5,6 +5,6 @@ package syscall // 64-bit ptrace(3C) doesn't exist -func raw_ptrace(request int, pid int, addr *byte, data *byte) int { +func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno { return ENOSYS } diff --git a/libgo/go/syscall/libcall_solaris_sparc.go b/libgo/go/syscall/libcall_solaris_sparc.go index ae45355..69e0db2 100644 --- a/libgo/go/syscall/libcall_solaris_sparc.go +++ b/libgo/go/syscall/libcall_solaris_sparc.go @@ -4,5 +4,5 @@ package syscall -//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (errno int) +//sysnb raw_ptrace(request int, pid int, addr *byte, data *byte) (err Errno) //ptrace(request int, pid Pid_t, addr *byte, data *byte) _C_long diff --git a/libgo/go/syscall/libcall_solaris_sparc64.go b/libgo/go/syscall/libcall_solaris_sparc64.go index f0d335d..69b11ba 100644 --- a/libgo/go/syscall/libcall_solaris_sparc64.go +++ b/libgo/go/syscall/libcall_solaris_sparc64.go @@ -5,6 +5,6 @@ package syscall // 64-bit ptrace(3C) doesn't exist -func raw_ptrace(request int, pid int, addr *byte, data *byte) int { +func raw_ptrace(request int, pid int, addr *byte, data *byte) Errno { return ENOSYS } diff --git a/libgo/go/syscall/libcall_support.go b/libgo/go/syscall/libcall_support.go index daed807..7449a0a 100644 --- a/libgo/go/syscall/libcall_support.go +++ b/libgo/go/syscall/libcall_support.go @@ -8,5 +8,5 @@ package syscall func entersyscall() func exitsyscall() -func GetErrno() int -func SetErrno(int) +func GetErrno() Errno +func SetErrno(Errno) diff --git a/libgo/go/syscall/libcall_uname.go b/libgo/go/syscall/libcall_uname.go index e4c32b1..519e6dc 100644 --- a/libgo/go/syscall/libcall_uname.go +++ b/libgo/go/syscall/libcall_uname.go @@ -4,5 +4,5 @@ package syscall -//sysnb Uname(buf *Utsname) (errno int) +//sysnb Uname(buf *Utsname) (err error) //uname(buf *Utsname) int diff --git a/libgo/go/syscall/libcall_wait4.go b/libgo/go/syscall/libcall_wait4.go index 7a63bc7..5786869 100644 --- a/libgo/go/syscall/libcall_wait4.go +++ b/libgo/go/syscall/libcall_wait4.go @@ -6,14 +6,13 @@ package syscall -//sys wait4(pid Pid_t, status *int, options int, rusage *Rusage) (wpid Pid_t, errno int) +//sys wait4(pid Pid_t, status *int, options int, rusage *Rusage) (wpid Pid_t, err error) //wait4(pid Pid_t, status *int, options int, rusage *Rusage) Pid_t -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { var status int r, err := wait4(Pid_t(pid), &status, options, rusage) wpid = int(r) - errno = err if wstatus != nil { *wstatus = WaitStatus(status) } diff --git a/libgo/go/syscall/libcall_waitpid.go b/libgo/go/syscall/libcall_waitpid.go index 0144463..1c476d8 100644 --- a/libgo/go/syscall/libcall_waitpid.go +++ b/libgo/go/syscall/libcall_waitpid.go @@ -6,14 +6,13 @@ package syscall -//sys waitpid(pid Pid_t, status *int, options int) (wpid Pid_t, errno int) +//sys waitpid(pid Pid_t, status *int, options int) (wpid Pid_t, err error) //waitpid(pid Pid_t, status *int, options int) Pid_t -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, errno int) { +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { var status int r, err := waitpid(Pid_t(pid), &status, options) wpid = int(r) - errno = err if wstatus != nil { *wstatus = WaitStatus(status) } diff --git a/libgo/go/syscall/lsf_linux.go b/libgo/go/syscall/lsf_linux.go index 0976688..05d653b 100644 --- a/libgo/go/syscall/lsf_linux.go +++ b/libgo/go/syscall/lsf_linux.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. -// GNU/Linux socket filter +// Linux socket filter package syscall @@ -18,10 +18,10 @@ func LsfJump(code, k, jt, jf int) *SockFilter { return &SockFilter{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} } -func LsfSocket(ifindex, proto int) (int, int) { +func LsfSocket(ifindex, proto int) (int, error) { var lsall SockaddrLinklayer s, e := Socket(AF_PACKET, SOCK_RAW, proto) - if e != 0 { + if e != nil { return 0, e } p := (*[2]byte)(unsafe.Pointer(&lsall.Protocol)) @@ -29,11 +29,11 @@ func LsfSocket(ifindex, proto int) (int, int) { p[1] = byte(proto) lsall.Ifindex = ifindex e = Bind(s, &lsall) - if e != 0 { + if e != nil { Close(s) return 0, e } - return s, 0 + return s, nil } type iflags struct { @@ -41,17 +41,17 @@ type iflags struct { flags uint16 } -func SetLsfPromisc(name string, m bool) int { +func SetLsfPromisc(name string, m bool) error { s, e := Socket(AF_INET, SOCK_DGRAM, 0) - if e != 0 { + if e != nil { return e } defer Close(s) var ifl iflags copy(ifl.name[:], []byte(name)) _, _, ep := Syscall(SYS_IOCTL, uintptr(s), SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifl))) - if e := int(ep); e != 0 { - return e + if ep != 0 { + return Errno(ep) } if m { ifl.flags |= uint16(IFF_PROMISC) @@ -59,20 +59,20 @@ func SetLsfPromisc(name string, m bool) int { ifl.flags &= ^uint16(IFF_PROMISC) } _, _, ep = Syscall(SYS_IOCTL, uintptr(s), SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifl))) - if e := int(ep); e != 0 { - return e + if ep != 0 { + return Errno(ep) } - return 0 + return nil } -func AttachLsf(fd int, i []SockFilter) int { +func AttachLsf(fd int, i []SockFilter) error { var p SockFprog p.Len = uint16(len(i)) p.Filter = (*SockFilter)(unsafe.Pointer(&i[0])) return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, uintptr(unsafe.Pointer(&p)), unsafe.Sizeof(p)) } -func DetachLsf(fd int) int { +func DetachLsf(fd int) error { var dummy int return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, uintptr(unsafe.Pointer(&dummy)), unsafe.Sizeof(dummy)) } diff --git a/libgo/go/syscall/mksyscall.awk b/libgo/go/syscall/mksyscall.awk index 49828d9..b02989c 100644 --- a/libgo/go/syscall/mksyscall.awk +++ b/libgo/go/syscall/mksyscall.awk @@ -12,7 +12,7 @@ # This includes return parameters. # * The parameter lists must give a type for each argument: # the (x, y, z int) shorthand is not allowed. -# * If the return parameter is an error number, it must be named errno. +# * If the return parameter is an error, it must be named err. # A line beginning with //sysnb is like //sys, except that the # goroutine will not be suspended during the execution of the library @@ -217,13 +217,13 @@ BEGIN { goname = goparam[1] gotype = goparam[2] - if (goname == "errno") { + if (goname == "err") { if (cfnresult ~ /^\*/) { print "\tif _r == nil {" } else { print "\tif _r < 0 {" } - print "\t\terrno = GetErrno()" + print "\t\terr = GetErrno()" print "\t}" } else if (gotype == "uintptr" && cfnresult ~ /^\*/) { printf("\t%s = (%s)(unsafe.Pointer(_r))\n", goname, gotype) diff --git a/libgo/go/syscall/netlink_linux.go b/libgo/go/syscall/netlink_linux.go index 4ee78d6..8683bb3 100644 --- a/libgo/go/syscall/netlink_linux.go +++ b/libgo/go/syscall/netlink_linux.go @@ -63,31 +63,28 @@ 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, int) { +func NetlinkRIB(proto, family int) ([]byte, error) { var ( - s int - e int lsanl SockaddrNetlink - seq int tab []byte ) - s, e = Socket(AF_NETLINK, SOCK_RAW, 0) - if e != 0 { + s, e := Socket(AF_NETLINK, SOCK_RAW, 0) + if e != nil { return nil, e } defer Close(s) lsanl.Family = AF_NETLINK e = Bind(s, &lsanl) - if e != 0 { + if e != nil { return nil, e } - seq++ + seq := 1 wb := newNetlinkRouteRequest(proto, seq, family) e = Sendto(s, wb, 0, &lsanl) - if e != 0 { + if e != nil { return nil, e } @@ -100,7 +97,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) { rb = make([]byte, Getpagesize()) nr, _, e = Recvfrom(s, rb, 0) - if e != 0 { + if e != nil { return nil, e } if nr < NLMSG_HDRLEN { @@ -111,7 +108,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) { msgs, _ := ParseNetlinkMessage(rb) for _, m := range msgs { - if lsa, e = Getsockname(s); e != 0 { + if lsa, e = Getsockname(s); e != nil { return nil, e } switch v := lsa.(type) { @@ -132,7 +129,7 @@ func NetlinkRIB(proto, family int) ([]byte, int) { } done: - return tab, 0 + return tab, nil } // NetlinkMessage represents the netlink message. @@ -143,18 +140,18 @@ type NetlinkMessage struct { // ParseNetlinkMessage parses buf as netlink messages and returns // the slice containing the NetlinkMessage structs. -func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, int) { +func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) { var ( h *NlMsghdr dbuf []byte dlen int - e int + e error msgs []NetlinkMessage ) for len(buf) >= NLMSG_HDRLEN { h, dbuf, dlen, e = netlinkMessageHeaderAndData(buf) - if e != 0 { + if e != nil { break } m := NetlinkMessage{} @@ -167,12 +164,12 @@ func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, int) { return msgs, e } -func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, int) { +func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) { h := (*NlMsghdr)(unsafe.Pointer(&buf[0])) if int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(buf) { return nil, nil, 0, EINVAL } - return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), 0 + return h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil } // NetlinkRouteAttr represents the netlink route attribute. @@ -184,13 +181,13 @@ type NetlinkRouteAttr struct { // ParseNetlinkRouteAttr parses msg's payload as netlink route // attributes and returns the slice containing the NetlinkRouteAttr // structs. -func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) { +func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) { var ( buf []byte a *RtAttr alen int vbuf []byte - e int + e error attrs []NetlinkRouteAttr ) @@ -207,7 +204,7 @@ func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) { for len(buf) >= SizeofRtAttr { a, vbuf, alen, e = netlinkRouteAttrAndValue(buf) - if e != 0 { + if e != nil { break } ra := NetlinkRouteAttr{} @@ -217,13 +214,13 @@ func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, int) { buf = buf[alen:] } - return attrs, 0 + return attrs, nil } -func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, int) { +func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) { h := (*RtAttr)(unsafe.Pointer(&buf[0])) if int(h.Len) < SizeofRtAttr || int(h.Len) > len(buf) { return nil, nil, 0, EINVAL } - return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), 0 + return h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil } diff --git a/libgo/go/syscall/route_bsd.go b/libgo/go/syscall/route_bsd.go index f6b124b..bc4c15e 100644 --- a/libgo/go/syscall/route_bsd.go +++ b/libgo/go/syscall/route_bsd.go @@ -29,29 +29,24 @@ func rsaAlignOf(salen int) int { // RouteRIB returns routing information base, as known as RIB, // which consists of network facility information, states and // parameters. -func RouteRIB(facility, param int) ([]byte, int) { - var ( - tab []byte - e int - ) - +func RouteRIB(facility, param int) ([]byte, error) { mib := []_C_int{CTL_NET, AF_ROUTE, 0, 0, _C_int(facility), _C_int(param)} // Find size. n := uintptr(0) - if e = sysctl(mib, nil, &n, nil, 0); e != 0 { - return nil, e + if err := sysctl(mib, nil, &n, nil, 0); err != nil { + return nil, err } if n == 0 { - return nil, 0 + return nil, nil } - tab = make([]byte, n) - if e = sysctl(mib, &tab[0], &n, nil, 0); e != 0 { - return nil, e + tab := make([]byte, n) + if err := sysctl(mib, &tab[0], &n, nil, 0); err != nil { + return nil, err } - return tab[:n], 0 + return tab[:n], nil } // RoutingMessage represents a routing message. @@ -91,7 +86,7 @@ func (m *RouteMessage) sockaddr() []Sockaddr { switch i { case RTAX_DST, RTAX_GATEWAY: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } if i == RTAX_DST { @@ -134,7 +129,7 @@ func (m *InterfaceMessage) sockaddr() (sas []Sockaddr) { return nil } sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(&m.Data[0]))) - if e != 0 { + if e != nil { return nil } return append(sas, sa) @@ -163,7 +158,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { switch i { case RTAX_IFA: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } sas = append(sas, sa) @@ -178,7 +173,7 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { // ParseRoutingMessage parses buf as routing messages and returns // the slice containing the RoutingMessage interfaces. -func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) { +func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, err error) { for len(buf) >= anyMessageLen { any := (*anyMessage)(unsafe.Pointer(&buf[0])) if any.Version != RTM_VERSION { @@ -187,11 +182,11 @@ func ParseRoutingMessage(buf []byte) (msgs []RoutingMessage, errno int) { msgs = append(msgs, any.toRoutingMessage(buf)) buf = buf[any.Msglen:] } - return msgs, 0 + return msgs, nil } // ParseRoutingMessage parses msg's payload as raw sockaddrs and // returns the slice containing the Sockaddr interfaces. -func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, errno int) { - return append(sas, msg.sockaddr()...), 0 +func ParseRoutingSockaddr(msg RoutingMessage) (sas []Sockaddr, err error) { + return append(sas, msg.sockaddr()...), nil } diff --git a/libgo/go/syscall/route_darwin.go b/libgo/go/syscall/route_darwin.go index 9d3a701..410e70a 100644 --- a/libgo/go/syscall/route_darwin.go +++ b/libgo/go/syscall/route_darwin.go @@ -63,7 +63,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { switch i { case RTAX_IFA: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } sas = append(sas, sa) diff --git a/libgo/go/syscall/route_freebsd.go b/libgo/go/syscall/route_freebsd.go index 0d61d08..094e170 100644 --- a/libgo/go/syscall/route_freebsd.go +++ b/libgo/go/syscall/route_freebsd.go @@ -63,7 +63,7 @@ func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { switch i { case RTAX_IFA: sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) - if e != 0 { + if e != nil { return nil } sas = append(sas, sa) diff --git a/libgo/go/syscall/sleep_rtems.go b/libgo/go/syscall/sleep_rtems.go index 8992eb5..9d72203 100644 --- a/libgo/go/syscall/sleep_rtems.go +++ b/libgo/go/syscall/sleep_rtems.go @@ -6,8 +6,8 @@ package syscall -func Sleep(nsec int64) (errno int) { +func Sleep(nsec int64) (err error) { ts := NsecToTimespec(nsec) - errno = Nanosleep(&ts, nil) + err = Nanosleep(&ts, nil) return } diff --git a/libgo/go/syscall/sleep_select.go b/libgo/go/syscall/sleep_select.go index 3ebaf58..533f554 100644 --- a/libgo/go/syscall/sleep_select.go +++ b/libgo/go/syscall/sleep_select.go @@ -6,8 +6,8 @@ package syscall -func Sleep(nsec int64) (errno int) { +func Sleep(nsec int64) (err error) { tv := NsecToTimeval(nsec); - _, err := Select(0, nil, nil, nil, &tv); + _, err = Select(0, nil, nil, nil, &tv); return err; } diff --git a/libgo/go/syscall/sockcmsg_linux.go b/libgo/go/syscall/sockcmsg_linux.go index b025ca5..0b4caa1 100644 --- a/libgo/go/syscall/sockcmsg_linux.go +++ b/libgo/go/syscall/sockcmsg_linux.go @@ -26,7 +26,7 @@ func UnixCredentials(ucred *Ucred) []byte { // ParseUnixCredentials decodes a socket control message that contains // credentials in a Ucred structure. To receive such a message, the // SO_PASSCRED option must be enabled on the socket. -func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, int) { +func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, error) { if msg.Header.Level != SOL_SOCKET { return nil, EINVAL } @@ -34,5 +34,5 @@ func ParseUnixCredentials(msg *SocketControlMessage) (*Ucred, int) { return nil, EINVAL } ucred := *(*Ucred)(unsafe.Pointer(&msg.Data[0])) - return &ucred, 0 + return &ucred, nil } diff --git a/libgo/go/syscall/sockcmsg_unix.go b/libgo/go/syscall/sockcmsg_unix.go index c9872aeb..84c1383 100644 --- a/libgo/go/syscall/sockcmsg_unix.go +++ b/libgo/go/syscall/sockcmsg_unix.go @@ -47,17 +47,17 @@ type SocketControlMessage struct { Data []byte } -func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, int) { +func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, error) { var ( h *Cmsghdr dbuf []byte - e int + e error cmsgs []SocketControlMessage ) for len(buf) >= CmsgLen(0) { h, dbuf, e = socketControlMessageHeaderAndData(buf) - if e != 0 { + if e != nil { break } m := SocketControlMessage{} @@ -70,12 +70,12 @@ func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, int) { return cmsgs, e } -func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, int) { +func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, error) { h := (*Cmsghdr)(unsafe.Pointer(&buf[0])) if h.Len < SizeofCmsghdr || int(h.Len) > len(buf) { return nil, nil, EINVAL } - return h, buf[cmsgAlignOf(SizeofCmsghdr):], 0 + return h, buf[cmsgAlignOf(SizeofCmsghdr):], nil } // UnixRights encodes a set of open file descriptors into a socket @@ -99,7 +99,7 @@ func UnixRights(fds ...int) []byte { // ParseUnixRights decodes a socket control message that contains an // integer array of open file descriptors from another process. -func ParseUnixRights(msg *SocketControlMessage) ([]int, int) { +func ParseUnixRights(msg *SocketControlMessage) ([]int, error) { if msg.Header.Level != SOL_SOCKET { return nil, EINVAL } @@ -111,5 +111,5 @@ func ParseUnixRights(msg *SocketControlMessage) ([]int, int) { fds[j] = int(*(*int32)(unsafe.Pointer(&msg.Data[i]))) j++ } - return fds, 0 + return fds, nil } diff --git a/libgo/go/syscall/socket.go b/libgo/go/syscall/socket.go index e0218ba..005fd84 100644 --- a/libgo/go/syscall/socket.go +++ b/libgo/go/syscall/socket.go @@ -17,7 +17,7 @@ import "unsafe" var SocketDisableIPv6 bool type Sockaddr interface { - sockaddr() (ptr *RawSockaddrAny, len Socklen_t, errno int) // lowercase; only we can define Sockaddrs + sockaddr() (ptr *RawSockaddrAny, len Socklen_t, err error) // lowercase; only we can define Sockaddrs } type RawSockaddrAny struct { @@ -33,7 +33,7 @@ type SockaddrInet4 struct { raw RawSockaddrInet4 } -func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, error) { if sa.Port < 0 || sa.Port > 0xFFFF { return nil, 0, EINVAL } @@ -45,7 +45,7 @@ func (sa *SockaddrInet4) sockaddr() (*RawSockaddrAny, Socklen_t, int) { for i := 0; i < len(sa.Addr); i++ { sa.raw.Addr[i] = sa.Addr[i] } - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, nil } type SockaddrInet6 struct { @@ -55,7 +55,7 @@ type SockaddrInet6 struct { raw RawSockaddrInet6 } -func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, error) { if sa.Port < 0 || sa.Port > 0xFFFF { return nil, 0, EINVAL } @@ -68,7 +68,7 @@ func (sa *SockaddrInet6) sockaddr() (*RawSockaddrAny, Socklen_t, int) { for i := 0; i < len(sa.Addr); i++ { sa.raw.Addr[i] = sa.Addr[i] } - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), n, nil } type SockaddrUnix struct { @@ -76,7 +76,7 @@ type SockaddrUnix struct { raw RawSockaddrUnix } -func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, error) { name := sa.Name n := len(name) if n >= len(sa.raw.Path) || n == 0 { @@ -92,186 +92,186 @@ func (sa *SockaddrUnix) sockaddr() (*RawSockaddrAny, Socklen_t, int) { } // length is family (uint16), name, NUL. - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), 2 + Socklen_t(n) + 1, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), 2 + Socklen_t(n) + 1, nil } -func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, int) { +func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) { switch rsa.Addr.Family { case AF_UNIX: pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa)) sa := new(SockaddrUnix) n, err := pp.getLen() - if err != 0 { + if err != nil { return nil, err } - bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0])); - sa.Name = string(bytes[0:n]); - return sa, 0; + bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0])) + sa.Name = string(bytes[0:n]) + return sa, nil case AF_INET: - pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa)); - sa := new(SockaddrInet4); - p := (*[2]byte)(unsafe.Pointer(&pp.Port)); - sa.Port = int(p[0])<<8 + int(p[1]); + pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa)) + sa := new(SockaddrInet4) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i]; + sa.Addr[i] = pp.Addr[i] } - return sa, 0; + return sa, nil case AF_INET6: - pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)); - sa := new(SockaddrInet6); - p := (*[2]byte)(unsafe.Pointer(&pp.Port)); - sa.Port = int(p[0])<<8 + int(p[1]); + pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa)) + sa := new(SockaddrInet6) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + sa.Port = int(p[0])<<8 + int(p[1]) for i := 0; i < len(sa.Addr); i++ { - sa.Addr[i] = pp.Addr[i]; + sa.Addr[i] = pp.Addr[i] } - return sa, 0; + return sa, nil } return anyToSockaddrOS(rsa) } -//sys accept(fd int, sa *RawSockaddrAny, len *Socklen_t) (nfd int, errno int) +//sys accept(fd int, sa *RawSockaddrAny, len *Socklen_t) (nfd int, err error) //accept(fd int, sa *RawSockaddrAny, len *Socklen_t) int -func Accept(fd int) (nfd int, sa Sockaddr, errno int) { +func Accept(fd int) (nfd int, sa Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - nfd, errno = accept(fd, &rsa, &len) - if errno != 0 { + nfd, err = accept(fd, &rsa, &len) + if err != nil { return } - sa, errno = anyToSockaddr(&rsa) - if errno != 0 { + sa, err = anyToSockaddr(&rsa) + if err != nil { Close(nfd) nfd = 0 } return } -//sysnb getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) (errno int) +//sysnb getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) (err error) //getsockname(fd int, sa *RawSockaddrAny, len *Socklen_t) int -func Getsockname(fd int) (sa Sockaddr, errno int) { +func Getsockname(fd int) (sa Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - if errno = getsockname(fd, &rsa, &len); errno != 0 { + if err = getsockname(fd, &rsa, &len); err != nil { return } return anyToSockaddr(&rsa) } -//sysnb getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) (errno int) +//sysnb getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) (err error) //getpeername(fd int, sa *RawSockaddrAny, len *Socklen_t) int -func Getpeername(fd int) (sa Sockaddr, errno int) { +func Getpeername(fd int) (sa Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - if getpeername(fd, &rsa, &len); errno != 0 { + if err = getpeername(fd, &rsa, &len); err != nil { return } return anyToSockaddr(&rsa) } -//sys bind(fd int, sa *RawSockaddrAny, len Socklen_t) (errno int) +//sys bind(fd int, sa *RawSockaddrAny, len Socklen_t) (err error) //bind(fd int, sa *RawSockaddrAny, len Socklen_t) int -func Bind(fd int, sa Sockaddr) (errno int) { +func Bind(fd int, sa Sockaddr) (err error) { ptr, n, err := sa.sockaddr() - if err != 0 { + if err != nil { return err } return bind(fd, ptr, n) } -//sys connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) (errno int) +//sys connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) (err error) //connect(s int, addr *RawSockaddrAny, addrlen Socklen_t) int -func Connect(fd int, sa Sockaddr) (errno int) { +func Connect(fd int, sa Sockaddr) (err error) { ptr, n, err := sa.sockaddr() - if err != 0 { + if err != nil { return err } return connect(fd, ptr, n) } -//sysnb socket(domain int, typ int, proto int) (fd int, errno int) +//sysnb socket(domain int, typ int, proto int) (fd int, err error) //socket(domain int, typ int, protocol int) int -func Socket(domain, typ, proto int) (fd, errno int) { +func Socket(domain, typ, proto int) (fd int, err error) { if domain == AF_INET6 && SocketDisableIPv6 { return -1, EAFNOSUPPORT } - fd, errno = socket(domain, typ, proto) + fd, err = socket(domain, typ, proto) return } -//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) +//sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (err error) //socketpair(domain int, typ int, protocol int, fd *[2]int) int -func Socketpair(domain, typ, proto int) (fd [2]int, errno int) { - errno = socketpair(domain, typ, proto, &fd) +func Socketpair(domain, typ, proto int) (fd [2]int, err error) { + err = socketpair(domain, typ, proto, &fd) return } -//sys getsockopt(s int, level int, name int, val uintptr, vallen *Socklen_t) (errno int) +//sys getsockopt(s int, level int, name int, val uintptr, vallen *Socklen_t) (err error) //getsockopt(s int, level int, name int, val *byte, vallen *Socklen_t) int -func GetsockoptInt(fd, level, opt int) (value, errno int) { +func GetsockoptInt(fd, level, opt int) (value int, err error) { var n int32 vallen := Socklen_t(4) - errno = getsockopt(fd, level, opt, (uintptr)(unsafe.Pointer(&n)), &vallen) - return int(n), errno + err = getsockopt(fd, level, opt, (uintptr)(unsafe.Pointer(&n)), &vallen) + return int(n), err } -func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, errno int) { +func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, err error) { vallen := Socklen_t(4) - errno = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen) - return value, errno + err = getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value[0])), &vallen) + return value, err } -func GetsockoptIPMreq(fd, level, opt int) (*IPMreq, int) { +func GetsockoptIPMreq(fd, level, opt int) (*IPMreq, error) { var value IPMreq vallen := Socklen_t(SizeofIPMreq) - errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) - return &value, errno + err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) + return &value, err } /* FIXME: mksysinfo needs to support IPMreqn. -func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, int) { +func GetsockoptIPMreqn(fd, level, opt int) (*IPMreqn, error) { var value IPMreqn vallen := Socklen_t(SizeofIPMreqn) - errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) - return &value, errno + err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) + return &value, err } */ /* FIXME: mksysinfo needs to support IPv6Mreq. -func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, int) { +func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) { var value IPv6Mreq vallen := Socklen_t(SizeofIPv6Mreq) - errno := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) - return &value, errno + err := getsockopt(fd, level, opt, uintptr(unsafe.Pointer(&value)), &vallen) + return &value, err } */ -//sys setsockopt(s int, level int, name int, val *byte, vallen Socklen_t) (errno int) +//sys setsockopt(s int, level int, name int, val *byte, vallen Socklen_t) (err error) //setsockopt(s int, level int, optname int, val *byte, vallen Socklen_t) int -func SetsockoptInt(fd, level, opt int, value int) (errno int) { +func SetsockoptInt(fd, level, opt int, value int) (err error) { var n = int32(value) return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&n)), 4) } -func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (errno int) { +func SetsockoptInet4Addr(fd, level, opt int, value [4]byte) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&value[0])), 4) } -func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) { +func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(tv)), Socklen_t(unsafe.Sizeof(*tv))) } @@ -280,58 +280,58 @@ type Linger struct { Linger int32; } -func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { +func SetsockoptLinger(fd, level, opt int, l *Linger) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(l)), Socklen_t(unsafe.Sizeof(*l))); } -func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (errno int) { +func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq))) } /* FIXME: mksysinfo needs to support IMPreqn. -func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (errno int) { +func SetsockoptIPMreqn(fd, level, opt int, mreq *IPMreqn) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq))) } */ -func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (errno int) { +func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(mreq)), Socklen_t(unsafe.Sizeof(*mreq))) } -func SetsockoptString(fd, level, opt int, s string) (errno int) { +func SetsockoptString(fd, level, opt int, s string) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(&[]byte(s)[0])), Socklen_t(len(s))) } -//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *Socklen_t) (n int, errno int) +//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *Socklen_t) (n int, err error) //recvfrom(fd int, buf *byte, len Size_t, flags int, from *RawSockaddrAny, fromlen *Socklen_t) Ssize_t -func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) { +func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) { var rsa RawSockaddrAny var len Socklen_t = SizeofSockaddrAny - if n, errno = recvfrom(fd, p, flags, &rsa, &len); errno != 0 { + if n, err = recvfrom(fd, p, flags, &rsa, &len); err != nil { return } - from, errno = anyToSockaddr(&rsa) + from, err = anyToSockaddr(&rsa) return } -//sys sendto(s int, buf []byte, flags int, to *RawSockaddrAny, tolen Socklen_t) (errno int) +//sys sendto(s int, buf []byte, flags int, to *RawSockaddrAny, tolen Socklen_t) (err error) //sendto(s int, buf *byte, len Size_t, flags int, to *RawSockaddrAny, tolen Socklen_t) Ssize_t -func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) { +func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) { ptr, n, err := to.sockaddr() - if err != 0 { + if err != nil { return err } return sendto(fd, p, flags, ptr, n) } -//sys recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) +//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error) //recvmsg(s int, msg *Msghdr, flags int) Ssize_t -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, errno int) { +func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { var msg Msghdr var rsa RawSockaddrAny msg.Name = (*byte)(unsafe.Pointer(&rsa)) @@ -353,28 +353,28 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from } msg.Iov = &iov msg.Iovlen = 1 - if n, errno = recvmsg(fd, &msg, flags); errno != 0 { + if n, err = recvmsg(fd, &msg, flags); err != nil { return } oobn = int(msg.Controllen) recvflags = int(msg.Flags) // source address is only specified if the socket is unconnected if rsa.Addr.Family != AF_UNSPEC { - from, errno = anyToSockaddr(&rsa) + from, err = anyToSockaddr(&rsa) } return } -//sys sendmsg(s int, msg *Msghdr, flags int) (errno int) +//sys sendmsg(s int, msg *Msghdr, flags int) (err error) //sendmsg(s int, msg *Msghdr, flags int) Ssize_t -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) { +func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) { var ptr *RawSockaddrAny var salen Socklen_t if to != nil { - var err int + var err error ptr, salen, err = to.sockaddr() - if err != 0 { + if err != nil { return err } } @@ -398,16 +398,16 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) { } msg.Iov = &iov msg.Iovlen = 1 - if errno = sendmsg(fd, &msg, flags); errno != 0 { + if err = sendmsg(fd, &msg, flags); err != nil { return } return } -//sys Listen(fd int, n int) (errno int) +//sys Listen(fd int, n int) (err error) //listen(fd int, n int) int -//sys Shutdown(fd int, how int) (errno int) +//sys Shutdown(fd int, how int) (err error) //shutdown(fd int, how int) int func (iov *Iovec) SetLen(length int) { diff --git a/libgo/go/syscall/socket_bsd.go b/libgo/go/syscall/socket_bsd.go index 735baf9..be55991 100644 --- a/libgo/go/syscall/socket_bsd.go +++ b/libgo/go/syscall/socket_bsd.go @@ -47,7 +47,7 @@ func (sa *RawSockaddrUnix) setLen(n int) { sa.Len = uint8(3 + n) // 2 for Family, Len; 1 for NUL. } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Len < 3 || sa.Len > SizeofSockaddrUnix { return 0, EINVAL } @@ -59,7 +59,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { break } } - return n, 0 + return n, nil } type RawSockaddr struct { @@ -69,10 +69,10 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return ENOSYS } -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { - return nil, EAFNOSUPPORT; +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { + return nil, EAFNOSUPPORT } diff --git a/libgo/go/syscall/socket_irix.go b/libgo/go/syscall/socket_irix.go index 8bd55b8..c1fdc65 100644 --- a/libgo/go/syscall/socket_irix.go +++ b/libgo/go/syscall/socket_irix.go @@ -41,7 +41,7 @@ type RawSockaddrUnix struct { func (sa *RawSockaddrUnix) setLen(int) { } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Path[0] == 0 { // "Abstract" Unix domain socket. // Rewrite leading NUL as @ for textual display. @@ -61,7 +61,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { n++ } - return n, 0 + return n, nil } type RawSockaddr struct { @@ -70,7 +70,7 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return ENOSYS } @@ -124,6 +124,6 @@ const ( EAI_MAX = 14 ) -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { - return nil, EAFNOSUPPORT; +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { + return nil, EAFNOSUPPORT } diff --git a/libgo/go/syscall/socket_linux.go b/libgo/go/syscall/socket_linux.go index 49aac87..212e0b2 100644 --- a/libgo/go/syscall/socket_linux.go +++ b/libgo/go/syscall/socket_linux.go @@ -24,7 +24,7 @@ type SockaddrLinklayer struct { raw RawSockaddrLinklayer } -func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, error) { if sa.Ifindex < 0 || sa.Ifindex > 0x7fffffff { return nil, 0, EINVAL } @@ -37,7 +37,7 @@ func (sa *SockaddrLinklayer) sockaddr() (*RawSockaddrAny, Socklen_t, int) { for i := 0; i < len(sa.Addr); i++ { sa.raw.Addr[i] = sa.Addr[i] } - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrLinklayer, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrLinklayer, nil } type SockaddrNetlink struct { @@ -48,12 +48,12 @@ type SockaddrNetlink struct { raw RawSockaddrNetlink } -func (sa *SockaddrNetlink) sockaddr() (*RawSockaddrAny, Socklen_t, int) { +func (sa *SockaddrNetlink) sockaddr() (*RawSockaddrAny, Socklen_t, error) { sa.raw.Family = AF_NETLINK sa.raw.Pad = sa.Pad sa.raw.Pid = sa.Pid sa.raw.Groups = sa.Groups - return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrNetlink, 0 + return (*RawSockaddrAny)(unsafe.Pointer(&sa.raw)), SizeofSockaddrNetlink, nil } type RawSockaddrInet4 struct { @@ -87,7 +87,7 @@ type RawSockaddrUnix struct { func (sa *RawSockaddrUnix) setLen(int) { } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Path[0] == 0 { // "Abstract" Unix domain socket. // Rewrite leading NUL as @ for textual display. @@ -107,7 +107,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { n++ } - return n, 0 + return n, nil } type RawSockaddrLinklayer struct { @@ -133,11 +133,11 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return SetsockoptString(fd, SOL_SOCKET, SO_BINDTODEVICE, device) } -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { switch rsa.Addr.Family { case AF_NETLINK: pp := (*RawSockaddrNetlink)(unsafe.Pointer(rsa)) @@ -146,7 +146,7 @@ func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { sa.Pad = pp.Pad sa.Pid = pp.Pid sa.Groups = pp.Groups - return sa, 0 + return sa, nil case AF_PACKET: pp := (*RawSockaddrLinklayer)(unsafe.Pointer(rsa)) @@ -159,16 +159,16 @@ func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { for i := 0; i < len(sa.Addr); i++ { sa.Addr[i] = pp.Addr[i] } - return sa, 0 + return sa, nil } return nil, EAFNOSUPPORT } -//sysnb EpollCreate(size int) (fd int, errno int) +//sysnb EpollCreate(size int) (fd int, err error) //epoll_create(size int) int -//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (errno int) +//sysnb EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) //epoll_ctl(epfd int, op int, fd int, event *EpollEvent) int -//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, errno int) +//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) //epoll_wait(epfd int, events *EpollEvent, maxevents int, timeout int) int diff --git a/libgo/go/syscall/socket_solaris.go b/libgo/go/syscall/socket_solaris.go index 0c3e6c9..0a03465 100644 --- a/libgo/go/syscall/socket_solaris.go +++ b/libgo/go/syscall/socket_solaris.go @@ -42,7 +42,7 @@ type RawSockaddrUnix struct { func (sa *RawSockaddrUnix) setLen(int) { } -func (sa *RawSockaddrUnix) getLen() (int, int) { +func (sa *RawSockaddrUnix) getLen() (int, error) { if sa.Path[0] == 0 { // "Abstract" Unix domain socket. // Rewrite leading NUL as @ for textual display. @@ -62,7 +62,7 @@ func (sa *RawSockaddrUnix) getLen() (int, int) { n++ } - return n, 0 + return n, nil } type RawSockaddr struct { @@ -71,10 +71,10 @@ type RawSockaddr struct { } // BindToDevice binds the socket associated with fd to device. -func BindToDevice(fd int, device string) (errno int) { +func BindToDevice(fd int, device string) (err error) { return ENOSYS } -func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, int) { - return nil, EAFNOSUPPORT; +func anyToSockaddrOS(rsa *RawSockaddrAny) (Sockaddr, error) { + return nil, EAFNOSUPPORT } diff --git a/libgo/go/syscall/syscall.go b/libgo/go/syscall/syscall.go index a802ba0..ad9df6c 100644 --- a/libgo/go/syscall/syscall.go +++ b/libgo/go/syscall/syscall.go @@ -9,8 +9,9 @@ // packages rather than this one if you can. // For details of the functions and data types in this package consult // the manuals for the appropriate operating system. -// These calls return errno == 0 to indicate success; otherwise -// errno is an operating system error number describing the failure. +// These calls return err == nil to indicate success; otherwise +// err is an operating system error describing the failure. +// On most systems, that error has type syscall.Errno. package syscall import "unsafe" diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index c734b2c..899a65c 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -23,7 +23,7 @@ func c_syscall64(trap int64, a1, a2, a3, a4, a5, a6 int64) int64 __asm__ ("sysca // Do a system call. We look at the size of uintptr to see how to pass // the arguments, so that we don't pass a 64-bit value when the function // expects a 32-bit one. -func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { entersyscall() var r uintptr if unsafe.Sizeof(r) == 4 { @@ -33,12 +33,12 @@ func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { r1 := c_syscall64(int64(trap), int64(a1), int64(a2), int64(a3), 0, 0, 0) r = uintptr(r1) } - errno := GetErrno() + err = GetErrno() exitsyscall() - return r, 0, uintptr(errno) + return r, 0, err } -func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { +func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { entersyscall() var r uintptr if unsafe.Sizeof(r) == 4 { @@ -50,12 +50,12 @@ func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { int64(a4), int64(a5), int64(a6)) r = uintptr(r1) } - errno := GetErrno() + err = GetErrno() exitsyscall() - return r, 0, uintptr(errno) + return r, 0, err } -func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { +func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { var r uintptr if unsafe.Sizeof(r) == 4 { r1 := c_syscall32(int32(trap), int32(a1), int32(a2), int32(a3), 0, 0, 0) @@ -64,11 +64,11 @@ func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) { r1 := c_syscall64(int64(trap), int64(a1), int64(a2), int64(a3), 0, 0, 0) r = uintptr(r1) } - errno := GetErrno() - return r, 0, uintptr(errno) + err = GetErrno() + return r, 0, err } -func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { +func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { var r uintptr if unsafe.Sizeof(r) == 4 { r1 := c_syscall32(int32(trap), int32(a1), int32(a2), int32(a3), @@ -79,8 +79,8 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { int64(a4), int64(a5), int64(a6)) r = uintptr(r1) } - errno := GetErrno() - return r, 0, uintptr(errno) + err = GetErrno() + return r, 0, err } // Mmap manager, for use by operating system-specific implementations. @@ -89,18 +89,18 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { type mmapper struct { sync.Mutex active map[*byte][]byte // active mappings; key is last byte in mapping - mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, int) - munmap func(addr uintptr, length uintptr) int + mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, error) + munmap func(addr uintptr, length uintptr) error } -func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) { +func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { if length <= 0 { return nil, EINVAL } // Map the requested memory. addr, errno := m.mmap(0, uintptr(length), prot, flags, fd, offset) - if errno != 0 { + if errno != nil { return nil, errno } @@ -119,10 +119,10 @@ func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (d m.Lock() defer m.Unlock() m.active[p] = b - return b, 0 + return b, nil } -func (m *mmapper) Munmap(data []byte) (errno int) { +func (m *mmapper) Munmap(data []byte) (err error) { if len(data) == 0 || len(data) != cap(data) { return EINVAL } @@ -137,11 +137,11 @@ func (m *mmapper) Munmap(data []byte) (errno int) { } // Unmap the memory and update m. - if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != 0 { + if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != nil { return errno } m.active[p] = nil, false - return 0 + return nil } var mapper = &mmapper{ @@ -150,10 +150,32 @@ var mapper = &mmapper{ munmap: munmap, } -func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) { +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { return mapper.Mmap(fd, offset, length, prot, flags) } -func Munmap(b []byte) (errno int) { +func Munmap(b []byte) (err error) { return mapper.Munmap(b) } + + +// 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 { + return Errstr(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 +} diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go index df4c4a1..4f049a3 100644 --- a/libgo/go/testing/benchmark.go +++ b/libgo/go/testing/benchmark.go @@ -205,7 +205,7 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ for _, Benchmark := range benchmarks { matched, err := matchString(*matchBenchmarks, Benchmark.Name) if err != nil { - println("invalid regexp for -test.bench:", err.Error()) + fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.bench: %s\n", err) os.Exit(1) } if !matched { @@ -218,11 +218,11 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ if procs != 1 { benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs) } - print(fmt.Sprintf("%s\t", benchName)) + fmt.Printf("%s\t", benchName) r := b.run() - print(fmt.Sprintf("%v\n", r)) + fmt.Printf("%v\n", r) if p := runtime.GOMAXPROCS(-1); p != procs { - print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p)) + fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p) } } } diff --git a/libgo/go/testing/example.go b/libgo/go/testing/example.go index 5b3e322..3b026ee 100644 --- a/libgo/go/testing/example.go +++ b/libgo/go/testing/example.go @@ -21,24 +21,23 @@ type InternalExample struct { func RunExamples(examples []InternalExample) (ok bool) { ok = true + var eg InternalExample + stdout, stderr := os.Stdout, os.Stderr defer func() { os.Stdout, os.Stderr = stdout, stderr if e := recover(); e != nil { - if err, ok := e.(error); ok { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - panic(e) + fmt.Printf("--- FAIL: %s\npanic: %v\n", eg.Name, e) + os.Exit(1) } }() - for _, eg := range examples { + for _, eg = range examples { if *chatty { - fmt.Fprintln(os.Stderr, "=== RUN:", eg.Name) + fmt.Printf("=== RUN: %s\n", eg.Name) } - // capture stdout and stderr for testing purposes + // capture stdout and stderr r, w, err := os.Pipe() if err != nil { fmt.Fprintln(os.Stderr, err) @@ -50,7 +49,7 @@ func RunExamples(examples []InternalExample) (ok bool) { buf := new(bytes.Buffer) _, err := io.Copy(buf, r) if err != nil { - fmt.Fprintln(os.Stderr, err) + fmt.Fprintf(stderr, "testing: copying pipe: %v\n", err) os.Exit(1) } outC <- buf.String() @@ -67,16 +66,15 @@ func RunExamples(examples []InternalExample) (ok bool) { out := <-outC // report any errors + tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) if out != eg.Output { - fmt.Fprintf( - os.Stderr, - "--- FAIL: %s\ngot:\n%s\nwant:\n%s\n", - eg.Name, out, eg.Output, + fmt.Printf( + "--- FAIL: %s %s\ngot:\n%s\nwant:\n%s\n", + eg.Name, tstr, out, eg.Output, ) ok = false } else if *chatty { - tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) - fmt.Fprintln(os.Stderr, "--- PASS:", eg.Name, tstr) + fmt.Printf("--- PASS: %s %s\n", eg.Name, tstr) } } diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index 5869642c..08443a3 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -75,8 +75,25 @@ func Short() bool { return *short } -// Insert final newline if needed and tabs after internal newlines. -func tabify(s string) string { +// decorate inserts the a final newline if needed and indentation tabs for formatting. +// If addFileLine is true, it also prefixes the string with the file and line of the call site. +func decorate(s string, addFileLine bool) string { + if addFileLine { + _, file, line, ok := runtime.Caller(3) // decorate + log + public function. + if ok { + // Truncate file name at last file name separator. + if index := strings.LastIndex(file, "/"); index >= 0 { + file = file[index+1:] + } else if index = strings.LastIndex(file, "\\"); index >= 0 { + file = file[index+1:] + } + } else { + file = "???" + line = 1 + } + s = fmt.Sprintf("%s:%d: %s", file, line, s) + } + s = "\t" + s // Every line is indented at least one tab. n := len(s) if n > 0 && s[n-1] != '\n' { s += "\n" @@ -84,7 +101,8 @@ func tabify(s string) string { } for i := 0; i < n-1; i++ { // -1 to avoid final newline if s[i] == '\n' { - return s[0:i+1] + "\t" + tabify(s[i+1:n]) + // Second and subsequent lines are indented an extra tab. + return s[0:i+1] + "\t" + decorate(s[i+1:n], false) } } return s @@ -116,37 +134,38 @@ func (t *T) FailNow() { runtime.Goexit() } +// log generates the output. It's always at the same stack depth. +func (t *T) log(s string) { t.errors += decorate(s, true) } + // Log formats its arguments using default formatting, analogous to Print(), // and records the text in the error log. -func (t *T) Log(args ...interface{}) { t.errors += "\t" + tabify(fmt.Sprintln(args...)) } +func (t *T) Log(args ...interface{}) { t.log(fmt.Sprintln(args...)) } // Logf formats its arguments according to the format, analogous to Printf(), // and records the text in the error log. -func (t *T) Logf(format string, args ...interface{}) { - t.errors += "\t" + tabify(fmt.Sprintf(format, args...)) -} +func (t *T) Logf(format string, args ...interface{}) { t.log(fmt.Sprintf(format, args...)) } // Error is equivalent to Log() followed by Fail(). func (t *T) Error(args ...interface{}) { - t.Log(args...) + t.log(fmt.Sprintln(args...)) t.Fail() } // Errorf is equivalent to Logf() followed by Fail(). func (t *T) Errorf(format string, args ...interface{}) { - t.Logf(format, args...) + t.log(fmt.Sprintf(format, args...)) t.Fail() } // Fatal is equivalent to Log() followed by FailNow(). func (t *T) Fatal(args ...interface{}) { - t.Log(args...) + t.log(fmt.Sprintln(args...)) t.FailNow() } // Fatalf is equivalent to Logf() followed by FailNow(). func (t *T) Fatalf(format string, args ...interface{}) { - t.Logf(format, args...) + t.log(fmt.Sprintf(format, args...)) t.FailNow() } @@ -182,10 +201,10 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, testOk := RunTests(matchString, tests) exampleOk := RunExamples(examples) if !testOk || !exampleOk { - fmt.Fprintln(os.Stderr, "FAIL") + fmt.Println("FAIL") os.Exit(1) } - fmt.Fprintln(os.Stderr, "PASS") + fmt.Println("PASS") stopAlarm() RunBenchmarks(matchString, benchmarks) after() @@ -195,9 +214,9 @@ func report(t *T) { tstr := fmt.Sprintf("(%.2f seconds)", float64(t.ns)/1e9) format := "--- %s: %s %s\n%s" if t.failed { - fmt.Fprintf(os.Stderr, format, "FAIL", t.name, tstr, t.errors) + fmt.Printf(format, "FAIL", t.name, tstr, t.errors) } else if *chatty { - fmt.Fprintf(os.Stderr, format, "PASS", t.name, tstr, t.errors) + fmt.Printf(format, "PASS", t.name, tstr, t.errors) } } @@ -217,7 +236,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT for i := 0; i < len(tests); i++ { matched, err := matchString(*match, tests[i].Name) if err != nil { - println("invalid regexp for -test.run:", err.Error()) + fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err) os.Exit(1) } if !matched { @@ -229,7 +248,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT } t := &T{ch: ch, name: testName, startParallel: startParallel} if *chatty { - println("=== RUN", t.name) + fmt.Printf("=== RUN %s\n", t.name) } go tRunner(t, &tests[i]) out := <-t.ch @@ -325,7 +344,7 @@ func parseCpuList() { for _, val := range strings.Split(*cpuListStr, ",") { cpu, err := strconv.Atoi(val) if err != nil || cpu <= 0 { - println("invalid value for -test.cpu") + fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val) os.Exit(1) } cpuList = append(cpuList, cpu) diff --git a/libgo/go/text/tabwriter/tabwriter.go b/libgo/go/text/tabwriter/tabwriter.go index c136ca2..201a685 100644 --- a/libgo/go/text/tabwriter/tabwriter.go +++ b/libgo/go/text/tabwriter/tabwriter.go @@ -13,7 +13,6 @@ package tabwriter import ( "bytes" "io" - "os" "unicode/utf8" ) @@ -221,7 +220,7 @@ type osError struct { func (b *Writer) write0(buf []byte) { n, err := b.output.Write(buf) if n != len(buf) && err == nil { - err = os.EIO + err = io.ErrShortWrite } if err != nil { panic(osError{err}) diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index 5721667..67b9416 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -487,7 +487,11 @@ func testExecute(execTests []execTest, set *Set, t *testing.T) { } for _, test := range execTests { tmpl := New(test.name).Funcs(funcs) - _, err := tmpl.ParseInSet(test.input, set) + theSet := set + if theSet == nil { + theSet = new(Set) + } + _, err := tmpl.ParseInSet(test.input, theSet) if err != nil { t.Errorf("%s: parse error: %s", test.name, err) continue diff --git a/libgo/go/text/template/parse.go b/libgo/go/text/template/parse.go index fa56214..7075f2a 100644 --- a/libgo/go/text/template/parse.go +++ b/libgo/go/text/template/parse.go @@ -62,7 +62,7 @@ func (t *Template) Funcs(funcMap FuncMap) *Template { // Parse parses the template definition string to construct an internal // representation of the template for execution. func (t *Template) Parse(s string) (tmpl *Template, err error) { - t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) + t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, nil, t.parseFuncs, builtins) if err != nil { return nil, err } @@ -71,19 +71,13 @@ func (t *Template) Parse(s string) (tmpl *Template, err error) { // ParseInSet parses the template definition string to construct an internal // representation of the template for execution. It also adds the template -// to the set. It is an error if s is already defined in the set. +// to the set, which must not be nil. It is an error if s is already defined in the set. // Function bindings are checked against those in the set. func (t *Template) ParseInSet(s string, set *Set) (tmpl *Template, err error) { - var setFuncs FuncMap - if set != nil { - setFuncs = set.parseFuncs - } - t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, t.parseFuncs, setFuncs, builtins) + t.Tree, err = parse.New(t.name).Parse(s, t.leftDelim, t.rightDelim, set.trees, t.parseFuncs, set.parseFuncs, builtins) if err != nil { return nil, err } - if set != nil { - err = set.add(t) - } + err = set.add(t) return t, err } diff --git a/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go index 1b6ab3a..e906ee8 100644 --- a/libgo/go/text/template/parse/parse.go +++ b/libgo/go/text/template/parse/parse.go @@ -146,29 +146,70 @@ func (t *Tree) atEOF() bool { // Parse parses the template definition string to construct an internal // representation of the template for execution. If either action delimiter // string is empty, the default ("{{" or "}}") is used. -func (t *Tree) Parse(s, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree *Tree, err error) { +func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { defer t.recover(&err) t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim)) - t.parse(true) + t.parse(treeSet) t.stopParse() return t, nil } -// parse is the helper for Parse. -// It triggers an error if we expect EOF but don't reach it. -func (t *Tree) parse(toEOF bool) (next Node) { - t.Root, next = t.itemList(true) - if toEOF && next != nil { - t.errorf("unexpected %s", next) +// parse is the top-level parser for a template, essentially the same +// as itemList except it also parses {{define}} actions. +// It runs to EOF. +func (t *Tree) parse(treeSet map[string]*Tree) (next Node) { + t.Root = newList() + for t.peek().typ != itemEOF { + if t.peek().typ == itemLeftDelim { + delim := t.next() + if t.next().typ == itemDefine { + newT := New("new definition") // name will be updated once we know it. + newT.startParse(t.funcs, t.lex) + newT.parseDefinition(treeSet) + continue + } + t.backup2(delim) + } + n := t.textOrAction() + if n.Type() == nodeEnd { + t.errorf("unexpected %s", n) + } + t.Root.append(n) } - return next + return nil +} + +// parseDefinition parses a {{define}} ... {{end}} template definition and +// installs the definition in the treeSet map. The "define" keyword has already +// been scanned. +func (t *Tree) parseDefinition(treeSet map[string]*Tree) { + if treeSet == nil { + t.errorf("no set specified for template definition") + } + const context = "define clause" + name := t.expect(itemString, context) + var err error + t.Name, err = strconv.Unquote(name.val) + if err != nil { + t.error(err) + } + t.expect(itemRightDelim, context) + var end Node + t.Root, end = t.itemList() + if end.Type() != nodeEnd { + t.errorf("unexpected %s in %s", end, context) + } + t.stopParse() + if _, present := treeSet[t.Name]; present { + t.errorf("template: %q multiply defined", name) + } + treeSet[t.Name] = t } // itemList: // textOrAction* -// Terminates at EOF and at {{end}} or {{else}}, which is returned separately. -// The toEOF flag tells whether we expect to reach EOF. -func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) { +// Terminates at {{end}} or {{else}}, returned separately. +func (t *Tree) itemList() (list *ListNode, next Node) { list = newList() for t.peek().typ != itemEOF { n := t.textOrAction() @@ -178,10 +219,8 @@ func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) { } list.append(n) } - if !toEOF { - t.unexpected(t.next(), "input") - } - return list, nil + t.errorf("unexpected EOF") + return } // textOrAction: @@ -276,11 +315,11 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, defer t.popVars(len(t.vars)) pipe = t.pipeline(context) var next Node - list, next = t.itemList(false) + list, next = t.itemList() switch next.Type() { case nodeEnd: //done case nodeElse: - elseList, next = t.itemList(false) + elseList, next = t.itemList() if next.Type() != nodeEnd { t.errorf("expected end; found %s", next) } diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go index f05f6e3..5c10086 100644 --- a/libgo/go/text/template/parse/parse_test.go +++ b/libgo/go/text/template/parse/parse_test.go @@ -236,7 +236,7 @@ var builtins = map[string]interface{}{ func TestParse(t *testing.T) { for _, test := range parseTests { - tmpl, err := New(test.name).Parse(test.input, "", "", builtins) + tmpl, err := New(test.name).Parse(test.input, "", "", nil, builtins) switch { case err == nil && !test.ok: t.Errorf("%q: expected error; got none", test.name) diff --git a/libgo/go/text/template/parse/set.go b/libgo/go/text/template/parse/set.go index d363eef..55f3ceb 100644 --- a/libgo/go/text/template/parse/set.go +++ b/libgo/go/text/template/parse/set.go @@ -4,46 +4,12 @@ package parse -import ( - "fmt" - "strconv" -) - // Set returns a slice of Trees created by parsing the template set // definition in the argument string. If an error is encountered, // parsing stops and an empty slice is returned with the error. func Set(text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (tree map[string]*Tree, err error) { tree = make(map[string]*Tree) - defer (*Tree)(nil).recover(&err) - lex := lex("set", text, leftDelim, rightDelim) - const context = "define clause" - for { - t := New("set") // name will be updated once we know it. - t.startParse(funcs, lex) - // Expect EOF or "{{ define name }}". - if t.atEOF() { - break - } - t.expect(itemLeftDelim, context) - t.expect(itemDefine, context) - name := t.expect(itemString, context) - t.Name, err = strconv.Unquote(name.val) - if err != nil { - t.error(err) - } - t.expect(itemRightDelim, context) - end := t.parse(false) - if end == nil { - t.errorf("unexpected EOF in %s", context) - } - if end.Type() != nodeEnd { - t.errorf("unexpected %s in %s", end, context) - } - t.stopParse() - if _, present := tree[t.Name]; present { - return nil, fmt.Errorf("template: %q multiply defined", name) - } - tree[t.Name] = t - } + // Top-level template name is needed but unused. TODO: clean this up. + _, err = New("ROOT").Parse(text, leftDelim, rightDelim, tree, funcs...) return } diff --git a/libgo/go/text/template/set.go b/libgo/go/text/template/set.go index 747cc78..4841704 100644 --- a/libgo/go/text/template/set.go +++ b/libgo/go/text/template/set.go @@ -16,6 +16,7 @@ import ( // A template may be a member of multiple sets. type Set struct { tmpl map[string]*Template + trees map[string]*parse.Tree // maintained by parse package leftDelim string rightDelim string parseFuncs FuncMap diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go index 314622d..967fca0 100644 --- a/libgo/go/time/sleep.go +++ b/libgo/go/time/sleep.go @@ -4,54 +4,60 @@ package time -import ( - "container/heap" - "sync" -) +// Interface to timers implemented in package runtime. +// Must be in sync with ../runtime/runtime.h:/^struct.Timer$ +type runtimeTimer struct { + i int32 + when int64 + period int64 + f func(int64, interface{}) + arg interface{} +} + +func startTimer(*runtimeTimer) +func stopTimer(*runtimeTimer) bool // The Timer type represents a single event. -// When the Timer expires, the current time will be sent on C -// unless the Timer represents an AfterFunc event. +// When the Timer expires, the current time will be sent on C, +// unless the Timer was created by AfterFunc. type Timer struct { C <-chan int64 - t int64 // The absolute time that the event should fire. - f func(int64) // The function to call when the event fires. - i int // The event's index inside eventHeap. + r runtimeTimer } -type timerHeap []*Timer - -// forever is the absolute time (in ns) of an event that is forever away. -const forever = 1 << 62 - -// maxSleepTime is the maximum length of time that a sleeper -// sleeps for before checking if it is defunct. -const maxSleepTime = 1e9 - -var ( - // timerMutex guards the variables inside this var group. - timerMutex sync.Mutex - - // timers holds a binary heap of pending events, terminated with a sentinel. - timers timerHeap - - // currentSleeper is an ever-incrementing counter which represents - // the current sleeper. It allows older sleepers to detect that they are - // defunct and exit. - currentSleeper int64 -) - -func init() { - timers.Push(&Timer{t: forever}) // sentinel +// Stop prevents the Timer from firing. +// It returns true if the call stops the timer, false if the timer has already +// expired or stopped. +func (t *Timer) Stop() (ok bool) { + return stopTimer(&t.r) } // NewTimer creates a new Timer that will send // the current time on its channel after at least ns nanoseconds. func NewTimer(ns int64) *Timer { c := make(chan int64, 1) - e := after(ns, func(t int64) { c <- t }) - e.C = c - return e + t := &Timer{ + C: c, + r: runtimeTimer{ + when: Nanoseconds() + ns, + f: sendTime, + arg: c, + }, + } + startTimer(&t.r) + return t +} + +func sendTime(now int64, c interface{}) { + // Non-blocking send of time on c. + // Used in NewTimer, it cannot block anyway (buffer). + // Used in NewTicker, dropping sends on the floor is + // the desired behavior when the reader gets behind, + // because the sends are periodic. + select { + case c.(chan int64) <- now: + default: + } } // After waits at least ns nanoseconds before sending the current time @@ -65,113 +71,17 @@ func After(ns int64) <-chan int64 { // in its own goroutine. It returns a Timer that can // be used to cancel the call using its Stop method. func AfterFunc(ns int64, f func()) *Timer { - return after(ns, func(_ int64) { - go f() - }) -} - -// Stop prevents the Timer from firing. -// It returns true if the call stops the timer, false if the timer has already -// expired or stopped. -func (e *Timer) Stop() (ok bool) { - timerMutex.Lock() - // Avoid removing the first event in the queue so that - // we don't start a new sleeper unnecessarily. - if e.i > 0 { - heap.Remove(timers, e.i) - } - ok = e.f != nil - e.f = nil - timerMutex.Unlock() - return -} - -// after is the implementation of After and AfterFunc. -// When the current time is after ns, it calls f with the current time. -// It assumes that f will not block. -func after(ns int64, f func(int64)) (e *Timer) { - now := Nanoseconds() - t := now + ns - if ns > 0 && t < now { - panic("time: time overflow") - } - timerMutex.Lock() - t0 := timers[0].t - e = &Timer{nil, t, f, -1} - heap.Push(timers, e) - // Start a new sleeper if the new event is before - // the first event in the queue. If the length of time - // until the new event is at least maxSleepTime, - // then we're guaranteed that the sleeper will wake up - // in time to service it, so no new sleeper is needed. - if t0 > t && (t0 == forever || ns < maxSleepTime) { - currentSleeper++ - go sleeper(currentSleeper) - } - timerMutex.Unlock() - return -} - -// sleeper continually looks at the earliest event in the queue, waits until it happens, -// then removes any events in the queue that are due. It stops when the queue -// is empty or when another sleeper has been started. -func sleeper(sleeperId int64) { - timerMutex.Lock() - e := timers[0] - t := Nanoseconds() - for e.t != forever { - if dt := e.t - t; dt > 0 { - if dt > maxSleepTime { - dt = maxSleepTime - } - timerMutex.Unlock() - sysSleep(dt) - timerMutex.Lock() - if currentSleeper != sleeperId { - // Another sleeper has been started, making this one redundant. - break - } - } - e = timers[0] - t = Nanoseconds() - for t >= e.t { - if e.f != nil { - e.f(t) - e.f = nil - } - heap.Pop(timers) - e = timers[0] - } + t := &Timer{ + r: runtimeTimer{ + when: Nanoseconds() + ns, + f: goFunc, + arg: f, + }, } - timerMutex.Unlock() -} - -func (timerHeap) Len() int { - return len(timers) -} - -func (timerHeap) Less(i, j int) bool { - return timers[i].t < timers[j].t -} - -func (timerHeap) Swap(i, j int) { - timers[i], timers[j] = timers[j], timers[i] - timers[i].i = i - timers[j].i = j -} - -func (timerHeap) Push(x interface{}) { - e := x.(*Timer) - e.i = len(timers) - timers = append(timers, e) + startTimer(&t.r) + return t } -func (timerHeap) Pop() interface{} { - // TODO: possibly shrink array. - n := len(timers) - 1 - e := timers[n] - timers[n] = nil - timers = timers[0:n] - e.i = -1 - return e +func goFunc(now int64, arg interface{}) { + go arg.(func())() } diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go index 0662e33..9171da3 100644 --- a/libgo/go/time/sleep_test.go +++ b/libgo/go/time/sleep_test.go @@ -7,7 +7,9 @@ package time_test import ( "errors" "fmt" + "runtime" "sort" + "sync/atomic" "testing" . "time" ) @@ -47,6 +49,23 @@ func TestAfterFunc(t *testing.T) { <-c } +func TestAfterStress(t *testing.T) { + stop := uint32(0) + go func() { + for atomic.LoadUint32(&stop) == 0 { + runtime.GC() + // Need to yield, because otherwise + // the main goroutine will never set the stop flag. + runtime.Gosched() + } + }() + c := Tick(1) + for i := 0; i < 100; i++ { + <-c + } + atomic.StoreUint32(&stop, 1) +} + func BenchmarkAfterFunc(b *testing.B) { i := b.N c := make(chan bool) diff --git a/libgo/go/time/sys.go b/libgo/go/time/sys.go index ca1d334..a5e529b 100644 --- a/libgo/go/time/sys.go +++ b/libgo/go/time/sys.go @@ -17,25 +17,4 @@ func Seconds() int64 { func Nanoseconds() int64 // Sleep pauses the current goroutine for at least ns nanoseconds. -// Higher resolution sleeping may be provided by syscall.Nanosleep -// on some operating systems. -func Sleep(ns int64) error { - _, err := sleep(Nanoseconds(), ns) - return err -} - -// sleep takes the current time and a duration, -// pauses for at least ns nanoseconds, and -// returns the current time and an error. -func sleep(t, ns int64) (int64, error) { - // TODO(cw): use monotonic-time once it's available - end := t + ns - for t < end { - err := sysSleep(end - t) - if err != nil { - return 0, err - } - t = Nanoseconds() - } - return t, nil -} +func Sleep(ns int64) diff --git a/libgo/go/time/sys_plan9.go b/libgo/go/time/sys_plan9.go index a630b3e..e58fb51 100644 --- a/libgo/go/time/sys_plan9.go +++ b/libgo/go/time/sys_plan9.go @@ -4,19 +4,6 @@ package time -import ( - "os" - "syscall" -) - -func sysSleep(t int64) error { - err := syscall.Sleep(t) - if err != nil { - return os.NewSyscallError("sleep", err) - } - return nil -} - // for testing: whatever interrupts a sleep func interrupt() { // cannot predict pid, don't want to kill group diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go index 17a6a2d..3d31322 100644 --- a/libgo/go/time/sys_unix.go +++ b/libgo/go/time/sys_unix.go @@ -11,14 +11,6 @@ import ( "syscall" ) -func sysSleep(t int64) error { - errno := syscall.Sleep(t) - if errno != 0 && errno != syscall.EINTR { - return os.NewSyscallError("sleep", errno) - } - return nil -} - // for testing: whatever interrupts a sleep func interrupt() { syscall.Kill(os.Getpid(), syscall.SIGCHLD) diff --git a/libgo/go/time/sys_windows.go b/libgo/go/time/sys_windows.go index f9d6e89..8c7242f 100644 --- a/libgo/go/time/sys_windows.go +++ b/libgo/go/time/sys_windows.go @@ -4,19 +4,6 @@ package time -import ( - "os" - "syscall" -) - -func sysSleep(t int64) error { - errno := syscall.Sleep(t) - if errno != 0 && errno != syscall.EINTR { - return os.NewSyscallError("sleep", errno) - } - return nil -} - // for testing: whatever interrupts a sleep func interrupt() { } diff --git a/libgo/go/time/tick.go b/libgo/go/time/tick.go index 92f9eb8..95941a1 100644 --- a/libgo/go/time/tick.go +++ b/libgo/go/time/tick.go @@ -4,156 +4,15 @@ package time -import ( - "errors" - "sync" -) +import "errors" // A Ticker holds a synchronous channel that delivers `ticks' of a clock // at intervals. type Ticker struct { - C <-chan int64 // The channel on which the ticks are delivered. - c chan<- int64 // The same channel, but the end we use. - ns int64 - shutdown chan bool // Buffered channel used to signal shutdown. - nextTick int64 - next *Ticker + C <-chan int64 // The channel on which the ticks are delivered. + r runtimeTimer } -// Stop turns off a ticker. After Stop, no more ticks will be sent. -func (t *Ticker) Stop() { - select { - case t.shutdown <- true: - // ok - default: - // Stop in progress already - } -} - -// Tick is a convenience wrapper for NewTicker providing access to the ticking -// channel only. Useful for clients that have no need to shut down the ticker. -func Tick(ns int64) <-chan int64 { - if ns <= 0 { - return nil - } - return NewTicker(ns).C -} - -type alarmer struct { - wakeUp chan bool // wakeup signals sent/received here - wakeMeAt chan int64 - wakeTime int64 -} - -// Set alarm to go off at time ns, if not already set earlier. -func (a *alarmer) set(ns int64) { - switch { - case a.wakeTime > ns: - // Next tick we expect is too late; shut down the late runner - // and (after fallthrough) start a new wakeLoop. - close(a.wakeMeAt) - fallthrough - case a.wakeMeAt == nil: - // There's no wakeLoop, start one. - a.wakeMeAt = make(chan int64) - a.wakeUp = make(chan bool, 1) - go wakeLoop(a.wakeMeAt, a.wakeUp) - fallthrough - case a.wakeTime == 0: - // Nobody else is waiting; it's just us. - a.wakeTime = ns - a.wakeMeAt <- ns - default: - // There's already someone scheduled. - } -} - -// Channel to notify tickerLoop of new Tickers being created. -var newTicker chan *Ticker - -func startTickerLoop() { - newTicker = make(chan *Ticker) - go tickerLoop() -} - -// wakeLoop delivers ticks at scheduled times, sleeping until the right moment. -// If another, earlier Ticker is created while it sleeps, tickerLoop() will start a new -// wakeLoop and signal that this one is done by closing the wakeMeAt channel. -func wakeLoop(wakeMeAt chan int64, wakeUp chan bool) { - for wakeAt := range wakeMeAt { - Sleep(wakeAt - Nanoseconds()) - wakeUp <- true - } -} - -// A single tickerLoop serves all ticks to Tickers. It waits for two events: -// either the creation of a new Ticker or a tick from the alarm, -// signaling a time to wake up one or more Tickers. -func tickerLoop() { - // Represents the next alarm to be delivered. - var alarm alarmer - var now, wakeTime int64 - var tickers *Ticker - for { - select { - case t := <-newTicker: - // Add Ticker to list - t.next = tickers - tickers = t - // Arrange for a new alarm if this one precedes the existing one. - alarm.set(t.nextTick) - case <-alarm.wakeUp: - now = Nanoseconds() - wakeTime = now + 1e15 // very long in the future - var prev *Ticker = nil - // Scan list of tickers, delivering updates to those - // that need it and determining the next wake time. - // TODO(r): list should be sorted in time order. - for t := tickers; t != nil; t = t.next { - select { - case <-t.shutdown: - // Ticker is done; remove it from list. - if prev == nil { - tickers = t.next - } else { - prev.next = t.next - } - continue - default: - } - if t.nextTick <= now { - if len(t.c) == 0 { - // Only send if there's room. We must not block. - // The channel is allocated with a one-element - // buffer, which is sufficient: if he hasn't picked - // up the last tick, no point in sending more. - t.c <- now - } - t.nextTick += t.ns - if t.nextTick <= now { - // Still behind; advance in one big step. - t.nextTick += (now - t.nextTick + t.ns) / t.ns * t.ns - } - } - if t.nextTick < wakeTime { - wakeTime = t.nextTick - } - prev = t - } - if tickers != nil { - // Please send wakeup at earliest required time. - // If there are no tickers, don't bother. - alarm.wakeTime = wakeTime - alarm.wakeMeAt <- wakeTime - } else { - alarm.wakeTime = 0 - } - } - } -} - -var onceStartTickerLoop sync.Once - // NewTicker returns a new Ticker containing a channel that will // send the time, in nanoseconds, every ns nanoseconds. It adjusts the // intervals to make up for pauses in delivery of the ticks. The value of @@ -162,16 +21,33 @@ func NewTicker(ns int64) *Ticker { if ns <= 0 { panic(errors.New("non-positive interval for NewTicker")) } - c := make(chan int64, 1) // See comment on send in tickerLoop + // Give the channel a 1-element time buffer. + // If the client falls behind while reading, we drop ticks + // on the floor until the client catches up. + c := make(chan int64, 1) t := &Ticker{ - C: c, - c: c, - ns: ns, - shutdown: make(chan bool, 1), - nextTick: Nanoseconds() + ns, + C: c, + r: runtimeTimer{ + when: Nanoseconds() + ns, + period: ns, + f: sendTime, + arg: c, + }, } - onceStartTickerLoop.Do(startTickerLoop) - // must be run in background so global Tickers can be created - go func() { newTicker <- t }() + startTimer(&t.r) return t } + +// Stop turns off a ticker. After Stop, no more ticks will be sent. +func (t *Ticker) Stop() { + stopTimer(&t.r) +} + +// Tick is a convenience wrapper for NewTicker providing access to the ticking +// channel only. Useful for clients that have no need to shut down the ticker. +func Tick(ns int64) <-chan int64 { + if ns <= 0 { + return nil + } + return NewTicker(ns).C +} diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index 859b316..e11d177 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -237,3 +237,63 @@ func (t *Time) Weekday() int { } return weekday } + +// julianDayNumber returns the time's Julian Day Number +// relative to the epoch 12:00 January 1, 4713 BC, Monday. +func julianDayNumber(year int64, month, day int) int64 { + a := int64(14-month) / 12 + y := year + 4800 - a + m := int64(month) + 12*a - 3 + return int64(day) + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045 +} + +// startOfFirstWeek returns the julian day number of the first day +// of the first week of the given year. +func startOfFirstWeek(year int64) (d int64) { + jan01 := julianDayNumber(year, 1, 1) + weekday := (jan01 % 7) + 1 + if weekday <= 4 { + d = jan01 - weekday + 1 + } else { + d = jan01 + 8 - weekday + } + return +} + +// dayOfWeek returns the weekday of the given date. +func dayOfWeek(year int64, month, day int) int { + t := Time{Year: year, Month: month, Day: day} + return t.Weekday() +} + +// ISOWeek returns the time's year and week number according to ISO 8601. +// Week ranges from 1 to 53. Jan 01 to Jan 03 of year n might belong to +// week 52 or 53 of year n-1, and Dec 29 to Dec 31 might belong to week 1 +// of year n+1. +func (t *Time) ISOWeek() (year int64, week int) { + d := julianDayNumber(t.Year, t.Month, t.Day) + week1Start := startOfFirstWeek(t.Year) + + if d < week1Start { + // Previous year, week 52 or 53 + year = t.Year - 1 + if dayOfWeek(t.Year-1, 1, 1) == 4 || dayOfWeek(t.Year-1, 12, 31) == 4 { + week = 53 + } else { + week = 52 + } + return + } + + if d < startOfFirstWeek(t.Year+1) { + // Current year, week 01..52(,53) + year = t.Year + week = int((d-week1Start)/7 + 1) + return + } + + // Next year, week 1 + year = t.Year + 1 + week = 1 + return +} diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index 8b373a1..01b8bea4a 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -478,6 +478,68 @@ func TestMinutesInTimeZone(t *testing.T) { } } +type ISOWeekTest struct { + year int64 // year + month, day int // month and day + yex int64 // expected year + wex int // expected week +} + +var isoWeekTests = []ISOWeekTest{ + {1981, 1, 1, 1981, 1}, {1982, 1, 1, 1981, 53}, {1983, 1, 1, 1982, 52}, + {1984, 1, 1, 1983, 52}, {1985, 1, 1, 1985, 1}, {1986, 1, 1, 1986, 1}, + {1987, 1, 1, 1987, 1}, {1988, 1, 1, 1987, 53}, {1989, 1, 1, 1988, 52}, + {1990, 1, 1, 1990, 1}, {1991, 1, 1, 1991, 1}, {1992, 1, 1, 1992, 1}, + {1993, 1, 1, 1992, 53}, {1994, 1, 1, 1993, 52}, {1995, 1, 2, 1995, 1}, + {1996, 1, 1, 1996, 1}, {1996, 1, 7, 1996, 1}, {1996, 1, 8, 1996, 2}, + {1997, 1, 1, 1997, 1}, {1998, 1, 1, 1998, 1}, {1999, 1, 1, 1998, 53}, + {2000, 1, 1, 1999, 52}, {2001, 1, 1, 2001, 1}, {2002, 1, 1, 2002, 1}, + {2003, 1, 1, 2003, 1}, {2004, 1, 1, 2004, 1}, {2005, 1, 1, 2004, 53}, + {2006, 1, 1, 2005, 52}, {2007, 1, 1, 2007, 1}, {2008, 1, 1, 2008, 1}, + {2009, 1, 1, 2009, 1}, {2010, 1, 1, 2009, 53}, {2010, 1, 1, 2009, 53}, + {2011, 1, 1, 2010, 52}, {2011, 1, 2, 2010, 52}, {2011, 1, 3, 2011, 1}, + {2011, 1, 4, 2011, 1}, {2011, 1, 5, 2011, 1}, {2011, 1, 6, 2011, 1}, + {2011, 1, 7, 2011, 1}, {2011, 1, 8, 2011, 1}, {2011, 1, 9, 2011, 1}, + {2011, 1, 10, 2011, 2}, {2011, 1, 11, 2011, 2}, {2011, 6, 12, 2011, 23}, + {2011, 6, 13, 2011, 24}, {2011, 12, 25, 2011, 51}, {2011, 12, 26, 2011, 52}, + {2011, 12, 27, 2011, 52}, {2011, 12, 28, 2011, 52}, {2011, 12, 29, 2011, 52}, + {2011, 12, 30, 2011, 52}, {2011, 12, 31, 2011, 52}, {1995, 1, 1, 1994, 52}, + {2012, 1, 1, 2011, 52}, {2012, 1, 2, 2012, 1}, {2012, 1, 8, 2012, 1}, + {2012, 1, 9, 2012, 2}, {2012, 12, 23, 2012, 51}, {2012, 12, 24, 2012, 52}, + {2012, 12, 30, 2012, 52}, {2012, 12, 31, 2013, 1}, {2013, 1, 1, 2013, 1}, + {2013, 1, 6, 2013, 1}, {2013, 1, 7, 2013, 2}, {2013, 12, 22, 2013, 51}, + {2013, 12, 23, 2013, 52}, {2013, 12, 29, 2013, 52}, {2013, 12, 30, 2014, 1}, + {2014, 1, 1, 2014, 1}, {2014, 1, 5, 2014, 1}, {2014, 1, 6, 2014, 2}, + {2015, 1, 1, 2015, 1}, {2016, 1, 1, 2015, 53}, {2017, 1, 1, 2016, 52}, + {2018, 1, 1, 2018, 1}, {2019, 1, 1, 2019, 1}, {2020, 1, 1, 2020, 1}, + {2021, 1, 1, 2020, 53}, {2022, 1, 1, 2021, 52}, {2023, 1, 1, 2022, 52}, + {2024, 1, 1, 2024, 1}, {2025, 1, 1, 2025, 1}, {2026, 1, 1, 2026, 1}, + {2027, 1, 1, 2026, 53}, {2028, 1, 1, 2027, 52}, {2029, 1, 1, 2029, 1}, + {2030, 1, 1, 2030, 1}, {2031, 1, 1, 2031, 1}, {2032, 1, 1, 2032, 1}, + {2033, 1, 1, 2032, 53}, {2034, 1, 1, 2033, 52}, {2035, 1, 1, 2035, 1}, + {2036, 1, 1, 2036, 1}, {2037, 1, 1, 2037, 1}, {2038, 1, 1, 2037, 53}, + {2039, 1, 1, 2038, 52}, {2040, 1, 1, 2039, 52}, +} + +func TestISOWeek(t *testing.T) { + // Selected dates and corner cases + for _, wt := range isoWeekTests { + dt := &Time{Year: wt.year, Month: wt.month, Day: wt.day} + y, w := dt.ISOWeek() + if w != wt.wex || y != wt.yex { + t.Errorf("got %d/%d; expected %d/%d for %d-%02d-%02d", + y, w, wt.yex, wt.wex, wt.year, wt.month, wt.day) + } + } + + // The only real invariant: Jan 04 is in week 1 + for year := int64(1950); year < 2100; year++ { + if y, w := (&Time{Year: year, Month: 1, Day: 4}).ISOWeek(); y != year || w != 1 { + t.Errorf("got %d/%d; expected %d/1 for Jan 04", y, w, year) + } + } +} + func BenchmarkSeconds(b *testing.B) { for i := 0; i < b.N; i++ { Seconds() diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go index 0dc4235..b552e58 100644 --- a/libgo/go/time/zoneinfo_unix.go +++ b/libgo/go/time/zoneinfo_unix.go @@ -12,7 +12,7 @@ package time import ( - "io/ioutil" + "bytes" "os" ) @@ -180,11 +180,17 @@ func parseinfo(bytes []byte) (zt []zonetime, ok bool) { } func readinfofile(name string) ([]zonetime, bool) { - buf, err := ioutil.ReadFile(name) + var b bytes.Buffer + + f, err := os.Open(name) if err != nil { return nil, false } - return parseinfo(buf) + defer f.Close() + if _, err := b.ReadFrom(f); err != nil { + return nil, false + } + return parseinfo(b.Bytes()) } func setupTestingZone() { diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go index ba152e0..995fd44 100644 --- a/libgo/go/time/zoneinfo_windows.go +++ b/libgo/go/time/zoneinfo_windows.go @@ -161,7 +161,7 @@ var onceSetupZone sync.Once func setupZone() { var i syscall.Timezoneinformation - if _, e := syscall.GetTimeZoneInformation(&i); e != 0 { + if _, e := syscall.GetTimeZoneInformation(&i); e != nil { initError = os.NewSyscallError("GetTimeZoneInformation", e) return } diff --git a/libgo/go/websocket/websocket.go b/libgo/go/websocket/websocket.go index 1e4036c..df4416e 100644 --- a/libgo/go/websocket/websocket.go +++ b/libgo/go/websocket/websocket.go @@ -10,12 +10,12 @@ import ( "bufio" "crypto/tls" "encoding/json" + "errors" "io" "io/ioutil" "net" "net/http" "net/url" - "os" "sync" ) @@ -243,12 +243,14 @@ func (ws *Conn) RemoteAddr() net.Addr { return &Addr{ws.config.Origin} } +var errSetTimeout = errors.New("websocket: cannot set timeout: not using a net.Conn") + // SetTimeout sets the connection's network timeout in nanoseconds. func (ws *Conn) SetTimeout(nsec int64) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetTimeout(nsec) } - return os.EINVAL + return errSetTimeout } // SetReadTimeout sets the connection's network read timeout in nanoseconds. @@ -256,7 +258,7 @@ func (ws *Conn) SetReadTimeout(nsec int64) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetReadTimeout(nsec) } - return os.EINVAL + return errSetTimeout } // SetWriteTimeout sets the connection's network write timeout in nanoseconds. @@ -264,7 +266,7 @@ func (ws *Conn) SetWriteTimeout(nsec int64) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetWriteTimeout(nsec) } - return os.EINVAL + return errSetTimeout } // Config returns the WebSocket config. |