diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-12-12 23:13:29 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-12-12 23:13:29 +0000 |
commit | a42a906c420d7bb196cb8541e0ab65264a0b04b0 (patch) | |
tree | 8c441679e35147b1e9bec048f733fc394fb0c161 | |
parent | bc77608b97abcc4bb3171f08a71e34ae342e9f8d (diff) | |
download | gcc-a42a906c420d7bb196cb8541e0ab65264a0b04b0.zip gcc-a42a906c420d7bb196cb8541e0ab65264a0b04b0.tar.gz gcc-a42a906c420d7bb196cb8541e0ab65264a0b04b0.tar.bz2 |
libgo: Update to current master library sources.
From-SVN: r194460
145 files changed, 6240 insertions, 1915 deletions
diff --git a/libgo/MERGE b/libgo/MERGE index 5ab9716..deeb596 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -1,4 +1,4 @@ -a070de932857 +c031aa767edf The first line of this file holds the Mercurial revision number of the last merge done from the master library sources. diff --git a/libgo/Makefile.am b/libgo/Makefile.am index 50a21b8..fe0f205 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -221,6 +221,7 @@ endif toolexeclibgoexpdir = $(toolexeclibgodir)/exp toolexeclibgoexp_DATA = \ + exp/cookiejar.gox \ exp/ebnf.gox \ exp/html.gox \ $(exp_inotify_gox) \ @@ -251,6 +252,7 @@ toolexeclibgogo_DATA = \ go/ast.gox \ go/build.gox \ go/doc.gox \ + go/format.gox \ go/parser.gox \ go/printer.gox \ go/scanner.gox \ @@ -1194,6 +1196,9 @@ go_encoding_xml_files = \ go/encoding/xml/typeinfo.go \ go/encoding/xml/xml.go +go_exp_cookiejar_files = \ + go/exp/cookiejar/jar.go \ + go/exp/cookiejar/storage.go go_exp_ebnf_files = \ go/exp/ebnf/ebnf.go \ go/exp/ebnf/parser.go @@ -1284,6 +1289,8 @@ go_go_doc_files = \ go/go/doc/filter.go \ go/go/doc/reader.go \ go/go/doc/synopsis.go +go_go_format_files = \ + go/go/format/format.go go_go_parser_files = \ go/go/parser/interface.go \ go/go/parser/parser.go @@ -1384,6 +1391,7 @@ go_math_rand_files = \ go_mime_multipart_files = \ go/mime/multipart/formdata.go \ go/mime/multipart/multipart.go \ + go/mime/multipart/quotedprintable.go \ go/mime/multipart/writer.go go_net_http_files = \ @@ -1456,6 +1464,7 @@ go_os_signal_files = \ go_os_user_files = \ go/os/user/user.go \ + go/os/user/lookup.go \ go/os/user/lookup_unix.go go_path_filepath_files = \ @@ -1822,6 +1831,7 @@ libgo_go_objs = \ encoding/json.lo \ encoding/pem.lo \ encoding/xml.lo \ + exp/cookiejar.lo \ exp/ebnf.lo \ exp/html.lo \ exp/html/atom.lo \ @@ -1836,6 +1846,7 @@ libgo_go_objs = \ go/ast.lo \ go/build.lo \ go/doc.lo \ + go/format.lo \ go/parser.lo \ go/printer.lo \ go/scanner.lo \ @@ -2658,6 +2669,15 @@ encoding/xml/check: $(CHECK_DEPS) @$(CHECK) .PHONY: encoding/xml/check +@go_include@ exp/cookiejar.lo.dep +exp/cookiejar.lo.dep: $(go_exp_cookiejar_files) + $(BUILDDEPS) +exp/cookiejar.lo: $(go_exp_cookiejar_files) + $(BUILDPACKAGE) +exp/cookiejar/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: exp/cookiejar/check + @go_include@ exp/ebnf.lo.dep exp/ebnf.lo.dep: $(go_exp_ebnf_files) $(BUILDDEPS) @@ -2802,6 +2822,15 @@ go/doc/check: $(CHECK_DEPS) @$(CHECK) .PHONY: go/doc/check +@go_include@ go/format.lo.dep +go/format.lo.dep: $(go_go_format_files) + $(BUILDDEPS) +go/format.lo: $(go_go_format_files) + $(BUILDPACKAGE) +go/format/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: go/format/check + @go_include@ go/parser.lo.dep go/parser.lo.dep: $(go_go_parser_files) $(BUILDDEPS) @@ -3450,6 +3479,8 @@ encoding/pem.gox: encoding/pem.lo encoding/xml.gox: encoding/xml.lo $(BUILDGOX) +exp/cookiejar.gox: exp/cookiejar.lo + $(BUILDGOX) exp/ebnf.gox: exp/ebnf.lo $(BUILDGOX) exp/html.gox: exp/html.lo @@ -3482,6 +3513,8 @@ go/build.gox: go/build.lo $(BUILDGOX) go/doc.gox: go/doc.lo $(BUILDGOX) +go/format.gox: go/format.lo + $(BUILDGOX) go/parser.gox: go/parser.lo $(BUILDGOX) go/printer.gox: go/printer.lo @@ -3681,6 +3714,7 @@ TEST_PACKAGES = \ encoding/json/check \ encoding/pem/check \ encoding/xml/check \ + exp/cookiejar/check \ exp/ebnf/check \ exp/html/check \ exp/html/atom/check \ @@ -3696,6 +3730,7 @@ TEST_PACKAGES = \ go/ast/check \ $(go_build_check_omitted_since_it_calls_6g) \ go/doc/check \ + go/format/check \ go/parser/check \ go/printer/check \ go/scanner/check \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 38b8ddf..9b8ea1a 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -153,26 +153,27 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \ debug/pe.lo encoding/ascii85.lo encoding/asn1.lo \ encoding/base32.lo encoding/base64.lo encoding/binary.lo \ encoding/csv.lo encoding/gob.lo encoding/hex.lo \ - encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \ - exp/html.lo exp/html/atom.lo exp/locale/collate.lo \ - exp/locale/collate/build.lo exp/norm.lo exp/proxy.lo \ - exp/terminal.lo exp/types.lo exp/utf8string.lo \ - html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \ - go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \ - hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \ - net/http/fcgi.lo net/http/httptest.lo net/http/httputil.lo \ - net/http/pprof.lo image/color.lo image/draw.lo image/gif.lo \ - image/jpeg.lo image/png.lo index/suffixarray.lo io/ioutil.lo \ - log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \ - math/rand.lo mime/multipart.lo net/http.lo net/mail.lo \ - net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \ - old/netchan.lo old/regexp.lo old/template.lo os/exec.lo \ - $(am__DEPENDENCIES_1) os/signal.lo os/user.lo path/filepath.lo \ - regexp/syntax.lo net/rpc/jsonrpc.lo runtime/debug.lo \ - runtime/pprof.lo sync/atomic.lo sync/atomic_c.lo \ - text/scanner.lo text/tabwriter.lo text/template.lo \ - text/template/parse.lo testing/iotest.lo testing/quick.lo \ - unicode/utf16.lo unicode/utf8.lo + encoding/json.lo encoding/pem.lo encoding/xml.lo \ + exp/cookiejar.lo exp/ebnf.lo exp/html.lo exp/html/atom.lo \ + exp/locale/collate.lo exp/locale/collate/build.lo exp/norm.lo \ + exp/proxy.lo exp/terminal.lo exp/types.lo exp/utf8string.lo \ + html/template.lo go/ast.lo go/build.lo go/doc.lo go/format.lo \ + go/parser.lo go/printer.lo go/scanner.lo go/token.lo \ + hash/adler32.lo hash/crc32.lo hash/crc64.lo hash/fnv.lo \ + net/http/cgi.lo net/http/fcgi.lo net/http/httptest.lo \ + net/http/httputil.lo net/http/pprof.lo image/color.lo \ + image/draw.lo image/gif.lo image/jpeg.lo image/png.lo \ + index/suffixarray.lo io/ioutil.lo log/syslog.lo \ + log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \ + mime/multipart.lo net/http.lo net/mail.lo net/rpc.lo \ + net/smtp.lo net/textproto.lo net/url.lo old/netchan.lo \ + old/regexp.lo old/template.lo os/exec.lo $(am__DEPENDENCIES_1) \ + os/signal.lo os/user.lo path/filepath.lo regexp/syntax.lo \ + net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \ + sync/atomic.lo sync/atomic_c.lo text/scanner.lo \ + text/tabwriter.lo text/template.lo text/template/parse.lo \ + testing/iotest.lo testing/quick.lo unicode/utf16.lo \ + unicode/utf8.lo libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ ../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -608,6 +609,7 @@ toolexeclibgoencoding_DATA = \ @LIBGO_IS_LINUX_TRUE@exp_inotify_gox = toolexeclibgoexpdir = $(toolexeclibgodir)/exp toolexeclibgoexp_DATA = \ + exp/cookiejar.gox \ exp/ebnf.gox \ exp/html.gox \ $(exp_inotify_gox) \ @@ -634,6 +636,7 @@ toolexeclibgogo_DATA = \ go/ast.gox \ go/build.gox \ go/doc.gox \ + go/format.gox \ go/parser.gox \ go/printer.gox \ go/scanner.gox \ @@ -1403,6 +1406,10 @@ go_encoding_xml_files = \ go/encoding/xml/typeinfo.go \ go/encoding/xml/xml.go +go_exp_cookiejar_files = \ + go/exp/cookiejar/jar.go \ + go/exp/cookiejar/storage.go + go_exp_ebnf_files = \ go/exp/ebnf/ebnf.go \ go/exp/ebnf/parser.go @@ -1506,6 +1513,9 @@ go_go_doc_files = \ go/go/doc/reader.go \ go/go/doc/synopsis.go +go_go_format_files = \ + go/go/format/format.go + go_go_parser_files = \ go/go/parser/interface.go \ go/go/parser/parser.go @@ -1614,6 +1624,7 @@ go_math_rand_files = \ go_mime_multipart_files = \ go/mime/multipart/formdata.go \ go/mime/multipart/multipart.go \ + go/mime/multipart/quotedprintable.go \ go/mime/multipart/writer.go go_net_http_files = \ @@ -1695,6 +1706,7 @@ go_os_signal_files = \ go_os_user_files = \ go/os/user/user.go \ + go/os/user/lookup.go \ go/os/user/lookup_unix.go go_path_filepath_files = \ @@ -1948,6 +1960,7 @@ libgo_go_objs = \ encoding/json.lo \ encoding/pem.lo \ encoding/xml.lo \ + exp/cookiejar.lo \ exp/ebnf.lo \ exp/html.lo \ exp/html/atom.lo \ @@ -1962,6 +1975,7 @@ libgo_go_objs = \ go/ast.lo \ go/build.lo \ go/doc.lo \ + go/format.lo \ go/parser.lo \ go/printer.lo \ go/scanner.lo \ @@ -2200,6 +2214,7 @@ TEST_PACKAGES = \ encoding/json/check \ encoding/pem/check \ encoding/xml/check \ + exp/cookiejar/check \ exp/ebnf/check \ exp/html/check \ exp/html/atom/check \ @@ -2215,6 +2230,7 @@ TEST_PACKAGES = \ go/ast/check \ $(go_build_check_omitted_since_it_calls_6g) \ go/doc/check \ + go/format/check \ go/parser/check \ go/printer/check \ go/scanner/check \ @@ -5096,6 +5112,15 @@ encoding/xml/check: $(CHECK_DEPS) @$(CHECK) .PHONY: encoding/xml/check +@go_include@ exp/cookiejar.lo.dep +exp/cookiejar.lo.dep: $(go_exp_cookiejar_files) + $(BUILDDEPS) +exp/cookiejar.lo: $(go_exp_cookiejar_files) + $(BUILDPACKAGE) +exp/cookiejar/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: exp/cookiejar/check + @go_include@ exp/ebnf.lo.dep exp/ebnf.lo.dep: $(go_exp_ebnf_files) $(BUILDDEPS) @@ -5240,6 +5265,15 @@ go/doc/check: $(CHECK_DEPS) @$(CHECK) .PHONY: go/doc/check +@go_include@ go/format.lo.dep +go/format.lo.dep: $(go_go_format_files) + $(BUILDDEPS) +go/format.lo: $(go_go_format_files) + $(BUILDPACKAGE) +go/format/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: go/format/check + @go_include@ go/parser.lo.dep go/parser.lo.dep: $(go_go_parser_files) $(BUILDDEPS) @@ -5880,6 +5914,8 @@ encoding/pem.gox: encoding/pem.lo encoding/xml.gox: encoding/xml.lo $(BUILDGOX) +exp/cookiejar.gox: exp/cookiejar.lo + $(BUILDGOX) exp/ebnf.gox: exp/ebnf.lo $(BUILDGOX) exp/html.gox: exp/html.lo @@ -5912,6 +5948,8 @@ go/build.gox: go/build.lo $(BUILDGOX) go/doc.gox: go/doc.lo $(BUILDGOX) +go/format.gox: go/format.lo + $(BUILDGOX) go/parser.gox: go/parser.lo $(BUILDGOX) go/printer.gox: go/printer.lo diff --git a/libgo/go/bufio/bufio.go b/libgo/go/bufio/bufio.go index cd51585..4b69019 100644 --- a/libgo/go/bufio/bufio.go +++ b/libgo/go/bufio/bufio.go @@ -64,6 +64,8 @@ func NewReader(rd io.Reader) *Reader { return NewReaderSize(rd, defaultBufSize) } +var errNegativeRead = errors.New("bufio: reader returned negative count from Read") + // fill reads a new chunk into the buffer. func (b *Reader) fill() { // Slide existing data to beginning. @@ -75,6 +77,9 @@ func (b *Reader) fill() { // Read new data. n, e := b.rd.Read(b.buf[b.w:]) + if n < 0 { + panic(errNegativeRead) + } b.w += n if e != nil { b.err = e @@ -282,6 +287,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { // of the line. The returned buffer is only valid until the next call to // ReadLine. ReadLine either returns a non-nil line or it returns an error, // never both. +// +// The text returned from ReadLine does not include the line end ("\r\n" or "\n"). +// No indication or error is given if the input ends without a final line end. func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { line, err = b.ReadSlice('\n') if err == ErrBufferFull { diff --git a/libgo/go/bufio/bufio_test.go b/libgo/go/bufio/bufio_test.go index 75d9edf..5646211 100644 --- a/libgo/go/bufio/bufio_test.go +++ b/libgo/go/bufio/bufio_test.go @@ -939,6 +939,29 @@ func (w *writeCountingDiscard) Write(p []byte) (int, error) { return len(p), nil } +type negativeReader int + +func (r *negativeReader) Read([]byte) (int, error) { return -1, nil } + +func TestNegativeRead(t *testing.T) { + // should panic with a description pointing at the reader, not at itself. + // (should NOT panic with slice index error, for example.) + b := NewReader(new(negativeReader)) + defer func() { + switch err := recover().(type) { + case nil: + t.Fatal("read did not panic") + case error: + if !strings.Contains(err.Error(), "reader returned negative count from Read") { + t.Fatal("wrong panic: %v", err) + } + default: + t.Fatalf("unexpected panic value: %T(%v)", err, err) + } + }() + b.Read(make([]byte, 100)) +} + // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have. type onlyReader struct { r io.Reader diff --git a/libgo/go/bytes/buffer.go b/libgo/go/bytes/buffer.go index efb9798..3ae9303 100644 --- a/libgo/go/bytes/buffer.go +++ b/libgo/go/bytes/buffer.go @@ -360,16 +360,24 @@ func (b *Buffer) UnreadByte() error { // ReadBytes returns err != nil if and only if the returned data does not end in // delim. func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { + slice, err := b.readSlice(delim) + // return a copy of slice. The buffer's backing array may + // be overwritten by later calls. + line = append(line, slice...) + return +} + +// readSlice is like readBytes but returns a reference to internal buffer data. +func (b *Buffer) readSlice(delim byte) (line []byte, err error) { i := IndexByte(b.buf[b.off:], delim) - size := i + 1 + end := b.off + i + 1 if i < 0 { - size = len(b.buf) - b.off + end = len(b.buf) err = io.EOF } - line = make([]byte, size) - copy(line, b.buf[b.off:]) - b.off += size - return + line = b.buf[b.off:end] + b.off = end + return line, err } // ReadString reads until the first occurrence of delim in the input, @@ -379,8 +387,8 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { // ReadString returns err != nil if and only if the returned data does not end // in delim. func (b *Buffer) ReadString(delim byte) (line string, err error) { - bytes, err := b.ReadBytes(delim) - return string(bytes), err + slice, err := b.readSlice(delim) + return string(slice), err } // NewBuffer creates and initializes a new Buffer using buf as its initial diff --git a/libgo/go/bytes/buffer_test.go b/libgo/go/bytes/buffer_test.go index 92e2914..c53544a 100644 --- a/libgo/go/bytes/buffer_test.go +++ b/libgo/go/bytes/buffer_test.go @@ -375,6 +375,41 @@ func TestReadBytes(t *testing.T) { } } +func TestReadString(t *testing.T) { + for _, test := range readBytesTests { + buf := NewBufferString(test.buffer) + var err error + for _, expected := range test.expected { + var s string + s, err = buf.ReadString(test.delim) + if s != expected { + t.Errorf("expected %q, got %q", expected, s) + } + if err != nil { + break + } + } + if err != test.err { + t.Errorf("expected error %v, got %v", test.err, err) + } + } +} + +func BenchmarkReadString(b *testing.B) { + const n = 32 << 10 + + data := make([]byte, n) + data[n-1] = 'x' + b.SetBytes(int64(n)) + for i := 0; i < b.N; i++ { + buf := NewBuffer(data) + _, err := buf.ReadString('x') + if err != nil { + b.Fatal(err) + } + } +} + func TestGrow(t *testing.T) { x := []byte{'x'} y := []byte{'y'} diff --git a/libgo/go/bytes/reader.go b/libgo/go/bytes/reader.go index b34dfc1..77511b9 100644 --- a/libgo/go/bytes/reader.go +++ b/libgo/go/bytes/reader.go @@ -125,7 +125,7 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) { func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { r.prevRune = -1 if r.i >= len(r.s) { - return 0, io.EOF + return 0, nil } b := r.s[r.i:] m, err := w.Write(b) diff --git a/libgo/go/bytes/reader_test.go b/libgo/go/bytes/reader_test.go index 6668818..f0a3e26 100644 --- a/libgo/go/bytes/reader_test.go +++ b/libgo/go/bytes/reader_test.go @@ -8,6 +8,7 @@ import ( . "bytes" "fmt" "io" + "io/ioutil" "os" "testing" ) @@ -88,16 +89,20 @@ func TestReaderAt(t *testing.T) { } func TestReaderWriteTo(t *testing.T) { - for i := 3; i < 30; i += 3 { - s := data[:len(data)/i] - r := NewReader(testBytes[:len(testBytes)/i]) + for i := 0; i < 30; i += 3 { + var l int + if i > 0 { + l = len(data) / i + } + s := data[:l] + r := NewReader(testBytes[:l]) var b Buffer n, err := r.WriteTo(&b) if expect := int64(len(s)); n != expect { t.Errorf("got %v; want %v", n, expect) } if err != nil { - t.Errorf("got error = %v; want nil", err) + t.Errorf("for length %d: got error = %v; want nil", l, err) } if b.String() != s { t.Errorf("got string %q; want %q", b.String(), s) @@ -107,3 +112,26 @@ func TestReaderWriteTo(t *testing.T) { } } } + +// verify that copying from an empty reader always has the same results, +// regardless of the presence of a WriteTo method. +func TestReaderCopyNothing(t *testing.T) { + type nErr struct { + n int64 + err error + } + type justReader struct { + io.Reader + } + type justWriter struct { + io.Writer + } + discard := justWriter{ioutil.Discard} // hide ReadFrom + + var with, withOut nErr + with.n, with.err = io.Copy(discard, NewReader(nil)) + withOut.n, withOut.err = io.Copy(discard, justReader{NewReader(nil)}) + if with != withOut { + t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut) + } +} diff --git a/libgo/go/container/heap/example_test.go b/libgo/go/container/heap/example_test.go index 2050bc8..70f654a 100644 --- a/libgo/go/container/heap/example_test.go +++ b/libgo/go/container/heap/example_test.go @@ -37,15 +37,10 @@ func (pq PriorityQueue) Swap(i, j int) { func (pq *PriorityQueue) Push(x interface{}) { // Push and Pop use pointer receivers because they modify the slice's length, // not just its contents. - // To simplify indexing expressions in these methods, we save a copy of the - // slice object. We could instead write (*pq)[i]. - a := *pq - n := len(a) - a = a[0 : n+1] + n := len(*pq) item := x.(*Item) item.index = n - a[n] = item - *pq = a + *pq = append(*pq, item) } func (pq *PriorityQueue) Pop() interface{} { diff --git a/libgo/go/container/ring/ring.go b/libgo/go/container/ring/ring.go index 1d96918..6d3b3e5b 100644 --- a/libgo/go/container/ring/ring.go +++ b/libgo/go/container/ring/ring.go @@ -74,7 +74,7 @@ func New(n int) *Ring { return r } -// Link connects ring r with with ring s such that r.Next() +// Link connects ring r with ring s such that r.Next() // becomes s and returns the original value for r.Next(). // r must not be empty. // diff --git a/libgo/go/crypto/tls/conn.go b/libgo/go/crypto/tls/conn.go index 44f3e66..d8c2be0 100644 --- a/libgo/go/crypto/tls/conn.go +++ b/libgo/go/crypto/tls/conn.go @@ -758,8 +758,28 @@ func (c *Conn) Write(b []byte) (int, error) { return 0, alertInternalError } + // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext + // attack when using block mode ciphers due to predictable IVs. + // This can be prevented by splitting each Application Data + // record into two records, effectively randomizing the IV. + // + // http://www.openssl.org/~bodo/tls-cbc.txt + // https://bugzilla.mozilla.org/show_bug.cgi?id=665814 + // http://www.imperialviolet.org/2012/01/15/beastfollowup.html + + var m int + if len(b) > 1 && c.vers <= versionTLS10 { + if _, ok := c.out.cipher.(cipher.BlockMode); ok { + n, err := c.writeRecord(recordTypeApplicationData, b[:1]) + if err != nil { + return n, c.setError(err) + } + m, b = 1, b[1:] + } + } + n, err := c.writeRecord(recordTypeApplicationData, b) - return n, c.setError(err) + return n + m, c.setError(err) } // Read can be made to time out and return a net.Error with Timeout() == true diff --git a/libgo/go/crypto/tls/handshake_client_test.go b/libgo/go/crypto/tls/handshake_client_test.go index 58ee618..9673947 100644 --- a/libgo/go/crypto/tls/handshake_client_test.go +++ b/libgo/go/crypto/tls/handshake_client_test.go @@ -245,67 +245,24 @@ var ecdheAESClientScript = [][]byte{ 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, }, { - 0x16, 0x03, 0x01, 0x00, 0x54, 0x02, 0x00, 0x00, - 0x50, 0x03, 0x01, 0x50, 0x77, 0x31, 0xf7, 0x5b, - 0xdb, 0x3d, 0x7a, 0x62, 0x76, 0x70, 0x95, 0x33, - 0x73, 0x71, 0x13, 0xfe, 0xa3, 0xb1, 0xd8, 0xb3, - 0x4d, 0x0d, 0xdc, 0xfe, 0x58, 0x6e, 0x6a, 0x3a, - 0xf9, 0xde, 0xdc, 0x20, 0x8e, 0xfa, 0x3d, 0x60, - 0xd0, 0xda, 0xa4, 0x0e, 0x36, 0xf0, 0xde, 0xb6, - 0x81, 0xb4, 0x80, 0x5e, 0xf9, 0xd2, 0x4c, 0xec, - 0xd1, 0x9c, 0x2a, 0x81, 0xc3, 0x36, 0x0b, 0x0f, - 0x4a, 0x3d, 0xdf, 0x75, 0xc0, 0x13, 0x00, 0x00, - 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, - 0x02, 0x16, 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00, - 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, - 0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, - 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, - 0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, - 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36, - 0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, - 0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31, - 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, - 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, - 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, - 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, - 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, - 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, - 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, - 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, - 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, - 0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52, - 0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, - 0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, - 0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, - 0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, - 0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, - 0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, - 0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, - 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, - 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, - 0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, - 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, - 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, - 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, - 0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, - 0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, - 0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, - 0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x16, 0x03, 0x01, 0x00, 0x52, 0x02, 0x00, 0x00, + 0x4e, 0x03, 0x01, 0x50, 0xad, 0x72, 0xb1, 0x14, + 0x45, 0xce, 0x0a, 0x95, 0xf9, 0x63, 0xef, 0xa8, + 0xe5, 0x07, 0x34, 0x04, 0xe9, 0x08, 0x0f, 0x38, + 0xe4, 0x28, 0x27, 0x91, 0x07, 0x03, 0xe2, 0xfe, + 0xe3, 0x25, 0xf7, 0x20, 0x08, 0x42, 0xa2, 0x01, + 0x69, 0x53, 0xf0, 0xd9, 0x4c, 0xfa, 0x01, 0xa1, + 0xce, 0x4b, 0xf8, 0x28, 0x21, 0xad, 0x06, 0xbe, + 0xe0, 0x1b, 0x3b, 0xf7, 0xec, 0xd2, 0x52, 0xae, + 0x2a, 0x57, 0xb7, 0xa8, 0xc0, 0x13, 0x00, 0x00, + 0x06, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x16, + 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00, 0x02, 0x35, + 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, 0x30, 0x82, + 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xb1, 0x35, + 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, @@ -314,39 +271,82 @@ var ecdheAESClientScript = [][]byte{ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, - 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, - 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, - 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x32, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37, + 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x31, + 0x35, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37, 0x31, + 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, + 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, + 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, + 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85, - 0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, - 0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, - 0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, - 0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, - 0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, - 0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, - 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, - 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, - 0x03, 0x01, 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87, - 0x03, 0x00, 0x17, 0x41, 0x04, 0xec, 0x06, 0x1f, - 0xa0, 0x5e, 0x29, 0x49, 0x71, 0x8b, 0x04, 0x9f, - 0x47, 0x87, 0xb1, 0xcb, 0xae, 0x57, 0x8f, 0xd7, - 0xf6, 0xf8, 0x59, 0x74, 0x64, 0x5d, 0x3a, 0x08, - 0xaf, 0x20, 0xc6, 0xd9, 0xfc, 0x5e, 0x36, 0x8b, - 0x62, 0x0e, 0xdb, 0xee, 0xd8, 0xcd, 0xef, 0x25, - 0x8a, 0x38, 0x88, 0x2d, 0x5c, 0x71, 0x50, 0x22, - 0xda, 0x3f, 0x94, 0x06, 0xc9, 0x68, 0x5b, 0x78, - 0x3d, 0x95, 0xca, 0x54, 0x44, 0x00, 0x40, 0x36, - 0xcf, 0x10, 0x81, 0xb4, 0x32, 0x45, 0x3c, 0xa5, - 0x2d, 0x3e, 0xb0, 0xf8, 0xf4, 0x51, 0xf5, 0x28, - 0x09, 0x85, 0x71, 0xa6, 0x79, 0x71, 0x4b, 0x4e, - 0xda, 0x32, 0x5a, 0xc7, 0xb3, 0x57, 0xfd, 0xe8, - 0x12, 0xab, 0xd8, 0x29, 0xfb, 0x8b, 0x43, 0x8f, - 0x7e, 0x27, 0x63, 0x91, 0x84, 0x9c, 0x51, 0x0c, - 0x26, 0x7e, 0x36, 0x3b, 0x37, 0x8d, 0x8f, 0x9e, - 0xe2, 0x82, 0x62, 0xbb, 0xe5, 0xdf, 0xfc, 0x16, - 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, + 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, 0xc3, 0x84, + 0x27, 0x95, 0xff, 0x12, 0x31, 0x52, 0x0f, 0x15, + 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, 0xe6, 0x36, + 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, 0x8d, 0xe0, + 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, 0x55, 0x66, + 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, 0xfe, 0xa8, + 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, 0xee, 0xd7, + 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, 0xff, 0x2a, + 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, 0xa4, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63, 0xb5, + 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c, 0x23, + 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0x30, 0x75, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x6e, 0x30, + 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63, + 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c, + 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0xa1, + 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, 0x35, 0x13, + 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, + 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x03, 0x41, 0x00, 0x85, 0x36, 0x40, + 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, 0x59, 0x9f, + 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, 0xec, 0x83, + 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, 0x39, 0xac, + 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, 0x1d, 0x99, + 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, 0x05, 0x08, + 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, 0xbb, 0x77, + 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, 0x5e, 0x9c, + 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, 0x03, 0x01, + 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87, 0x03, 0x00, + 0x17, 0x41, 0x04, 0x1c, 0x8f, 0x9c, 0x6d, 0xe7, + 0xab, 0x3e, 0xf8, 0x0a, 0x5d, 0xe1, 0x86, 0xb4, + 0xe2, 0x8e, 0xb2, 0x1c, 0x3b, 0xd9, 0xb6, 0x08, + 0x80, 0x58, 0x21, 0xe9, 0x0e, 0xc6, 0x66, 0x67, + 0x97, 0xcb, 0xb9, 0x92, 0x07, 0x00, 0xc4, 0xe5, + 0xec, 0x5f, 0xb4, 0xe2, 0x20, 0xa9, 0xc9, 0x62, + 0xd0, 0x98, 0xd5, 0xe3, 0x53, 0xff, 0xd0, 0x0a, + 0x6e, 0x29, 0x69, 0x39, 0x2a, 0x4b, 0x5c, 0xd8, + 0x6c, 0xf5, 0xfe, 0x00, 0x40, 0x35, 0xa7, 0x26, + 0x2e, 0xc2, 0x48, 0x93, 0x32, 0xf7, 0x7d, 0x0f, + 0x0d, 0x77, 0x56, 0x9a, 0x85, 0x0c, 0xa6, 0x74, + 0x06, 0xb8, 0x3d, 0x90, 0x56, 0x12, 0x63, 0xff, + 0x00, 0x5e, 0x0f, 0xf7, 0x24, 0xf7, 0xdb, 0x48, + 0x71, 0xe9, 0x2e, 0x03, 0xd3, 0xfa, 0x3a, 0xae, + 0xa0, 0xc1, 0x77, 0x3c, 0x4c, 0x59, 0xce, 0x33, + 0x1a, 0xd2, 0x47, 0x83, 0xfa, 0xea, 0xd8, 0x1e, + 0x06, 0xe7, 0x7d, 0xa0, 0x9b, 0x16, 0x03, 0x01, + 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, }, { 0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00, @@ -359,34 +359,50 @@ var ecdheAESClientScript = [][]byte{ 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49, 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b, 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x9a, 0xaa, - 0xca, 0x5b, 0x57, 0xae, 0x34, 0x92, 0x80, 0x45, - 0x7f, 0xe6, 0xf9, 0x09, 0x19, 0xd0, 0xf0, 0x1e, - 0x4b, 0xc3, 0xda, 0x71, 0xce, 0x34, 0x33, 0x56, - 0x9f, 0x20, 0x9f, 0xf9, 0xa8, 0x62, 0x6c, 0x38, - 0x1b, 0x41, 0xf5, 0x54, 0xf2, 0x79, 0x42, 0x6c, - 0xb5, 0x0e, 0xe7, 0xe1, 0xbc, 0x54, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xd9, 0xa7, + 0x80, 0x56, 0x3f, 0xa3, 0x8f, 0x96, 0x72, 0x4e, + 0x4e, 0x6e, 0x23, 0x41, 0x8f, 0xda, 0x91, 0xb2, + 0x9e, 0x63, 0x23, 0x82, 0x64, 0xcd, 0x07, 0x24, + 0xd3, 0x40, 0x20, 0x22, 0x4c, 0xe3, 0xff, 0x38, + 0xbb, 0x43, 0x9d, 0x57, 0x11, 0xd5, 0x46, 0xa5, + 0x05, 0x29, 0x92, 0x02, 0xce, 0xdf, }, { 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x30, 0x62, 0x82, 0x41, 0x75, 0x2b, - 0xee, 0x0f, 0xdc, 0x6c, 0x48, 0x5a, 0x63, 0xd6, - 0xcb, 0x0a, 0xfd, 0x0a, 0x0e, 0xde, 0x8b, 0x41, - 0x19, 0x0c, 0x13, 0x6b, 0x12, 0xd1, 0xc2, 0x53, - 0xeb, 0x1e, 0xf3, 0x7a, 0xbf, 0x23, 0xc5, 0xa6, - 0x81, 0xa1, 0xdb, 0xab, 0x2f, 0x2c, 0xbc, 0x35, - 0x96, 0x72, 0x83, + 0x01, 0x00, 0x90, 0xe7, 0xba, 0x0e, 0xb1, 0xda, + 0x92, 0xb5, 0x77, 0x56, 0x38, 0xa6, 0x22, 0xc1, + 0x72, 0xeb, 0x8a, 0x68, 0x09, 0xb6, 0x74, 0xad, + 0xb3, 0x4a, 0xf2, 0xdd, 0x09, 0x9b, 0xc9, 0x4f, + 0x84, 0x73, 0x8b, 0xd6, 0x97, 0x50, 0x23, 0x1c, + 0xa0, 0xc2, 0x0c, 0x25, 0x18, 0xdd, 0x5e, 0x15, + 0x4d, 0xd9, 0xef, 0x4f, 0x6a, 0x43, 0x61, 0x9c, + 0x95, 0xde, 0x3c, 0x66, 0xc4, 0xc1, 0x33, 0x56, + 0xdd, 0x2f, 0x90, 0xaf, 0x68, 0x5c, 0x9c, 0xa4, + 0x90, 0x6d, 0xbf, 0x51, 0x1d, 0x68, 0xcb, 0x81, + 0x77, 0x52, 0xa0, 0x93, 0x2a, 0xf8, 0xc7, 0x61, + 0x87, 0x76, 0xca, 0x93, 0x9e, 0xd6, 0xee, 0x6f, + 0x3f, 0xeb, 0x7d, 0x06, 0xdd, 0x73, 0x4e, 0x27, + 0x16, 0x63, 0x92, 0xe4, 0xb2, 0x3f, 0x91, 0x23, + 0x21, 0x97, 0x90, 0xce, 0x53, 0xb8, 0xb0, 0x9d, + 0xbd, 0xbd, 0x33, 0x84, 0xad, 0x6b, 0x2e, 0x7b, + 0xf5, 0xeb, 0x1d, 0x64, 0x37, 0x2e, 0x29, 0x4e, + 0xb0, 0x93, 0xdb, 0x92, 0xc7, 0xaa, 0x94, 0xa5, + 0x3b, 0x64, 0xd0, }, { - 0x17, 0x03, 0x01, 0x00, 0x20, 0xaf, 0x5d, 0x35, - 0x57, 0x10, 0x60, 0xb3, 0x25, 0x7c, 0x26, 0x0f, - 0xf3, 0x5e, 0xb3, 0x0d, 0xad, 0x14, 0x53, 0xcc, - 0x0c, 0x08, 0xd9, 0xa2, 0x67, 0xab, 0xf4, 0x03, - 0x17, 0x20, 0xf1, 0x7e, 0xca, 0x15, 0x03, 0x01, - 0x00, 0x20, 0x30, 0xd0, 0xc1, 0xfb, 0x5f, 0xa6, - 0x1b, 0xb4, 0x48, 0xc2, 0x0b, 0x98, 0xa8, 0x88, - 0x7a, 0xba, 0xdf, 0x36, 0x06, 0xd8, 0xcc, 0xe9, - 0x34, 0xdd, 0x64, 0xc8, 0x73, 0xc5, 0xa2, 0x34, - 0x64, 0xb7, + 0x17, 0x03, 0x01, 0x00, 0x20, 0x11, 0xd8, 0x6b, + 0x3c, 0xf6, 0xbe, 0xf4, 0x54, 0x87, 0xec, 0x75, + 0x0c, 0x44, 0xdb, 0x92, 0xfc, 0xde, 0x7e, 0x0f, + 0x9f, 0x87, 0x87, 0x9c, 0x03, 0xd5, 0x07, 0x84, + 0xe0, 0x3a, 0xf8, 0xae, 0x14, 0x17, 0x03, 0x01, + 0x00, 0x20, 0xba, 0x54, 0xef, 0x5b, 0xce, 0xfd, + 0x47, 0x76, 0x6d, 0xa1, 0x8b, 0xfd, 0x48, 0xde, + 0x6e, 0x26, 0xc1, 0x0c, 0x9d, 0x54, 0xbf, 0x98, + 0xf6, 0x1c, 0x80, 0xb9, 0xca, 0x93, 0x81, 0x0a, + 0x2e, 0x06, 0x15, 0x03, 0x01, 0x00, 0x20, 0x93, + 0x3e, 0x38, 0x17, 0xc9, 0x0a, 0xc3, 0xea, 0xd3, + 0x92, 0x75, 0xa6, 0x53, 0x37, 0x4d, 0x74, 0x94, + 0xbe, 0x01, 0xdc, 0x5c, 0x5a, 0x0f, 0x09, 0xf6, + 0x57, 0x33, 0xc3, 0xbc, 0x3f, 0x7a, 0x4d, }, } diff --git a/libgo/go/crypto/tls/handshake_server_test.go b/libgo/go/crypto/tls/handshake_server_test.go index 2546f16..6d2e28b 100644 --- a/libgo/go/crypto/tls/handshake_server_test.go +++ b/libgo/go/crypto/tls/handshake_server_test.go @@ -550,33 +550,99 @@ var rc4ServerScript = [][]byte{ var des3ServerScript = [][]byte{ { - 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00, - 0x50, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xe3, 0x8e, - 0x48, 0xe6, 0xbd, 0x6d, 0x72, 0x8a, 0x1a, 0x11, - 0xb0, 0x8a, 0x7e, 0xff, 0x29, 0x07, 0xa8, 0x91, - 0xbc, 0xea, 0x1e, 0x3e, 0x62, 0xc9, 0x8e, 0x72, - 0x26, 0xd3, 0xca, 0x00, 0x00, 0x28, 0x00, 0x39, - 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, - 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, - 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, - 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, - 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, - 0x00, + 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, + 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5d, 0x38, 0xec, + 0xaa, 0x2f, 0x41, 0xf9, 0xd2, 0x7b, 0xa1, 0xfd, + 0x0f, 0xff, 0x4e, 0x54, 0x0e, 0x15, 0x57, 0xaf, + 0x2c, 0x91, 0xb5, 0x35, 0x5b, 0x2e, 0xb0, 0xec, + 0x20, 0xe5, 0xd2, 0x00, 0x00, 0x50, 0xc0, 0x09, + 0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24, + 0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27, + 0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12, + 0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e, + 0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2, + 0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13, + 0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41, + 0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84, + 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, + 0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a, + 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01, + 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01, + 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x03, }, { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, @@ -585,163 +651,207 @@ var des3ServerScript = [][]byte{ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, - 0x00, 0x00, 0x00, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, + 0x00, }, { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x33, 0x52, 0xe2, 0xd6, 0x79, - 0xf8, 0xe6, 0xd4, 0xe2, 0x08, 0xb0, 0x73, 0x36, - 0xa7, 0x61, 0x72, 0x19, 0xfb, 0xd1, 0x1f, 0xf5, - 0xbc, 0x7c, 0x84, 0xdd, 0xed, 0x99, 0xd7, 0x5e, - 0x3d, 0x11, 0xc3, 0x19, 0xb0, 0x7f, 0x10, 0x94, - 0x72, 0x64, 0xf3, 0x2c, 0x3f, 0x8d, 0x73, 0x39, - 0x9e, 0xca, 0x2e, 0x09, 0xbd, 0xb7, 0x8d, 0x4c, - 0x5b, 0x58, 0xff, 0x4f, 0x53, 0xa9, 0xd4, 0x7c, - 0x34, 0xe0, 0xaa, 0xa8, 0x14, 0xc0, 0x14, 0x25, - 0x0b, 0xaa, 0x55, 0xab, 0x10, 0x34, 0x45, 0x72, - 0xe8, 0x26, 0x6f, 0xf5, 0xbb, 0x3a, 0xfa, 0xd8, - 0x4f, 0x70, 0xe1, 0xc1, 0xb6, 0x11, 0x1e, 0xd1, - 0xe0, 0x0b, 0xa1, 0x3a, 0xdc, 0x94, 0x89, 0x7f, - 0x88, 0x5e, 0x5a, 0xf1, 0x0c, 0x98, 0xe2, 0xab, - 0x0e, 0x3a, 0xa8, 0x2f, 0xbb, 0xc5, 0x02, 0x07, - 0x15, 0x5e, 0x46, 0x82, 0x54, 0x9c, 0x09, 0xea, - 0xb9, 0x56, 0xf7, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x28, 0xb9, 0xbf, - 0x9a, 0xb8, 0xe4, 0x14, 0x6b, 0xc6, 0xf0, 0x27, - 0xb7, 0xdb, 0xb2, 0xbc, 0x16, 0xd1, 0x3c, 0x0b, - 0xc1, 0xe6, 0x1c, 0xa1, 0x29, 0xc7, 0x37, 0xe6, - 0x56, 0x1d, 0x16, 0xb5, 0xa8, 0x0d, 0x4d, 0xdb, - 0x9d, 0xf8, 0xb2, 0x6a, 0x90, 0x96, + 0x82, 0x00, 0x80, 0x51, 0x04, 0xf1, 0x7a, 0xbf, + 0xe8, 0xa5, 0x86, 0x09, 0xa7, 0xf3, 0xcc, 0x93, + 0x00, 0x10, 0x5b, 0xb8, 0xc1, 0x51, 0x0d, 0x5b, + 0xcd, 0xed, 0x26, 0x01, 0x69, 0x73, 0xf4, 0x05, + 0x8a, 0x6a, 0xc3, 0xb1, 0x9e, 0x84, 0x4e, 0x39, + 0xcf, 0x5e, 0x55, 0xa9, 0x70, 0x19, 0x96, 0x91, + 0xcd, 0x2c, 0x78, 0x3c, 0xa2, 0x6d, 0xb0, 0x49, + 0x86, 0xf6, 0xd1, 0x3a, 0xde, 0x00, 0x4b, 0xa6, + 0x25, 0xbf, 0x85, 0x39, 0xce, 0xb1, 0xcf, 0xbc, + 0x16, 0xc7, 0x66, 0xac, 0xf8, 0xd2, 0x3b, 0xd1, + 0xcc, 0x16, 0xac, 0x63, 0x3c, 0xbe, 0xd9, 0xb6, + 0x6a, 0xe4, 0x13, 0x8a, 0xf4, 0x56, 0x2f, 0x92, + 0x54, 0xd8, 0xf0, 0x84, 0x01, 0x32, 0x1a, 0xa9, + 0x2d, 0xaf, 0x82, 0x0e, 0x00, 0xfa, 0x07, 0x88, + 0xd9, 0x87, 0xe7, 0xdc, 0x9e, 0xe9, 0x72, 0x49, + 0xb8, 0xfa, 0x8c, 0x7b, 0x07, 0x0b, 0x03, 0x7c, + 0x10, 0x8c, 0x8a, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0xa8, 0x61, 0xa4, + 0xf4, 0x5f, 0x8a, 0x1f, 0x5c, 0x92, 0x3f, 0x8c, + 0xdb, 0xd6, 0x10, 0xcd, 0x9e, 0xe7, 0xf0, 0xc4, + 0x3c, 0xb6, 0x1c, 0x9a, 0x56, 0x73, 0x7f, 0xa6, + 0x14, 0x24, 0xcb, 0x96, 0x1f, 0xe0, 0xaf, 0xcd, + 0x3c, 0x66, 0x43, 0xb7, 0x37, 0x65, 0x34, 0x47, + 0xf8, 0x43, 0xf1, 0xcc, 0x15, 0xb8, 0xdc, 0x35, + 0xe0, 0xa4, 0x2d, 0x78, 0x94, 0xe0, 0x02, 0xf3, + 0x76, 0x46, 0xf7, 0x9b, 0x8d, 0x0d, 0x5d, 0x0b, + 0xd3, 0xdd, 0x9a, 0x9e, 0x62, 0x2e, 0xc5, 0x98, + 0x75, 0x63, 0x0c, 0xbf, 0x8e, 0x49, 0x33, 0x23, + 0x7c, 0x00, 0xcf, 0xfb, 0xcf, 0xba, 0x0f, 0x41, + 0x39, 0x89, 0xb9, 0xcc, 0x59, 0xd0, 0x2b, 0xb6, + 0xec, 0x04, 0xe2, 0xc0, 0x52, 0xc7, 0xcf, 0x71, + 0x47, 0xff, 0x70, 0x7e, 0xa9, 0xbd, 0x1c, 0xdd, + 0x17, 0xa5, 0x6c, 0xb7, 0x10, 0x4f, 0x42, 0x18, + 0x37, 0x69, 0xa9, 0xd2, 0xb3, 0x18, 0x84, 0x92, + 0xa7, 0x47, 0x21, 0xf6, 0x95, 0x63, 0x29, 0xd6, + 0xa5, 0xb6, 0xda, 0x65, 0x67, 0x69, 0xc4, 0x26, + 0xac, 0x8b, 0x08, 0x58, 0xdd, 0x3c, 0x31, 0x20, + 0xd5, 0x0c, 0x88, 0x72, 0x18, 0x16, 0x88, 0x1e, + 0x4a, 0x0f, 0xe1, 0xcf, 0x95, 0x24, }, { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x28, 0x5d, 0xc9, 0xad, 0xcf, 0xf8, - 0x37, 0x05, 0xec, 0x5e, 0xb2, 0x77, 0xb3, 0x1a, - 0x91, 0x75, 0x1d, 0x8d, 0xdd, 0x1a, 0xff, 0xb6, - 0xca, 0xf7, 0x59, 0x04, 0xb2, 0x11, 0x0a, 0x25, - 0x7e, 0xc5, 0x7d, 0xba, 0x8a, 0x50, 0xcc, 0xe9, - 0x89, 0xa0, 0x91, 0x17, 0x03, 0x01, 0x00, 0x28, - 0x30, 0x68, 0x28, 0x1e, 0x75, 0x82, 0x04, 0xe7, - 0xd3, 0x3b, 0xb1, 0x17, 0x32, 0x10, 0x7f, 0xae, - 0x77, 0xeb, 0xf1, 0x46, 0xcc, 0xe5, 0xe0, 0xbe, - 0x07, 0x37, 0x0d, 0x84, 0x54, 0xa1, 0x88, 0xac, - 0xe5, 0x06, 0x7b, 0xee, 0xe6, 0xa1, 0xee, 0xb0, - 0x15, 0x03, 0x01, 0x00, 0x18, 0x73, 0xa9, 0xf8, - 0x5a, 0xd4, 0xfc, 0xd9, 0xa9, 0x82, 0x97, 0x50, - 0x14, 0x76, 0x6c, 0x27, 0x9f, 0xa2, 0xf1, 0x52, - 0xa0, 0xe3, 0xbd, 0xcb, 0xd3, + 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xde, 0xef, 0xba, 0x3e, 0x18, 0x1c, + 0x1e, 0x5e, 0xbc, 0x87, 0xf1, 0x87, 0x8d, 0x72, + 0xe3, 0xbe, 0x0f, 0xdf, 0xfd, 0xd0, 0xb2, 0x89, + 0xf8, 0x05, 0x9a, 0x52, 0x47, 0x77, 0x9e, 0xe8, + 0xb1, 0x1d, 0x18, 0xed, 0x6a, 0x4b, 0x63, 0x1d, + 0xf1, 0x62, 0xd2, 0x65, 0x21, 0x26, 0x73, 0xd4, + 0x35, 0x5b, 0x95, 0x89, 0x12, 0x59, 0x23, 0x8c, + 0xc3, 0xfc, 0xf9, 0x4d, 0x21, 0x79, 0xa0, 0xbd, + 0xff, 0x33, 0xa2, 0x3d, 0x0b, 0x6f, 0x89, 0xc9, + 0x23, 0xe4, 0xe7, 0x9f, 0x1d, 0x98, 0xf6, 0xed, + 0x02, 0x8d, 0xac, 0x1a, 0xf9, 0xcb, 0xa5, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x28, 0x91, 0x56, 0x80, 0xe2, 0x6d, 0x51, + 0x88, 0x03, 0xf8, 0x49, 0xe6, 0x6a, 0x5a, 0xfb, + 0x2f, 0x0b, 0xb5, 0xa1, 0x0d, 0x63, 0x83, 0xae, + 0xb9, 0xbc, 0x05, 0xf0, 0x81, 0x00, 0x61, 0x83, + 0x38, 0xda, 0x14, 0xf6, 0xea, 0xd8, 0x78, 0x65, + 0xc7, 0x26, 0x17, 0x03, 0x01, 0x00, 0x18, 0x81, + 0x30, 0x8b, 0x22, 0x5a, 0xd3, 0x7f, 0xc8, 0xf2, + 0x8a, 0x6b, 0xa3, 0xba, 0x4d, 0xe7, 0x6e, 0xd2, + 0xfd, 0xbf, 0xf2, 0xc5, 0x28, 0xa0, 0x62, 0x17, + 0x03, 0x01, 0x00, 0x28, 0x17, 0x83, 0x3c, 0x78, + 0x18, 0xfa, 0x8d, 0x58, 0x5c, 0xaa, 0x05, 0x7d, + 0x67, 0x96, 0x11, 0x60, 0x11, 0xc0, 0x1e, 0x0d, + 0x6a, 0x6e, 0x5f, 0x1d, 0x98, 0x4b, 0xff, 0x82, + 0xee, 0x21, 0x06, 0x29, 0xd3, 0x8b, 0x80, 0x78, + 0x39, 0x05, 0x34, 0x9b, 0x15, 0x03, 0x01, 0x00, + 0x18, 0xa9, 0x38, 0x18, 0x4f, 0x9d, 0x84, 0x75, + 0x88, 0x53, 0xd6, 0x85, 0xc2, 0x15, 0x4b, 0xe3, + 0xe3, 0x35, 0x9a, 0x74, 0xc9, 0x3e, 0x13, 0xc1, + 0x8c, }, } var aesServerScript = [][]byte{ { - 0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00, - 0x7b, 0x03, 0x02, 0x4d, 0x08, 0x2d, 0x0b, 0xb3, - 0x57, 0x85, 0x71, 0x4b, 0xfb, 0x34, 0xab, 0x16, - 0xd4, 0x92, 0x50, 0x81, 0x16, 0x95, 0x11, 0x28, - 0x1a, 0xcb, 0xff, 0x09, 0x4d, 0x23, 0xa6, 0xfe, - 0x2e, 0xbb, 0x78, 0x00, 0x00, 0x34, 0x00, 0x33, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16, - 0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87, - 0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91, - 0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41, - 0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05, - 0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b, - 0x00, 0x8a, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x09, - 0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0xff, - 0x01, 0x00, 0x01, 0x00, + 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, + 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5c, 0xe9, 0x5e, + 0x31, 0x93, 0x82, 0xa5, 0x6f, 0x51, 0x82, 0xc8, + 0x55, 0x4f, 0x1f, 0x2e, 0x90, 0x98, 0x81, 0x13, + 0x27, 0x80, 0x68, 0xb4, 0x2d, 0xba, 0x3a, 0x76, + 0xd8, 0xd7, 0x2c, 0x00, 0x00, 0x50, 0xc0, 0x09, + 0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24, + 0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27, + 0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12, + 0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e, + 0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16, + 0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2, + 0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13, + 0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41, + 0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84, + 0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, + 0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, + 0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, + 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a, + 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01, + 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01, + 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x03, }, - { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, @@ -750,120 +860,126 @@ var aesServerScript = [][]byte{ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, - 0x00, 0x00, 0x00, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, + 0x00, }, - { 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x71, 0x9c, 0xe7, 0x23, 0xfc, - 0xb9, 0x19, 0x29, 0x82, 0xbf, 0xef, 0x08, 0xf7, - 0x99, 0x36, 0xc3, 0x4c, 0x6f, 0x05, 0xd2, 0x8b, - 0x62, 0x2b, 0x19, 0x9b, 0x7f, 0xc0, 0xcc, 0x48, - 0x30, 0x5f, 0xcd, 0xc3, 0x70, 0x55, 0x53, 0x73, - 0xfa, 0x79, 0x74, 0xf3, 0xa3, 0x76, 0x9f, 0xa1, - 0x7f, 0x98, 0xc2, 0xc0, 0xe3, 0xc5, 0xa0, 0x31, - 0x2f, 0xa6, 0xe8, 0x1e, 0x61, 0x46, 0xb3, 0x9b, - 0x4b, 0x16, 0xf1, 0x2d, 0xc7, 0x63, 0x7f, 0x79, - 0x22, 0x30, 0xd1, 0xf2, 0xfc, 0x77, 0x98, 0x0a, - 0x16, 0x11, 0x63, 0x71, 0x7f, 0x70, 0xef, 0x16, - 0xbb, 0x39, 0x87, 0x34, 0xac, 0x49, 0xbd, 0x07, - 0x67, 0xcb, 0x9c, 0xcc, 0xde, 0xef, 0xb1, 0xe0, - 0xdb, 0x01, 0xb5, 0x35, 0xa9, 0xb3, 0x10, 0x0c, - 0x4b, 0xee, 0xb3, 0x4e, 0xfd, 0xbe, 0x15, 0x27, - 0xf0, 0x46, 0xb2, 0x38, 0xba, 0x5f, 0xcc, 0x89, - 0xec, 0x29, 0x82, 0x14, 0x03, 0x01, 0x00, 0x01, - 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x3c, 0xfb, - 0xa4, 0x12, 0xcb, 0x00, 0xf9, 0x57, 0x7e, 0x9b, - 0xc9, 0xdc, 0x0c, 0xba, 0x9a, 0x81, 0x62, 0xfb, - 0x26, 0x13, 0x53, 0xfe, 0xaa, 0xcc, 0x82, 0xbb, - 0xb6, 0x67, 0x7f, 0x39, 0xbe, 0x4d, 0xbb, 0xc0, - 0x6c, 0x24, 0x31, 0x83, 0xa5, 0x50, 0x3a, 0x75, - 0x32, 0x64, 0xb5, 0xdb, 0xbe, 0x0a, + 0x82, 0x00, 0x80, 0x51, 0x2e, 0xec, 0x0d, 0x86, + 0xf3, 0x9f, 0xf2, 0x77, 0x04, 0x27, 0x2b, 0x0e, + 0x9c, 0xab, 0x35, 0x84, 0x65, 0xff, 0x36, 0xef, + 0xc0, 0x08, 0xc9, 0x1d, 0x9f, 0x29, 0xae, 0x8d, + 0xc5, 0x66, 0x81, 0x31, 0x92, 0x5e, 0x3d, 0xac, + 0xaa, 0x37, 0x28, 0x2c, 0x06, 0x91, 0xa6, 0xc2, + 0xd0, 0x83, 0x34, 0x24, 0x1c, 0x88, 0xfc, 0x0a, + 0xcf, 0xbf, 0xc2, 0x94, 0xe2, 0xed, 0xa7, 0x6a, + 0xa8, 0x8d, 0x3d, 0xf7, 0x06, 0x7d, 0x69, 0xf8, + 0x0d, 0xb2, 0xf7, 0xe4, 0x45, 0xcb, 0x0a, 0x25, + 0xcb, 0xb2, 0x2e, 0x38, 0x9a, 0x84, 0x75, 0xe8, + 0xe1, 0x42, 0x39, 0xa2, 0x18, 0x0e, 0x48, 0xca, + 0x33, 0x16, 0x4e, 0xf6, 0x2f, 0xec, 0x07, 0xe7, + 0x57, 0xe1, 0x20, 0x40, 0x40, 0x6d, 0x4e, 0x29, + 0x04, 0x1a, 0x8c, 0x99, 0xfb, 0x19, 0x3c, 0xaa, + 0x75, 0x64, 0xd3, 0xa6, 0xe6, 0xed, 0x3f, 0x5a, + 0xd2, 0xc9, 0x80, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x01, 0x10, 0xe9, 0x9e, + 0x06, 0x92, 0x18, 0xbf, 0x5e, 0xaf, 0x33, 0xc1, + 0xbf, 0x0e, 0x12, 0x07, 0x48, 0x4f, 0x6b, 0x6c, + 0xf5, 0x23, 0x5e, 0x87, 0xa7, 0xd3, 0x50, 0x79, + 0x38, 0xdc, 0xe0, 0x49, 0xd3, 0x81, 0x21, 0x12, + 0xd0, 0x3d, 0x9a, 0xfb, 0x83, 0xc1, 0x8b, 0xfc, + 0x14, 0xd5, 0xd5, 0xa7, 0xa3, 0x34, 0x14, 0x71, + 0xbe, 0xea, 0x37, 0x18, 0x12, 0x7f, 0x41, 0xfb, + 0xc5, 0x51, 0x17, 0x9d, 0x96, 0x58, 0x14, 0xfb, + 0x4f, 0xd7, 0xd3, 0x15, 0x0f, 0xec, 0x5a, 0x0d, + 0x35, 0xbb, 0x3c, 0x81, 0x5b, 0x3f, 0xdf, 0x52, + 0xa4, 0x4c, 0xcd, 0x13, 0xe1, 0x10, 0x37, 0x34, + 0xbf, 0xb4, 0x80, 0x1e, 0x8d, 0xe2, 0xc3, 0x7a, + 0x0f, 0x7b, 0x7d, 0x23, 0xeb, 0xd0, 0x99, 0x69, + 0xad, 0x0a, 0x2d, 0xb3, 0x6c, 0xd6, 0x80, 0x11, + 0x7f, 0x6c, 0xed, 0x1b, 0xcd, 0x08, 0x22, 0x56, + 0x90, 0x0e, 0xa4, 0xc3, 0x29, 0x33, 0x96, 0x30, + 0x34, 0x94, 0xa1, 0xeb, 0x9c, 0x1b, 0x5a, 0xd1, + 0x03, 0x61, 0xf9, 0xdd, 0xf3, 0x64, 0x8a, 0xfd, + 0x5f, 0x44, 0xdb, 0x2e, 0xa7, 0xfd, 0xe1, 0x1a, + 0x66, 0xc5, 0x01, 0x9c, 0xc7, 0xd1, 0xc4, 0xd3, + 0xea, 0x14, 0x3c, 0xed, 0x74, 0xbb, 0x1b, 0x97, + 0x8f, 0xf1, 0x29, 0x39, 0x33, 0x92, 0x93, 0x4e, + 0xf5, 0x87, 0x91, 0x61, 0x65, 0x8d, 0x27, 0x8d, + 0x76, 0xc1, 0xfa, 0x6a, 0x99, 0x80, 0xb1, 0x9b, + 0x29, 0x53, 0xce, 0x3e, 0xb6, 0x9a, 0xce, 0x3c, + 0x19, 0x5e, 0x48, 0x83, 0xaa, 0xa7, 0x66, 0x98, + 0x59, 0xf4, 0xbb, 0xf2, 0xbc, 0xd9, 0xc5, 0x9a, + 0xc8, 0x2c, 0x63, 0x58, 0xd5, 0xd4, 0xbc, 0x03, + 0xa9, 0x06, 0xa9, 0x80, 0x0d, 0xb3, 0x46, 0x2d, + 0xe3, 0xc6, 0xaf, 0x1a, 0x39, 0x18, 0x7e, 0x1e, + 0x83, 0x80, 0x46, 0x11, 0xd2, 0x13, 0x9f, 0xda, + 0xfc, 0x2d, 0x42, 0xaa, 0x5a, 0x1d, 0x4c, 0x31, + 0xe5, 0x58, 0x78, 0x5e, 0xe2, 0x04, 0xd6, 0x23, + 0x7f, 0x3f, 0x06, 0xc0, 0x54, 0xf8, }, - { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x30, 0x43, 0x24, 0x42, 0x55, 0x08, - 0xe4, 0xc2, 0x15, 0xc9, 0xdb, 0x71, 0x69, 0xee, - 0x09, 0xc5, 0x1c, 0xfd, 0x46, 0x10, 0xa0, 0x68, - 0x21, 0xf2, 0x48, 0xac, 0x6c, 0xc0, 0x2b, 0x62, - 0x07, 0x8f, 0x48, 0x33, 0x0a, 0x6b, 0x62, 0x28, - 0x2e, 0x2c, 0xad, 0xcb, 0x34, 0x85, 0xca, 0x2e, - 0xcd, 0x84, 0xf0, + 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xfb, 0xef, 0xba, 0xed, 0xc5, 0x36, + 0xc8, 0x5a, 0x41, 0x3f, 0x05, 0xfa, 0xfe, 0x48, + 0xc3, 0x91, 0x12, 0x8b, 0xe8, 0x32, 0x6a, 0x9f, + 0xdc, 0x97, 0xe2, 0x77, 0xb9, 0x96, 0x2d, 0xd4, + 0xe5, 0xbd, 0xa1, 0xfd, 0x94, 0xbb, 0x74, 0x63, + 0xb1, 0x0c, 0x38, 0xbc, 0x6f, 0x69, 0xaf, 0xa3, + 0x46, 0x9c, 0x96, 0x41, 0xde, 0x59, 0x23, 0xff, + 0x15, 0x6b, 0x3a, 0xef, 0x91, 0x6d, 0x92, 0x44, + 0xdc, 0x72, 0x1f, 0x40, 0x3d, 0xb5, 0x34, 0x8f, + 0x2a, 0xac, 0x21, 0x69, 0x05, 0x6f, 0xb2, 0x60, + 0x32, 0x5d, 0x3d, 0x97, 0xb4, 0x24, 0x99, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x30, 0x68, 0x27, 0x97, 0xca, 0x63, 0x09, + 0x22, 0xed, 0x0e, 0x61, 0x7c, 0x76, 0x31, 0x9c, + 0xbe, 0x27, 0xc9, 0xe6, 0x09, 0xc3, 0xc3, 0xc2, + 0xf4, 0xa2, 0x32, 0xba, 0x7c, 0xf2, 0x0f, 0xb8, + 0x3d, 0xcb, 0xe2, 0x4c, 0xc0, 0x7d, 0x8e, 0x5b, + 0x5a, 0xed, 0x05, 0x5c, 0x15, 0x96, 0x69, 0xc2, + 0x6f, 0x5f, 0x17, 0x03, 0x01, 0x00, 0x20, 0x5a, + 0xfe, 0x0b, 0xe1, 0x6f, 0xa8, 0x54, 0x19, 0x78, + 0xca, 0xba, 0x2e, 0x1e, 0x2e, 0xe1, 0x5d, 0x17, + 0xe5, 0x97, 0x05, 0x2c, 0x08, 0x0c, 0xff, 0xa8, + 0x59, 0xa9, 0xde, 0x5e, 0x21, 0x34, 0x04, 0x17, + 0x03, 0x01, 0x00, 0x30, 0x86, 0xb1, 0x3f, 0x88, + 0x43, 0xf0, 0x07, 0xee, 0xa8, 0xf4, 0xbc, 0xe7, + 0x5f, 0xc6, 0x8c, 0x86, 0x4c, 0xca, 0x70, 0x88, + 0xcc, 0x6a, 0xb4, 0x3d, 0x40, 0xe8, 0x54, 0x89, + 0x19, 0x43, 0x1f, 0x76, 0xe2, 0xac, 0xb2, 0x5b, + 0x92, 0xf8, 0x57, 0x39, 0x2a, 0xc3, 0x6d, 0x13, + 0x45, 0xfa, 0x36, 0x9e, 0x15, 0x03, 0x01, 0x00, + 0x20, 0x6d, 0xed, 0x7b, 0x59, 0x28, 0x2a, 0x27, + 0x04, 0x15, 0x07, 0x4e, 0xeb, 0x13, 0x00, 0xe3, + 0x3a, 0x3f, 0xf8, 0xaa, 0x2b, 0x3b, 0x1a, 0x8c, + 0x12, 0xd6, 0x4c, 0xec, 0x2a, 0xaf, 0x33, 0x60, + 0xaf, }, } diff --git a/libgo/go/crypto/tls/tls.go b/libgo/go/crypto/tls/tls.go index 182506c..9230656 100644 --- a/libgo/go/crypto/tls/tls.go +++ b/libgo/go/crypto/tls/tls.go @@ -155,7 +155,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) err = errors.New("crypto/tls: failed to parse key PEM data") return } - if strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { + if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { break } } diff --git a/libgo/go/crypto/tls/tls_test.go b/libgo/go/crypto/tls/tls_test.go index 31b858d..3822901 100644 --- a/libgo/go/crypto/tls/tls_test.go +++ b/libgo/go/crypto/tls/tls_test.go @@ -33,6 +33,19 @@ D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g== -----END RSA PRIVATE KEY----- ` +// keyPEM is the same as rsaKeyPEM, but declares itself as just +// "PRIVATE KEY", not "RSA PRIVATE KEY". http://golang.org/issue/4477 +var keyPEM = `-----BEGIN PRIVATE KEY----- +MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo +k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G +6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N +MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW +SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T +xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi +D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g== +-----END PRIVATE KEY----- +` + var ecdsaCertPEM = `-----BEGIN CERTIFICATE----- MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 @@ -62,21 +75,22 @@ kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ== var keyPairTests = []struct { algo string - cert *string - key *string + cert string + key string }{ - {"ECDSA", &ecdsaCertPEM, &ecdsaKeyPEM}, - {"RSA", &rsaCertPEM, &rsaKeyPEM}, + {"ECDSA", ecdsaCertPEM, ecdsaKeyPEM}, + {"RSA", rsaCertPEM, rsaKeyPEM}, + {"RSA-untyped", rsaCertPEM, keyPEM}, // golang.org/issue/4477 } func TestX509KeyPair(t *testing.T) { var pem []byte for _, test := range keyPairTests { - pem = []byte(*test.cert + *test.key) + pem = []byte(test.cert + test.key) if _, err := X509KeyPair(pem, pem); err != nil { t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err) } - pem = []byte(*test.key + *test.cert) + pem = []byte(test.key + test.cert) if _, err := X509KeyPair(pem, pem); err != nil { t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err) } diff --git a/libgo/go/encoding/binary/binary.go b/libgo/go/encoding/binary/binary.go index 0667014..04d5723 100644 --- a/libgo/go/encoding/binary/binary.go +++ b/libgo/go/encoding/binary/binary.go @@ -184,7 +184,7 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error { // values, or a pointer to such data. // Bytes written to w are encoded using the specified byte order // and read from successive fields of the data. -// When writing structs, zero values are are written for fields +// When writing structs, zero values are written for fields // with blank (_) field names. func Write(w io.Writer, order ByteOrder, data interface{}) error { // Fast path for basic types. diff --git a/libgo/go/encoding/csv/writer.go b/libgo/go/encoding/csv/writer.go index 324944c..17e4850 100644 --- a/libgo/go/encoding/csv/writer.go +++ b/libgo/go/encoding/csv/writer.go @@ -22,7 +22,7 @@ import ( // // If UseCRLF is true, the Writer ends each record with \r\n instead of \n. type Writer struct { - Comma rune // Field delimiter (set to to ',' by NewWriter) + Comma rune // Field delimiter (set to ',' by NewWriter) UseCRLF bool // True to use \r\n as the line terminator w *bufio.Writer } diff --git a/libgo/go/encoding/gob/doc.go b/libgo/go/encoding/gob/doc.go index 6d77c17..8b6fcfb 100644 --- a/libgo/go/encoding/gob/doc.go +++ b/libgo/go/encoding/gob/doc.go @@ -328,7 +328,7 @@ reserved). 01 // Add 1 to get field number 0: field[1].name 01 // 1 byte 59 // structType.field[1].name = "Y" - 01 // Add 1 to get field number 1: field[0].id + 01 // Add 1 to get field number 1: field[1].id 04 // struct.Type.field[1].typeId is 2 (signed int). 00 // End of structType.field[1]; end of structType.field. 00 // end of wireType.structType structure diff --git a/libgo/go/encoding/gob/timing_test.go b/libgo/go/encoding/gob/timing_test.go index b9371c4..9a0e51d 100644 --- a/libgo/go/encoding/gob/timing_test.go +++ b/libgo/go/encoding/gob/timing_test.go @@ -50,6 +50,7 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) { } func TestCountEncodeMallocs(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) var buf bytes.Buffer enc := NewEncoder(&buf) bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} @@ -69,6 +70,7 @@ func TestCountEncodeMallocs(t *testing.T) { } func TestCountDecodeMallocs(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) var buf bytes.Buffer enc := NewEncoder(&buf) bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")} diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go index 8592a0c..17134c5 100644 --- a/libgo/go/encoding/xml/marshal.go +++ b/libgo/go/encoding/xml/marshal.go @@ -45,7 +45,7 @@ const ( // - a field with tag "name,attr" becomes an attribute with // the given name in the XML element. // - a field with tag ",attr" becomes an attribute with the -// field name in the in the XML element. +// field name in the XML element. // - a field with tag ",chardata" is written as character data, // not as an XML element. // - a field with tag ",innerxml" is written verbatim, not subject diff --git a/libgo/go/exp/cookiejar/jar.go b/libgo/go/exp/cookiejar/jar.go new file mode 100644 index 0000000..2159ec5 --- /dev/null +++ b/libgo/go/exp/cookiejar/jar.go @@ -0,0 +1,89 @@ +// Copyright 2012 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 cookiejar implements an RFC 6265-compliant http.CookieJar. +// +// TODO: example code to create a memory-backed cookie jar with the default +// public suffix list. +package cookiejar + +import ( + "net/http" + "net/url" +) + +// PublicSuffixList provides the public suffix of a domain. For example: +// - the public suffix of "example.com" is "com", +// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and +// - the public suffix of "bar.pvt.k12.wy.us" is "pvt.k12.wy.us". +// +// Implementations of PublicSuffixList must be safe for concurrent use by +// multiple goroutines. +// +// An implementation that always returns "" is valid and may be useful for +// testing but it is not secure: it means that the HTTP server for foo.com can +// set a cookie for bar.com. +type PublicSuffixList interface { + // PublicSuffix returns the public suffix of domain. + // + // TODO: specify which of the caller and callee is responsible for IP + // addresses, for leading and trailing dots, for case sensitivity, and + // for IDN/Punycode. + PublicSuffix(domain string) string + + // String returns a description of the source of this public suffix list. + // A Jar will store its PublicSuffixList's description in its storage, + // and update the stored cookies if its list has a different description + // than the stored list. The description will typically contain something + // like a time stamp or version number. + String() string +} + +// Options are the options for creating a new Jar. +type Options struct { + // Storage is the cookie jar storage. It may not be nil. + Storage Storage + + // PublicSuffixList is the public suffix list that determines whether an + // HTTP server can set a cookie for a domain. It may not be nil. + PublicSuffixList PublicSuffixList + + // TODO: ErrorFunc for handling storage errors? +} + +// Jar implements the http.CookieJar interface from the net/http package. +type Jar struct { + storage Storage + psList PublicSuffixList +} + +// New returns a new cookie jar. +func New(o *Options) *Jar { + return &Jar{ + storage: o.Storage, + psList: o.PublicSuffixList, + } +} + +// TODO(nigeltao): how do we reject HttpOnly cookies? Do we post-process the +// return value from Jar.Cookies? +// +// HttpOnly cookies are those for regular HTTP(S) requests but should not be +// visible from JavaScript. The HttpOnly bit mitigates XSS attacks; it's not +// for HTTP vs HTTPS vs FTP transports. + +// Cookies implements the Cookies method of the http.CookieJar interface. +// +// It returns an empty slice if the URL's scheme is not HTTP or HTTPS. +func (j *Jar) Cookies(u *url.URL) []*http.Cookie { + // TODO. + return nil +} + +// SetCookies implements the SetCookies method of the http.CookieJar interface. +// +// It does nothing if the URL's scheme is not HTTP or HTTPS. +func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) { + // TODO. +} diff --git a/libgo/go/exp/cookiejar/storage.go b/libgo/go/exp/cookiejar/storage.go new file mode 100644 index 0000000..5294f58 --- /dev/null +++ b/libgo/go/exp/cookiejar/storage.go @@ -0,0 +1,101 @@ +// Copyright 2012 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 cookiejar + +import ( + "time" +) + +// Storage is a Jar's storage. It is a multi-map, mapping keys to one or more +// entries. Each entry consists of a subkey, creation time, last access time, +// and some arbitrary data. +// +// The Add and Delete methods have undefined behavior if the key is invalid. +// A valid key must use only bytes in the character class [a-z0-9.-] and +// must have at least one non-. byte. Note that this excludes any key +// containing a capital ASCII letter as well as the empty string. +type Storage interface { + // A client must call Lock before calling other methods and must call + // Unlock when finished. Between the calls to Lock and Unlock, a client + // can assume that other clients are not modifying the Storage. + Lock() + Unlock() + + // Add adds entries to the storage. Each entry's Subkey and Data must + // both be non-empty. + // + // If the Storage already contains an entry with the same key and + // subkey then the new entry is stored with the creation time of the + // old entry, and the old entry is deleted. + // + // Adding entries may cause other entries to be deleted, to maintain an + // implementation-specific storage constraint. + Add(key string, entries ...Entry) error + + // Delete deletes all entries for the given key. + Delete(key string) error + + // Entries calls f for each entry stored for that key. If f returns a + // non-nil error then the iteration stops and Entries returns that + // error. Iteration is not guaranteed to be in any particular order. + // + // If f returns an Update action then that stored entry's LastAccess + // time will be set to the time that f returned. If f returns a Delete + // action then that entry will be deleted from the Storage. + // + // f may call a Storage's Add and Delete methods; those modifications + // will not affect the list of entries visited in this call to Entries. + Entries(key string, f func(entry Entry) (Action, time.Time, error)) error + + // Keys calls f for each key stored. f will not be called on a key with + // zero entries. If f returns a non-nil error then the iteration stops + // and Keys returns that error. Iteration is not guaranteed to be in any + // particular order. + // + // f may call a Storage's Add, Delete and Entries methods; those + // modifications will not affect the list of keys visited in this call + // to Keys. + Keys(f func(key string) error) error +} + +// Entry is an entry in a Storage. +type Entry struct { + Subkey string + Data string + Creation time.Time + LastAccess time.Time +} + +// Action is an action returned by the function passed to Entries. +type Action int + +const ( + // Pass means to take no further action with an Entry. + Pass Action = iota + // Update means to update the LastAccess time of an Entry. + Update + // Delete means to delete an Entry. + Delete +) + +// ValidStorageKey returns whether the given key is valid for a Storage. +func ValidStorageKey(key string) bool { + hasNonDot := false + for i := 0; i < len(key); i++ { + switch c := key[i]; { + case 'a' <= c && c <= 'z': + fallthrough + case '0' <= c && c <= '9': + fallthrough + case c == '-': + hasNonDot = true + case c == '.': + // No-op. + default: + return false + } + } + return hasNonDot +} diff --git a/libgo/go/exp/cookiejar/storage_test.go b/libgo/go/exp/cookiejar/storage_test.go new file mode 100644 index 0000000..de6aa2b --- /dev/null +++ b/libgo/go/exp/cookiejar/storage_test.go @@ -0,0 +1,48 @@ +// Copyright 2012 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 cookiejar + +import ( + "testing" +) + +var validStorageKeyTests = map[string]bool{ + "": false, + ".": false, + "..": false, + "/": false, + "EXAMPLE.com": false, + "\n": false, + "\r": false, + "\r\n": false, + "\x00": false, + "back\\slash": false, + "co:lon": false, + "com,ma": false, + "semi;colon": false, + "sl/ash": false, + "sp ace": false, + "under_score": false, + "Ï€": false, + + "-": true, + ".dot": true, + ".dot.": true, + ".metadata": true, + ".x..y..z...": true, + "dot.": true, + "example.com": true, + "foo": true, + "hy-phen": true, + "xn--bcher-kva.ch": true, +} + +func TestValidStorageKey(t *testing.T) { + for key, want := range validStorageKeyTests { + if got := ValidStorageKey(key); got != want { + t.Errorf("%q: got %v, want %v", key, got, want) + } + } +} diff --git a/libgo/go/exp/gotype/gotype_test.go b/libgo/go/exp/gotype/gotype_test.go index 42d716d..2d58f32 100644 --- a/libgo/go/exp/gotype/gotype_test.go +++ b/libgo/go/exp/gotype/gotype_test.go @@ -5,20 +5,38 @@ package main import ( + "go/build" "path/filepath" "runtime" + "strings" "testing" ) -func runTest(t *testing.T, path, pkg string) { +func runTest(t *testing.T, path string) { exitCode = 0 - *pkgName = pkg - *recursive = false - if pkg == "" { + *recursive = false + if suffix := ".go"; strings.HasSuffix(path, suffix) { + // single file + path = filepath.Join(runtime.GOROOT(), "src/pkg", path) + path, file := filepath.Split(path) + *pkgName = file[:len(file)-len(suffix)] processFiles([]string{path}, true) } else { - processDirectory(path) + // package directory + // TODO(gri) gotype should use the build package instead + pkg, err := build.Import(path, "", 0) + if err != nil { + t.Errorf("build.Import error for path = %s: %s", path, err) + return + } + // TODO(gri) there ought to be a more direct way using the build package... + files := make([]string, len(pkg.GoFiles)) + for i, file := range pkg.GoFiles { + files[i] = filepath.Join(pkg.Dir, file) + } + *pkgName = pkg.Name + processFiles(files, true) } if exitCode != 0 { @@ -26,26 +44,167 @@ func runTest(t *testing.T, path, pkg string) { } } -var tests = []struct { - path string - pkg string -}{ +var tests = []string{ // individual files - {"testdata/test1.go", ""}, + "exp/gotype/testdata/test1.go", // directories - {filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"}, - {filepath.Join(runtime.GOROOT(), "src/pkg/go/build"), "build"}, - {filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"}, - {filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"}, - {filepath.Join(runtime.GOROOT(), "src/pkg/go/printer"), "printer"}, - {filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"}, - {filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "token"}, - {filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"}, + // Note: packages that don't typecheck yet are commented out + // "archive/tar", // investigate + "archive/zip", + + "bufio", + "bytes", + + "compress/bzip2", + "compress/flate", + "compress/gzip", + "compress/lzw", + "compress/zlib", + + "container/heap", + "container/list", + "container/ring", + + "crypto", + "crypto/aes", + "crypto/cipher", + "crypto/des", + "crypto/dsa", + "crypto/ecdsa", + "crypto/elliptic", + "crypto/hmac", + "crypto/md5", + "crypto/rand", + "crypto/rc4", + // "crypto/rsa", // investigate (GOARCH=386) + "crypto/sha1", + "crypto/sha256", + "crypto/sha512", + "crypto/subtle", + "crypto/tls", + // "crypto/x509", // investigate + "crypto/x509/pkix", + + "database/sql", + "database/sql/driver", + + "debug/dwarf", + "debug/elf", + "debug/gosym", + "debug/macho", + "debug/pe", + + "encoding/ascii85", + "encoding/asn1", + "encoding/base32", + "encoding/base64", + // "encoding/binary", // complex() doesn't work yet + "encoding/csv", + // "encoding/gob", // complex() doesn't work yet + "encoding/hex", + "encoding/json", + "encoding/pem", + "encoding/xml", + + "errors", + "expvar", + "flag", + "fmt", + + "exp/types", + "exp/gotype", + + "go/ast", + "go/build", + // "go/doc", // variadic parameters don't work yet fully + "go/format", + "go/parser", + "go/printer", + "go/scanner", + "go/token", + + "hash/adler32", + // "hash/crc32", // investigate + "hash/crc64", + "hash/fnv", + + "image", + "image/color", + "image/draw", + "image/gif", + "image/jpeg", + "image/png", + + "index/suffixarray", + + "io", + // "io/ioutil", // investigate + + "log", + "log/syslog", + + "math", + // "math/big", // investigate + // "math/cmplx", // complex doesn't work yet + "math/rand", + + "mime", + "mime/multipart", + + // "net", // depends on C files + "net/http", + "net/http/cgi", + // "net/http/fcgi", // investigate + "net/http/httptest", + "net/http/httputil", + // "net/http/pprof", // investigate + "net/mail", + // "net/rpc", // investigate + "net/rpc/jsonrpc", + "net/smtp", + "net/textproto", + "net/url", + + // "path", // variadic parameters don't work yet fully + // "path/filepath", // investigate + + // "reflect", // investigate + + "regexp", + "regexp/syntax", + + "runtime", + // "runtime/cgo", // import "C" + "runtime/debug", + "runtime/pprof", + + "sort", + // "strconv", // investigate + "strings", + + // "sync", // platform-specific files + // "sync/atomic", // platform-specific files + + // "syscall", // platform-specific files + + "testing", + "testing/iotest", + "testing/quick", + + "text/scanner", + "text/tabwriter", + // "text/template", // variadic parameters don't work yet fully + // "text/template/parse", // variadic parameters don't work yet fully + + // "time", // platform-specific files + "unicode", + "unicode/utf16", + "unicode/utf8", } func Test(t *testing.T) { for _, test := range tests { - runTest(t, test.path, test.pkg) + runTest(t, test) } } diff --git a/libgo/go/exp/gotype/testdata/test1.go b/libgo/go/exp/gotype/testdata/test1.go index ba8a51f..6a6f477 100644 --- a/libgo/go/exp/gotype/testdata/test1.go +++ b/libgo/go/exp/gotype/testdata/test1.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. -package p +package test1 func _() { // the scope of a local type declaration starts immediately after the type name diff --git a/libgo/go/exp/locale/collate/collate.go b/libgo/go/exp/locale/collate/collate.go index a08dcae..8a5c9dc 100644 --- a/libgo/go/exp/locale/collate/collate.go +++ b/libgo/go/exp/locale/collate/collate.go @@ -450,7 +450,7 @@ func (c *Collator) keyFromElems(buf *Buffer, ws []colElem) { } // Derive the quaternary weights from the options and other levels. // Note that we represent maxQuaternary as 0xFF. The first byte of the - // representation of a a primary weight is always smaller than 0xFF, + // representation of a primary weight is always smaller than 0xFF, // so using this single byte value will compare correctly. if Quaternary <= c.Strength && c.Alternate >= AltShifted { if c.Alternate == AltShiftTrimmed { diff --git a/libgo/go/exp/types/check.go b/libgo/go/exp/types/check.go index 1300d0a..af2d0c64 100644 --- a/libgo/go/exp/types/check.go +++ b/libgo/go/exp/types/check.go @@ -36,7 +36,7 @@ type checker struct { // // TODO(gri) This is very similar to the declare function in go/parser; it // is only used to associate methods with their respective receiver base types. -// In a future version, it might be simpler and cleaner do to all the resolution +// In a future version, it might be simpler and cleaner to do all the resolution // in the type-checking phase. It would simplify the parser, AST, and also // reduce some amount of code duplication. // @@ -188,14 +188,13 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) { case ast.Fun: fdecl := obj.Decl.(*ast.FuncDecl) - if fdecl.Recv != nil { - // This will ensure that the method base type is - // type-checked - check.collectFields(token.FUNC, fdecl.Recv, true) - } + check.collectParams(fdecl.Recv, false) // ensure method base is type-checked ftyp := check.typ(fdecl.Type, cycleOk).(*Signature) obj.Type = ftyp - check.function(ftyp, fdecl.Body) + // functions implemented elsewhere (say in assembly) have no body + if fdecl.Body != nil { + check.function(ftyp, fdecl.Body) + } default: panic("unreachable") @@ -355,12 +354,19 @@ func check(fset *token.FileSet, pkg *ast.Package, errh func(token.Pos, string), check.mapf = f check.initexprs = make(map[*ast.ValueSpec][]ast.Expr) - // handle bailouts + // handle panics defer func() { - if p := recover(); p != nil { - _ = p.(bailout) // re-panic if not a bailout + switch p := recover().(type) { + case nil: + // normal return - nothing to do + case bailout: + // early exit + err = check.firsterr + default: + // unexpected panic: don't crash clients + // panic(p) // enable for debugging + err = fmt.Errorf("types.check internal error: %v", p) } - err = check.firsterr }() // determine missing constant initialization expressions diff --git a/libgo/go/exp/types/check_test.go b/libgo/go/exp/types/check_test.go index bd60f5b..9a80278 100644 --- a/libgo/go/exp/types/check_test.go +++ b/libgo/go/exp/types/check_test.go @@ -49,6 +49,7 @@ var tests = []struct { {"decls0", []string{"testdata/decls0.src"}}, {"decls1", []string{"testdata/decls1.src"}}, {"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}}, + {"decls3", []string{"testdata/decls3.src"}}, {"const0", []string{"testdata/const0.src"}}, {"expr0", []string{"testdata/expr0.src"}}, {"expr1", []string{"testdata/expr1.src"}}, diff --git a/libgo/go/exp/types/const.go b/libgo/go/exp/types/const.go index 5501040..c678e47 100644 --- a/libgo/go/exp/types/const.go +++ b/libgo/go/exp/types/const.go @@ -278,7 +278,7 @@ func isRepresentableConst(x interface{}, as BasicKind) bool { return as == String || as == UntypedString case nilType: - return as == UntypedNil + return as == UntypedNil || as == UnsafePointer default: unreachable() diff --git a/libgo/go/exp/types/expr.go b/libgo/go/exp/types/expr.go index 58a33d0..e1f627b 100644 --- a/libgo/go/exp/types/expr.go +++ b/libgo/go/exp/types/expr.go @@ -12,75 +12,131 @@ import ( "strconv" ) -// TODO(gri) +// TODO(gri) Cleanups // - don't print error messages referring to invalid types (they are likely spurious errors) // - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values? // - rethink error handling: should all callers check if x.mode == valid after making a call? +// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used -func (check *checker) tag(field *ast.Field) string { - if t := field.Tag; t != nil { - assert(t.Kind == token.STRING) - if tag, err := strconv.Unquote(t.Value); err == nil { - return tag +// TODO(gri) API issues +// - clients need access to result type information (tuples) +// - clients need access to constant values +// - clients need access to built-in type information + +// TODO(gri) Bugs +// - expression hints are (correctly) used untyped for composite literal components, but also +// in possibly overlapping use as hints for shift expressions - investigate + +func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params ObjList, isVariadic bool) { + if list == nil { + return + } + var last *ast.Object + for i, field := range list.List { + ftype := field.Type + if t, _ := ftype.(*ast.Ellipsis); t != nil { + ftype = t.Elt + if variadicOk && i == len(list.List)-1 { + isVariadic = true + } else { + check.invalidAST(field.Pos(), "... not permitted") + // ok to continue + } + } + // the parser ensures that f.Tag is nil and we don't + // care if a constructed AST contains a non-nil tag + typ := check.typ(ftype, true) + if len(field.Names) > 0 { + // named parameter + for _, name := range field.Names { + obj := name.Obj + obj.Type = typ + params = append(params, obj) + last = obj + } + } else { + // anonymous parameter + obj := ast.NewObj(ast.Var, "") + obj.Type = typ + params = append(params, obj) + last = obj } - check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value) } - return "" + // For a variadic function, change the last parameter's object type + // from T to []T (this is the type used inside the function), but + // keep a copy of the object with the original type T in the params + // list (this is the externally visible type). + if isVariadic { + // if isVariadic is set, last must exist and len(params) > 0 + copy := *last + last.Type = &Slice{Elt: last.Type.(Type)} + params[len(params)-1] = © + } + return } -// collectFields collects interface methods (tok = token.INTERFACE), and function arguments/results (tok = token.FUNC). -func (check *checker) collectFields(tok token.Token, list *ast.FieldList, cycleOk bool) (fields ObjList, tags []string, isVariadic bool) { - if list != nil { - for _, field := range list.List { - ftype := field.Type - if t, ok := ftype.(*ast.Ellipsis); ok { - ftype = t.Elt - isVariadic = true +func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) { + if list == nil { + return + } + for _, f := range list.List { + typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces + // the parser ensures that f.Tag is nil and we don't + // care if a constructed AST contains a non-nil tag + if len(f.Names) > 0 { + // methods (the parser ensures that there's only one + // and we don't care if a constructed AST has more) + if _, ok := typ.(*Signature); !ok { + check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ) + continue } - typ := check.typ(ftype, cycleOk) - tag := check.tag(field) - if len(field.Names) > 0 { - // named fields - for _, name := range field.Names { - obj := name.Obj - obj.Type = typ - fields = append(fields, obj) - if tok == token.STRUCT { - tags = append(tags, tag) - } - } - } else { - // anonymous field - switch tok { - case token.FUNC: - obj := ast.NewObj(ast.Var, "") - obj.Type = typ - fields = append(fields, obj) - case token.INTERFACE: - utyp := underlying(typ) - if typ, ok := utyp.(*Interface); ok { - // TODO(gri) This is not good enough. Check for double declarations! - fields = append(fields, typ.Methods...) - } else if utyp != Typ[Invalid] { - // if utyp is invalid, don't complain (the root cause was reported before) - check.errorf(ftype.Pos(), "interface contains embedded non-interface type") - } - default: - panic("unreachable") - } + for _, name := range f.Names { + obj := name.Obj + obj.Type = typ + methods = append(methods, obj) + } + } else { + // embedded interface + utyp := underlying(typ) + if ityp, ok := utyp.(*Interface); ok { + methods = append(methods, ityp.Methods...) + } else if utyp != Typ[Invalid] { + // if utyp is invalid, don't complain (the root cause was reported before) + check.errorf(f.Type.Pos(), "%s is not an interface type", typ) } } } + // check for double declarations + methods.Sort() + prev := "" + for _, obj := range methods { + if obj.Name == prev { + check.errorf(list.Pos(), "multiple methods named %s", prev) + return // keep multiple entries, lookup will only return the first entry + } + } return } -func (check *checker) collectStructFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) { +func (check *checker) tag(t *ast.BasicLit) string { + if t != nil { + if t.Kind == token.STRING { + if val, err := strconv.Unquote(t.Value); err == nil { + return val + } + } + check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value) + } + return "" +} + +func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) { if list == nil { return } for _, f := range list.List { typ := check.typ(f.Type, cycleOk) - tag := check.tag(f) + tag := check.tag(f.Tag) if len(f.Names) > 0 { // named fields for _, name := range f.Names { @@ -90,9 +146,9 @@ func (check *checker) collectStructFields(list *ast.FieldList, cycleOk bool) (fi // anonymous field switch t := deref(typ).(type) { case *Basic: - fields = append(fields, &StructField{t.Name, t, tag, true}) + fields = append(fields, &StructField{t.Name, typ, tag, true}) case *NamedType: - fields = append(fields, &StructField{t.Obj.Name, t, tag, true}) + fields = append(fields, &StructField{t.Obj.Name, typ, tag, true}) default: if typ != Typ[Invalid] { check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ) @@ -115,9 +171,6 @@ var unaryOpPredicates = opPredicates{ func (check *checker) op(m opPredicates, x *operand, op token.Token) bool { if pred := m[op]; pred != nil { if !pred(x.typ) { - // TODO(gri) better error message for <-x where x is a send-only channel - // (<- is defined but not permitted). Special-case here or - // handle higher up. check.invalidOp(x.pos(), "operator %s not defined for %s", op, x) return false } @@ -173,7 +226,7 @@ func (check *checker) unary(x *operand, op token.Token) { } // Typed constants must be representable in // their type after each constant operation. - check.isRepresentable(x, x.typ.(*Basic)) + check.isRepresentable(x, underlying(x.typ).(*Basic)) return } @@ -424,34 +477,107 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) { } // index checks an index expression for validity. If length >= 0, it is the upper -// bound for the index. The result is a valid integer constant, or nil. +// bound for the index. The result is a valid index >= 0, or a negative value. // -func (check *checker) index(index ast.Expr, length int64, iota int) interface{} { +func (check *checker) index(index ast.Expr, length int64, iota int) int64 { var x operand check.expr(&x, index, nil, iota) if !x.isInteger() { check.errorf(x.pos(), "index %s must be integer", &x) - return nil + return -1 } if x.mode != constant { - return nil // we cannot check more + return -1 // we cannot check more } - // x.mode == constant and the index value must be >= 0 - if isNegConst(x.val) { + // The spec doesn't require int64 indices, but perhaps it should. + i, ok := x.val.(int64) + if !ok { + check.errorf(x.pos(), "stupid index %s", &x) + return -1 + } + if i < 0 { check.errorf(x.pos(), "index %s must not be negative", &x) - return nil + return -1 } - // x.val >= 0 - if length >= 0 && compareConst(x.val, length, token.GEQ) { + if length >= 0 && i >= length { check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length) - return nil + return -1 + } + + return i +} + +// indexElts checks the elements (elts) of an array or slice composite literal +// against the literals element type (typ), and the element indices against +// the literal length if known (length >= 0). It returns the length of the +// literal (maximum index value + 1). +// +func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota int) int64 { + visited := make(map[int64]bool, len(elts)) + var index, max int64 + for _, e := range elts { + // determine and check index + validIndex := false + eval := e + if kv, _ := e.(*ast.KeyValueExpr); kv != nil { + if i := check.index(kv.Key, length, iota); i >= 0 { + index = i + validIndex = true + } + eval = kv.Value + } else if length >= 0 && index >= length { + check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length) + } else { + validIndex = true + } + + // if we have a valid index, check for duplicate entries + if validIndex { + if visited[index] { + check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index) + } + visited[index] = true + } + index++ + if index > max { + max = index + } + + // check element against composite literal element type + var x operand + check.expr(&x, eval, typ, iota) + if !x.isAssignable(typ) { + check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ) + } + } + return max +} + +func (check *checker) argument(sig *Signature, i int, arg ast.Expr) { + var par *ast.Object + if n := len(sig.Params); i < n { + par = sig.Params[i] + } else if sig.IsVariadic { + par = sig.Params[n-1] + } else { + check.errorf(arg.Pos(), "too many arguments") + return } - return x.val + // TODO(gri) deal with ... last argument + var z, x operand + z.mode = variable + z.expr = nil // TODO(gri) can we do better here? + z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually + check.expr(&x, arg, z.typ, -1) + if x.mode == invalid { + return // ignore this argument + } + check.assignOperand(&z, &x) } -func (check *checker) callRecord(x *operand) { +func (check *checker) recordType(x *operand) { if x.mode != invalid { check.mapf(x.expr, x.typ) } @@ -470,7 +596,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } if check.mapf != nil { - defer check.callRecord(x) + defer check.recordType(x) } switch e := e.(type) { @@ -527,7 +653,10 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle x.typ = obj.Type.(Type) case *ast.Ellipsis: - unimplemented() + // ellipses are handled explictly where they are legal + // (array composite literals and parameter lists) + check.errorf(e.Pos(), "invalid use of '...'") + goto Error case *ast.BasicLit: x.setConst(e.Kind, e.Value) @@ -537,27 +666,146 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } case *ast.FuncLit: - x.mode = value - x.typ = check.typ(e.Type, false) - // TODO(gri) handle errors (e.g. x.typ is not a *Signature) - check.function(x.typ.(*Signature), e.Body) + if typ, ok := check.typ(e.Type, false).(*Signature); ok { + x.mode = value + x.typ = typ + check.function(typ, e.Body) + } else { + check.invalidAST(e.Pos(), "invalid function literal %s", e) + goto Error + } case *ast.CompositeLit: - // TODO(gri) - // - determine element type if nil - // - deal with map elements - var typ Type + typ := hint + openArray := false if e.Type != nil { - // TODO(gri) Fix this - just to get going for now - typ = check.typ(e.Type, false) + // [...]T array types may only appear with composite literals. + // Check for them here so we don't have to handle ... in general. + typ = nil + if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil { + if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil { + // We have an "open" [...]T array type. + // Create a new ArrayType with unknown length (-1) + // and finish setting it up after analyzing the literal. + typ = &Array{Len: -1, Elt: check.typ(atyp.Elt, cycleOk)} + openArray = true + } + } + if typ == nil { + typ = check.typ(e.Type, false) + } } - for _, e := range e.Elts { - var x operand - check.expr(&x, e, hint, iota) - // TODO(gri) check assignment compatibility to element type + if typ == nil { + check.errorf(e.Pos(), "missing type in composite literal") + goto Error + } + + switch utyp := underlying(deref(typ)).(type) { + case *Struct: + if len(e.Elts) == 0 { + break + } + fields := utyp.Fields + if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok { + // all elements must have keys + visited := make([]bool, len(fields)) + for _, e := range e.Elts { + kv, _ := e.(*ast.KeyValueExpr) + if kv == nil { + check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal") + continue + } + key, _ := kv.Key.(*ast.Ident) + if key == nil { + check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key) + continue + } + i := utyp.fieldIndex(key.Name) + if i < 0 { + check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name) + continue + } + // 0 <= i < len(fields) + if visited[i] { + check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name) + continue + } + visited[i] = true + check.expr(x, kv.Value, nil, iota) + etyp := fields[i].Type + if !x.isAssignable(etyp) { + check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp) + continue + } + } + } else { + // no element must have a key + for i, e := range e.Elts { + if kv, _ := e.(*ast.KeyValueExpr); kv != nil { + check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal") + continue + } + check.expr(x, e, nil, iota) + if i >= len(fields) { + check.errorf(x.pos(), "too many values in struct literal") + break // cannot continue + } + // i < len(fields) + etyp := fields[i].Type + if !x.isAssignable(etyp) { + check.errorf(x.pos(), "cannot use %s as an element of type %s in struct literal", x, etyp) + continue + } + } + if len(e.Elts) < len(fields) { + check.errorf(e.Rbrace, "too few values in struct literal") + // ok to continue + } + } + + case *Array: + n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota) + // if we have an "open" [...]T array, set the length now that we know it + if openArray { + utyp.Len = n + } + + case *Slice: + check.indexedElts(e.Elts, utyp.Elt, -1, iota) + + case *Map: + visited := make(map[interface{}]bool, len(e.Elts)) + for _, e := range e.Elts { + kv, _ := e.(*ast.KeyValueExpr) + if kv == nil { + check.errorf(e.Pos(), "missing key in map literal") + continue + } + check.expr(x, kv.Key, nil, iota) + if !x.isAssignable(utyp.Key) { + check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key) + continue + } + if x.mode == constant { + if visited[x.val] { + check.errorf(x.pos(), "duplicate key %s in map literal", x.val) + continue + } + visited[x.val] = true + } + check.expr(x, kv.Value, utyp.Elt, iota) + if !x.isAssignable(utyp.Elt) { + check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt) + continue + } + } + + default: + check.errorf(e.Pos(), "%s is not a valid composite literal type", typ) + goto Error } - // TODO(gri) this is not correct - leave for now to get going - x.mode = variable + + x.mode = variable // TODO(gri) mode is really a value - keep for now to get going x.typ = typ case *ast.ParenExpr: @@ -604,7 +852,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } mode, typ := lookupField(x.typ, sel) if mode == invalid { - check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel) + check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel) goto Error } if x.mode == typexpr { @@ -617,7 +865,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle // the receiver type becomes the type of the first function // argument of the method expression's function type // TODO(gri) at the moment, method sets don't correctly track - // pointer vs non-pointer receivers -> typechecker is too lenient + // pointer vs non-pointer receivers => typechecker is too lenient arg := ast.NewObj(ast.Var, "") arg.Type = x.typ x.mode = value @@ -659,15 +907,29 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } x.typ = typ.Elt + case *Pointer: + if typ, _ := underlying(typ.Base).(*Array); typ != nil { + valid = true + length = typ.Len + x.mode = variable + x.typ = typ.Elt + } + case *Slice: valid = true x.mode = variable x.typ = typ.Elt case *Map: - // TODO(gri) check index type + var key operand + check.expr(&key, e.Index, nil, iota) + if key.mode == invalid || !key.isAssignable(typ.Key) { + check.invalidOp(x.pos(), "cannot use %s as map index of type %s", &key, typ.Key) + goto Error + } x.mode = valueok x.typ = typ.Elt + x.expr = e return } @@ -698,7 +960,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } // a sliced string always yields a string value // of the same type as the original string (not - // a constant) even if the string and the indexes + // a constant) even if the string and the indices // are constant x.mode = value // x.typ doesn't change @@ -713,6 +975,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle } x.typ = &Slice{Elt: typ.Elt} + case *Pointer: + if typ, _ := underlying(typ.Base).(*Array); typ != nil { + valid = true + length = typ.Len + 1 // +1 for slice + x.mode = variable + x.typ = &Slice{Elt: typ.Elt} + } + case *Slice: valid = true x.mode = variable @@ -724,33 +994,55 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle goto Error } - var lo interface{} = zeroConst + lo := int64(0) if e.Low != nil { lo = check.index(e.Low, length, iota) } - var hi interface{} + hi := int64(-1) if e.High != nil { hi = check.index(e.High, length, iota) } else if length >= 0 { hi = length } - if lo != nil && hi != nil && compareConst(lo, hi, token.GTR) { - check.errorf(e.Low.Pos(), "inverted slice range: %v > %v", lo, hi) + if lo >= 0 && hi >= 0 && lo > hi { + check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi) // ok to continue } case *ast.TypeAssertExpr: check.expr(x, e.X, hint, iota) - if _, ok := underlying(x.typ).(*Interface); !ok { - check.invalidOp(e.X.Pos(), "non-interface type %s in type assertion", x.typ) + if x.mode == invalid { + goto Error + } + var T *Interface + if T, _ = underlying(x.typ).(*Interface); T == nil { + check.invalidOp(x.pos(), "%s is not an interface", x) + goto Error + } + // x.(type) expressions are handled explicitly in type switches + if e.Type == nil { + check.errorf(e.Pos(), "use of .(type) outside type switch") + goto Error + } + typ := check.typ(e.Type, false) + if typ == Typ[Invalid] { + goto Error + } + if method, wrongType := missingMethod(typ, T); method != nil { + var msg string + if wrongType { + msg = "%s cannot have dynamic type %s (wrong type for method %s)" + } else { + msg = "%s cannot have dynamic type %s (missing method %s)" + } + check.errorf(e.Type.Pos(), msg, x, typ, method.Name) // ok to continue } - // TODO(gri) some type asserts are compile-time decidable x.mode = valueok x.expr = e - x.typ = check.typ(e.Type, false) + x.typ = typ case *ast.CallExpr: check.exprOrType(x, e.Fun, nil, iota, false) @@ -760,21 +1052,11 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.conversion(x, e, x.typ, iota) } else if sig, ok := underlying(x.typ).(*Signature); ok { // check parameters - // TODO(gri) complete this - // - deal with various forms of calls - // - handle variadic calls - if len(sig.Params) == len(e.Args) { - var z, x operand - z.mode = variable - for i, arg := range e.Args { - z.expr = nil // TODO(gri) can we do better here? - z.typ = sig.Params[i].Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually - check.expr(&x, arg, z.typ, iota) - if x.mode == invalid { - goto Error - } - check.assignOperand(&z, &x) - } + // TODO(gri) + // - deal with single multi-valued function arguments: f(g()) + // - variadic functions only partially addressed + for i, arg := range e.Args { + check.argument(sig, i, arg) } // determine result @@ -827,32 +1109,26 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.binary(x, &y, e.Op, hint) case *ast.KeyValueExpr: - unimplemented() + // key:value expressions are handled in composite literals + check.invalidAST(e.Pos(), "no key:value expected") + goto Error case *ast.ArrayType: if e.Len != nil { - var n int64 = -1 - if ellip, ok := e.Len.(*ast.Ellipsis); ok { - // TODO(gri) need to check somewhere that [...]T types are only used with composite literals - if ellip.Elt != nil { - check.invalidAST(ellip.Pos(), "ellipsis only expected") - // ok to continue - } - } else { - check.expr(x, e.Len, nil, 0) - if x.mode == invalid { - goto Error - } - if x.mode == constant { - if i, ok := x.val.(int64); ok && i == int64(int(i)) { - n = i - } - } - if n < 0 { - check.errorf(e.Len.Pos(), "invalid array bound %s", e.Len) - // ok to continue - n = 0 + check.expr(x, e.Len, nil, iota) + if x.mode == invalid { + goto Error + } + if x.mode != constant { + if x.mode != invalid { + check.errorf(x.pos(), "array length %s must be constant", x) } + goto Error + } + n, ok := x.val.(int64) + if !ok || n < 0 { + check.errorf(x.pos(), "invalid array length %s", x) + goto Error } x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)} } else { @@ -862,19 +1138,17 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle case *ast.StructType: x.mode = typexpr - x.typ = &Struct{Fields: check.collectStructFields(e.Fields, cycleOk)} + x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)} case *ast.FuncType: - params, _, isVariadic := check.collectFields(token.FUNC, e.Params, true) - results, _, _ := check.collectFields(token.FUNC, e.Results, true) + params, isVariadic := check.collectParams(e.Params, true) + results, _ := check.collectParams(e.Results, false) x.mode = typexpr x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic} case *ast.InterfaceType: - methods, _, _ := check.collectFields(token.INTERFACE, e.Methods, cycleOk) - methods.Sort() x.mode = typexpr - x.typ = &Interface{Methods: methods} + x.typ = &Interface{Methods: check.collectMethods(e.Methods)} case *ast.MapType: x.mode = typexpr @@ -920,10 +1194,7 @@ func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) { } } -// expr is like rawExpr but reports an error if e doesn't represents a type. -// It returns e's type, or Typ[Invalid] if an error occured. -// -func (check *checker) typ(e ast.Expr, cycleOk bool) Type { +func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type { var x operand check.rawExpr(&x, e, nil, -1, cycleOk) switch x.mode { @@ -933,8 +1204,27 @@ func (check *checker) typ(e ast.Expr, cycleOk bool) Type { check.errorf(x.pos(), "%s used as type", &x) case typexpr: return x.typ + case constant: + if nilOk && x.isNil() { + return nil + } + fallthrough default: check.errorf(x.pos(), "%s is not a type", &x) } return Typ[Invalid] } + +// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil. +// It returns e's type, nil, or Typ[Invalid] if an error occured. +// +func (check *checker) typOrNil(e ast.Expr, cycleOk bool) Type { + return check.rawTyp(e, cycleOk, true) +} + +// typ is like rawExpr but reports an error if e doesn't represents a type. +// It returns e's type, or Typ[Invalid] if an error occured. +// +func (check *checker) typ(e ast.Expr, cycleOk bool) Type { + return check.rawTyp(e, cycleOk, false) +} diff --git a/libgo/go/exp/types/operand.go b/libgo/go/exp/types/operand.go index 49ba899..1a5e517 100644 --- a/libgo/go/exp/types/operand.go +++ b/libgo/go/exp/types/operand.go @@ -119,21 +119,15 @@ func (x *operand) setConst(tok token.Token, lit string) { } } -// implements reports whether x implements interface T. -func (x *operand) implements(T *Interface) bool { - if x.mode == invalid { - return true // avoid spurious errors - } - - unimplemented() - return true -} - // isNil reports whether x is the predeclared nil constant. func (x *operand) isNil() bool { return x.mode == constant && x.val == nilConst } +// TODO(gri) The functions operand.isAssignable, checker.convertUntyped, +// checker.isRepresentable, and checker.assignOperand are +// overlapping in functionality. Need to simplify and clean up. + // isAssignable reports whether x is assignable to a variable of type T. func (x *operand) isAssignable(T Type) bool { if x.mode == invalid || T == Typ[Invalid] { @@ -157,8 +151,10 @@ func (x *operand) isAssignable(T Type) bool { } // T is an interface type and x implements T - if Ti, ok := Tu.(*Interface); ok && x.implements(Ti) { - return true + if Ti, ok := Tu.(*Interface); ok { + if m, _ := missingMethod(x.typ, Ti); m == nil { + return true + } } // x is a bidirectional channel value, T is a channel @@ -181,8 +177,18 @@ func (x *operand) isAssignable(T Type) bool { } // x is an untyped constant representable by a value of type T - // - this is taken care of in the assignment check - // TODO(gri) double-check - isAssignable is used elsewhere + // TODO(gri) This is borrowing from checker.convertUntyped and + // checker.isRepresentable. Need to clean up. + if isUntyped(Vu) { + switch t := Tu.(type) { + case *Basic: + return x.mode == constant && isRepresentableConst(x.val, t.Kind) + case *Interface: + return x.isNil() || len(t.Methods) == 0 + case *Pointer, *Signature, *Slice, *Map, *Chan: + return x.isNil() + } + } return false } @@ -199,35 +205,50 @@ type lookupResult struct { typ Type } -// lookupFieldRecursive is similar to FieldByNameFunc in reflect/type.go -// TODO(gri): FieldByNameFunc seems more complex - what are we missing? -func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) { - // visited records the types that have been searched already - visited := make(map[Type]bool) +type embeddedType struct { + typ *NamedType + multiples bool // if set, typ is embedded multiple times at the same level +} + +// lookupFieldBreadthFirst searches all types in list for a single entry (field +// or method) of the given name. If such a field is found, the result describes +// the field mode and type; otherwise the result mode is invalid. +// (This function is similar in structure to FieldByNameFunc in reflect/type.go) +// +func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult) { + // visited records the types that have been searched already. + visited := make(map[*NamedType]bool) // embedded types of the next lower level - var next []*NamedType + var next []embeddedType - potentialMatch := func(mode operandMode, typ Type) bool { - if res.mode != invalid { - // name appeared multiple times at this level - annihilate + // potentialMatch is invoked every time a match is found. + potentialMatch := func(multiples bool, mode operandMode, typ Type) bool { + if multiples || res.mode != invalid { + // name appeared already at this level - annihilate res.mode = invalid return false } + // first appearance of name res.mode = mode res.typ = typ return true } - // look for name in all types of this level + // Search the current level if there is any work to do and collect + // embedded types of the next lower level in the next list. for len(list) > 0 { + // The res.mode indicates whether we have found a match already + // on this level (mode != invalid), or not (mode == invalid). assert(res.mode == invalid) - for _, typ := range list { + + // start with empty next list (don't waste underlying array) + next = next[:0] + + // look for name in all types at this level + for _, e := range list { + typ := e.typ if visited[typ] { - // We have seen this type before, at a higher level. - // That higher level shadows the lower level we are - // at now, and either we would have found or not - // found the field before. Ignore this type now. continue } visited[typ] = true @@ -236,7 +257,7 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) { if data := typ.Obj.Data; data != nil { if obj := data.(*ast.Scope).Lookup(name); obj != nil { assert(obj.Type != nil) - if !potentialMatch(value, obj.Type.(Type)) { + if !potentialMatch(e.multiples, value, obj.Type.(Type)) { return // name collision } } @@ -244,21 +265,26 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) { switch typ := underlying(typ).(type) { case *Struct: - // look for a matching fieldm and collect embedded types + // look for a matching field and collect embedded types for _, f := range typ.Fields { if f.Name == name { assert(f.Type != nil) - if !potentialMatch(variable, f.Type) { + if !potentialMatch(e.multiples, variable, f.Type) { return // name collision } continue } // Collect embedded struct fields for searching the next - // lower level, but only if we have not seen a match yet. + // lower level, but only if we have not seen a match yet + // (if we have a match it is either the desired field or + // we have a name collision on the same level; in either + // case we don't need to look further). // Embedded fields are always of the form T or *T where - // T is a named type. + // T is a named type. If typ appeared multiple times at + // this level, f.Type appears multiple times at the next + // level. if f.IsAnonymous && res.mode == invalid { - next = append(next, deref(f.Type).(*NamedType)) + next = append(next, embeddedType{deref(f.Type).(*NamedType), e.multiples}) } } @@ -267,7 +293,7 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) { for _, obj := range typ.Methods { if obj.Name == name { assert(obj.Type != nil) - if !potentialMatch(value, obj.Type.(Type)) { + if !potentialMatch(e.multiples, value, obj.Type.(Type)) { return // name collision } } @@ -276,17 +302,41 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) { } if res.mode != invalid { - // we found a match on this level + // we found a single match on this level return } - // search the next level - list = append(list[:0], next...) // don't waste underlying arrays - next = next[:0] + // No match and no collision so far. + // Compute the list to search for the next level. + list = list[:0] // don't waste underlying array + for _, e := range next { + // Instead of adding the same type multiple times, look for + // it in the list and mark it as multiple if it was added + // before. + // We use a sequential search (instead of a map for next) + // because the lists tend to be small, can easily be reused, + // and explicit search appears to be faster in this case. + if alt := findType(list, e.typ); alt != nil { + alt.multiples = true + } else { + list = append(list, e) + } + } + } + return } +func findType(list []embeddedType, typ *NamedType) *embeddedType { + for i := range list { + if p := &list[i]; p.typ == typ { + return p + } + } + return nil +} + func lookupField(typ Type, name string) (operandMode, Type) { typ = deref(typ) @@ -301,17 +351,20 @@ func lookupField(typ Type, name string) (operandMode, Type) { switch typ := underlying(typ).(type) { case *Struct: - var list []*NamedType + var next []embeddedType for _, f := range typ.Fields { if f.Name == name { return variable, f.Type } if f.IsAnonymous { - list = append(list, deref(f.Type).(*NamedType)) + // Possible optimization: If the embedded type + // is a pointer to the current type we could + // ignore it. + next = append(next, embeddedType{typ: deref(f.Type).(*NamedType)}) } } - if len(list) > 0 { - res := lookupFieldRecursive(list, name) + if len(next) > 0 { + res := lookupFieldBreadthFirst(next, name) return res.mode, res.typ } diff --git a/libgo/go/exp/types/predicates.go b/libgo/go/exp/types/predicates.go index 503027e2..2c1a991 100644 --- a/libgo/go/exp/types/predicates.go +++ b/libgo/go/exp/types/predicates.go @@ -6,6 +6,8 @@ package types +import "go/ast" + func isNamed(typ Type) bool { if _, ok := typ.(*Basic); ok { return ok @@ -247,3 +249,34 @@ func defaultType(typ Type) Type { } return typ } + +// missingMethod returns (nil, false) if typ implements T, otherwise +// it returns the first missing method required by T and whether it +// is missing or simply has the wrong type. +// +func missingMethod(typ Type, T *Interface) (method *ast.Object, wrongType bool) { + // TODO(gri): distinguish pointer and non-pointer receivers + // an interface type implements T if it has no methods with conflicting signatures + // Note: This is stronger than the current spec. Should the spec require this? + if ityp, _ := underlying(typ).(*Interface); ityp != nil { + for _, m := range T.Methods { + mode, sig := lookupField(ityp, m.Name) // TODO(gri) no need to go via lookupField + if mode != invalid && !isIdentical(sig, m.Type.(Type)) { + return m, true + } + } + return + } + + // a concrete type implements T if it implements all methods of T. + for _, m := range T.Methods { + mode, sig := lookupField(typ, m.Name) + if mode == invalid { + return m, false + } + if !isIdentical(sig, m.Type.(Type)) { + return m, true + } + } + return +} diff --git a/libgo/go/exp/types/stmt.go b/libgo/go/exp/types/stmt.go index 4f01249..e2c6448 100644 --- a/libgo/go/exp/types/stmt.go +++ b/libgo/go/exp/types/stmt.go @@ -27,8 +27,8 @@ func (check *checker) assignOperand(z, x *operand) { } } -// assign1to1 typechecks a single assignment of the form lhs := rhs (if rhs != nil), -// or lhs := x (if rhs == nil). If decl is set, the lhs operand must be an identifier. +// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil), +// or lhs = x (if rhs == nil). If decl is set, the lhs operand must be an identifier. // If its type is not set, it is deduced from the type or value of x. If lhs has a // type it is used as a hint when evaluating rhs, if present. // @@ -226,6 +226,38 @@ func (check *checker) stmtList(list []ast.Stmt) { } } +func (check *checker) call(call *ast.CallExpr) { + var x operand + check.rawExpr(&x, call, nil, -1, false) // don't check if value is used + // TODO(gri) If a builtin is called, the builtin must be valid in statement context. +} + +func (check *checker) multipleDefaults(list []ast.Stmt) { + var first ast.Stmt + for _, s := range list { + var d ast.Stmt + switch c := s.(type) { + case *ast.CaseClause: + if len(c.List) == 0 { + d = s + } + case *ast.CommClause: + if c.Comm == nil { + d = s + } + default: + check.invalidAST(s.Pos(), "case/communication clause expected") + } + if d != nil { + if first != nil { + check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos()) + } else { + first = d + } + } + } +} + // stmt typechecks statement s. func (check *checker) stmt(s ast.Stmt) { switch s := s.(type) { @@ -265,7 +297,7 @@ func (check *checker) stmt(s ast.Stmt) { } check.rawExpr(&x, s.X, nil, -1, false) if x.mode == typexpr { - check.errorf(x.pos(), "%s is not an expression", x) + check.errorf(x.pos(), "%s is not an expression", &x) } case *ast.SendStmt: @@ -347,10 +379,10 @@ func (check *checker) stmt(s ast.Stmt) { } case *ast.GoStmt: - unimplemented() + check.call(s.Call) case *ast.DeferStmt: - unimplemented() + check.call(s.Call) case *ast.ReturnStmt: sig := check.functypes[len(check.functypes)-1] @@ -403,31 +435,122 @@ func (check *checker) stmt(s ast.Stmt) { x.typ = Typ[UntypedBool] x.val = true } + + check.multipleDefaults(s.Body.List) for _, s := range s.Body.List { - if clause, ok := s.(*ast.CaseClause); ok { - for _, expr := range clause.List { - var y operand - check.expr(&y, expr, nil, -1) - // TODO(gri) x and y must be comparable - } - check.stmtList(clause.Body) - } else { - check.errorf(s.Pos(), "invalid AST: case clause expected") + clause, _ := s.(*ast.CaseClause) + if clause == nil { + continue // error reported before } + for _, expr := range clause.List { + var y operand + check.expr(&y, expr, nil, -1) + // TODO(gri) x and y must be comparable + } + check.stmtList(clause.Body) } case *ast.TypeSwitchStmt: - unimplemented() + check.optionalStmt(s.Init) + + // A type switch guard must be of the form: + // + // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . + // + // The parser is checking syntactic correctness; + // remaining syntactic errors are considered AST errors here. + // TODO(gri) better factoring of error handling (invalid ASTs) + // + var lhs *ast.Object // lhs identifier object or nil + var rhs ast.Expr + switch guard := s.Assign.(type) { + case *ast.ExprStmt: + rhs = guard.X + case *ast.AssignStmt: + if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 { + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + ident, _ := guard.Lhs[0].(*ast.Ident) + if ident == nil { + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + lhs = ident.Obj + rhs = guard.Rhs[0] + default: + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + + // rhs must be of the form: expr.(type) and expr must be an interface + expr, _ := rhs.(*ast.TypeAssertExpr) + if expr == nil || expr.Type != nil { + check.invalidAST(s.Pos(), "incorrect form of type switch guard") + return + } + var x operand + check.expr(&x, expr.X, nil, -1) + if x.mode == invalid { + return + } + var T *Interface + if T, _ = underlying(x.typ).(*Interface); T == nil { + check.errorf(x.pos(), "%s is not an interface", &x) + return + } + + check.multipleDefaults(s.Body.List) + for _, s := range s.Body.List { + clause, _ := s.(*ast.CaseClause) + if clause == nil { + continue // error reported before + } + // Check each type in this type switch case. + var typ Type + for _, expr := range clause.List { + typ = check.typOrNil(expr, false) + if typ != nil && typ != Typ[Invalid] { + if method, wrongType := missingMethod(typ, T); method != nil { + var msg string + if wrongType { + msg = "%s cannot have dynamic type %s (wrong type for method %s)" + } else { + msg = "%s cannot have dynamic type %s (missing method %s)" + } + check.errorf(expr.Pos(), msg, &x, typ, method.Name) + // ok to continue + } + } + } + // If lhs exists, set its type for each clause. + if lhs != nil { + // In clauses with a case listing exactly one type, the variable has that type; + // otherwise, the variable has the type of the expression in the TypeSwitchGuard. + if len(clause.List) != 1 || typ == nil { + typ = x.typ + } + lhs.Type = typ + } + check.stmtList(clause.Body) + } + + // There is only one object (lhs) associated with a lhs identifier, but that object + // assumes different types for different clauses. Set it to nil when we are done so + // that the type cannot be used by mistake. + if lhs != nil { + lhs.Type = nil + } case *ast.SelectStmt: + check.multipleDefaults(s.Body.List) for _, s := range s.Body.List { - c, ok := s.(*ast.CommClause) - if !ok { - check.invalidAST(s.Pos(), "communication clause expected") - continue + clause, _ := s.(*ast.CommClause) + if clause == nil { + continue // error reported before } - check.optionalStmt(c.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt) - check.stmtList(c.Body) + check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt) + check.stmtList(clause.Body) } case *ast.ForStmt: @@ -443,7 +566,79 @@ func (check *checker) stmt(s ast.Stmt) { check.stmt(s.Body) case *ast.RangeStmt: - unimplemented() + // check expression to iterate over + decl := s.Tok == token.DEFINE + var x operand + check.expr(&x, s.X, nil, -1) + if x.mode == invalid { + // if we don't have a declaration, we can still check the loop's body + if !decl { + check.stmt(s.Body) + } + return + } + + // determine key/value types + var key, val Type + switch typ := underlying(x.typ).(type) { + case *Basic: + if isString(typ) { + key = Typ[UntypedInt] + val = Typ[UntypedRune] + } + case *Array: + key = Typ[UntypedInt] + val = typ.Elt + case *Slice: + key = Typ[UntypedInt] + val = typ.Elt + case *Pointer: + if typ, _ := underlying(typ.Base).(*Array); typ != nil { + key = Typ[UntypedInt] + val = typ.Elt + } + case *Map: + key = typ.Key + val = typ.Elt + case *Chan: + key = typ.Elt + if typ.Dir&ast.RECV == 0 { + check.errorf(x.pos(), "cannot range over send-only channel %s", &x) + // ok to continue + } + if s.Value != nil { + check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x) + // ok to continue + } + } + + if key == nil { + check.errorf(x.pos(), "cannot range over %s", &x) + // if we don't have a declaration, we can still check the loop's body + if !decl { + check.stmt(s.Body) + } + return + } + + // check assignment to/declaration of iteration variables + // TODO(gri) The error messages/positions are not great here, + // they refer to the expression in the range clause. + // Should give better messages w/o too much code + // duplication (assignment checking). + if s.Key != nil { + x.typ = key + check.assign1to1(s.Key, nil, &x, decl, -1) + } else { + check.invalidAST(s.Pos(), "range clause requires index iteration variable") + // ok to continue + } + if s.Value != nil { + x.typ = val + check.assign1to1(s.Value, nil, &x, decl, -1) + } + + check.stmt(s.Body) default: check.errorf(s.Pos(), "invalid statement") diff --git a/libgo/go/exp/types/testdata/decls0.src b/libgo/go/exp/types/testdata/decls0.src index 3537a9e..70623c6 100644 --- a/libgo/go/exp/types/testdata/decls0.src +++ b/libgo/go/exp/types/testdata/decls0.src @@ -40,8 +40,17 @@ type ( ) +// invalid array types type ( - p1 pi /* ERROR "no field or method foo" */ .foo + iA0 [... /* ERROR "invalid use of '...'" */ ]byte + iA1 [1 /* ERROR "invalid array length" */ <<100]int + iA2 [- /* ERROR "invalid array length" */ 1]complex128 + iA3 ["foo" /* ERROR "invalid array length" */ ]string +) + + +type ( + p1 pi /* ERROR "no single field or method foo" */ .foo p2 unsafe.Pointer ) @@ -131,7 +140,7 @@ type ( m1(I5) } I6 interface { - S0 /* ERROR "non-interface" */ + S0 /* ERROR "not an interface" */ } I7 interface { I1 diff --git a/libgo/go/exp/types/testdata/decls1.src b/libgo/go/exp/types/testdata/decls1.src index 16da045..be92709 100644 --- a/libgo/go/exp/types/testdata/decls1.src +++ b/libgo/go/exp/types/testdata/decls1.src @@ -73,7 +73,7 @@ var ( // Various more complex expressions var ( - u1 = x /* ERROR "non-interface type" */ .(int) + u1 = x /* ERROR "not an interface" */ .(int) u2 = iface.([]int) u3 = iface.(a /* ERROR "not a type" */ ) u4, ok = iface.(int) diff --git a/libgo/go/exp/types/testdata/expr3.src b/libgo/go/exp/types/testdata/expr3.src index 890f5e9..a5ea4d2 100644 --- a/libgo/go/exp/types/testdata/expr3.src +++ b/libgo/go/exp/types/testdata/expr3.src @@ -6,43 +6,43 @@ package expr3 -// TODO(gri) Move the code below into function "shifts" once we check -// declarations with initilizations inside functions. -var ( - i0 int - u0 uint -) - -var ( - v0 = 1<<0 - v1 = 1<<i0 /* ERROR "must be unsigned" */ - v2 = 1<<u0 - v3 = 1<<"foo" /* ERROR "must be unsigned" */ - v4 = 1<<- /* ERROR "stupid shift" */ 1 - v5 = 1<<1025 /* ERROR "stupid shift" */ - v6 = 1 /* ERROR "overflows" */ <<100 - - v10 uint = 1 << 0 - v11 uint = 1 << u0 - v12 float32 = 1 /* ERROR "must be integer" */ << u0 -) - -// TODO(gri) enable commented out tests below. - -// from the spec -var ( - s uint = 33 - i = 1<<s // 1 has type int - j int32 = 1<<s // 1 has type int32; j == 0 - k = uint64(1<<s) // 1 has type uint64; k == 1<<33 - m int = 1.0<<s // 1.0 has type int -// n = 1.0<<s != 0 // 1.0 has type int; n == false if ints are 32bits in size - o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size -// p = 1<<s == 1 /* ERROR "overflows" */ <<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int - u = 1.0 /* ERROR "must be integer" */ <<s // illegal: 1.0 has type float64, cannot shift - v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift - w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression -) +func shifts1() { + var ( + i0 int + u0 uint + ) + + var ( + v0 = 1<<0 + v1 = 1<<i0 /* ERROR "must be unsigned" */ + v2 = 1<<u0 + v3 = 1<<"foo" /* ERROR "must be unsigned" */ + v4 = 1<<- /* ERROR "stupid shift" */ 1 + v5 = 1<<1025 /* ERROR "stupid shift" */ + v6 = 1 /* ERROR "overflows" */ <<100 + + v10 uint = 1 << 0 + v11 uint = 1 << u0 + v12 float32 = 1 /* ERROR "must be integer" */ << u0 + ) +} + +func shifts2() { + // TODO(gri) enable commented out tests below. + var ( + s uint = 33 + i = 1<<s // 1 has type int + j int32 = 1<<s // 1 has type int32; j == 0 + k = uint64(1<<s) // 1 has type uint64; k == 1<<33 + m int = 1.0<<s // 1.0 has type int + // n = 1.0<<s != 0 // 1.0 has type int; n == false if ints are 32bits in size + o = 1<<s == 2<<s // 1 and 2 have type int; o == true if ints are 32bits in size + // p = 1<<s == 1 /* ERROR "overflows" */ <<33 // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int + u = 1.0 /* ERROR "must be integer" */ <<s // illegal: 1.0 has type float64, cannot shift + v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift + w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression + ) +} // TODO(gri) The error messages below depond on adjusting the spec // to reflect what gc is doing at the moment (the spec @@ -67,11 +67,24 @@ func indexes() { a1 = a /* ERROR "cannot assign" */ [1] _ = a[9] _ = a[10 /* ERROR "index .* out of bounds" */ ] + _ = a[1 /* ERROR "stupid index" */ <<100] _ = a[10:] _ = a[:10] _ = a[10:10] _ = a[11 /* ERROR "index .* out of bounds" */ :] _ = a[: 11 /* ERROR "index .* out of bounds" */ ] + _ = a[: 1 /* ERROR "stupid index" */ <<100] + + pa := &a + _ = pa[9] + _ = pa[10 /* ERROR "index .* out of bounds" */ ] + _ = pa[1 /* ERROR "stupid index" */ <<100] + _ = pa[10:] + _ = pa[:10] + _ = pa[10:10] + _ = pa[11 /* ERROR "index .* out of bounds" */ :] + _ = pa[: 11 /* ERROR "index .* out of bounds" */ ] + _ = pa[: 1 /* ERROR "stupid index" */ <<100] var b [0]int _ = b[0 /* ERROR "index .* out of bounds" */ ] @@ -88,11 +101,9 @@ func indexes() { _ = s[1 : 2] _ = s[2 /* ERROR "inverted slice range" */ : 1] _ = s[2 :] - _ = s[: 1<<100] - _ = s[1<<100 :] - _ = s[1<<100 : 1<<100] - _ = s[1 /* ERROR "inverted slice range" */ <<100+1 : 1<<100] - _ = s[1 /* ERROR "inverted slice range" */ <<100+1 : 10] + _ = s[: 1 /* ERROR "stupid index" */ <<100] + _ = s[1 /* ERROR "stupid index" */ <<100 :] + _ = s[1 /* ERROR "stupid index" */ <<100 : 1 /* ERROR "stupid index" */ <<100] var t string _ = t[- /* ERROR "index .* negative" */ 1] @@ -126,9 +137,152 @@ type T struct { func (*T) m() {} func method_expressions() { - _ = T /* ERROR "no field or method" */ .a + _ = T /* ERROR "no single field or method" */ .a _ = T /* ERROR "has no method" */ .x _ = T.m var f func(*T) = (*T).m var g func(*T) = ( /* ERROR "cannot assign" */ T).m -}
\ No newline at end of file +} + +func struct_literals() { + type T0 struct { + a, b, c int + } + + type T1 struct { + T0 + a, b int + u float64 + s string + } + + // keyed elements + _ = T1{} + _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ } + _ = T1{aa /* ERROR "unknown field" */ : 0} + _ = T1{1 /* ERROR "invalid field name" */ : 0} + _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10} + _ = T1{a: "foo" /* ERROR "cannot use" */ } + _ = T1{c /* ERROR "unknown field" */ : 0} + _ = T1{T0: { /* ERROR "missing type" */ }} + _ = T1{T0: T0{}} + _ = T1{T0 /* ERROR "invalid field name" */ .a: 0} + + // unkeyed elements + _ = T0{1, 2, 3} + _ = T0{1, b /* ERROR "mixture" */ : 2, 3} + _ = T0{1, 2} /* ERROR "too few values" */ + _ = T0{1, 2, 3, 4 /* ERROR "too many values" */ } + _ = T0{1, "foo" /* ERROR "cannot use" */, 3.4 /* ERROR "cannot use" */} +} + +func array_literals() { + type A0 [0]int + _ = A0{} + _ = A0{0 /* ERROR "index .* out of bounds" */} + _ = A0{0 /* ERROR "index .* out of bounds" */ : 0} + + type A1 [10]int + _ = A1{} + _ = A1{0, 1, 2} + _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ } + _ = A1{- /* ERROR "index .* negative" */ 1: 0} + _ = A1{8: 8, 9} + _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ } + _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} + _ = A1{5: 5, 6, 7, 3: 3, 4} + _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } + _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10} + _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ } + _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4} + _ = A1{2.0} + _ = A1{2.1 /* ERROR "cannot use" */ } + _ = A1{"foo" /* ERROR "cannot use" */ } + + a0 := [...]int{} + assert(len(a0) == 0) + + a1 := [...]int{0, 1, 2} + assert(len(a1) == 3) + var a13 [3]int + var a14 [4]int + a13 = a1 + a14 = a1 /* ERROR "cannot assign" */ + + a2 := [...]int{- /* ERROR "index .* negative" */ 1: 0} + + a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} + assert(len(a3) == 5) // somewhat arbitrary + + a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14} + assert(len(a4) == 1024) + + // from the spec + type Point struct { x, y float32 } + _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}} + _ = [...]Point{{1.5, -3.5}, {0, 0}} + _ = [][]int{[]int{1, 2, 3}, []int{4, 5}} + _ = [][]int{{1, 2, 3}, {4, 5}} + _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}} + _ = [...]*Point{{1.5, -3.5}, {0, 0}} +} + +func slice_literals() { + type S0 []int + _ = S0{} + _ = S0{0, 1, 2} + _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + _ = S0{- /* ERROR "index .* negative" */ 1: 0} + _ = S0{8: 8, 9} + _ = S0{8: 8, 9, 10} + _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} + _ = S0{5: 5, 6, 7, 3: 3, 4} + _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ } + _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10} + _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ } + _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4} + _ = S0{2.0} + _ = S0{2.1 /* ERROR "cannot use" */ } + _ = S0{"foo" /* ERROR "cannot use" */ } +} + +func map_literals() { + type M0 map[string]int + + _ = M0{} + _ = M0{1 /* ERROR "missing key" */ } + _ = M0{1 /* ERROR "cannot use .* as string key" */ : 2} + _ = M0{"foo": "bar" /* ERROR "cannot use .* as int value" */ } + _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 } +} + +type I interface { + m() +} + +type I2 interface { + m(int) +} + +type T1 struct{} +type T2 struct{} + +func (T2) m(int) {} + +func type_asserts() { + var x int + _ = x /* ERROR "not an interface" */ .(int) + + var e interface{} + var ok bool + x, ok = e.(int) + + var t I + _ = t /* ERROR "use of .* outside type switch" */ .(type) + _ = t.(T) + _ = t.(T1 /* ERROR "missing method m" */ ) + _ = t.(T2 /* ERROR "wrong type for method m" */ ) + _ = t.(I2 /* ERROR "wrong type for method m" */ ) +} diff --git a/libgo/go/exp/types/testdata/stmt0.src b/libgo/go/exp/types/testdata/stmt0.src index e3436bc..d3cc3ac 100644 --- a/libgo/go/exp/types/testdata/stmt0.src +++ b/libgo/go/exp/types/testdata/stmt0.src @@ -71,4 +71,170 @@ func _selects() { x = t case <-sc /* ERROR "cannot receive from send-only channel" */ : } + select { + default: + default /* ERROR "multiple defaults" */ : + } +} + +func _gos() { + go 1 /* ERROR "expected function/method call" */ + go _gos() + var c chan int + go close(c) + go len(c) // TODO(gri) this should not be legal +} + +func _defers() { + defer 1 /* ERROR "expected function/method call" */ + defer _defers() + var c chan int + defer close(c) + defer len(c) // TODO(gri) this should not be legal +} + +func _switches() { + var x int + + switch x { + default: + default /* ERROR "multiple defaults" */ : + } + + // TODO(gri) more tests +} + +type I interface { + m() +} + +type I2 interface { + m(int) +} + +type T struct{} +type T1 struct{} +type T2 struct{} + +func (T) m() {} +func (T2) m(int) {} + +func _typeswitches() { + var i int + var x interface{} + + switch x.(type) {} + switch (x /* ERROR "outside type switch" */ .(type)) {} + + switch x.(type) { + default: + default /* ERROR "multiple defaults" */ : + } + + switch x := x.(type) {} + + switch x := x.(type) { + case int: + var y int = x + } + + switch x := i /* ERROR "not an interface" */ .(type) {} + + switch t := x.(type) { + case nil: + var v bool = t /* ERROR "cannot assign" */ + case int: + var v int = t + case float32, complex64: + var v float32 = t /* ERROR "cannot assign" */ + default: + var v float32 = t /* ERROR "cannot assign" */ + } + + var t I + switch t.(type) { + case T: + case T1 /* ERROR "missing method m" */ : + case T2 /* ERROR "wrong type for method m" */ : + case I2 /* ERROR "wrong type for method m" */ : + } +} + +func _rangeloops() { + var ( + x int + a [10]float32 + b []string + p *[10]complex128 + pp **[10]complex128 + s string + m map[int]bool + c chan int + sc chan<- int + rc <-chan int + ) + + for _ = range x /* ERROR "cannot range over" */ {} + for i := range x /* ERROR "cannot range over" */ {} + + for i := range a { + var ii int + ii = i + } + for i, x := range a { + var ii int + ii = i + var xx float64 + xx = x /* ERROR "cannot assign" */ + } + var ii int + var xx float32 + for ii, xx := range a {} + + for i := range b { + var ii int + ii = i + } + for i, x := range b { + var ii int + ii = i + var xx string + xx = x + } + + for i := range s { + var ii int + ii = i + } + for i, x := range s { + var ii int + ii = i + var xx rune + xx = x + } + + for _, x := range p { + var xx complex128 + xx = x + } + + for _, x := range pp /* ERROR "cannot range over" */ {} + + for k := range m { + var kk int32 + kk = k /* ERROR "cannot assign" */ + } + for k, v := range m { + var kk int + kk = k + if v {} + } + + for _, _ /* ERROR "only one iteration variable" */ = range c {} + for e := range c { + var ee int + ee = e + } + for _ = range sc /* ERROR "cannot range over send-only channel" */ {} + for _ = range rc {} }
\ No newline at end of file diff --git a/libgo/go/exp/types/types.go b/libgo/go/exp/types/types.go index eed0c8a..83a0826 100644 --- a/libgo/go/exp/types/types.go +++ b/libgo/go/exp/types/types.go @@ -126,6 +126,15 @@ type Struct struct { Fields []*StructField } +func (typ *Struct) fieldIndex(name string) int { + for i, f := range typ.Fields { + if f.Name == name { + return i + } + } + return -1 +} + // A Pointer represents a pointer type *Base. type Pointer struct { implementsType diff --git a/libgo/go/exp/types/universe.go b/libgo/go/exp/types/universe.go index bb8b6a2..0fbaa33 100644 --- a/libgo/go/exp/types/universe.go +++ b/libgo/go/exp/types/universe.go @@ -116,10 +116,12 @@ func init() { // error type { + res := ast.NewObj(ast.Var, "") + res.Type = Typ[String] + err := ast.NewObj(ast.Fun, "Error") + err.Type = &Signature{Results: ObjList{res}} obj := def(ast.Typ, "error") - // TODO(gri) set up correct interface type - typ := &NamedType{Underlying: &Interface{}, Obj: obj} - obj.Type = typ + obj.Type = &NamedType{Underlying: &Interface{Methods: ObjList{err}}, Obj: obj} } // predeclared constants diff --git a/libgo/go/exp/winfsnotify/winfsnotify_test.go b/libgo/go/exp/winfsnotify/winfsnotify_test.go index 4a1929a..a0bd432 100644 --- a/libgo/go/exp/winfsnotify/winfsnotify_test.go +++ b/libgo/go/exp/winfsnotify/winfsnotify_test.go @@ -9,6 +9,7 @@ package winfsnotify import ( "io/ioutil" "os" + "sync/atomic" "testing" "time" ) @@ -105,14 +106,14 @@ func TestNotifyClose(t *testing.T) { watcher, _ := NewWatcher() watcher.Close() - done := false + var done int32 go func() { watcher.Close() - done = true + atomic.StoreInt32(&done, 1) }() time.Sleep(50 * time.Millisecond) - if !done { + if atomic.LoadInt32(&done) == 0 { t.Fatal("double Close() test failed: second Close() call didn't return") } diff --git a/libgo/go/fmt/fmt_test.go b/libgo/go/fmt/fmt_test.go index f704b7d03..3406113 100644 --- a/libgo/go/fmt/fmt_test.go +++ b/libgo/go/fmt/fmt_test.go @@ -583,6 +583,7 @@ var mallocTest = []struct { var _ bytes.Buffer func TestCountMallocs(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) for _, mt := range mallocTest { const N = 100 memstats := new(runtime.MemStats) diff --git a/libgo/go/go/ast/import.go b/libgo/go/go/ast/import.go index 2d4f69a..a68a484 100644 --- a/libgo/go/go/ast/import.go +++ b/libgo/go/go/ast/import.go @@ -20,7 +20,7 @@ func SortImports(fset *token.FileSet, f *File) { break } - if d.Lparen == token.NoPos { + if !d.Lparen.IsValid() { // Not a block: sorted by default. continue } diff --git a/libgo/go/go/format/format.go b/libgo/go/go/format/format.go new file mode 100644 index 0000000..286296e --- /dev/null +++ b/libgo/go/go/format/format.go @@ -0,0 +1,200 @@ +// Copyright 2012 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 format implements standard formatting of Go source. +package format + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "io" + "strings" +) + +var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} + +// Node formats node in canonical gofmt style and writes the result to dst. +// +// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, +// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, +// or ast.Stmt. Node does not modify node. Imports are not sorted for +// nodes representing partial source files (i.e., if the node is not an +// *ast.File or a *printer.CommentedNode not wrapping an *ast.File). +// +// The function may return early (before the entire result is written) +// and return a formatting error, for instance due to an incorrect AST. +// +func Node(dst io.Writer, fset *token.FileSet, node interface{}) error { + // Determine if we have a complete source file (file != nil). + var file *ast.File + var cnode *printer.CommentedNode + switch n := node.(type) { + case *ast.File: + file = n + case *printer.CommentedNode: + if f, ok := n.Node.(*ast.File); ok { + file = f + cnode = n + } + } + + // Sort imports if necessary. + if file != nil && hasUnsortedImports(file) { + // Make a copy of the AST because ast.SortImports is destructive. + // TODO(gri) Do this more efficently. + var buf bytes.Buffer + err := config.Fprint(&buf, fset, file) + if err != nil { + return err + } + file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments) + if err != nil { + // We should never get here. If we do, provide good diagnostic. + return fmt.Errorf("format.Node internal error (%s)", err) + } + ast.SortImports(fset, file) + + // Use new file with sorted imports. + node = file + if cnode != nil { + node = &printer.CommentedNode{Node: file, Comments: cnode.Comments} + } + } + + return config.Fprint(dst, fset, node) +} + +// Source formats src in canonical gofmt style and writes the result to dst +// or returns an I/O or syntax error. src is expected to be a syntactically +// correct Go source file, or a list of Go declarations or statements. +// +// If src is a partial source file, the leading and trailing space of src +// is applied to the result (such that it has the same leading and trailing +// space as src), and the formatted src is indented by the same amount as +// the first line of src containing code. Imports are not sorted for partial +// source files. +// +func Source(src []byte) ([]byte, error) { + fset := token.NewFileSet() + node, err := parse(fset, src) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + if file, ok := node.(*ast.File); ok { + // Complete source file. + ast.SortImports(fset, file) + err := config.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + + } else { + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // index of last line in leading space + } + j++ + } + buf.Write(src[:i]) + + // Determine indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ + } + } + if indent == 0 && hasSpace { + indent = 1 + } + + // Format the source. + cfg := config + cfg.Indent = indent + err := cfg.Fprint(&buf, fset, node) + if err != nil { + return nil, err + } + + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- + } + buf.Write(src[i:]) + } + + return buf.Bytes(), nil +} + +func hasUnsortedImports(file *ast.File) bool { + for _, d := range file.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.IMPORT { + // Not an import declaration, so we're done. + // Imports are always first. + return false + } + if d.Lparen.IsValid() { + // For now assume all grouped imports are unsorted. + // TODO(gri) Should check if they are sorted already. + return true + } + // Ungrouped imports are sorted by default. + } + return false +} + +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' +} + +func parse(fset *token.FileSet, src []byte) (interface{}, error) { + // Try as a complete source file. + file, err := parser.ParseFile(fset, "", src, parser.ParseComments) + if err == nil { + return file, nil + } + // If the source is missing a package clause, try as a source fragment; otherwise fail. + if !strings.Contains(err.Error(), "expected 'package'") { + return nil, err + } + + // Try as a declaration list by prepending a package clause in front of src. + // Use ';' not '\n' to keep line numbers intact. + psrc := append([]byte("package p;"), src...) + file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments) + if err == nil { + return file.Decls, nil + } + // If the source is missing a declaration, try as a statement list; otherwise fail. + if !strings.Contains(err.Error(), "expected declaration") { + return nil, err + } + + // Try as statement list by wrapping a function around src. + fsrc := append(append([]byte("package p; func _() {"), src...), '}') + file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments) + if err == nil { + return file.Decls[0].(*ast.FuncDecl).Body.List, nil + } + + // Failed, and out of options. + return nil, err +} diff --git a/libgo/go/go/format/format_test.go b/libgo/go/go/format/format_test.go new file mode 100644 index 0000000..7d7940b --- /dev/null +++ b/libgo/go/go/format/format_test.go @@ -0,0 +1,125 @@ +// Copyright 2012 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 format + +import ( + "bytes" + "go/parser" + "go/token" + "io/ioutil" + "strings" + "testing" +) + +const testfile = "format_test.go" + +func diff(t *testing.T, dst, src []byte) { + line := 1 + offs := 0 // line offset + for i := 0; i < len(dst) && i < len(src); i++ { + d := dst[i] + s := src[i] + if d != s { + t.Errorf("dst:%d: %s\n", line, dst[offs:i+1]) + t.Errorf("src:%d: %s\n", line, src[offs:i+1]) + return + } + if s == '\n' { + line++ + offs = i + 1 + } + } + if len(dst) != len(src) { + t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src) + } +} + +func TestNode(t *testing.T) { + src, err := ioutil.ReadFile(testfile) + if err != nil { + t.Fatal(err) + } + + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + + var buf bytes.Buffer + + if err = Node(&buf, fset, file); err != nil { + t.Fatal("Node failed:", err) + } + + diff(t, buf.Bytes(), src) +} + +func TestSource(t *testing.T) { + src, err := ioutil.ReadFile(testfile) + if err != nil { + t.Fatal(err) + } + + res, err := Source(src) + if err != nil { + t.Fatal("Source failed:", err) + } + + diff(t, res, src) +} + +// Test cases that are expected to fail are marked by the prefix "ERROR". +var tests = []string{ + // declaration lists + `import "go/format"`, + "var x int", + "var x int\n\ntype T struct{}", + + // statement lists + "x := 0", + "f(a, b, c)\nvar x int = f(1, 2, 3)", + + // indentation, leading and trailing space + "\tx := 0\n\tgo f()", + "\tx := 0\n\tgo f()\n\n\n", + "\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n", + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n", + "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings + + // erroneous programs + "ERRORvar x", + "ERROR1 + 2 +", + "ERRORx := 0", +} + +func String(s string) (string, error) { + res, err := Source([]byte(s)) + if err != nil { + return "", err + } + return string(res), nil +} + +func TestPartial(t *testing.T) { + for _, src := range tests { + if strings.HasPrefix(src, "ERROR") { + // test expected to fail + src = src[5:] // remove ERROR prefix + res, err := String(src) + if err == nil && res == src { + t.Errorf("formatting succeeded but was expected to fail:\n%q", src) + } + } else { + // test expected to succeed + res, err := String(src) + if err != nil { + t.Errorf("formatting failed (%s):\n%q", err, src) + } else if res != src { + t.Errorf("formatting incorrect:\nsource: %q\nresult: %q", src, res) + } + } + } +} diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go index 26b31b2..00757e0 100644 --- a/libgo/go/go/parser/parser.go +++ b/libgo/go/go/parser/parser.go @@ -578,14 +578,15 @@ func (p *parser) parseTypeName() ast.Expr { return ident } -func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr { +func (p *parser) parseArrayType() ast.Expr { if p.trace { defer un(trace(p, "ArrayType")) } lbrack := p.expect(token.LBRACK) var len ast.Expr - if ellipsisOk && p.tok == token.ELLIPSIS { + // always permit ellipsis for more fault-tolerant parsing + if p.tok == token.ELLIPSIS { len = &ast.Ellipsis{Ellipsis: p.pos} p.next() } else if p.tok != token.RBRACK { @@ -697,7 +698,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr { if isParam && p.tok == token.ELLIPSIS { pos := p.pos p.next() - typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message + typ := p.tryIdentOrType() // don't use parseType so we can provide better error message if typ != nil { p.resolve(typ) } else { @@ -706,7 +707,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr { } return &ast.Ellipsis{Ellipsis: pos, Elt: typ} } - return p.tryIdentOrType(false) + return p.tryIdentOrType() } // If the result is an identifier, it is not resolved. @@ -943,12 +944,12 @@ func (p *parser) parseChanType() *ast.ChanType { } // If the result is an identifier, it is not resolved. -func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr { +func (p *parser) tryIdentOrType() ast.Expr { switch p.tok { case token.IDENT: return p.parseTypeName() case token.LBRACK: - return p.parseArrayType(ellipsisOk) + return p.parseArrayType() case token.STRUCT: return p.parseStructType() case token.MUL: @@ -975,7 +976,7 @@ func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr { } func (p *parser) tryType() ast.Expr { - typ := p.tryIdentOrType(false) + typ := p.tryIdentOrType() if typ != nil { p.resolve(typ) } @@ -1083,7 +1084,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { return p.parseFuncTypeOrLit() } - if typ := p.tryIdentOrType(true); typ != nil { + if typ := p.tryIdentOrType(); typ != nil { // could be type for composite literal or conversion _, isIdent := typ.(*ast.Ident) assert(!isIdent, "type cannot be identifier") @@ -1802,7 +1803,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt { // // switch t := 0; t := x.(T) { ... } // - // (this code is not valid Go because the first t will + // (this code is not valid Go because the first t // cannot be accessed and thus is never used, the extra // scope is needed for the correct error message). // diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index e99a2e3..cd5b67b 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -730,7 +730,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { case *ast.FuncLit: p.expr(x.Type) - p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true) + p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body) case *ast.ParenExpr: if _, hasParens := x.X.(*ast.ParenExpr); hasParens { @@ -900,7 +900,11 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) { if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty { // _indent == 0 only for lists of switch/select case clauses; // in those cases each clause is a new section - p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine) + if len(p.output) > 0 { + // only print line break if we are not at the beginning of the output + // (i.e., we are not printing only a partial program) + p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine) + } p.stmt(s, nextIsRBrace && i == len(list)-1) multiLine = p.isMultiLine(s) i++ @@ -912,11 +916,11 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) { } // block prints an *ast.BlockStmt; it always spans at least two lines. -func (p *printer) block(s *ast.BlockStmt, nindent int) { - p.print(s.Pos(), token.LBRACE) - p.stmtList(s.List, nindent, true) - p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true) - p.print(s.Rbrace, token.RBRACE) +func (p *printer) block(b *ast.BlockStmt, nindent int) { + p.print(b.Lbrace, token.LBRACE) + p.stmtList(b.List, nindent, true) + p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true) + p.print(b.Rbrace, token.RBRACE) } func isTypeName(x ast.Expr) bool { @@ -1421,19 +1425,19 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { return } -func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { +// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's. +func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int { pos1 := b.Pos() pos2 := b.Rbrace if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) { // opening and closing brace are on different lines - don't make it a one-liner - return false + return maxSize + 1 } if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) { // too many statements or there is a comment inside - don't make it a one-liner - return false + return maxSize + 1 } // otherwise, estimate body size - const maxSize = 100 bodySize := 0 for i, s := range b.List { if i > 0 { @@ -1441,19 +1445,23 @@ func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool { } bodySize += p.nodeSize(s, maxSize) } - return headerSize+bodySize <= maxSize + return bodySize } -func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) { +// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following +// a header (e.g., a for-loop control clause or function signature) of given headerSize. +// If the header's and block's size are "small enough" and the block is "simple enough", +// the block is printed on the current line, without line breaks, spaced from the header +// by sep. Otherwise the block's opening "{" is printed on the current line, followed by +// lines for the block's statements and its closing "}". +// +func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) { if b == nil { return } - if p.isOneLineFunc(b, headerSize) { - sep := vtab - if isLit { - sep = blank - } + const maxSize = 100 + if headerSize+p.bodySize(b, maxSize) <= maxSize { p.print(sep, b.Lbrace, token.LBRACE) if len(b.List) > 0 { p.print(blank) @@ -1469,17 +1477,20 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) { return } - p.print(blank) + if sep != ignore { + p.print(blank) // always use blank + } p.block(b, 1) } -// distance returns the column difference between from and to if both -// are on the same line; if they are on different lines (or unknown) -// the result is infinity. -func (p *printer) distance(from0 token.Pos, to token.Position) int { - from := p.posFor(from0) - if from.IsValid() && to.IsValid() && from.Line == to.Line { - return to.Column - from.Column +// distanceFrom returns the column difference between from and p.pos (the current +// estimated position) if both are on the same line; if they are on different lines +// (or unknown) the result is infinity. +func (p *printer) distanceFrom(from token.Pos) int { + if from.IsValid() && p.pos.IsValid() { + if f := p.posFor(from); f.Line == p.pos.Line { + return p.pos.Column - f.Column + } } return infinity } @@ -1493,7 +1504,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) { } p.expr(d.Name) p.signature(d.Type.Params, d.Type.Results) - p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false) + p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body) } func (p *printer) decl(decl ast.Decl) { @@ -1523,31 +1534,35 @@ func declToken(decl ast.Decl) (tok token.Token) { return } -func (p *printer) file(src *ast.File) { - p.setComment(src.Doc) - p.print(src.Pos(), token.PACKAGE, blank) - p.expr(src.Name) - - if len(src.Decls) > 0 { - tok := token.ILLEGAL - for _, d := range src.Decls { - prev := tok - tok = declToken(d) - // if the declaration token changed (e.g., from CONST to TYPE) - // or the next declaration has documentation associated with it, - // print an empty line between top-level declarations - // (because p.linebreak is called with the position of d, which - // is past any documentation, the minimum requirement is satisfied - // even w/o the extra getDoc(d) nil-check - leave it in case the - // linebreak logic improves - there's already a TODO). +func (p *printer) declList(list []ast.Decl) { + tok := token.ILLEGAL + for _, d := range list { + prev := tok + tok = declToken(d) + // If the declaration token changed (e.g., from CONST to TYPE) + // or the next declaration has documentation associated with it, + // print an empty line between top-level declarations. + // (because p.linebreak is called with the position of d, which + // is past any documentation, the minimum requirement is satisfied + // even w/o the extra getDoc(d) nil-check - leave it in case the + // linebreak logic improves - there's already a TODO). + if len(p.output) > 0 { + // only print line break if we are not at the beginning of the output + // (i.e., we are not printing only a partial program) min := 1 if prev != tok || getDoc(d) != nil { min = 2 } p.linebreak(p.lineFor(d.Pos()), min, ignore, false) - p.decl(d) } + p.decl(d) } +} +func (p *printer) file(src *ast.File) { + p.setComment(src.Doc) + p.print(src.Pos(), token.PACKAGE, blank) + p.expr(src.Name) + p.declList(src.Decls) p.print(newline) } diff --git a/libgo/go/go/printer/performance_test.go b/libgo/go/go/printer/performance_test.go index 31f5ef0..5b29aff 100644 --- a/libgo/go/go/printer/performance_test.go +++ b/libgo/go/go/printer/performance_test.go @@ -20,7 +20,7 @@ import ( var testfile *ast.File func testprint(out io.Writer, file *ast.File) { - if err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil { + if err := (&Config{TabIndent | UseSpaces, 8, 0}).Fprint(out, fset, file); err != nil { log.Fatalf("print error: %s", err) } } diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index e79e3ff..5d75f09 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -165,15 +165,15 @@ func (p *printer) atLineBegin(pos token.Position) { // write indentation // use "hard" htabs - indentation columns // must not be discarded by the tabwriter - for i := 0; i < p.indent; i++ { + n := p.Config.Indent + p.indent // include base indentation + for i := 0; i < n; i++ { p.output = append(p.output, '\t') } // update positions - i := p.indent - p.pos.Offset += i - p.pos.Column += i - p.out.Column += i + p.pos.Offset += n + p.pos.Column += n + p.out.Column += n } // writeByte writes ch n times to p.output and updates p.pos. @@ -452,7 +452,7 @@ func trimRight(s string) string { // stripCommonPrefix removes a common prefix from /*-style comment lines (unless no // comment line is indented, all but the first line have some form of space prefix). -// The prefix is computed using heuristics such that is is likely that the comment +// The prefix is computed using heuristics such that is likely that the comment // contents are nicely laid out after re-printing each line using the printer's // current indentation. // @@ -1032,9 +1032,9 @@ func (p *printer) printNode(node interface{}) error { case ast.Expr: p.expr(n) case ast.Stmt: - // A labeled statement will un-indent to position the - // label. Set indent to 1 so we don't get indent "underflow". - if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt { + // A labeled statement will un-indent to position the label. + // Set p.indent to 1 so we don't get indent "underflow". + if _, ok := n.(*ast.LabeledStmt); ok { p.indent = 1 } p.stmt(n, false) @@ -1042,6 +1042,17 @@ func (p *printer) printNode(node interface{}) error { p.decl(n) case ast.Spec: p.spec(n, 1, false) + case []ast.Stmt: + // A labeled statement will un-indent to position the label. + // Set p.indent to 1 so we don't get indent "underflow". + for _, s := range n { + if _, ok := s.(*ast.LabeledStmt); ok { + p.indent = 1 + } + } + p.stmtList(n, 0, false) + case []ast.Decl: + p.declList(n) case *ast.File: p.file(n) default: @@ -1174,6 +1185,7 @@ const ( type Config struct { Mode Mode // default: 0 Tabwidth int // default: 8 + Indent int // default: 0 (all code is indented at least by this much) } // fprint implements Fprint and takes a nodesSizes map for setting up the printer state. @@ -1235,8 +1247,8 @@ type CommentedNode struct { // Fprint "pretty-prints" an AST node to output for a given configuration cfg. // Position information is interpreted relative to the file set fset. -// The node type must be *ast.File, *CommentedNode, or assignment-compatible -// to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. +// The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt, +// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. // func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error { return cfg.fprint(output, fset, node, make(map[ast.Node]int)) diff --git a/libgo/go/go/printer/printer_test.go b/libgo/go/go/printer/printer_test.go index 36d1bf7..8454ac1 100644 --- a/libgo/go/go/printer/printer_test.go +++ b/libgo/go/go/printer/printer_test.go @@ -434,6 +434,98 @@ func (t *t) foo(a, b, c int) int { } } +var decls = []string{ + `import "fmt"`, + "const pi = 3.1415\nconst e = 2.71828\n\nvar x = pi", + "func sum(x, y int) int\t{ return x + y }", +} + +func TestDeclLists(t *testing.T) { + for _, src := range decls { + file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments) + if err != nil { + panic(err) // error in test + } + + var buf bytes.Buffer + err = Fprint(&buf, fset, file.Decls) // only print declarations + if err != nil { + panic(err) // error in test + } + + out := buf.String() + if out != src { + t.Errorf("\ngot : %q\nwant: %q\n", out, src) + } + } +} + +var stmts = []string{ + "i := 0", + "select {}\nvar a, b = 1, 2\nreturn a + b", + "go f()\ndefer func() {}()", +} + +func TestStmtLists(t *testing.T) { + for _, src := range stmts { + file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments) + if err != nil { + panic(err) // error in test + } + + var buf bytes.Buffer + err = Fprint(&buf, fset, file.Decls[0].(*ast.FuncDecl).Body.List) // only print statements + if err != nil { + panic(err) // error in test + } + + out := buf.String() + if out != src { + t.Errorf("\ngot : %q\nwant: %q\n", out, src) + } + } +} + +func TestBaseIndent(t *testing.T) { + // The testfile must not contain multi-line raw strings since those + // are not indented (because their values must not change) and make + // this test fail. + const filename = "printer.go" + src, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) // error in test + } + + file, err := parser.ParseFile(fset, filename, src, 0) + if err != nil { + panic(err) // error in test + } + + var buf bytes.Buffer + for indent := 0; indent < 4; indent++ { + buf.Reset() + (&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file) + // all code must be indented by at least 'indent' tabs + lines := bytes.Split(buf.Bytes(), []byte{'\n'}) + for i, line := range lines { + if len(line) == 0 { + continue // empty lines don't have indentation + } + n := 0 + for j, b := range line { + if b != '\t' { + // end of indentation + n = j + break + } + } + if n < indent { + t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line) + } + } + } +} + // TestFuncType tests that an ast.FuncType with a nil Params field // can be printed (per go/ast specification). Test case for issue 3870. func TestFuncType(t *testing.T) { diff --git a/libgo/go/go/printer/testdata/statements.golden b/libgo/go/go/printer/testdata/statements.golden index 1f3cabe..3b298f9 100644 --- a/libgo/go/go/printer/testdata/statements.golden +++ b/libgo/go/go/printer/testdata/statements.golden @@ -241,7 +241,7 @@ func _() { } } -// Formatting of for-statement headers. +// Formatting of for-statement headers for single-line for-loops. func _() { for { } @@ -279,6 +279,86 @@ func _() { } // no parens printed } +// Formatting of for-statement headers for multi-line for-loops. +func _() { + for { + } + for expr { + } + for expr { + } // no parens printed + for { + } // no semicolons printed + for x := expr; ; { + use(x) + } + for expr { + } // no semicolons printed + for expr { + } // no semicolons and parens printed + for ; ; expr = false { + } + for x := expr; expr; { + use(x) + } + for x := expr; ; expr = false { + use(x) + } + for ; expr; expr = false { + } + for x := expr; expr; expr = false { + use(x) + } + for x := range []int{} { + use(x) + } + for x := range []int{} { + use(x) + } // no parens printed +} + +// Formatting of selected short single- and multi-line statements. +func _() { + if cond { + } + if cond { + } // multiple lines + if cond { + } else { + } // else clause always requires multiple lines + + for { + } + for i := 0; i < len(a); 1++ { + } + for i := 0; i < len(a); 1++ { + a[i] = i + } + for i := 0; i < len(a); 1++ { + a[i] = i + } // multiple lines + + for i := range a { + } + for i := range a { + a[i] = i + } + for i := range a { + a[i] = i + } // multiple lines + + go func() { + for { + a <- <-b + } + }() + defer func() { + if x := recover(); x != nil { + err = fmt.Sprintf("error: %s", x.msg) + } + }() +} + // Don't remove mandatory parentheses around composite literals in control clauses. func _() { // strip parentheses - no composite literals or composite literals don't start with a type name diff --git a/libgo/go/go/printer/testdata/statements.input b/libgo/go/go/printer/testdata/statements.input index f93eea8..e7fcc0e 100644 --- a/libgo/go/go/printer/testdata/statements.input +++ b/libgo/go/go/printer/testdata/statements.input @@ -223,7 +223,7 @@ func _() { } -// Formatting of for-statement headers. +// Formatting of for-statement headers for single-line for-loops. func _() { for{} for expr {} @@ -235,14 +235,70 @@ func _() { for; ; expr = false {} for x :=expr; expr; {use(x)} for x := expr;; expr=false {use(x)} - for;expr;expr =false { - } + for;expr;expr =false {} for x := expr;expr;expr = false { use(x) } for x := range []int{} { use(x) } for x := range (([]int{})) { use(x) } // no parens printed } +// Formatting of for-statement headers for multi-line for-loops. +func _() { + for{ + } + for expr { + } + for (expr) { + } // no parens printed + for;;{ + } // no semicolons printed + for x :=expr;; {use( x) + } + for; expr;{ + } // no semicolons printed + for; ((expr));{ + } // no semicolons and parens printed + for; ; expr = false { + } + for x :=expr; expr; {use(x) + } + for x := expr;; expr=false {use(x) + } + for;expr;expr =false { + } + for x := expr;expr;expr = false { + use(x) + } + for x := range []int{} { + use(x) } + for x := range (([]int{})) { + use(x) } // no parens printed +} + + +// Formatting of selected short single- and multi-line statements. +func _() { + if cond {} + if cond { + } // multiple lines + if cond {} else {} // else clause always requires multiple lines + + for {} + for i := 0; i < len(a); 1++ {} + for i := 0; i < len(a); 1++ { a[i] = i } + for i := 0; i < len(a); 1++ { a[i] = i + } // multiple lines + + for i := range a {} + for i := range a { a[i] = i } + for i := range a { a[i] = i + } // multiple lines + + go func() { for { a <- <-b } }() + defer func() { if x := recover(); x != nil { err = fmt.Sprintf("error: %s", x.msg) } }() +} + + // Don't remove mandatory parentheses around composite literals in control clauses. func _() { // strip parentheses - no composite literals or composite literals don't start with a type name diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go index a895a50..a974093 100644 --- a/libgo/go/html/template/js.go +++ b/libgo/go/html/template/js.go @@ -14,7 +14,7 @@ import ( ) // nextJSCtx returns the context that determines whether a slash after the -// given run of tokens tokens starts a regular expression instead of a division +// given run of tokens starts a regular expression instead of a division // operator: / or /=. // // This assumes that the token run does not include any string tokens, comment diff --git a/libgo/go/image/jpeg/huffman.go b/libgo/go/image/jpeg/huffman.go index 2fc64ad..9b731fd 100644 --- a/libgo/go/image/jpeg/huffman.go +++ b/libgo/go/image/jpeg/huffman.go @@ -163,7 +163,7 @@ func (d *decoder) processDHT(n int) error { // Returns the next Huffman-coded value from the bit stream, decoded according to h. // TODO(nigeltao): This decoding algorithm is simple, but slow. A lookahead table, instead of always -// peeling off only 1 bit at at time, ought to be faster. +// peeling off only 1 bit at time, ought to be faster. func (d *decoder) decodeHuffman(h *huffman) (uint8, error) { if h.length == 0 { return 0, FormatError("uninitialized Huffman table") diff --git a/libgo/go/log/syslog/syslog.go b/libgo/go/log/syslog/syslog.go index e5620e1..c4ad12f 100644 --- a/libgo/go/log/syslog/syslog.go +++ b/libgo/go/log/syslog/syslog.go @@ -4,9 +4,9 @@ // +build !windows,!plan9 -// Package syslog provides a simple interface to the system log service. It -// can send messages to the syslog daemon using UNIX domain sockets, UDP, or -// TCP connections. +// Package syslog provides a simple interface to the system log +// service. It can send messages to the syslog daemon using UNIX +// domain sockets, UDP, or TCP connections. package syslog import ( @@ -15,11 +15,21 @@ import ( "log" "net" "os" + "time" ) +// The Priority is a combination of the syslog facility and +// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity +// message from the FTP facility. The default severity is LOG_EMERG; +// the default facility is LOG_KERN. type Priority int +const severityMask = 0x07 +const facilityMask = 0xf8 + const ( + // Severity. + // From /usr/include/sys/syslog.h. // These are the same on Linux, BSD, and OS X. LOG_EMERG Priority = iota @@ -32,16 +42,47 @@ const ( LOG_DEBUG ) +const ( + // Facility. + + // From /usr/include/sys/syslog.h. + // These are the same up to LOG_FTP on Linux, BSD, and OS X. + LOG_KERN Priority = iota << 3 + LOG_USER + LOG_MAIL + LOG_DAEMON + LOG_AUTH + LOG_SYSLOG + LOG_LPR + LOG_NEWS + LOG_UUCP + LOG_CRON + LOG_AUTHPRIV + LOG_FTP + _ // unused + _ // unused + _ // unused + _ // unused + LOG_LOCAL0 + LOG_LOCAL1 + LOG_LOCAL2 + LOG_LOCAL3 + LOG_LOCAL4 + LOG_LOCAL5 + LOG_LOCAL6 + LOG_LOCAL7 +) + // A Writer is a connection to a syslog server. type Writer struct { priority Priority - prefix string + tag string + hostname string conn serverConn } type serverConn interface { - writeBytes(p Priority, prefix string, b []byte) (int, error) - writeString(p Priority, prefix string, s string) (int, error) + writeString(p Priority, hostname, tag, s string) (int, error) close() error } @@ -49,116 +90,130 @@ type netConn struct { conn net.Conn } -// New establishes a new connection to the system log daemon. -// Each write to the returned writer sends a log message with -// the given priority and prefix. -func New(priority Priority, prefix string) (w *Writer, err error) { - return Dial("", "", priority, prefix) +// New establishes a new connection to the system log daemon. Each +// write to the returned writer sends a log message with the given +// priority and prefix. +func New(priority Priority, tag string) (w *Writer, err error) { + return Dial("", "", priority, tag) } -// Dial establishes a connection to a log daemon by connecting -// to address raddr on the network net. -// Each write to the returned writer sends a log message with -// the given priority and prefix. -func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, err error) { - if prefix == "" { - prefix = os.Args[0] +// Dial establishes a connection to a log daemon by connecting to +// address raddr on the network net. Each write to the returned +// writer sends a log message with the given facility, severity and +// tag. +func Dial(network, raddr string, priority Priority, tag string) (w *Writer, err error) { + if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG { + return nil, errors.New("log/syslog: invalid priority") } + + if tag == "" { + tag = os.Args[0] + } + + hostname, _ := os.Hostname() + var conn serverConn if network == "" { conn, err = unixSyslog() + if hostname == "" { + hostname = "localhost" + } } else { var c net.Conn c, err = net.Dial(network, raddr) conn = netConn{c} + if hostname == "" { + hostname = c.LocalAddr().String() + } + } + if err != nil { + return nil, err } - return &Writer{priority, prefix, conn}, err + + return &Writer{priority: priority, tag: tag, hostname: hostname, conn: conn}, nil } // 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, errors.New("log/syslog: invalid priority") - } - return w.conn.writeBytes(w.priority, w.prefix, b) -} - -func (w *Writer) writeString(p Priority, s string) (int, error) { - return w.conn.writeString(p, w.prefix, s) + return w.writeString(w.priority, string(b)) } func (w *Writer) Close() error { return w.conn.close() } -// Emerg logs a message using the LOG_EMERG priority. +// Emerg logs a message with severity LOG_EMERG, ignoring the severity +// passed to New. func (w *Writer) Emerg(m string) (err error) { _, err = w.writeString(LOG_EMERG, m) return err } -// Alert logs a message using the LOG_ALERT priority. +// Alert logs a message with severity LOG_ALERT, ignoring the severity +// passed to New. func (w *Writer) Alert(m string) (err error) { _, err = w.writeString(LOG_ALERT, m) return err } -// Crit logs a message using the LOG_CRIT priority. +// Crit logs a message with severity LOG_CRIT, ignoring the severity +// passed to New. func (w *Writer) Crit(m string) (err error) { _, err = w.writeString(LOG_CRIT, m) return err } -// Err logs a message using the LOG_ERR priority. +// Err logs a message with severity LOG_ERR, ignoring the severity +// passed to New. func (w *Writer) Err(m string) (err error) { _, err = w.writeString(LOG_ERR, m) return err } -// Warning logs a message using the LOG_WARNING priority. +// Wanring logs a message with severity LOG_WARNING, ignoring the +// severity passed to New. func (w *Writer) Warning(m string) (err error) { _, err = w.writeString(LOG_WARNING, m) return err } -// Notice logs a message using the LOG_NOTICE priority. +// Notice logs a message with severity LOG_NOTICE, ignoring the +// severity passed to New. func (w *Writer) Notice(m string) (err error) { _, err = w.writeString(LOG_NOTICE, m) return err } -// Info logs a message using the LOG_INFO priority. +// Info logs a message with severity LOG_INFO, ignoring the severity +// passed to New. func (w *Writer) Info(m string) (err error) { _, err = w.writeString(LOG_INFO, m) return err } -// Debug logs a message using the LOG_DEBUG priority. +// Debug logs a message with severity LOG_DEBUG, ignoring the severity +// passed to New. func (w *Writer) Debug(m string) (err error) { _, err = w.writeString(LOG_DEBUG, m) return err } -func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) { - nl := "" - if len(b) == 0 || b[len(b)-1] != '\n' { - nl = "\n" - } - _, err := fmt.Fprintf(n.conn, "<%d>%s: %s%s", p, prefix, b, nl) - if err != nil { - return 0, err - } - return len(b), nil +func (w *Writer) writeString(p Priority, s string) (int, error) { + return w.conn.writeString((w.priority&facilityMask)|(p&severityMask), + w.hostname, w.tag, s) } -func (n netConn) writeString(p Priority, prefix string, s string) (int, error) { +// writeString: generates and writes a syslog formatted string. The +// format is as follows: <PRI>1 TIMESTAMP HOSTNAME TAG[PID]: MSG +func (n netConn) writeString(p Priority, hostname, tag, msg string) (int, error) { nl := "" - if len(s) == 0 || s[len(s)-1] != '\n' { + if len(msg) == 0 || msg[len(msg)-1] != '\n' { nl = "\n" } - _, err := fmt.Fprintf(n.conn, "<%d>%s: %s%s", p, prefix, s, nl) - if err != nil { + timestamp := time.Now().Format(time.RFC3339) + if _, err := fmt.Fprintf(n.conn, "<%d>1 %s %s %s[%d]: %s%s", p, timestamp, hostname, + tag, os.Getpid(), msg, nl); err != nil { return 0, err } - return len(s), nil + return len(msg), nil } func (n netConn) close() error { diff --git a/libgo/go/log/syslog/syslog_test.go b/libgo/go/log/syslog/syslog_test.go index b7579c3..4c0bf1f 100644 --- a/libgo/go/log/syslog/syslog_test.go +++ b/libgo/go/log/syslog/syslog_test.go @@ -7,9 +7,11 @@ package syslog import ( + "fmt" "io" "log" "net" + "os" "testing" "time" ) @@ -49,10 +51,14 @@ func skipNetTest(t *testing.T) bool { } func TestNew(t *testing.T) { + if LOG_LOCAL7 != 23<<3 { + t.Fatalf("LOG_LOCAL7 has wrong value") + } if skipNetTest(t) { return } - s, err := New(LOG_INFO, "") + + s, err := New(LOG_INFO|LOG_USER, "") if err != nil { t.Fatalf("New() failed: %s", err) } @@ -64,7 +70,7 @@ func TestNewLogger(t *testing.T) { if skipNetTest(t) { return } - f, err := NewLogger(LOG_INFO, 0) + f, err := NewLogger(LOG_USER|LOG_INFO, 0) if f == nil { t.Error(err) } @@ -74,7 +80,15 @@ func TestDial(t *testing.T) { if skipNetTest(t) { return } - l, err := Dial("", "", LOG_ERR, "syslog_test") + f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test") + if f != nil { + t.Fatalf("Should have trapped bad priority") + } + f, err = Dial("", "", -1, "syslog_test") + if f != nil { + t.Fatalf("Should have trapped bad priority") + } + l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test") if err != nil { t.Fatalf("Dial() failed: %s", err) } @@ -84,16 +98,23 @@ func TestDial(t *testing.T) { func TestUDPDial(t *testing.T) { done := make(chan string) startServer(done) - l, err := Dial("udp", serverAddr, LOG_INFO, "syslog_test") + l, err := Dial("udp", serverAddr, LOG_USER|LOG_INFO, "syslog_test") if err != nil { t.Fatalf("syslog.Dial() failed: %s", err) } msg := "udp test" l.Info(msg) - expected := "<6>syslog_test: udp test\n" + expected := fmt.Sprintf("<%d>1 ", LOG_USER+LOG_INFO) + "%s %s syslog_test[%d]: udp test\n" rcvd := <-done - if rcvd != expected { - t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected) + var parsedHostname, timestamp string + var pid int + if hostname, err := os.Hostname(); err != nil { + t.Fatalf("Error retrieving hostname") + } else { + if n, err := fmt.Sscanf(rcvd, expected, ×tamp, &parsedHostname, &pid); n != 3 || + err != nil || hostname != parsedHostname { + t.Fatalf("s.Info() = '%q', didn't match '%q'", rcvd, expected) + } } } @@ -104,26 +125,34 @@ func TestWrite(t *testing.T) { msg string exp string }{ - {LOG_ERR, "syslog_test", "", "<3>syslog_test: \n"}, - {LOG_ERR, "syslog_test", "write test", "<3>syslog_test: write test\n"}, + {LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"}, + {LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"}, // Write should not add \n if there already is one - {LOG_ERR, "syslog_test", "write test 2\n", "<3>syslog_test: write test 2\n"}, + {LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"}, } - for _, test := range tests { - done := make(chan string) - startServer(done) - l, err := Dial("udp", serverAddr, test.pri, test.pre) - if err != nil { - t.Fatalf("syslog.Dial() failed: %s", err) - } - _, err = io.WriteString(l, test.msg) - if err != nil { - t.Fatalf("WriteString() failed: %s", err) - } - rcvd := <-done - if rcvd != test.exp { - t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, test.exp) + if hostname, err := os.Hostname(); err != nil { + t.Fatalf("Error retrieving hostname") + } else { + for _, test := range tests { + done := make(chan string) + startServer(done) + l, err := Dial("udp", serverAddr, test.pri, test.pre) + if err != nil { + t.Fatalf("syslog.Dial() failed: %s", err) + } + _, err = io.WriteString(l, test.msg) + if err != nil { + t.Fatalf("WriteString() failed: %s", err) + } + rcvd := <-done + test.exp = fmt.Sprintf("<%d>1 ", test.pri) + test.exp + var parsedHostname, timestamp string + var pid int + if n, err := fmt.Sscanf(rcvd, test.exp, ×tamp, &parsedHostname, &pid); n != 3 || + err != nil || hostname != parsedHostname { + t.Fatalf("s.Info() = '%q', didn't match '%q'", rcvd, test.exp) + } } } } diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go index 6244eee..2dd7bf6 100644 --- a/libgo/go/math/big/nat_test.go +++ b/libgo/go/math/big/nat_test.go @@ -180,6 +180,7 @@ func allocBytes(f func()) uint64 { // does not cause deep recursion and in turn allocate too much memory. // Test case for issue 3807. func TestMulUnbalanced(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) x := rndNat(50000) y := rndNat(40) allocSize := allocBytes(func() { diff --git a/libgo/go/mime/multipart/multipart.go b/libgo/go/mime/multipart/multipart.go index fb07e1a..77e969b 100644 --- a/libgo/go/mime/multipart/multipart.go +++ b/libgo/go/mime/multipart/multipart.go @@ -37,6 +37,11 @@ type Part struct { disposition string dispositionParams map[string]string + + // r is either a reader directly reading from mr, or it's a + // wrapper around such a reader, decoding the + // Content-Transfer-Encoding + r io.Reader } // FormName returns the name parameter if p has a Content-Disposition @@ -94,6 +99,12 @@ func newPart(mr *Reader) (*Part, error) { if err := bp.populateHeaders(); err != nil { return nil, err } + bp.r = partReader{bp} + const cte = "Content-Transfer-Encoding" + if bp.Header.Get(cte) == "quoted-printable" { + bp.Header.Del(cte) + bp.r = newQuotedPrintableReader(bp.r) + } return bp, nil } @@ -109,6 +120,17 @@ func (bp *Part) populateHeaders() error { // Read reads the body of a part, after its headers and before the // next part (if any) begins. func (p *Part) Read(d []byte) (n int, err error) { + return p.r.Read(d) +} + +// partReader implements io.Reader by reading raw bytes directly from the +// wrapped *Part, without doing any Transfer-Encoding decoding. +type partReader struct { + p *Part +} + +func (pr partReader) Read(d []byte) (n int, err error) { + p := pr.p defer func() { p.bytesRead += n }() diff --git a/libgo/go/mime/multipart/multipart_test.go b/libgo/go/mime/multipart/multipart_test.go index cd65e17..d662e83 100644 --- a/libgo/go/mime/multipart/multipart_test.go +++ b/libgo/go/mime/multipart/multipart_test.go @@ -339,9 +339,10 @@ func TestLineContinuation(t *testing.T) { if err != nil { t.Fatalf("didn't get a part") } - n, err := io.Copy(ioutil.Discard, part) + var buf bytes.Buffer + n, err := io.Copy(&buf, part) if err != nil { - t.Errorf("error reading part: %v", err) + t.Errorf("error reading part: %v\nread so far: %q", err, buf.String()) } if n <= 0 { t.Errorf("read %d bytes; expected >0", n) @@ -349,6 +350,29 @@ func TestLineContinuation(t *testing.T) { } } +func TestQuotedPrintableEncoding(t *testing.T) { + // From http://golang.org/issue/4411 + body := "--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=text\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words\r\n--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--0016e68ee29c5d515f04cedf6733--" + r := NewReader(strings.NewReader(body), "0016e68ee29c5d515f04cedf6733") + part, err := r.NextPart() + if err != nil { + t.Fatal(err) + } + if te, ok := part.Header["Content-Transfer-Encoding"]; ok { + t.Errorf("unexpected Content-Transfer-Encoding of %q", te) + } + var buf bytes.Buffer + _, err = io.Copy(&buf, part) + if err != nil { + t.Error(err) + } + got := buf.String() + want := "words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words" + if got != want { + t.Errorf("wrong part value:\n got: %q\nwant: %q", got, want) + } +} + // Test parsing an image attachment from gmail, which previously failed. func TestNested(t *testing.T) { // nested-mime is the body part of a multipart/mixed email diff --git a/libgo/go/mime/multipart/quotedprintable.go b/libgo/go/mime/multipart/quotedprintable.go new file mode 100644 index 0000000..0a60a6ed --- /dev/null +++ b/libgo/go/mime/multipart/quotedprintable.go @@ -0,0 +1,92 @@ +// Copyright 2012 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. + +// The file define a quoted-printable decoder, as specified in RFC 2045. + +package multipart + +import ( + "bufio" + "bytes" + "fmt" + "io" +) + +type qpReader struct { + br *bufio.Reader + rerr error // last read error + line []byte // to be consumed before more of br +} + +func newQuotedPrintableReader(r io.Reader) io.Reader { + return &qpReader{ + br: bufio.NewReader(r), + } +} + +func fromHex(b byte) (byte, error) { + switch { + case b >= '0' && b <= '9': + return b - '0', nil + case b >= 'A' && b <= 'F': + return b - 'A' + 10, nil + } + return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b) +} + +func (q *qpReader) readHexByte(v []byte) (b byte, err error) { + if len(v) < 2 { + return 0, io.ErrUnexpectedEOF + } + var hb, lb byte + if hb, err = fromHex(v[0]); err != nil { + return 0, err + } + if lb, err = fromHex(v[1]); err != nil { + return 0, err + } + return hb<<4 | lb, nil +} + +func isQPDiscardWhitespace(r rune) bool { + switch r { + case '\n', '\r', ' ', '\t': + return true + } + return false +} + +func (q *qpReader) Read(p []byte) (n int, err error) { + for len(p) > 0 { + if len(q.line) == 0 { + if q.rerr != nil { + return n, q.rerr + } + q.line, q.rerr = q.br.ReadSlice('\n') + q.line = bytes.TrimRightFunc(q.line, isQPDiscardWhitespace) + continue + } + if len(q.line) == 1 && q.line[0] == '=' { + // Soft newline; skipped. + q.line = nil + continue + } + b := q.line[0] + switch { + case b == '=': + b, err = q.readHexByte(q.line[1:]) + if err != nil { + return n, err + } + q.line = q.line[2:] // 2 of the 3; other 1 is done below + case b != '\t' && (b < ' ' || b > '~'): + return n, fmt.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b) + } + p[0] = b + p = p[1:] + q.line = q.line[1:] + n++ + } + return n, nil +} diff --git a/libgo/go/mime/multipart/quotedprintable_test.go b/libgo/go/mime/multipart/quotedprintable_test.go new file mode 100644 index 0000000..796a41f --- /dev/null +++ b/libgo/go/mime/multipart/quotedprintable_test.go @@ -0,0 +1,52 @@ +// Copyright 2012 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 multipart + +import ( + "bytes" + "fmt" + "io" + "strings" + "testing" +) + +func TestQuotedPrintable(t *testing.T) { + tests := []struct { + in, want string + err interface{} + }{ + {in: "foo bar", want: "foo bar"}, + {in: "foo bar=3D", want: "foo bar="}, + {in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF}, + {in: "foo bar=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"}, + {in: "foo bar=0D=0A", want: "foo bar\r\n"}, + {in: "foo bar=\r\n baz", want: "foo bar baz"}, + {in: "foo=\nbar", want: "foobar"}, + {in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"}, + {in: "foo bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"}, + } + for _, tt := range tests { + var buf bytes.Buffer + _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(tt.in))) + if got := buf.String(); got != tt.want { + t.Errorf("for %q, got %q; want %q", tt.in, got, tt.want) + } + switch verr := tt.err.(type) { + case nil: + if err != nil { + t.Errorf("for %q, got unexpected error: %v", tt.in, err) + } + case string: + if got := fmt.Sprint(err); got != verr { + t.Errorf("for %q, got error %q; want %q", tt.in, got, verr) + } + case error: + if err != verr { + t.Errorf("for %q, got error %q; want %q", tt.in, err, verr) + } + } + } + +} diff --git a/libgo/go/mime/multipart/writer.go b/libgo/go/mime/multipart/writer.go index ec70be4..e13a956 100644 --- a/libgo/go/mime/multipart/writer.go +++ b/libgo/go/mime/multipart/writer.go @@ -30,11 +30,38 @@ func NewWriter(w io.Writer) *Writer { } } -// Boundary returns the Writer's randomly selected boundary string. +// Boundary returns the Writer's boundary. func (w *Writer) Boundary() string { return w.boundary } +// SetBoundary overrides the Writer's default randomly-generated +// boundary separator with an explicit value. +// +// SetBoundary must be called before any parts are created, may only +// contain certain ASCII characters, and must be 1-69 bytes long. +func (w *Writer) SetBoundary(boundary string) error { + if w.lastpart != nil { + return errors.New("mime: SetBoundary called after write") + } + // rfc2046#section-5.1.1 + if len(boundary) < 1 || len(boundary) > 69 { + return errors.New("mime: invalid boundary length") + } + for _, b := range boundary { + if 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' { + continue + } + switch b { + case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?': + continue + } + return errors.New("mime: invalid boundary character") + } + w.boundary = boundary + return nil +} + // FormDataContentType returns the Content-Type for an HTTP // multipart/form-data with this Writer's Boundary. func (w *Writer) FormDataContentType() string { diff --git a/libgo/go/mime/multipart/writer_test.go b/libgo/go/mime/multipart/writer_test.go index 494e936..52d68bc 100644 --- a/libgo/go/mime/multipart/writer_test.go +++ b/libgo/go/mime/multipart/writer_test.go @@ -7,6 +7,7 @@ package multipart import ( "bytes" "io/ioutil" + "strings" "testing" ) @@ -76,3 +77,37 @@ func TestWriter(t *testing.T) { t.Fatalf("expected end of parts; got %v, %v", part, err) } } + +func TestWriterSetBoundary(t *testing.T) { + var b bytes.Buffer + w := NewWriter(&b) + tests := []struct { + b string + ok bool + }{ + {"abc", true}, + {"", false}, + {"ungültig", false}, + {"!", false}, + {strings.Repeat("x", 69), true}, + {strings.Repeat("x", 70), false}, + {"bad!ascii!", false}, + {"my-separator", true}, + } + for i, tt := range tests { + err := w.SetBoundary(tt.b) + got := err == nil + if got != tt.ok { + t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok) + } else if tt.ok { + got := w.Boundary() + if got != tt.b { + t.Errorf("boundary = %q; want %q", got, tt.b) + } + } + } + w.Close() + if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") { + t.Errorf("expected my-separator in output. got: %q", got) + } +} diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index a85e3c6..0c46084 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/net/dial.go @@ -15,6 +15,7 @@ func parseDialNetwork(net string) (afnet string, proto int, err error) { switch net { case "tcp", "tcp4", "tcp6": case "udp", "udp4", "udp6": + case "ip", "ip4", "ip6": case "unix", "unixgram", "unixpacket": default: return "", 0, UnknownNetworkError(net) @@ -54,12 +55,8 @@ func resolveAfnetAddr(afnet, addr string, deadline time.Time) (Addr, error) { return nil, nil } switch afnet { - case "tcp", "tcp4", "tcp6": - return resolveTCPAddr(afnet, addr, deadline) - case "udp", "udp4", "udp6": - return resolveUDPAddr(afnet, addr, deadline) - case "ip", "ip4", "ip6": - return resolveIPAddr(afnet, addr, deadline) + case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6": + return resolveInternetAddr(afnet, addr, deadline) case "unix", "unixgram", "unixpacket": return ResolveUnixAddr(afnet, addr) } @@ -218,8 +215,8 @@ func Listen(net, laddr string) (Listener, error) { // ListenPacket announces on the local network address laddr. // The network string net must be a packet-oriented network: // "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram". -func ListenPacket(net, addr string) (PacketConn, error) { - afnet, a, err := resolveNetAddr("listen", net, addr, noDeadline) +func ListenPacket(net, laddr string) (PacketConn, error) { + afnet, a, err := resolveNetAddr("listen", net, laddr, noDeadline) if err != nil { return nil, err } diff --git a/libgo/go/net/fd_posix_test.go b/libgo/go/net/fd_posix_test.go new file mode 100644 index 0000000..8be0335 --- /dev/null +++ b/libgo/go/net/fd_posix_test.go @@ -0,0 +1,57 @@ +// Copyright 2012 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 netbsd openbsd windows + +package net + +import ( + "testing" + "time" +) + +var deadlineSetTimeTests = []struct { + input time.Time + expected int64 +}{ + {time.Time{}, 0}, + {time.Date(2009, 11, 10, 23, 00, 00, 00, time.UTC), 1257894000000000000}, // 2009-11-10 23:00:00 +0000 UTC +} + +func TestDeadlineSetTime(t *testing.T) { + for _, tt := range deadlineSetTimeTests { + var d deadline + d.setTime(tt.input) + actual := d.value() + expected := int64(0) + if !tt.input.IsZero() { + expected = tt.input.UnixNano() + } + if actual != expected { + t.Errorf("set/value failed: expected %v, actual %v", expected, actual) + } + } +} + +var deadlineExpiredTests = []struct { + deadline time.Time + expired bool +}{ + // note, times are relative to the start of the test run, not + // the start of TestDeadlineExpired + {time.Now().Add(5 * time.Minute), false}, + {time.Now().Add(-5 * time.Minute), true}, + {time.Time{}, false}, // no deadline set +} + +func TestDeadlineExpired(t *testing.T) { + for _, tt := range deadlineExpiredTests { + var d deadline + d.set(tt.deadline.UnixNano()) + expired := d.expired() + if expired != tt.expired { + t.Errorf("expire failed: expected %v, actual %v", tt.expired, expired) + } + } +} diff --git a/libgo/go/net/fd_unix.go b/libgo/go/net/fd_unix.go index 096ad41..6d8af0a 100644 --- a/libgo/go/net/fd_unix.go +++ b/libgo/go/net/fd_unix.go @@ -37,11 +37,11 @@ type netFD struct { laddr Addr raddr Addr - // owned by client - rdeadline int64 - rio sync.Mutex - wdeadline int64 - wio sync.Mutex + // serialize access to Read and Write methods + rio, wio sync.Mutex + + // read and write deadlines + rdeadline, wdeadline deadline // owned by fd wait server ncr, ncw int @@ -82,11 +82,11 @@ func (s *pollServer) AddFD(fd *netFD, mode int) error { key := intfd << 1 if mode == 'r' { fd.ncr++ - t = fd.rdeadline + t = fd.rdeadline.value() } else { fd.ncw++ key++ - t = fd.wdeadline + t = fd.wdeadline.value() } s.pending[key] = fd doWakeup := false @@ -153,12 +153,8 @@ func (s *pollServer) WakeFD(fd *netFD, mode int, err error) { } } -func (s *pollServer) Now() int64 { - return time.Now().UnixNano() -} - func (s *pollServer) CheckDeadlines() { - now := s.Now() + now := time.Now().UnixNano() // TODO(rsc): This will need to be handled more efficiently, // probably with a heap indexed by wakeup time. @@ -172,21 +168,19 @@ func (s *pollServer) CheckDeadlines() { mode = 'w' } if mode == 'r' { - t = fd.rdeadline + t = fd.rdeadline.value() } else { - t = fd.wdeadline + t = fd.wdeadline.value() } if t > 0 { if t <= now { delete(s.pending, key) if mode == 'r' { s.poll.DelFD(fd.sysfd, mode) - fd.rdeadline = -1 } else { s.poll.DelFD(fd.sysfd, mode) - fd.wdeadline = -1 } - s.WakeFD(fd, mode, nil) + s.WakeFD(fd, mode, errTimeout) } else if nextDeadline == 0 || t < nextDeadline { nextDeadline = t } @@ -200,15 +194,15 @@ func (s *pollServer) Run() { s.Lock() defer s.Unlock() for { - var t = s.deadline - if t > 0 { - t = t - s.Now() - if t <= 0 { + var timeout int64 // nsec to wait for or 0 for none + if s.deadline > 0 { + timeout = s.deadline - time.Now().UnixNano() + if timeout <= 0 { s.CheckDeadlines() continue } } - fd, mode, err := s.poll.WaitFD(s, t) + fd, mode, err := s.poll.WaitFD(s, timeout) if err != nil { print("pollServer WaitFD: ", err.Error(), "\n") return @@ -329,14 +323,10 @@ func (fd *netFD) name() string { func (fd *netFD) connect(ra syscall.Sockaddr) error { err := syscall.Connect(fd.sysfd, ra) - hadTimeout := fd.wdeadline > 0 if err == syscall.EINPROGRESS { if err = fd.pollServer.WaitWrite(fd); err != nil { return err } - if hadTimeout && fd.wdeadline < 0 { - return errTimeout - } var e int e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) if err != nil { @@ -381,8 +371,8 @@ func (fd *netFD) decref() { func (fd *netFD) Close() error { fd.pollServer.Lock() // needed for both fd.incref(true) and pollserver.Evict - defer fd.pollServer.Unlock() if err := fd.incref(true); err != nil { + fd.pollServer.Unlock() return err } // Unblock any I/O. Once it all unblocks and returns, @@ -391,6 +381,7 @@ func (fd *netFD) Close() error { // fairly quickly, since all the I/O is non-blocking, and any // attempts to block in the pollserver will return errClosing. fd.pollServer.Evict(fd) + fd.pollServer.Unlock() fd.decref() return nil } @@ -423,20 +414,20 @@ func (fd *netFD) Read(p []byte) (n int, err error) { } defer fd.decref() for { - n, err = syscall.Read(int(fd.sysfd), p) - if err == syscall.EAGAIN { + if fd.rdeadline.expired() { err = errTimeout - if fd.rdeadline >= 0 { + break + } + n, err = syscall.Read(int(fd.sysfd), p) + if err != nil { + n = 0 + if err == syscall.EAGAIN { if err = fd.pollServer.WaitRead(fd); err == nil { continue } } } - if err != nil { - n = 0 - } else if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM { - err = io.EOF - } + err = chkReadErr(n, err, fd) break } if err != nil && err != io.EOF { @@ -453,18 +444,20 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { } defer fd.decref() for { - n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) - if err == syscall.EAGAIN { + if fd.rdeadline.expired() { err = errTimeout - if fd.rdeadline >= 0 { + break + } + n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0) + if err != nil { + n = 0 + if err == syscall.EAGAIN { if err = fd.pollServer.WaitRead(fd); err == nil { continue } } } - if err != nil { - n = 0 - } + err = chkReadErr(n, err, fd) break } if err != nil && err != io.EOF { @@ -481,41 +474,47 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S } defer fd.decref() for { - n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) - if err == syscall.EAGAIN { + if fd.rdeadline.expired() { err = errTimeout - if fd.rdeadline >= 0 { + break + } + n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0) + if err != nil { + // TODO(dfc) should n and oobn be set to 0 + if err == syscall.EAGAIN { if err = fd.pollServer.WaitRead(fd); err == nil { continue } } } - if err == nil && n == 0 { - err = io.EOF - } + err = chkReadErr(n, err, fd) break } if err != nil && err != io.EOF { err = &OpError{"read", fd.net, fd.laddr, err} - return } return } -func (fd *netFD) Write(p []byte) (int, error) { +func chkReadErr(n int, err error, fd *netFD) error { + if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM && fd.sotype != syscall.SOCK_RAW { + return io.EOF + } + return err +} + +func (fd *netFD) Write(p []byte) (nn int, err error) { fd.wio.Lock() defer fd.wio.Unlock() if err := fd.incref(false); err != nil { return 0, err } defer fd.decref() - if fd.sysfile == nil { - return 0, syscall.EINVAL - } - - var err error - nn := 0 for { + if fd.wdeadline.expired() { + err = errTimeout + break + } var n int n, err = syscall.Write(int(fd.sysfd), p[nn:]) if n > 0 { @@ -525,11 +524,8 @@ func (fd *netFD) Write(p []byte) (int, error) { break } if err == syscall.EAGAIN { - err = errTimeout - if fd.wdeadline >= 0 { - if err = fd.pollServer.WaitWrite(fd); err == nil { - continue - } + if err = fd.pollServer.WaitWrite(fd); err == nil { + continue } } if err != nil { @@ -555,13 +551,14 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { } defer fd.decref() for { + if fd.wdeadline.expired() { + err = errTimeout + break + } err = syscall.Sendto(fd.sysfd, p, 0, sa) if err == syscall.EAGAIN { - err = errTimeout - if fd.wdeadline >= 0 { - if err = fd.pollServer.WaitWrite(fd); err == nil { - continue - } + if err = fd.pollServer.WaitWrite(fd); err == nil { + continue } } break @@ -582,13 +579,14 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob } defer fd.decref() for { + if fd.wdeadline.expired() { + err = errTimeout + break + } err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0) if err == syscall.EAGAIN { - err = errTimeout - if fd.wdeadline >= 0 { - if err = fd.pollServer.WaitWrite(fd); err == nil { - continue - } + if err = fd.pollServer.WaitWrite(fd); err == nil { + continue } } break @@ -619,11 +617,8 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e if err != nil { syscall.ForkLock.RUnlock() if err == syscall.EAGAIN { - err = errTimeout - if fd.rdeadline >= 0 { - if err = fd.pollServer.WaitRead(fd); err == nil { - continue - } + if err = fd.pollServer.WaitRead(fd); err == nil { + continue } } else if err == syscall.ECONNABORTED { // This means that a socket on the listen queue was closed diff --git a/libgo/go/net/fd_unix_test.go b/libgo/go/net/fd_unix_test.go index d1eb573..fd1385e 100644 --- a/libgo/go/net/fd_unix_test.go +++ b/libgo/go/net/fd_unix_test.go @@ -7,33 +7,34 @@ package net import ( + "io" + "syscall" "testing" ) // Issue 3590. netFd.AddFD should return an error // from the underlying pollster rather than panicing. func TestAddFDReturnsError(t *testing.T) { - l, err := Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer l.Close() - + ln := newLocalListener(t).(*TCPListener) + defer ln.Close() + connected := make(chan bool) go func() { for { - c, err := l.Accept() + c, err := ln.Accept() if err != nil { return } + connected <- true defer c.Close() } }() - c, err := Dial("tcp", l.Addr().String()) + c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr)) if err != nil { t.Fatal(err) } defer c.Close() + <-connected // replace c's pollServer with a closed version. ps, err := newPollServer() @@ -41,7 +42,7 @@ func TestAddFDReturnsError(t *testing.T) { t.Fatal(err) } ps.poll.Close() - c.(*TCPConn).conn.fd.pollServer = ps + c.conn.fd.pollServer = ps var b [1]byte _, err = c.Read(b[:]) @@ -56,5 +57,50 @@ func TestAddFDReturnsError(t *testing.T) { } } } - t.Error(err) + t.Error("unexpected error:", err) +} + +var chkReadErrTests = []struct { + n int + err error + fd *netFD + expected error +}{ + + {100, nil, &netFD{sotype: syscall.SOCK_STREAM}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing}, + + {100, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing}, + + {100, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing}, + + {100, nil, &netFD{sotype: syscall.SOCK_RAW}, nil}, + {100, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF}, + {100, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing}, + {0, nil, &netFD{sotype: syscall.SOCK_RAW}, nil}, + {0, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF}, + {0, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing}, +} + +func TestChkReadErr(t *testing.T) { + for _, tt := range chkReadErrTests { + actual := chkReadErr(tt.n, tt.err, tt.fd) + if actual != tt.expected { + t.Errorf("chkReadError(%v, %v, %v): expected %v, actual %v", tt.n, tt.err, tt.fd.sotype, tt.expected, actual) + } + } } diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index 5338def..1871219 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -169,6 +169,15 @@ func (s *ioSrv) ProcessRemoteIO() { func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { var err error o := oi.Op() + // Calculate timeout delta. + var delta int64 + if deadline != 0 { + delta = deadline - time.Now().UnixNano() + if delta <= 0 { + return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout} + } + } + // Start IO. if canCancelIO { err = oi.Submit() } else { @@ -188,12 +197,8 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { } // Setup timer, if deadline is given. var timer <-chan time.Time - if deadline != 0 { - dt := deadline - time.Now().UnixNano() - if dt < 1 { - dt = 1 - } - t := time.NewTimer(time.Duration(dt) * time.Nanosecond) + if delta > 0 { + t := time.NewTimer(time.Duration(delta) * time.Nanosecond) defer t.Stop() timer = t.C } @@ -280,11 +285,11 @@ type netFD struct { errnoc [2]chan error // read/write submit or cancel operation errors closec chan bool // used by Close to cancel pending IO - // owned by client - rdeadline int64 - rio sync.Mutex - wdeadline int64 - wio sync.Mutex + // serialize access to Read and Write methods + rio, wio sync.Mutex + + // read and write deadlines + rdeadline, wdeadline deadline } func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD { @@ -295,7 +300,6 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD { net: net, closec: make(chan bool), } - runtime.SetFinalizer(netfd, (*netFD).Close) return netfd } @@ -314,6 +318,7 @@ func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) { func (fd *netFD) setAddr(laddr, raddr Addr) { fd.laddr = laddr fd.raddr = raddr + runtime.SetFinalizer(fd, (*netFD).closesocket) } func (fd *netFD) connect(ra syscall.Sockaddr) error { @@ -393,6 +398,10 @@ func (fd *netFD) CloseWrite() error { return fd.shutdown(syscall.SHUT_WR) } +func (fd *netFD) closesocket() error { + return closesocket(fd.sysfd) +} + // Read from network. type readOp struct { @@ -417,7 +426,7 @@ func (fd *netFD) Read(buf []byte) (int, error) { defer fd.rio.Unlock() var o readOp o.Init(fd, buf, 'r') - n, err := iosrv.ExecIO(&o, fd.rdeadline) + n, err := iosrv.ExecIO(&o, fd.rdeadline.value()) if err == nil && n == 0 { err = io.EOF } @@ -454,7 +463,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { var o readFromOp o.Init(fd, buf, 'r') o.rsan = int32(unsafe.Sizeof(o.rsa)) - n, err = iosrv.ExecIO(&o, fd.rdeadline) + n, err = iosrv.ExecIO(&o, fd.rdeadline.value()) if err != nil { return 0, nil, err } @@ -486,7 +495,7 @@ func (fd *netFD) Write(buf []byte) (int, error) { defer fd.wio.Unlock() var o writeOp o.Init(fd, buf, 'w') - return iosrv.ExecIO(&o, fd.wdeadline) + return iosrv.ExecIO(&o, fd.wdeadline.value()) } // WriteTo to network. @@ -518,7 +527,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { var o writeToOp o.Init(fd, buf, 'w') o.sa = sa - return iosrv.ExecIO(&o, fd.wdeadline) + return iosrv.ExecIO(&o, fd.wdeadline.value()) } // Accept new network connections. @@ -552,7 +561,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { s, err := syscall.Socket(fd.family, fd.sotype, 0) if err != nil { syscall.ForkLock.RUnlock() - return nil, err + return nil, &OpError{"socket", fd.net, fd.laddr, err} } syscall.CloseOnExec(s) syscall.ForkLock.RUnlock() @@ -560,6 +569,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { // Associate our new socket with IOCP. onceStartServer.Do(startServer) if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil { + closesocket(s) return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err} } @@ -567,7 +577,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { var o acceptOp o.Init(fd, 'r') o.newsock = s - _, err = iosrv.ExecIO(&o, fd.rdeadline) + _, err = iosrv.ExecIO(&o, fd.rdeadline.value()) if err != nil { closesocket(s) return nil, err @@ -577,7 +587,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) if err != nil { closesocket(s) - return nil, err + return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err} } // Get local and peer addr out of AcceptEx buffer. diff --git a/libgo/go/net/file_plan9.go b/libgo/go/net/file_plan9.go index 04f7ee0..ae3ac15 100644 --- a/libgo/go/net/file_plan9.go +++ b/libgo/go/net/file_plan9.go @@ -19,8 +19,8 @@ func FileConn(f *os.File) (c Conn, err error) { // FileListener returns a copy of the network listener corresponding // to the open file f. It is the caller's responsibility to close l -// when finished. Closing c does not affect l, and closing l does not -// affect c. +// when finished. Closing l does not affect f, and closing f does not +// affect l. func FileListener(f *os.File) (l Listener, err error) { return nil, syscall.EPLAN9 } diff --git a/libgo/go/net/http/cgi/host_test.go b/libgo/go/net/http/cgi/host_test.go index 859911f..85b52c9 100644 --- a/libgo/go/net/http/cgi/host_test.go +++ b/libgo/go/net/http/cgi/host_test.go @@ -405,7 +405,8 @@ func TestDirUnix(t *testing.T) { } func TestDirWindows(t *testing.T) { - if skipTest(t) || runtime.GOOS != "windows" { + if runtime.GOOS != "windows" { + t.Logf("Skipping windows specific test.") return } @@ -415,6 +416,7 @@ func TestDirWindows(t *testing.T) { var err error perl, err = exec.LookPath("perl") if err != nil { + t.Logf("Skipping test: perl not found.") return } perl, _ = filepath.Abs(perl) @@ -457,6 +459,7 @@ func TestEnvOverride(t *testing.T) { var err error perl, err = exec.LookPath("perl") if err != nil { + t.Logf("Skipping test: perl not found.") return } perl, _ = filepath.Abs(perl) diff --git a/libgo/go/net/http/cgi/testdata/test.cgi b/libgo/go/net/http/cgi/testdata/test.cgi index dcefe4e..1b25bc2 100644 --- a/libgo/go/net/http/cgi/testdata/test.cgi +++ b/libgo/go/net/http/cgi/testdata/test.cgi @@ -10,23 +10,6 @@ use Cwd; binmode STDOUT; -sub on_windows { - return $^O eq 'MSWin32' || $^O eq 'msys'; -} - -# normalize_windows_path normalizes the various Windows Perl path -# formats into Go's format. -sub normalize_windows_path { - my $dir = shift; - return $dir unless on_windows(); - $dir =~ s!^[a-z]:!uc($&)!e; - if ($dir =~ s!^/([a-zA-Z])/!!) { - $dir = uc($1) . ":\\$dir"; - } - $dir =~ s!/!\\!g; - return $dir; -} - my $q = MiniCGI->new; my $params = $q->Vars; @@ -35,40 +18,43 @@ if ($params->{"loc"}) { exit(0); } -my $NL = "\r\n"; -$NL = "\n" if $params->{mode} eq "NL"; - -my $p = sub { - print "$_[0]$NL"; -}; - -# With carriage returns -$p->("Content-Type: text/html"); -$p->("X-CGI-Pid: $$"); -$p->("X-Test-Header: X-Test-Value"); -$p->(""); +print "Content-Type: text/html\r\n"; +print "X-CGI-Pid: $$\r\n"; +print "X-Test-Header: X-Test-Value\r\n"; +print "\r\n"; if ($params->{"bigresponse"}) { for (1..1024) { - print "A" x 1024, "\n"; + print "A" x 1024, "\r\n"; } exit 0; } -print "test=Hello CGI\n"; +print "test=Hello CGI\r\n"; foreach my $k (sort keys %$params) { - print "param-$k=$params->{$k}\n"; + print "param-$k=$params->{$k}\r\n"; } foreach my $k (sort keys %ENV) { my $clean_env = $ENV{$k}; $clean_env =~ s/[\n\r]//g; - print "env-$k=$clean_env\n"; + print "env-$k=$clean_env\r\n"; } -my $dir = normalize_windows_path(getcwd()); -print "cwd=$dir\n"; +# NOTE: msys perl returns /c/go/src/... not C:\go\.... +my $dir = getcwd(); +if ($^O eq 'MSWin32' || $^O eq 'msys') { + if ($dir =~ /^.:/) { + $dir =~ s!/!\\!g; + } else { + my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe'; + $cmd =~ s!\\!/!g; + $dir = `$cmd /c cd`; + chomp $dir; + } +} +print "cwd=$dir\r\n"; # A minimal version of CGI.pm, for people without the perl-modules # package installed. (CGI.pm used to be part of the Perl core, but @@ -102,24 +88,3 @@ sub _urldecode { $v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; return $v; } - -package Tests; - -sub test_normalize_windows_paths { - my @tests = ( - {in => "C:\\foo\\bar", want => "C:\\foo\\bar"}, - {in => "C:/foo/bar", want => "C:\\foo\\bar"}, - {in => "c:/foo/bar", want => "C:\\foo\\bar"}, - {in => "/c/foo/bar", want => "C:\\foo\\bar"}, - ); - foreach my $tt (@tests) { - my $got = ::normalize_windows_path($tt->{in}); - unless ($got eq $tt->{want}) { - die "For path $tt->{in}, normalize = $got; want $tt->{want}\n"; - } - } -} - -BEGIN { - test_normalize_windows_paths() if ::on_windows(); -} diff --git a/libgo/go/net/http/chunked.go b/libgo/go/net/http/chunked.go index 7cf39cf..91db017 100644 --- a/libgo/go/net/http/chunked.go +++ b/libgo/go/net/http/chunked.go @@ -11,11 +11,9 @@ package http import ( "bufio" - "bytes" "errors" "fmt" "io" - "strconv" ) const maxLineLength = 4096 // assumed <= bufio.defaultBufSize @@ -45,12 +43,12 @@ type chunkedReader struct { func (cr *chunkedReader) beginChunk() { // chunk-size CRLF - var line string + var line []byte line, cr.err = readLine(cr.r) if cr.err != nil { return } - cr.n, cr.err = strconv.ParseUint(line, 16, 64) + cr.n, cr.err = parseHexUint(line) if cr.err != nil { return } @@ -89,7 +87,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { // 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) { +func readLine(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. @@ -103,20 +101,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) { if len(p) >= maxLineLength { return nil, ErrLineTooLong } - - // Chop off trailing white space. - p = bytes.TrimRight(p, " \r\t\n") - - return p, nil + return trimTrailingWhitespace(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 +func trimTrailingWhitespace(b []byte) []byte { + for len(b) > 0 && isASCIISpace(b[len(b)-1]) { + b = b[:len(b)-1] } - return string(p), nil + return b +} + +func isASCIISpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP @@ -167,3 +163,21 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } + +func parseHexUint(v []byte) (n uint64, err error) { + for _, b := range v { + n <<= 4 + switch { + case '0' <= b && b <= '9': + b = b - '0' + case 'a' <= b && b <= 'f': + b = b - 'a' + 10 + case 'A' <= b && b <= 'F': + b = b - 'A' + 10 + default: + return 0, errors.New("invalid byte in chunk length") + } + n |= uint64(b) + } + return +} diff --git a/libgo/go/net/http/chunked_test.go b/libgo/go/net/http/chunked_test.go index b77ee2f..0b18c7b 100644 --- a/libgo/go/net/http/chunked_test.go +++ b/libgo/go/net/http/chunked_test.go @@ -9,7 +9,10 @@ package http import ( "bytes" + "fmt" + "io" "io/ioutil" + "runtime" "testing" ) @@ -37,3 +40,54 @@ func TestChunk(t *testing.T) { t.Errorf("chunk reader read %q; want %q", g, e) } } + +func TestChunkReaderAllocs(t *testing.T) { + // temporarily set GOMAXPROCS to 1 as we are testing memory allocations + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + var buf bytes.Buffer + w := newChunkedWriter(&buf) + a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc") + w.Write(a) + w.Write(b) + w.Write(c) + w.Close() + + r := newChunkedReader(&buf) + readBuf := make([]byte, len(a)+len(b)+len(c)+1) + + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + m0 := ms.Mallocs + + n, err := io.ReadFull(r, readBuf) + + runtime.ReadMemStats(&ms) + mallocs := ms.Mallocs - m0 + if mallocs > 1 { + t.Errorf("%d mallocs; want <= 1", mallocs) + } + + if n != len(readBuf)-1 { + t.Errorf("read %d bytes; want %d", n, len(readBuf)-1) + } + if err != io.ErrUnexpectedEOF { + t.Errorf("read error = %v; want ErrUnexpectedEOF", err) + } +} + +func TestParseHexUint(t *testing.T) { + for i := uint64(0); i <= 1234; i++ { + line := []byte(fmt.Sprintf("%x", i)) + got, err := parseHexUint(line) + if err != nil { + t.Fatalf("on %d: %v", i, err) + } + if got != i { + t.Errorf("for input %q = %d; want %d", line, got, i) + } + } + _, err := parseHexUint([]byte("bogus")) + if err == nil { + t.Error("expected error on bogus input") + } +} diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index 9a45b14..f4ba6a9 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -527,3 +527,38 @@ func TestClientWithIncorrectTLSServerName(t *testing.T) { t.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err) } } + +// Verify Response.ContentLength is populated. http://golang.org/issue/4126 +func TestClientHeadContentLength(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + if v := r.FormValue("cl"); v != "" { + w.Header().Set("Content-Length", v) + } + })) + defer ts.Close() + tests := []struct { + suffix string + want int64 + }{ + {"/?cl=1234", 1234}, + {"/?cl=0", 0}, + {"", -1}, + } + for _, tt := range tests { + req, _ := NewRequest("HEAD", ts.URL+tt.suffix, nil) + res, err := DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + if res.ContentLength != tt.want { + t.Errorf("Content-Length = %d; want %d", res.ContentLength, tt.want) + } + bs, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + if len(bs) != 0 { + t.Errorf("Unexpected content: %q", bs) + } + } +} diff --git a/libgo/go/net/http/export_test.go b/libgo/go/net/http/export_test.go index 313c6af..a7a0785 100644 --- a/libgo/go/net/http/export_test.go +++ b/libgo/go/net/http/export_test.go @@ -7,7 +7,14 @@ package http -import "time" +import ( + "net" + "time" +) + +func NewLoggingConn(baseName string, c net.Conn) net.Conn { + return newLoggingConn(baseName, c) +} func (t *Transport) IdleConnKeysForTesting() (keys []string) { keys = make([]string, 0) diff --git a/libgo/go/net/http/header_test.go b/libgo/go/net/http/header_test.go index fd971a6..01bb4dc 100644 --- a/libgo/go/net/http/header_test.go +++ b/libgo/go/net/http/header_test.go @@ -188,6 +188,7 @@ type errorfer interface { } func doHeaderWriteSubset(n int, t errorfer) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) h := Header(map[string][]string{ "Content-Length": {"123"}, "Content-Type": {"text/plain"}, @@ -204,7 +205,7 @@ func doHeaderWriteSubset(n int, t errorfer) { var m1 runtime.MemStats runtime.ReadMemStats(&m1) if mallocs := m1.Mallocs - m0.Mallocs; n >= 100 && mallocs >= uint64(n) { - // TODO(bradfitz,rsc): once we can sort with allocating, + // TODO(bradfitz,rsc): once we can sort without allocating, // make this an error. See http://golang.org/issue/3761 // t.Errorf("did %d mallocs (>= %d iterations); should have avoided mallocs", mallocs, n) } diff --git a/libgo/go/net/http/httptest/server.go b/libgo/go/net/http/httptest/server.go index 165600e..fc52c9a 100644 --- a/libgo/go/net/http/httptest/server.go +++ b/libgo/go/net/http/httptest/server.go @@ -21,7 +21,7 @@ import ( type Server struct { URL string // base URL of form http://ipaddr:port with no trailing slash Listener net.Listener - TLS *tls.Config // nil if not using using TLS + TLS *tls.Config // nil if not using TLS // Config may be changed after calling NewUnstartedServer and // before Start or StartTLS. @@ -36,13 +36,16 @@ type Server struct { // accepted. type historyListener struct { net.Listener - history []net.Conn + sync.Mutex // protects history + history []net.Conn } func (hs *historyListener) Accept() (c net.Conn, err error) { c, err = hs.Listener.Accept() if err == nil { + hs.Lock() hs.history = append(hs.history, c) + hs.Unlock() } return } @@ -96,7 +99,7 @@ func (s *Server) Start() { if s.URL != "" { panic("Server already started") } - s.Listener = &historyListener{s.Listener, make([]net.Conn, 0)} + s.Listener = &historyListener{Listener: s.Listener} s.URL = "http://" + s.Listener.Addr().String() s.wrapHandler() go s.Config.Serve(s.Listener) @@ -122,7 +125,7 @@ func (s *Server) StartTLS() { } tlsListener := tls.NewListener(s.Listener, s.TLS) - s.Listener = &historyListener{tlsListener, make([]net.Conn, 0)} + s.Listener = &historyListener{Listener: tlsListener} s.URL = "https://" + s.Listener.Addr().String() s.wrapHandler() go s.Config.Serve(s.Listener) @@ -152,6 +155,10 @@ func NewTLSServer(handler http.Handler) *Server { func (s *Server) Close() { s.Listener.Close() s.wg.Wait() + s.CloseClientConnections() + if t, ok := http.DefaultTransport.(*http.Transport); ok { + t.CloseIdleConnections() + } } // CloseClientConnections closes any currently open HTTP connections @@ -161,9 +168,11 @@ func (s *Server) CloseClientConnections() { if !ok { return } + hl.Lock() for _, conn := range hl.history { conn.Close() } + hl.Unlock() } // waitGroupHandler wraps a handler, incrementing and decrementing a diff --git a/libgo/go/net/http/httputil/chunked.go b/libgo/go/net/http/httputil/chunked.go index 26daee5..b66d409 100644 --- a/libgo/go/net/http/httputil/chunked.go +++ b/libgo/go/net/http/httputil/chunked.go @@ -13,11 +13,9 @@ package httputil import ( "bufio" - "bytes" "errors" "fmt" "io" - "strconv" ) const maxLineLength = 4096 // assumed <= bufio.defaultBufSize @@ -47,12 +45,12 @@ type chunkedReader struct { func (cr *chunkedReader) beginChunk() { // chunk-size CRLF - var line string + var line []byte line, cr.err = readLine(cr.r) if cr.err != nil { return } - cr.n, cr.err = strconv.ParseUint(line, 16, 64) + cr.n, cr.err = parseHexUint(line) if cr.err != nil { return } @@ -91,7 +89,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) { // 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) { +func readLine(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. @@ -105,20 +103,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) { if len(p) >= maxLineLength { return nil, ErrLineTooLong } - - // Chop off trailing white space. - p = bytes.TrimRight(p, " \r\t\n") - - return p, nil + return trimTrailingWhitespace(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 +func trimTrailingWhitespace(b []byte) []byte { + for len(b) > 0 && isASCIISpace(b[len(b)-1]) { + b = b[:len(b)-1] } - return string(p), nil + return b +} + +func isASCIISpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' } // NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP @@ -169,3 +165,21 @@ func (cw *chunkedWriter) Close() error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } + +func parseHexUint(v []byte) (n uint64, err error) { + for _, b := range v { + n <<= 4 + switch { + case '0' <= b && b <= '9': + b = b - '0' + case 'a' <= b && b <= 'f': + b = b - 'a' + 10 + case 'A' <= b && b <= 'F': + b = b - 'A' + 10 + default: + return 0, errors.New("invalid byte in chunk length") + } + n |= uint64(b) + } + return +} diff --git a/libgo/go/net/http/httputil/chunked_test.go b/libgo/go/net/http/httputil/chunked_test.go index 155a32b..a06bffa 100644 --- a/libgo/go/net/http/httputil/chunked_test.go +++ b/libgo/go/net/http/httputil/chunked_test.go @@ -11,7 +11,10 @@ package httputil import ( "bytes" + "fmt" + "io" "io/ioutil" + "runtime" "testing" ) @@ -39,3 +42,54 @@ func TestChunk(t *testing.T) { t.Errorf("chunk reader read %q; want %q", g, e) } } + +func TestChunkReaderAllocs(t *testing.T) { + // temporarily set GOMAXPROCS to 1 as we are testing memory allocations + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + var buf bytes.Buffer + w := NewChunkedWriter(&buf) + a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc") + w.Write(a) + w.Write(b) + w.Write(c) + w.Close() + + r := NewChunkedReader(&buf) + readBuf := make([]byte, len(a)+len(b)+len(c)+1) + + var ms runtime.MemStats + runtime.ReadMemStats(&ms) + m0 := ms.Mallocs + + n, err := io.ReadFull(r, readBuf) + + runtime.ReadMemStats(&ms) + mallocs := ms.Mallocs - m0 + if mallocs > 1 { + t.Errorf("%d mallocs; want <= 1", mallocs) + } + + if n != len(readBuf)-1 { + t.Errorf("read %d bytes; want %d", n, len(readBuf)-1) + } + if err != io.ErrUnexpectedEOF { + t.Errorf("read error = %v; want ErrUnexpectedEOF", err) + } +} + +func TestParseHexUint(t *testing.T) { + for i := uint64(0); i <= 1234; i++ { + line := []byte(fmt.Sprintf("%x", i)) + got, err := parseHexUint(line) + if err != nil { + t.Fatalf("on %d: %v", i, err) + } + if got != i { + t.Errorf("for input %q = %d; want %d", line, got, i) + } + } + _, err := parseHexUint([]byte("bogus")) + if err == nil { + t.Error("expected error on bogus input") + } +} diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 61557ff..0b6e6cb 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -124,6 +124,7 @@ type Request struct { // The host on which the URL is sought. // Per RFC 2616, this is either the value of the Host: header // or the host name given in the URL itself. + // It may be of the form "host:port". Host string // Form contains the parsed form data, including both the URL @@ -643,16 +644,20 @@ func parsePostForm(r *Request) (vs url.Values, err error) { return } -// ParseForm parses the raw query from the URL. +// ParseForm parses the raw query from the URL and updates r.Form. +// +// For POST or PUT requests, it also parses the request body as a form and +// put the results into both r.PostForm and r.Form. +// POST and PUT body parameters take precedence over URL query string values +// in r.Form. // -// For POST or PUT requests, it also parses the request body as a form. -// POST and PUT body parameters take precedence over URL query string values. // If the request Body's size has not already been limited by MaxBytesReader, // the size is capped at 10MB. // // ParseMultipartForm calls ParseForm automatically. // It is idempotent. -func (r *Request) ParseForm() (err error) { +func (r *Request) ParseForm() error { + var err error if r.PostForm == nil { if r.Method == "POST" || r.Method == "PUT" { r.PostForm, err = parsePostForm(r) @@ -728,6 +733,7 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error { // FormValue returns the first value for the named component of the query. // POST and PUT body parameters take precedence over URL query string values. // FormValue calls ParseMultipartForm and ParseForm if necessary. +// To access multiple values of the same key use ParseForm. func (r *Request) FormValue(key string) string { if r.Form == nil { r.ParseMultipartForm(defaultMaxMemory) diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go index c0b738c..2f34d12 100644 --- a/libgo/go/net/http/request_test.go +++ b/libgo/go/net/http/request_test.go @@ -228,6 +228,16 @@ func TestReadRequestErrors(t *testing.T) { } } +func TestNewRequestHost(t *testing.T) { + req, err := NewRequest("GET", "http://localhost:1234/", nil) + if err != nil { + t.Fatal(err) + } + if req.Host != "localhost:1234" { + t.Errorf("Host = %q; want localhost:1234", req.Host) + } +} + func testMissingFile(t *testing.T, req *Request) { f, fh, err := req.FormFile("missing") if f != nil { diff --git a/libgo/go/net/http/response.go b/libgo/go/net/http/response.go index 92d2f49..7901c49 100644 --- a/libgo/go/net/http/response.go +++ b/libgo/go/net/http/response.go @@ -49,7 +49,7 @@ type Response struct { Body io.ReadCloser // ContentLength records the length of the associated content. The - // value -1 indicates that the length is unknown. Unless RequestMethod + // value -1 indicates that the length is unknown. Unless Request.Method // is "HEAD", values >= 0 indicate that the given number of bytes may // be read from Body. ContentLength int64 @@ -178,7 +178,7 @@ func (r *Response) ProtoAtLeast(major, minor int) bool { // StatusCode // ProtoMajor // ProtoMinor -// RequestMethod +// Request.Method // TransferEncoding // Trailer // Body diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go index 6eed488..f31e5d0 100644 --- a/libgo/go/net/http/response_test.go +++ b/libgo/go/net/http/response_test.go @@ -193,7 +193,7 @@ var respTests = []respTest{ Request: dummyReq("HEAD"), Header: Header{}, Close: true, - ContentLength: 0, + ContentLength: -1, }, "", diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index 355efb2..8ca227f 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -1252,6 +1252,42 @@ func TestContentLengthZero(t *testing.T) { } } +func TestCloseNotifier(t *testing.T) { + gotReq := make(chan bool, 1) + sawClose := make(chan bool, 1) + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + gotReq <- true + cc := rw.(CloseNotifier).CloseNotify() + <-cc + sawClose <- true + })) + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("error dialing: %v", err) + } + diec := make(chan bool) + go func() { + _, err = fmt.Fprintf(conn, "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n") + if err != nil { + t.Fatal(err) + } + <-diec + conn.Close() + }() +For: + for { + select { + case <-gotReq: + diec <- true + case <-sawClose: + break For + case <-time.After(5 * time.Second): + t.Fatal("timeout") + } + } + ts.Close() +} + // goTimeout runs f, failing t if f takes more than ns to complete. func goTimeout(t *testing.T, d time.Duration, f func()) { ch := make(chan bool, 2) diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index 719cecf..c4ddbec 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -11,7 +11,6 @@ package http import ( "bufio" - "bytes" "crypto/tls" "errors" "fmt" @@ -21,7 +20,7 @@ import ( "net" "net/url" "path" - "runtime/debug" + "runtime" "strconv" "strings" "sync" @@ -94,16 +93,104 @@ type Hijacker interface { Hijack() (net.Conn, *bufio.ReadWriter, error) } +// The CloseNotifier interface is implemented by ResponseWriters which +// allow detecting when the underlying connection has gone away. +// +// This mechanism can be used to cancel long operations on the server +// if the client has disconnected before the response is ready. +type CloseNotifier interface { + // CloseNotify returns a channel that receives a single value + // when the client connection has gone away. + CloseNotify() <-chan bool +} + // A conn represents the server side of an HTTP connection. type conn struct { remoteAddr string // network address of remote side server *Server // the Server on which the connection arrived rwc net.Conn // i/o connection - lr *io.LimitedReader // io.LimitReader(rwc) - buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->rwc - hijacked bool // connection has been hijacked by handler + sr switchReader // where the LimitReader reads from; usually the rwc + lr *io.LimitedReader // io.LimitReader(sr) + buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc tlsState *tls.ConnectionState // or nil when not using TLS body []byte + + mu sync.Mutex // guards the following + clientGone bool // if client has disconnected mid-request + closeNotifyc chan bool // made lazily + hijackedv bool // connection has been hijacked by handler +} + +func (c *conn) hijacked() bool { + c.mu.Lock() + defer c.mu.Unlock() + return c.hijackedv +} + +func (c *conn) hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.hijackedv { + return nil, nil, ErrHijacked + } + if c.closeNotifyc != nil { + return nil, nil, errors.New("http: Hijack is incompatible with use of CloseNotifier") + } + c.hijackedv = true + rwc = c.rwc + buf = c.buf + c.rwc = nil + c.buf = nil + return +} + +func (c *conn) closeNotify() <-chan bool { + c.mu.Lock() + defer c.mu.Unlock() + if c.closeNotifyc == nil { + c.closeNotifyc = make(chan bool) + if c.hijackedv { + // to obey the function signature, even though + // it'll never receive a value. + return c.closeNotifyc + } + pr, pw := io.Pipe() + + readSource := c.sr.r + c.sr.Lock() + c.sr.r = pr + c.sr.Unlock() + go func() { + _, err := io.Copy(pw, readSource) + if err == nil { + err = io.EOF + } + pw.CloseWithError(err) + c.noteClientGone() + }() + } + return c.closeNotifyc +} + +func (c *conn) noteClientGone() { + c.mu.Lock() + defer c.mu.Unlock() + if c.closeNotifyc != nil && !c.clientGone { + c.closeNotifyc <- true + } + c.clientGone = true +} + +type switchReader struct { + sync.Mutex + r io.Reader +} + +func (sr *switchReader) Read(p []byte) (n int, err error) { + sr.Lock() + r := sr.r + sr.Unlock() + return r.Read(p) } // A response represents the server side of an HTTP response. @@ -127,7 +214,7 @@ type response struct { // requestBodyLimitHit is set by requestTooLarge when // maxBytesReader hits its max size. It is checked in - // WriteHeader, to make sure we don't consume the the + // WriteHeader, to make sure we don't consume the // remaining request body to try to advance to the next HTTP // request. Instead, when this is set, we stop reading // subsequent requests on this connection and stop reading @@ -171,16 +258,24 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) { // noLimit is an effective infinite upper bound for io.LimitedReader const noLimit int64 = (1 << 63) - 1 +// debugServerConnections controls whether all server connections are wrapped +// with a verbose logging wrapper. +const debugServerConnections = false + // Create new connection from rwc. func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) { c = new(conn) c.remoteAddr = rwc.RemoteAddr().String() c.server = srv c.rwc = rwc + if debugServerConnections { + c.rwc = newLoggingConn("server", c.rwc) + } + c.sr = switchReader{r: c.rwc} c.body = make([]byte, sniffLen) - c.lr = io.LimitReader(rwc, noLimit).(*io.LimitedReader) + c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader) br := bufio.NewReader(c.lr) - bw := bufio.NewWriter(rwc) + bw := bufio.NewWriter(c.rwc) c.buf = bufio.NewReadWriter(br, bw) return c, nil } @@ -207,9 +302,9 @@ type expectContinueReader struct { func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { if ecr.closed { - return 0, errors.New("http: Read after Close on request Body") + return 0, ErrBodyReadAfterClose } - if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked { + if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() { ecr.resp.wroteContinue = true io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n") ecr.resp.conn.buf.Flush() @@ -232,7 +327,7 @@ var errTooLarge = errors.New("http: request too large") // Read next request from connection. func (c *conn) readRequest() (w *response, err error) { - if c.hijacked { + if c.hijacked() { return nil, ErrHijacked } c.lr.N = int64(c.server.maxHeaderBytes()) + 4096 /* bufio slop */ @@ -273,7 +368,7 @@ func (w *response) Header() Header { const maxPostHandlerReadBytes = 256 << 10 func (w *response) WriteHeader(code int) { - if w.conn.hijacked { + if w.conn.hijacked() { log.Print("http: response.WriteHeader on hijacked connection") return } @@ -363,6 +458,8 @@ func (w *response) WriteHeader(code int) { if w.req.Method == "HEAD" || code == StatusNotModified { // do nothing + } else if code == StatusNoContent { + w.header.Del("Transfer-Encoding") } else if hasCL { w.contentLength = contentLength w.header.Del("Transfer-Encoding") @@ -447,7 +544,7 @@ func (w *response) bodyAllowed() bool { } func (w *response) Write(data []byte) (n int, err error) { - if w.conn.hijacked { + if w.conn.hijacked() { log.Print("http: response.Write on hijacked connection") return 0, ErrHijacked } @@ -496,7 +593,7 @@ func (w *response) Write(data []byte) (n int, err error) { // then there would be fewer chunk headers. // On the other hand, it would make hijacking more difficult. if w.chunking { - fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt + fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) } n, err = w.conn.buf.Write(data) if err == nil && w.chunking { @@ -517,7 +614,7 @@ func (w *response) finishRequest() { // HTTP/1.0 clients keep their "keep-alive" connections alive, and for // HTTP/1.1 clients is just as good as the alternative: sending a // chunked response and immediately sending the zero-length EOF chunk. - if w.written == 0 && w.header.get("Content-Length") == "" { + if w.written == 0 && w.header.get("Content-Length") == "" && w.req.Method != "HEAD" { w.header.Set("Content-Length", "0") } // If this was an HTTP/1.0 request with keep-alive and we sent a @@ -610,10 +707,10 @@ func (c *conn) serve() { return } - var buf bytes.Buffer - fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err) - buf.Write(debug.Stack()) - log.Print(buf.String()) + const size = 4096 + buf := make([]byte, size) + buf = buf[:runtime.Stack(buf, false)] + log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) if c.rwc != nil { // may be nil if connection hijacked c.rwc.Close() @@ -665,21 +762,7 @@ func (c *conn) serve() { } req.Header.Del("Expect") } else if req.Header.get("Expect") != "" { - // TODO(bradfitz): let ServeHTTP handlers handle - // requests with non-standard expectation[s]? Seems - // theoretical at best, and doesn't fit into the - // current ServeHTTP model anyway. We'd need to - // make the ResponseWriter an optional - // "ExpectReplier" interface or something. - // - // For now we'll just obey RFC 2616 14.20 which says - // "If a server receives a request containing an - // Expect field that includes an expectation- - // extension that it does not support, it MUST - // respond with a 417 (Expectation Failed) status." - w.Header().Set("Connection", "close") - w.WriteHeader(StatusExpectationFailed) - w.finishRequest() + w.sendExpectationFailed() break } @@ -694,7 +777,7 @@ func (c *conn) serve() { // [*] Not strictly true: HTTP pipelining. We could let them all process // in parallel even if their responses need to be serialized. handler.ServeHTTP(w, w.req) - if c.hijacked { + if c.hijacked() { return } w.finishRequest() @@ -708,18 +791,32 @@ func (c *conn) serve() { c.close() } +func (w *response) sendExpectationFailed() { + // TODO(bradfitz): let ServeHTTP handlers handle + // requests with non-standard expectation[s]? Seems + // theoretical at best, and doesn't fit into the + // current ServeHTTP model anyway. We'd need to + // make the ResponseWriter an optional + // "ExpectReplier" interface or something. + // + // For now we'll just obey RFC 2616 14.20 which says + // "If a server receives a request containing an + // Expect field that includes an expectation- + // extension that it does not support, it MUST + // respond with a 417 (Expectation Failed) status." + w.Header().Set("Connection", "close") + w.WriteHeader(StatusExpectationFailed) + w.finishRequest() +} + // Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter // and a Hijacker. func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) { - if w.conn.hijacked { - return nil, nil, ErrHijacked - } - w.conn.hijacked = true - rwc = w.conn.rwc - buf = w.conn.buf - w.conn.rwc = nil - w.conn.buf = nil - return + return w.conn.hijack() +} + +func (w *response) CloseNotify() <-chan bool { + return w.conn.closeNotify() } // The HandlerFunc type is an adapter to allow the use of @@ -1310,3 +1407,45 @@ func (tw *timeoutWriter) WriteHeader(code int) { tw.mu.Unlock() tw.w.WriteHeader(code) } + +// loggingConn is used for debugging. +type loggingConn struct { + name string + net.Conn +} + +var ( + uniqNameMu sync.Mutex + uniqNameNext = make(map[string]int) +) + +func newLoggingConn(baseName string, c net.Conn) net.Conn { + uniqNameMu.Lock() + defer uniqNameMu.Unlock() + uniqNameNext[baseName]++ + return &loggingConn{ + name: fmt.Sprintf("%s-%d", baseName, uniqNameNext[baseName]), + Conn: c, + } +} + +func (c *loggingConn) Write(p []byte) (n int, err error) { + log.Printf("%s.Write(%d) = ....", c.name, len(p)) + n, err = c.Conn.Write(p) + log.Printf("%s.Write(%d) = %d, %v", c.name, len(p), n, err) + return +} + +func (c *loggingConn) Read(p []byte) (n int, err error) { + log.Printf("%s.Read(%d) = ....", c.name, len(p)) + n, err = c.Conn.Read(p) + log.Printf("%s.Read(%d) = %d, %v", c.name, len(p), n, err) + return +} + +func (c *loggingConn) Close() (err error) { + log.Printf("%s.Close() = ...", c.name) + err = c.Conn.Close() + log.Printf("%s.Close() = %v", c.name, err) + return +} diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go index 9833ddd..70ea15b 100644 --- a/libgo/go/net/http/transfer.go +++ b/libgo/go/net/http/transfer.go @@ -294,10 +294,19 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { return err } - t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) + realLength, err := fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) if err != nil { return err } + if isResponse && t.RequestMethod == "HEAD" { + if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil { + return err + } else { + t.ContentLength = n + } + } else { + t.ContentLength = realLength + } // Trailer t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding) @@ -310,7 +319,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { // See RFC2616, section 4.4. switch msg.(type) { case *Response: - if t.ContentLength == -1 && + if realLength == -1 && !chunked(t.TransferEncoding) && bodyAllowedForStatus(t.StatusCode) { // Unbounded body. @@ -323,11 +332,11 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { switch { case chunked(t.TransferEncoding): t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close} - case t.ContentLength >= 0: + case realLength >= 0: // TODO: limit the Content-Length. This is an easy DoS vector. - t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close} + t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close} default: - // t.ContentLength < 0, i.e. "Content-Length" not mentioned in header + // realLength < 0, i.e. "Content-Length" not mentioned in header if t.Close { // Close semantics (i.e. HTTP/1.0) t.Body = &body{Reader: r, closing: t.Close} @@ -434,9 +443,9 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header, // Logic based on Content-Length cl := strings.TrimSpace(header.get("Content-Length")) if cl != "" { - n, err := strconv.ParseInt(cl, 10, 64) - if err != nil || n < 0 { - return -1, &badStringError{"bad Content-Length", cl} + n, err := parseContentLength(cl) + if err != nil { + return -1, err } return n, nil } else { @@ -525,11 +534,11 @@ type body struct { res *response // response writer for server requests, else nil } -// ErrBodyReadAfterClose is returned when reading a Request Body after -// the body has been closed. This typically happens when the body is +// ErrBodyReadAfterClose is returned when reading a Request or Response +// Body after the body has been closed. This typically happens when the body is // read after an HTTP Handler calls WriteHeader or Write on its // ResponseWriter. -var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed request Body") +var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body") func (b *body) Read(p []byte) (n int, err error) { if b.closed { @@ -641,3 +650,18 @@ func (b *body) Close() error { } return nil } + +// parseContentLength trims whitespace from s and returns -1 if no value +// is set, or the value if it's >= 0. +func parseContentLength(cl string) (int64, error) { + cl = strings.TrimSpace(cl) + if cl == "" { + return -1, nil + } + n, err := strconv.ParseInt(cl, 10, 64) + if err != nil || n < 0 { + return 0, &badStringError{"bad Content-Length", cl} + } + return n, nil + +} diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 38ea6f7..7b4afeb 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -24,15 +24,14 @@ import ( "os" "strings" "sync" - "sync/atomic" "time" ) // DefaultTransport is the default implementation of Transport and is -// used by DefaultClient. It establishes a new network connection for -// each call to Do and uses HTTP proxies as directed by the -// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy) -// environment variables. +// used by DefaultClient. It establishes network connections as needed +// and caches them for reuse by subsequent calls. It uses HTTP proxies +// as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and +// $no_proxy) environment variables. var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment} // DefaultMaxIdleConnsPerHost is the default value of Transport's @@ -71,7 +70,7 @@ type Transport struct { DisableCompression bool // MaxIdleConnsPerHost, if non-zero, controls the maximum idle - // (keep-alive) to keep to keep per-host. If zero, + // (keep-alive) to keep per-host. If zero, // DefaultMaxIdleConnsPerHost is used. MaxIdleConnsPerHost int } @@ -91,7 +90,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) { return nil, nil } proxyURL, err := url.Parse(proxy) - if err != nil || proxyURL.Scheme == "" { + if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") { if u, err := url.Parse("http://" + proxy); err == nil { proxyURL = u err = nil @@ -605,22 +604,23 @@ func (pc *persistConn) readLoop() { alive = false } - // TODO(bradfitz): this hasBody conflicts with the defition - // above which excludes HEAD requests. Is this one - // incomplete? - hasBody := resp != nil && resp.ContentLength != 0 + hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0 var waitForBodyRead chan bool if hasBody { lastbody = resp.Body waitForBodyRead = make(chan bool, 1) - resp.Body.(*bodyEOFSignal).fn = func() { - if alive && !pc.t.putIdleConn(pc) { - alive = false + resp.Body.(*bodyEOFSignal).fn = func(err error) { + alive1 := alive + if err != nil { + alive1 = false } - if !alive || pc.isBroken() { + if alive1 && !pc.t.putIdleConn(pc) { + alive1 = false + } + if !alive1 || pc.isBroken() { pc.close() } - waitForBodyRead <- true + waitForBodyRead <- alive1 } } @@ -644,7 +644,7 @@ func (pc *persistConn) readLoop() { // Wait for the just-returned response body to be fully consumed // before we race and peek on the underlying bufio reader. if waitForBodyRead != nil { - <-waitForBodyRead + alive = <-waitForBodyRead } if !alive { @@ -810,50 +810,61 @@ func canonicalAddr(url *url.URL) string { } // bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most -// once, right before the final Read() or Close() call returns, but after -// EOF has been seen. +// once, right before its final (error-producing) Read or Close call +// returns. type bodyEOFSignal struct { - body io.ReadCloser - fn func() - isClosed uint32 // atomic bool, non-zero if true - once sync.Once + body io.ReadCloser + mu sync.Mutex // guards closed, rerr and fn + closed bool // whether Close has been called + rerr error // sticky Read error + fn func(error) // error will be nil on Read io.EOF } func (es *bodyEOFSignal) Read(p []byte) (n int, err error) { - n, err = es.body.Read(p) - if es.closed() && n > 0 { - panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725") + es.mu.Lock() + closed, rerr := es.closed, es.rerr + es.mu.Unlock() + if closed { + return 0, errors.New("http: read on closed response body") } - if err == io.EOF { - es.condfn() + if rerr != nil { + return 0, rerr } - return -} -func (es *bodyEOFSignal) Close() (err error) { - if !es.setClosed() { - // already closed - return nil - } - err = es.body.Close() - if err == nil { - es.condfn() + n, err = es.body.Read(p) + if err != nil { + es.mu.Lock() + defer es.mu.Unlock() + if es.rerr == nil { + es.rerr = err + } + es.condfn(err) } return } -func (es *bodyEOFSignal) condfn() { - if es.fn != nil { - es.once.Do(es.fn) +func (es *bodyEOFSignal) Close() error { + es.mu.Lock() + defer es.mu.Unlock() + if es.closed { + return nil } + es.closed = true + err := es.body.Close() + es.condfn(err) + return err } -func (es *bodyEOFSignal) closed() bool { - return atomic.LoadUint32(&es.isClosed) != 0 -} - -func (es *bodyEOFSignal) setClosed() bool { - return atomic.CompareAndSwapUint32(&es.isClosed, 0, 1) +// caller must hold es.mu. +func (es *bodyEOFSignal) condfn(err error) { + if es.fn == nil { + return + } + if err == io.EOF { + err = nil + } + es.fn(err) + es.fn = nil } type readFirstCloseBoth struct { diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index e4072e8..f1d4158 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -281,7 +281,7 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) { c := &Client{Transport: tr} // Start 3 outstanding requests and wait for the server to get them. - // Their responses will hang until we we write to resch, though. + // Their responses will hang until we write to resch, though. donech := make(chan bool) doReq := func() { resp, err := c.Get(ts.URL) @@ -464,7 +464,7 @@ func TestTransportHeadResponses(t *testing.T) { if e, g := "123", res.Header.Get("Content-Length"); e != g { t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g) } - if e, g := int64(0), res.ContentLength; e != g { + if e, g := int64(123), res.ContentLength; e != g { t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g) } } @@ -857,6 +857,30 @@ func TestIssue3595(t *testing.T) { } } +// From http://golang.org/issue/4454 , +// "client fails to handle requests with no body and chunked encoding" +func TestChunkedNoContent(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.WriteHeader(StatusNoContent) + })) + defer ts.Close() + + for _, closeBody := range []bool{true, false} { + c := &Client{Transport: &Transport{}} + const n = 4 + for i := 1; i <= n; i++ { + res, err := c.Get(ts.URL) + if err != nil { + t.Errorf("closingBody=%v, req %d/%d: %v", closeBody, i, n, err) + } else { + if closeBody { + res.Body.Close() + } + } + } + } +} + func TestTransportConcurrency(t *testing.T) { const maxProcs = 16 const numReqs = 500 @@ -901,6 +925,113 @@ func TestTransportConcurrency(t *testing.T) { wg.Wait() } +func TestIssue4191_InfiniteGetTimeout(t *testing.T) { + const debug = false + mux := NewServeMux() + mux.HandleFunc("/get", func(w ResponseWriter, r *Request) { + io.Copy(w, neverEnding('a')) + }) + ts := httptest.NewServer(mux) + + client := &Client{ + Transport: &Transport{ + Dial: func(n, addr string) (net.Conn, error) { + conn, err := net.Dial(n, addr) + if err != nil { + return nil, err + } + conn.SetDeadline(time.Now().Add(100 * time.Millisecond)) + if debug { + conn = NewLoggingConn("client", conn) + } + return conn, nil + }, + DisableKeepAlives: true, + }, + } + + nRuns := 5 + if testing.Short() { + nRuns = 1 + } + for i := 0; i < nRuns; i++ { + if debug { + println("run", i+1, "of", nRuns) + } + sres, err := client.Get(ts.URL + "/get") + if err != nil { + t.Errorf("Error issuing GET: %v", err) + break + } + _, err = io.Copy(ioutil.Discard, sres.Body) + if err == nil { + t.Errorf("Unexpected successful copy") + break + } + } + if debug { + println("tests complete; waiting for handlers to finish") + } + ts.Close() +} + +func TestIssue4191_InfiniteGetToPutTimeout(t *testing.T) { + const debug = false + mux := NewServeMux() + mux.HandleFunc("/get", func(w ResponseWriter, r *Request) { + io.Copy(w, neverEnding('a')) + }) + mux.HandleFunc("/put", func(w ResponseWriter, r *Request) { + defer r.Body.Close() + io.Copy(ioutil.Discard, r.Body) + }) + ts := httptest.NewServer(mux) + + client := &Client{ + Transport: &Transport{ + Dial: func(n, addr string) (net.Conn, error) { + conn, err := net.Dial(n, addr) + if err != nil { + return nil, err + } + conn.SetDeadline(time.Now().Add(100 * time.Millisecond)) + if debug { + conn = NewLoggingConn("client", conn) + } + return conn, nil + }, + DisableKeepAlives: true, + }, + } + + nRuns := 5 + if testing.Short() { + nRuns = 1 + } + for i := 0; i < nRuns; i++ { + if debug { + println("run", i+1, "of", nRuns) + } + sres, err := client.Get(ts.URL + "/get") + if err != nil { + t.Errorf("Error issuing GET: %v", err) + break + } + req, _ := NewRequest("PUT", ts.URL+"/put", sres.Body) + _, err = client.Do(req) + if err == nil { + sres.Body.Close() + t.Errorf("Unexpected successful PUT") + break + } + sres.Body.Close() + } + if debug { + println("tests complete; waiting for handlers to finish") + } + ts.Close() +} + type fooProto struct{} func (fooProto) RoundTrip(req *Request) (*Response, error) { @@ -937,6 +1068,9 @@ var proxyFromEnvTests = []struct { wanterr error }{ {"127.0.0.1:8080", "http://127.0.0.1:8080", nil}, + {"cache.corp.example.com:1234", "http://cache.corp.example.com:1234", nil}, + {"cache.corp.example.com", "http://cache.corp.example.com", nil}, + {"https://cache.corp.example.com", "https://cache.corp.example.com", nil}, {"http://127.0.0.1:8080", "http://127.0.0.1:8080", nil}, {"https://127.0.0.1:8080", "https://127.0.0.1:8080", nil}, {"", "<nil>", nil}, diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go index 979d7ac..0aac3d1 100644 --- a/libgo/go/net/ip.go +++ b/libgo/go/net/ip.go @@ -36,6 +36,7 @@ type IPMask []byte type IPNet struct { IP IP // network number Mask IPMask // network mask + Zone string // IPv6 scoped addressing zone } // IPv4 returns the IP address (in 16-byte form) of the @@ -645,5 +646,5 @@ func ParseCIDR(s string) (IP, *IPNet, error) { return nil, nil, &ParseError{"CIDR address", s} } m := CIDRMask(n, 8*iplen) - return ip, &IPNet{ip.Mask(m), m}, nil + return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil } diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go index df647ef..8324d2a 100644 --- a/libgo/go/net/ip_test.go +++ b/libgo/go/net/ip_test.go @@ -114,23 +114,23 @@ var parsecidrtests = []struct { net *IPNet err error }{ - {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255)}, nil}, - {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil}, - {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil}, - {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255)}, nil}, - {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil}, - {"::1/128", ParseIP("::1"), &IPNet{ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil}, - {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil}, - {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil}, - {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil}, - {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil}, - {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::"))}, nil}, - {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::"))}, nil}, - {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::"))}, nil}, - {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil}, - {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil}, - {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, - {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, + {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 255)}, nil}, + {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IP: IPv4(0, 0, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil}, + {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil}, + {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 1), Mask: IPv4Mask(255, 255, 255, 255)}, nil}, + {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil}, + {"::1/128", ParseIP("::1"), &IPNet{IP: ParseIP("::1"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil}, + {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil}, + {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil}, + {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil}, + {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil}, + {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:8000::"))}, nil}, + {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff::"))}, nil}, + {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{IP: ParseIP("abcd:2344::"), Mask: IPMask(ParseIP("ffff:fffe::"))}, nil}, + {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil}, + {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil}, + {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, + {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil}, {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{"CIDR address", "192.168.1.1/255.255.255.0"}}, {"192.168.1.1/35", nil, nil, &ParseError{"CIDR address", "192.168.1.1/35"}}, {"2001:db8::1/-1", nil, nil, &ParseError{"CIDR address", "2001:db8::1/-1"}}, @@ -154,14 +154,14 @@ var ipnetcontainstests = []struct { net *IPNet ok bool }{ - {IPv4(172, 16, 1, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(12, 32)}, true}, - {IPv4(172, 24, 0, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(13, 32)}, false}, - {IPv4(192, 168, 0, 3), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 0, 255, 252)}, true}, - {IPv4(192, 168, 0, 4), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 255, 0, 252)}, false}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), CIDRMask(47, 128)}, true}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:2::"), CIDRMask(47, 128)}, false}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("ffff:0:ffff::"))}, true}, - {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("0:0:0:ffff::"))}, false}, + {IPv4(172, 16, 1, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(12, 32)}, true}, + {IPv4(172, 24, 0, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(13, 32)}, false}, + {IPv4(192, 168, 0, 3), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 0, 255, 252)}, true}, + {IPv4(192, 168, 0, 4), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 255, 0, 252)}, false}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: CIDRMask(47, 128)}, true}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:2::"), Mask: CIDRMask(47, 128)}, false}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("ffff:0:ffff::"))}, true}, + {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("0:0:0:ffff::"))}, false}, } func TestIPNetContains(t *testing.T) { @@ -176,10 +176,10 @@ var ipnetstringtests = []struct { in *IPNet out string }{ - {&IPNet{IPv4(192, 168, 1, 0), CIDRMask(26, 32)}, "192.168.1.0/26"}, - {&IPNet{IPv4(192, 168, 1, 0), IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"}, - {&IPNet{ParseIP("2001:db8::"), CIDRMask(55, 128)}, "2001:db8::/55"}, - {&IPNet{ParseIP("2001:db8::"), IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"}, + {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"}, + {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"}, + {&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"}, + {&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"}, } func TestIPNetString(t *testing.T) { @@ -233,27 +233,27 @@ var networknumberandmasktests = []struct { in IPNet out IPNet }{ - {IPNet{v4addr, v4mask}, IPNet{v4addr, v4mask}}, - {IPNet{v4addr, v4mappedv6mask}, IPNet{v4addr, v4mask}}, - {IPNet{v4mappedv6addr, v4mappedv6mask}, IPNet{v4addr, v4mask}}, - {IPNet{v4mappedv6addr, v6mask}, IPNet{v4addr, v4maskzero}}, - {IPNet{v4addr, v6mask}, IPNet{v4addr, v4maskzero}}, - {IPNet{v6addr, v6mask}, IPNet{v6addr, v6mask}}, - {IPNet{v6addr, v4mappedv6mask}, IPNet{v6addr, v4mappedv6mask}}, - {in: IPNet{v6addr, v4mask}}, - {in: IPNet{v4addr, badmask}}, - {in: IPNet{v4mappedv6addr, badmask}}, - {in: IPNet{v6addr, badmask}}, - {in: IPNet{badaddr, v4mask}}, - {in: IPNet{badaddr, v4mappedv6mask}}, - {in: IPNet{badaddr, v6mask}}, - {in: IPNet{badaddr, badmask}}, + {IPNet{IP: v4addr, Mask: v4mask}, IPNet{IP: v4addr, Mask: v4mask}}, + {IPNet{IP: v4addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}}, + {IPNet{IP: v4mappedv6addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}}, + {IPNet{IP: v4mappedv6addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}}, + {IPNet{IP: v4addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}}, + {IPNet{IP: v6addr, Mask: v6mask}, IPNet{IP: v6addr, Mask: v6mask}}, + {IPNet{IP: v6addr, Mask: v4mappedv6mask}, IPNet{IP: v6addr, Mask: v4mappedv6mask}}, + {in: IPNet{IP: v6addr, Mask: v4mask}}, + {in: IPNet{IP: v4addr, Mask: badmask}}, + {in: IPNet{IP: v4mappedv6addr, Mask: badmask}}, + {in: IPNet{IP: v6addr, Mask: badmask}}, + {in: IPNet{IP: badaddr, Mask: v4mask}}, + {in: IPNet{IP: badaddr, Mask: v4mappedv6mask}}, + {in: IPNet{IP: badaddr, Mask: v6mask}}, + {in: IPNet{IP: badaddr, Mask: badmask}}, } func TestNetworkNumberAndMask(t *testing.T) { for _, tt := range networknumberandmasktests { ip, m := networkNumberAndMask(&tt.in) - out := &IPNet{ip, m} + out := &IPNet{IP: ip, Mask: m} if !reflect.DeepEqual(&tt.out, out) { t.Errorf("networkNumberAndMask(%v) = %v; want %v", tt.in, out, &tt.out) } @@ -268,6 +268,7 @@ var splitjointests = []struct { {"www.google.com", "80", "www.google.com:80"}, {"127.0.0.1", "1234", "127.0.0.1:1234"}, {"::1", "80", "[::1]:80"}, + {"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior } func TestSplitHostPort(t *testing.T) { diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go index 43b02ae..f21889f 100644 --- a/libgo/go/net/ipraw_test.go +++ b/libgo/go/net/ipraw_test.go @@ -9,11 +9,46 @@ package net import ( "bytes" "os" + "reflect" "syscall" "testing" "time" ) +var resolveIPAddrTests = []struct { + net string + litAddr string + addr *IPAddr + err error +}{ + {"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + {"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + {"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + + {"ip", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, + {"ip6", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, + {"ip6:icmp", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, + + {"", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, // Go 1.0 behavior + {"", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, // Go 1.0 behavior + + {"l2tp", "127.0.0.1", nil, UnknownNetworkError("l2tp")}, + {"l2tp:gre", "127.0.0.1", nil, UnknownNetworkError("l2tp:gre")}, + {"tcp", "1.2.3.4:123", nil, UnknownNetworkError("tcp")}, +} + +func TestResolveIPAddr(t *testing.T) { + for _, tt := range resolveIPAddrTests { + addr, err := ResolveIPAddr(tt.net, tt.litAddr) + if err != tt.err { + t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + } + if !reflect.DeepEqual(addr, tt.addr) { + t.Fatalf("got %#v; expected %#v", addr, tt.addr) + } + } +} + var icmpTests = []struct { net string laddr string diff --git a/libgo/go/net/iprawsock.go b/libgo/go/net/iprawsock.go index d7bffc6..13bfd62 100644 --- a/libgo/go/net/iprawsock.go +++ b/libgo/go/net/iprawsock.go @@ -2,17 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// (Raw) IP sockets +// Raw IP sockets package net -import ( - "time" -) - // IPAddr represents the address of an IP end point. type IPAddr struct { - IP IP + IP IP + Zone string // IPv6 scoped addressing zone } // Network returns the address's network name, "ip". @@ -27,47 +24,23 @@ func (a *IPAddr) String() string { // ResolveIPAddr parses addr as an IP address and resolves domain // names to numeric addresses on the network net, which must be -// "ip", "ip4" or "ip6". A literal IPv6 host address must be -// enclosed in square brackets, as in "[::]". +// "ip", "ip4" or "ip6". func ResolveIPAddr(net, addr string) (*IPAddr, error) { - return resolveIPAddr(net, addr, noDeadline) -} - -func resolveIPAddr(net, addr string, deadline time.Time) (*IPAddr, error) { - ip, err := hostToIP(net, addr, deadline) + if net == "" { // a hint wildcard for Go 1.0 undocumented behavior + net = "ip" + } + afnet, _, err := parseDialNetwork(net) if err != nil { return nil, err } - return &IPAddr{ip}, nil -} - -// Convert "host" into IP address. -func hostToIP(net, host string, deadline time.Time) (ip IP, err error) { - var addr IP - // Try as an IP address. - addr = ParseIP(host) - if addr == nil { - filter := anyaddr - if net != "" && net[len(net)-1] == '4' { - filter = ipv4only - } - if net != "" && net[len(net)-1] == '6' { - filter = ipv6only - } - // Not an IP address. Try as a DNS name. - addrs, err1 := lookupHostDeadline(host, deadline) - if err1 != nil { - err = err1 - goto Error - } - addr = firstFavoriteAddr(filter, addrs) - if addr == nil { - // should not happen - err = &AddrError{"LookupHost returned no suitable address", addrs[0]} - goto Error - } + switch afnet { + case "ip", "ip4", "ip6": + default: + return nil, UnknownNetworkError(net) + } + a, err := resolveInternetAddr(afnet, addr, noDeadline) + if err != nil { + return nil, err } - return addr, nil -Error: - return nil, err + return a.(*IPAddr), nil } diff --git a/libgo/go/net/iprawsock_plan9.go b/libgo/go/net/iprawsock_plan9.go index e77c547..88e3b2c 100644 --- a/libgo/go/net/iprawsock_plan9.go +++ b/libgo/go/net/iprawsock_plan9.go @@ -2,83 +2,21 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// (Raw) IP sockets stubs for Plan 9 +// Raw IP sockets for Plan 9 package net import ( - "os" "syscall" "time" ) // IPConn is the implementation of the Conn and PacketConn interfaces // for IP network connections. -type IPConn bool - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *IPConn) Read(b []byte) (int, error) { - return 0, syscall.EPLAN9 -} - -// Write implements the Conn Write method. -func (c *IPConn) Write(b []byte) (int, error) { - return 0, syscall.EPLAN9 -} - -// LocalAddr returns the local network address. -func (c *IPConn) LocalAddr() Addr { - return nil -} - -// RemoteAddr returns the remote network address. -func (c *IPConn) RemoteAddr() Addr { - return nil -} - -// SetDeadline implements the Conn SetDeadline method. -func (c *IPConn) SetDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *IPConn) SetReadDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *IPConn) SetWriteDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetReadBuffer sets the size of the operating system's receive -// buffer associated with the connection. -func (c *IPConn) SetReadBuffer(bytes int) error { - return syscall.EPLAN9 -} - -// SetWriteBuffer sets the size of the operating system's transmit -// buffer associated with the connection. -func (c *IPConn) SetWriteBuffer(bytes int) error { - return syscall.EPLAN9 +type IPConn struct { + conn } -// File returns a copy of the underlying os.File, set to blocking -// mode. It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *IPConn) File() (f *os.File, err error) { - return nil, syscall.EPLAN9 -} - -// Close closes the IP connection. -func (c *IPConn) Close() error { - return syscall.EPLAN9 -} - -// IP-specific methods. - // ReadFromIP reads an IP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address // that was on the packet. diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go index 00e87cf..7a8cd44 100644 --- a/libgo/go/net/iprawsock_posix.go +++ b/libgo/go/net/iprawsock_posix.go @@ -4,7 +4,7 @@ // +build darwin freebsd linux netbsd openbsd windows -// (Raw) IP sockets +// Raw IP sockets for POSIX package net @@ -16,9 +16,9 @@ import ( func sockaddrToIP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: - return &IPAddr{sa.Addr[0:]} + return &IPAddr{IP: sa.Addr[0:]} case *syscall.SockaddrInet6: - return &IPAddr{sa.Addr[0:]} + return &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))} } return nil } @@ -41,7 +41,7 @@ func (a *IPAddr) isWildcard() bool { } func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, 0) + return ipToSockaddr(family, a.IP, 0, a.Zone) } func (a *IPAddr) toAddr() sockaddr { @@ -59,8 +59,6 @@ type IPConn struct { func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} } -// IP-specific methods. - // ReadFromIP reads an IP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address // that was on the packet. @@ -78,14 +76,14 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) { n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { case *syscall.SockaddrInet4: - addr = &IPAddr{sa.Addr[0:]} + addr = &IPAddr{IP: sa.Addr[0:]} if len(b) >= IPv4len { // discard ipv4 header hsize := (int(b[0]) & 0xf) * 4 copy(b, b[hsize:]) n -= hsize } case *syscall.SockaddrInet6: - addr = &IPAddr{sa.Addr[0:]} + addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))} } return n, addr, err } @@ -95,8 +93,8 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } - n, uaddr, err := c.ReadFromIP(b) - return n, uaddr.toAddr(), err + n, addr, err := c.ReadFromIP(b) + return n, addr.toAddr(), err } // ReadMsgIP reads a packet from c, copying the payload into b and the @@ -111,9 +109,9 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob) switch sa := sa.(type) { case *syscall.SockaddrInet4: - addr = &IPAddr{sa.Addr[0:]} + addr = &IPAddr{IP: sa.Addr[0:]} case *syscall.SockaddrInet6: - addr = &IPAddr{sa.Addr[0:]} + addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))} } return } diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index 9d48e8c..5636c85 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.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. -// IP sockets +// Internet protocol family sockets package net @@ -72,15 +72,18 @@ func (e InvalidAddrError) Temporary() bool { return false } // "host:port" or "[host]:port" into host and port. // The latter form must be used when host contains a colon. func SplitHostPort(hostport string) (host, port string, err error) { + host, port, _, err = splitHostPort(hostport) + return +} + +func splitHostPort(hostport string) (host, port, zone string, err error) { // The port starts after the last colon. i := last(hostport, ':') if i < 0 { err = &AddrError{"missing port in address", hostport} return } - - host, port = hostport[0:i], hostport[i+1:] - + host, port = hostport[:i], hostport[i+1:] // Can put brackets around host ... if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { host = host[1 : len(host)-1] @@ -104,42 +107,84 @@ func JoinHostPort(host, port string) string { return host + ":" + port } -// Convert "host:port" into IP address and port. -func hostPortToIP(net, hostport string, deadline time.Time) (ip IP, iport int, err error) { - host, port, err := SplitHostPort(hostport) - if err != nil { - return nil, 0, err - } - - var addr IP - if host != "" { - // Try as an IP address. - addr = ParseIP(host) - if addr == nil { - var filter func(IP) IP - if net != "" && net[len(net)-1] == '4' { - filter = ipv4only - } - if net != "" && net[len(net)-1] == '6' { - filter = ipv6only - } - // Not an IP address. Try as a DNS name. - addrs, err := lookupHostDeadline(host, deadline) - if err != nil { - return nil, 0, err +func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { + var ( + err error + host, port, zone string + portnum int + ) + switch net { + case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": + if addr != "" { + if host, port, zone, err = splitHostPort(addr); err != nil { + return nil, err } - addr = firstFavoriteAddr(filter, addrs) - if addr == nil { - // should not happen - return nil, 0, &AddrError{"LookupHost returned no suitable address", addrs[0]} + if portnum, err = parsePort(net, port); err != nil { + return nil, err } } + case "ip", "ip4", "ip6": + if addr != "" { + host = addr + } + default: + return nil, UnknownNetworkError(net) } - - p, err := parsePort(net, port) + inetaddr := func(net string, ip IP, port int, zone string) Addr { + switch net { + case "tcp", "tcp4", "tcp6": + return &TCPAddr{IP: ip, Port: port, Zone: zone} + case "udp", "udp4", "udp6": + return &UDPAddr{IP: ip, Port: port, Zone: zone} + case "ip", "ip4", "ip6": + return &IPAddr{IP: ip, Zone: zone} + } + return nil + } + if host == "" { + return inetaddr(net, nil, portnum, zone), nil + } + // Try as an IP address. + if ip := ParseIP(host); ip != nil { + return inetaddr(net, ip, portnum, zone), nil + } + var filter func(IP) IP + if net != "" && net[len(net)-1] == '4' { + filter = ipv4only + } + if net != "" && net[len(net)-1] == '6' { + filter = ipv6only + } + // Try as a DNS name. + addrs, err := lookupHostDeadline(host, deadline) if err != nil { - return nil, 0, err + return nil, err + } + ip := firstFavoriteAddr(filter, addrs) + if ip == nil { + // should not happen + return nil, &AddrError{"LookupHost returned no suitable address", addrs[0]} } + return inetaddr(net, ip, portnum, zone), nil +} - return addr, p, nil +func zoneToString(zone int) string { + if zone == 0 { + return "" + } + if ifi, err := InterfaceByIndex(zone); err == nil { + return ifi.Name + } + return itod(uint(zone)) +} + +func zoneToInt(zone string) int { + if zone == "" { + return 0 + } + if ifi, err := InterfaceByName(zone); err == nil { + return ifi.Index + } + n, _, _ := dtoi(zone, 0) + return n } diff --git a/libgo/go/net/ipsock_plan9.go b/libgo/go/net/ipsock_plan9.go index 138c3b4..eaef768 100644 --- a/libgo/go/net/ipsock_plan9.go +++ b/libgo/go/net/ipsock_plan9.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. -// IP sockets stubs for Plan 9 +// Internet protocol family sockets for Plan 9 package net @@ -59,9 +59,9 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) { } switch proto { case "tcp": - addr = &TCPAddr{ip, port} + addr = &TCPAddr{IP: ip, Port: port} case "udp": - addr = &UDPAddr{ip, port} + addr = &UDPAddr{IP: ip, Port: port} default: return nil, errors.New("unknown protocol " + proto) } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index 87a2288..4c37616 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -4,6 +4,8 @@ // +build darwin freebsd linux netbsd openbsd windows +// Internet protocol family sockets for POSIX + package net import ( @@ -155,7 +157,7 @@ Error: return nil, &OpError{mode, net, addr, err} } -func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { +func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) { switch family { case syscall.AF_INET: if len(ip) == 0 { @@ -164,12 +166,12 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { if ip = ip.To4(); ip == nil { return nil, InvalidAddrError("non-IPv4 address") } - s := new(syscall.SockaddrInet4) + sa := new(syscall.SockaddrInet4) for i := 0; i < IPv4len; i++ { - s.Addr[i] = ip[i] + sa.Addr[i] = ip[i] } - s.Port = port - return s, nil + sa.Port = port + return sa, nil case syscall.AF_INET6: if len(ip) == 0 { ip = IPv6zero @@ -183,12 +185,13 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) { if ip = ip.To16(); ip == nil { return nil, InvalidAddrError("non-IPv6 address") } - s := new(syscall.SockaddrInet6) + sa := new(syscall.SockaddrInet6) for i := 0; i < IPv6len; i++ { - s.Addr[i] = ip[i] + sa.Addr[i] = ip[i] } - s.Port = port - return s, nil + sa.Port = port + sa.ZoneId = uint32(zoneToInt(zone)) + return sa, nil } return nil, InvalidAddrError("unexpected socket family") } diff --git a/libgo/go/net/multicast_posix_test.go b/libgo/go/net/multicast_posix_test.go index d4a8a35..bcc13ee 100644 --- a/libgo/go/net/multicast_posix_test.go +++ b/libgo/go/net/multicast_posix_test.go @@ -21,26 +21,26 @@ var multicastListenerTests = []struct { }{ // cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers - {"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false}, - {"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false}, - {"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true}, + {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false}, + {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, 0, false}, + {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true}, - {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false}, - {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false}, + {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false}, + {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, 0, false}, - {"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, 0, true}, - {"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true}, - {"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, 0, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true}, + {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true}, } // TestMulticastListener tests both single and double listen to a test diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index feb92a2..a3d1759 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -44,7 +44,9 @@ package net import ( "errors" + "io" "os" + "sync" "syscall" "time" ) @@ -195,9 +197,13 @@ func (c *conn) SetWriteBuffer(bytes int) error { return setWriteBuffer(c.fd, bytes) } -// File returns a copy of the underlying os.File, set to blocking mode. +// File sets the underlying os.File to blocking mode and returns a copy. // It is the caller's responsibility to close f when finished. // Closing c does not affect f, and closing f does not affect c. +// +// The returned os.File's file descriptor is different from the connection's. +// Attempting to change properties of the original using this duplicate +// may or may not have the desired effect. func (c *conn) File() (f *os.File, err error) { return c.fd.dup() } // An Error represents a network error. @@ -363,3 +369,47 @@ func (e *DNSConfigError) Error() string { func (e *DNSConfigError) Timeout() bool { return false } func (e *DNSConfigError) Temporary() bool { return false } + +type writerOnly struct { + io.Writer +} + +// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't +// applicable. +func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) { + // Use wrapper to hide existing r.ReadFrom from io.Copy. + return io.Copy(writerOnly{w}, r) +} + +// deadline is an atomically-accessed number of nanoseconds since 1970 +// or 0, if no deadline is set. +type deadline struct { + sync.Mutex + val int64 +} + +func (d *deadline) expired() bool { + t := d.value() + return t > 0 && time.Now().UnixNano() >= t +} + +func (d *deadline) value() (v int64) { + d.Lock() + v = d.val + d.Unlock() + return +} + +func (d *deadline) set(v int64) { + d.Lock() + d.val = v + d.Unlock() +} + +func (d *deadline) setTime(t time.Time) { + if t.IsZero() { + d.set(0) + } else { + d.set(t.UnixNano()) + } +} diff --git a/libgo/go/net/rpc/server.go b/libgo/go/net/rpc/server.go index 8898b98..e71b6fb 100644 --- a/libgo/go/net/rpc/server.go +++ b/libgo/go/net/rpc/server.go @@ -112,7 +112,7 @@ // Asynchronous call quotient := new(Quotient) - divCall := client.Go("Arith.Divide", args, "ient, nil) + divCall := client.Go("Arith.Divide", args, quotient, nil) replyCall := <-divCall.Done // will be equal to divCall // check errors, print, etc. @@ -219,8 +219,8 @@ func isExportedOrBuiltinType(t reflect.Type) bool { // - exported method // - two arguments, both pointers to exported structs // - one return value, of type error -// It returns an error if the receiver is not an exported type or has no -// suitable methods. +// It returns an error if the receiver is not an exported type or has +// no methods or unsuitable methods. It also logs the error using package log. // The client accesses each method using a string of the form "Type.Method", // where Type is the receiver's concrete type. func (server *Server) Register(rcvr interface{}) error { diff --git a/libgo/go/net/rpc/server_test.go b/libgo/go/net/rpc/server_test.go index d9ebe71..2c734a4 100644 --- a/libgo/go/net/rpc/server_test.go +++ b/libgo/go/net/rpc/server_test.go @@ -446,6 +446,7 @@ func dialHTTP() (*Client, error) { } func countMallocs(dial func() (*Client, error), t *testing.T) uint64 { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) once.Do(startServer) client, err := dial() if err != nil { diff --git a/libgo/go/net/sendfile_freebsd.go b/libgo/go/net/sendfile_freebsd.go index 8500006..8008bc3 100644 --- a/libgo/go/net/sendfile_freebsd.go +++ b/libgo/go/net/sendfile_freebsd.go @@ -82,7 +82,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { if n == 0 && err1 == nil { break } - if err1 == syscall.EAGAIN && c.wdeadline >= 0 { + if err1 == syscall.EAGAIN { if err1 = c.pollServer.WaitWrite(c); err1 == nil { continue } diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go index 5ee18f9..3357e65 100644 --- a/libgo/go/net/sendfile_linux.go +++ b/libgo/go/net/sendfile_linux.go @@ -58,7 +58,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { if n == 0 && err1 == nil { break } - if err1 == syscall.EAGAIN && c.wdeadline >= 0 { + if err1 == syscall.EAGAIN { if err1 = c.pollServer.WaitWrite(c); err1 == nil { continue } diff --git a/libgo/go/net/sock_posix.go b/libgo/go/net/sock_posix.go index 78417fd..12015ef 100644 --- a/libgo/go/net/sock_posix.go +++ b/libgo/go/net/sock_posix.go @@ -9,7 +9,6 @@ package net import ( - "io" "syscall" "time" ) @@ -57,16 +56,13 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, } if ursa != nil { - if !deadline.IsZero() { - fd.wdeadline = deadline.UnixNano() - } + fd.wdeadline.setTime(deadline) if err = fd.connect(ursa); err != nil { closesocket(s) - fd.Close() return nil, err } fd.isConnected = true - fd.wdeadline = 0 + fd.wdeadline.set(0) } lsa, _ := syscall.Getsockname(s) @@ -76,14 +72,3 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, fd.setAddr(laddr, raddr) return fd, nil } - -type writerOnly struct { - io.Writer -} - -// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't -// applicable. -func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) { - // Use wrapper to hide existing r.ReadFrom from io.Copy. - return io.Copy(writerOnly{w}, r) -} diff --git a/libgo/go/net/sockopt_posix.go b/libgo/go/net/sockopt_posix.go index b139c42..fe371fe 100644 --- a/libgo/go/net/sockopt_posix.go +++ b/libgo/go/net/sockopt_posix.go @@ -119,29 +119,22 @@ func setWriteBuffer(fd *netFD, bytes int) error { return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes)) } +// TODO(dfc) these unused error returns could be removed + func setReadDeadline(fd *netFD, t time.Time) error { - if t.IsZero() { - fd.rdeadline = 0 - } else { - fd.rdeadline = t.UnixNano() - } + fd.rdeadline.setTime(t) return nil } func setWriteDeadline(fd *netFD, t time.Time) error { - if t.IsZero() { - fd.wdeadline = 0 - } else { - fd.wdeadline = t.UnixNano() - } + fd.wdeadline.setTime(t) return nil } func setDeadline(fd *netFD, t time.Time) error { - if err := setReadDeadline(fd, t); err != nil { - return err - } - return setWriteDeadline(fd, t) + setReadDeadline(fd, t) + setWriteDeadline(fd, t) + return nil } func setKeepAlive(fd *netFD, keepalive bool) error { diff --git a/libgo/go/net/tcp_test.go b/libgo/go/net/tcp_test.go index f6e4df3..bca7488 100644 --- a/libgo/go/net/tcp_test.go +++ b/libgo/go/net/tcp_test.go @@ -5,6 +5,7 @@ package net import ( + "reflect" "runtime" "testing" "time" @@ -117,6 +118,36 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool) { } } +var resolveTCPAddrTests = []struct { + net string + litAddr string + addr *TCPAddr + err error +}{ + {"tcp", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, + {"tcp4", "127.0.0.1:65535", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil}, + + {"tcp", "[::1]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1}, nil}, + {"tcp6", "[::1]:65534", &TCPAddr{IP: ParseIP("::1"), Port: 65534}, nil}, + + {"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior + {"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior + + {"http", "127.0.0.1:0", nil, UnknownNetworkError("http")}, +} + +func TestResolveTCPAddr(t *testing.T) { + for _, tt := range resolveTCPAddrTests { + addr, err := ResolveTCPAddr(tt.net, tt.litAddr) + if err != tt.err { + t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + } + if !reflect.DeepEqual(addr, tt.addr) { + t.Fatalf("got %#v; expected %#v", addr, tt.addr) + } + } +} + var tcpListenerNameTests = []struct { net string laddr *TCPAddr diff --git a/libgo/go/net/tcpsock.go b/libgo/go/net/tcpsock.go index 6aba1f8..d5158b2 100644 --- a/libgo/go/net/tcpsock.go +++ b/libgo/go/net/tcpsock.go @@ -6,12 +6,11 @@ package net -import "time" - // TCPAddr represents the address of a TCP end point. type TCPAddr struct { IP IP Port int + Zone string // IPv6 scoped addressing zone } // Network returns the address's network name, "tcp". @@ -30,13 +29,16 @@ func (a *TCPAddr) String() string { // "tcp4" or "tcp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". func ResolveTCPAddr(net, addr string) (*TCPAddr, error) { - return resolveTCPAddr(net, addr, noDeadline) -} - -func resolveTCPAddr(net, addr string, deadline time.Time) (*TCPAddr, error) { - ip, port, err := hostPortToIP(net, addr, deadline) + switch net { + case "tcp", "tcp4", "tcp6": + case "": // a hint wildcard for Go 1.0 undocumented behavior + net = "tcp" + default: + return nil, UnknownNetworkError(net) + } + a, err := resolveInternetAddr(net, addr, noDeadline) if err != nil { return nil, err } - return &TCPAddr{ip, port}, nil + return a.(*TCPAddr), nil } diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go index cec5bd2..954c99a 100644 --- a/libgo/go/net/tcpsock_plan9.go +++ b/libgo/go/net/tcpsock_plan9.go @@ -25,7 +25,7 @@ func newTCPConn(fd *netFD) *TCPConn { // ReadFrom implements the io.ReaderFrom ReadFrom method. func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) { - return 0, syscall.EPLAN9 + return genericReadFrom(c, r) } // CloseRead shuts down the reading side of the TCP connection. @@ -78,7 +78,7 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error { // 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 DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) { +func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { return dialTCP(net, laddr, raddr, noDeadline) } diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index e5b3a09..4f91595 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -23,9 +23,9 @@ import ( func sockaddrToTCP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: - return &TCPAddr{sa.Addr[0:], sa.Port} + return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: - return &TCPAddr{sa.Addr[0:], sa.Port} + return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} default: if sa != nil { // Diagnose when we will turn a non-nil sockaddr into a nil. @@ -53,7 +53,7 @@ func (a *TCPAddr) isWildcard() bool { } func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, a.Port) + return ipToSockaddr(family, a.IP, a.Port, a.Zone) } func (a *TCPAddr) toAddr() sockaddr { @@ -299,7 +299,5 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) { closesocket(fd.sysfd) return nil, &OpError{"listen", net, laddr, err} } - l := new(TCPListener) - l.fd = fd - return l, nil + return &TCPListener{fd}, nil } diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go index 68d8ced..21223cc 100644 --- a/libgo/go/net/timeout_test.go +++ b/libgo/go/net/timeout_test.go @@ -6,11 +6,169 @@ package net import ( "fmt" + "io" + "io/ioutil" "runtime" "testing" "time" ) +func isTimeout(err error) bool { + e, ok := err.(Error) + return ok && e.Timeout() +} + +type copyRes struct { + n int64 + err error + d time.Duration +} + +func TestAcceptTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + ln := newLocalListener(t).(*TCPListener) + defer ln.Close() + ln.SetDeadline(time.Now().Add(-1 * time.Second)) + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + ln.SetDeadline(time.Now().Add(100 * time.Millisecond)) + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + if _, err := ln.Accept(); !isTimeout(err) { + t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) + } + ln.SetDeadline(noDeadline) + errc := make(chan error) + go func() { + _, err := ln.Accept() + errc <- err + }() + time.Sleep(100 * time.Millisecond) + select { + case err := <-errc: + t.Fatalf("Expected Accept() to not return, but it returned with %v\n", err) + default: + } + ln.Close() + if err := <-errc; err.(*OpError).Err != errClosing { + t.Fatalf("Accept: expected err %v, got %v", errClosing, err.(*OpError).Err) + } +} + +func TestReadTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + ln := newLocalListener(t) + defer ln.Close() + c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr)) + if err != nil { + t.Fatalf("Connect: %v", err) + } + defer c.Close() + c.SetDeadline(time.Now().Add(time.Hour)) + c.SetReadDeadline(time.Now().Add(-1 * time.Second)) + buf := make([]byte, 1) + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + c.SetDeadline(time.Now().Add(100 * time.Millisecond)) + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + if _, err = c.Read(buf); !isTimeout(err) { + t.Fatalf("Read: expected err %v, got %v", errTimeout, err) + } + c.SetReadDeadline(noDeadline) + c.SetWriteDeadline(time.Now().Add(-1 * time.Second)) + errc := make(chan error) + go func() { + _, err := c.Read(buf) + errc <- err + }() + time.Sleep(100 * time.Millisecond) + select { + case err := <-errc: + t.Fatalf("Expected Read() to not return, but it returned with %v\n", err) + default: + } + c.Close() + if err := <-errc; err.(*OpError).Err != errClosing { + t.Fatalf("Read: expected err %v, got %v", errClosing, err.(*OpError).Err) + } +} + +func TestWriteTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + ln := newLocalListener(t) + defer ln.Close() + c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr)) + if err != nil { + t.Fatalf("Connect: %v", err) + } + defer c.Close() + c.SetDeadline(time.Now().Add(time.Hour)) + c.SetWriteDeadline(time.Now().Add(-1 * time.Second)) + buf := make([]byte, 4096) + writeUntilTimeout := func() { + for { + _, err := c.Write(buf) + if err != nil { + if isTimeout(err) { + return + } + t.Fatalf("Write: expected err %v, got %v", errTimeout, err) + } + } + } + writeUntilTimeout() + c.SetDeadline(time.Now().Add(10 * time.Millisecond)) + writeUntilTimeout() + writeUntilTimeout() + c.SetWriteDeadline(noDeadline) + c.SetReadDeadline(time.Now().Add(-1 * time.Second)) + errc := make(chan error) + go func() { + for { + _, err := c.Write(buf) + if err != nil { + errc <- err + } + } + }() + time.Sleep(100 * time.Millisecond) + select { + case err := <-errc: + t.Fatalf("Expected Write() to not return, but it returned with %v\n", err) + default: + } + c.Close() + if err := <-errc; err.(*OpError).Err != errClosing { + t.Fatalf("Write: expected err %v, got %v", errClosing, err.(*OpError).Err) + } +} + func testTimeout(t *testing.T, net, addr string, readFrom bool) { c, err := Dial(net, addr) if err != nil { @@ -104,7 +262,7 @@ func TestDeadlineReset(t *testing.T) { defer ln.Close() tl := ln.(*TCPListener) tl.SetDeadline(time.Now().Add(1 * time.Minute)) - tl.SetDeadline(time.Time{}) // reset it + tl.SetDeadline(noDeadline) // reset it errc := make(chan error, 1) go func() { _, err := ln.Accept() @@ -174,6 +332,7 @@ func TestReadWriteDeadline(t *testing.T) { if err != nil { t.Fatalf("ListenTCP on :0: %v", err) } + defer ln.Close() lnquit := make(chan bool) @@ -230,3 +389,233 @@ func TestReadWriteDeadline(t *testing.T) { <-quit <-lnquit } + +type neverEnding byte + +func (b neverEnding) Read(p []byte) (n int, err error) { + for i := range p { + p[i] = byte(b) + } + return len(p), nil +} + +func TestVariousDeadlines1Proc(t *testing.T) { + testVariousDeadlines(t, 1) +} + +func TestVariousDeadlines4Proc(t *testing.T) { + testVariousDeadlines(t, 4) +} + +func testVariousDeadlines(t *testing.T, maxProcs int) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) + ln := newLocalListener(t) + defer ln.Close() + acceptc := make(chan error, 1) + + // The server, with no timeouts of its own, sending bytes to clients + // as fast as it can. + servec := make(chan copyRes) + go func() { + for { + c, err := ln.Accept() + if err != nil { + acceptc <- err + return + } + go func() { + t0 := time.Now() + n, err := io.Copy(c, neverEnding('a')) + d := time.Since(t0) + c.Close() + servec <- copyRes{n, err, d} + }() + } + }() + + for _, timeout := range []time.Duration{ + 1 * time.Nanosecond, + 2 * time.Nanosecond, + 5 * time.Nanosecond, + 50 * time.Nanosecond, + 100 * time.Nanosecond, + 200 * time.Nanosecond, + 500 * time.Nanosecond, + 750 * time.Nanosecond, + 1 * time.Microsecond, + 5 * time.Microsecond, + 25 * time.Microsecond, + 250 * time.Microsecond, + 500 * time.Microsecond, + 1 * time.Millisecond, + 5 * time.Millisecond, + 100 * time.Millisecond, + 250 * time.Millisecond, + 500 * time.Millisecond, + 1 * time.Second, + } { + numRuns := 3 + if testing.Short() { + numRuns = 1 + if timeout > 500*time.Microsecond { + continue + } + } + for run := 0; run < numRuns; run++ { + name := fmt.Sprintf("%v run %d/%d", timeout, run+1, numRuns) + t.Log(name) + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + clientc := make(chan copyRes) + go func() { + t0 := time.Now() + c.SetDeadline(t0.Add(timeout)) + n, err := io.Copy(ioutil.Discard, c) + d := time.Since(t0) + c.Close() + clientc <- copyRes{n, err, d} + }() + + const tooLong = 2000 * time.Millisecond + select { + case res := <-clientc: + if isTimeout(res.err) { + t.Logf("for %v, good client timeout after %v, reading %d bytes", name, res.d, res.n) + } else { + t.Fatalf("for %v: client Copy = %d, %v (want timeout)", name, res.n, res.err) + } + case <-time.After(tooLong): + t.Fatalf("for %v: timeout (%v) waiting for client to timeout (%v) reading", name, tooLong, timeout) + } + + select { + case res := <-servec: + t.Logf("for %v: server in %v wrote %d, %v", name, res.d, res.n, res.err) + case err := <-acceptc: + t.Fatalf("for %v: server Accept = %v", name, err) + case <-time.After(tooLong): + t.Fatalf("for %v, timeout waiting for server to finish writing", name) + } + } + } +} + +// TestReadDeadlineDataAvailable tests that read deadlines work, even +// if there's data ready to be read. +func TestReadDeadlineDataAvailable(t *testing.T) { + ln := newLocalListener(t) + defer ln.Close() + + servec := make(chan copyRes) + const msg = "data client shouldn't read, even though it it'll be waiting" + go func() { + c, err := ln.Accept() + if err != nil { + t.Fatalf("Accept: %v", err) + } + defer c.Close() + n, err := c.Write([]byte(msg)) + servec <- copyRes{n: int64(n), err: err} + }() + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer c.Close() + if res := <-servec; res.err != nil || res.n != int64(len(msg)) { + t.Fatalf("unexpected server Write: n=%d, err=%d; want n=%d, err=nil", res.n, res.err, len(msg)) + } + c.SetReadDeadline(time.Now().Add(-5 * time.Second)) // in the psat. + buf := make([]byte, len(msg)/2) + n, err := c.Read(buf) + if n > 0 || !isTimeout(err) { + t.Fatalf("client read = %d (%q) err=%v; want 0, timeout", n, buf[:n], err) + } +} + +// TestWriteDeadlineBufferAvailable tests that write deadlines work, even +// if there's buffer space available to write. +func TestWriteDeadlineBufferAvailable(t *testing.T) { + ln := newLocalListener(t) + defer ln.Close() + + servec := make(chan copyRes) + go func() { + c, err := ln.Accept() + if err != nil { + t.Fatalf("Accept: %v", err) + } + defer c.Close() + c.SetWriteDeadline(time.Now().Add(-5 * time.Second)) // in the past + n, err := c.Write([]byte{'x'}) + servec <- copyRes{n: int64(n), err: err} + }() + + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer c.Close() + res := <-servec + if res.n != 0 { + t.Errorf("Write = %d; want 0", res.n) + } + if !isTimeout(res.err) { + t.Errorf("Write error = %v; want timeout", res.err) + } +} + +// TestProlongTimeout tests concurrent deadline modification. +// Known to cause data races in the past. +func TestProlongTimeout(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Logf("skipping test on %q", runtime.GOOS) + return + } + + ln := newLocalListener(t) + defer ln.Close() + connected := make(chan bool) + go func() { + s, err := ln.Accept() + connected <- true + if err != nil { + t.Fatalf("ln.Accept: %v", err) + } + defer s.Close() + s.SetDeadline(time.Now().Add(time.Hour)) + go func() { + var buf [4096]byte + for { + _, err := s.Write(buf[:]) + if err != nil { + break + } + s.SetDeadline(time.Now().Add(time.Hour)) + } + }() + buf := make([]byte, 1) + for { + _, err := s.Read(buf) + if err != nil { + break + } + s.SetDeadline(time.Now().Add(time.Hour)) + } + }() + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("DialTCP: %v", err) + } + defer c.Close() + <-connected + for i := 0; i < 1024; i++ { + var buf [1]byte + c.Write(buf[:]) + } +} diff --git a/libgo/go/net/udp_test.go b/libgo/go/net/udp_test.go index 37b904f..d3594b4 100644 --- a/libgo/go/net/udp_test.go +++ b/libgo/go/net/udp_test.go @@ -5,10 +5,41 @@ package net import ( + "reflect" "runtime" "testing" ) +var resolveUDPAddrTests = []struct { + net string + litAddr string + addr *UDPAddr + err error +}{ + {"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, + {"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil}, + + {"udp", "[::1]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1}, nil}, + {"udp6", "[::1]:65534", &UDPAddr{IP: ParseIP("::1"), Port: 65534}, nil}, + + {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior + {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior + + {"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")}, +} + +func TestResolveUDPAddr(t *testing.T) { + for _, tt := range resolveUDPAddrTests { + addr, err := ResolveUDPAddr(tt.net, tt.litAddr) + if err != tt.err { + t.Fatalf("ResolveUDPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + } + if !reflect.DeepEqual(addr, tt.addr) { + t.Fatalf("got %#v; expected %#v", addr, tt.addr) + } + } +} + func TestWriteToUDP(t *testing.T) { switch runtime.GOOS { case "plan9": diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go index bf2107b..6e5e902 100644 --- a/libgo/go/net/udpsock.go +++ b/libgo/go/net/udpsock.go @@ -6,10 +6,7 @@ package net -import ( - "errors" - "time" -) +import "errors" var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP") @@ -17,6 +14,7 @@ var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP") type UDPAddr struct { IP IP Port int + Zone string // IPv6 scoped addressing zone } // Network returns the address's network name, "udp". @@ -35,13 +33,16 @@ func (a *UDPAddr) String() string { // "udp4" or "udp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". func ResolveUDPAddr(net, addr string) (*UDPAddr, error) { - return resolveUDPAddr(net, addr, noDeadline) -} - -func resolveUDPAddr(net, addr string, deadline time.Time) (*UDPAddr, error) { - ip, port, err := hostPortToIP(net, addr, deadline) + switch net { + case "udp", "udp4", "udp6": + case "": // a hint wildcard for Go 1.0 undocumented behavior + net = "udp" + default: + return nil, UnknownNetworkError(net) + } + a, err := resolveInternetAddr(net, addr, noDeadline) if err != nil { return nil, err } - return &UDPAddr{ip, port}, nil + return a.(*UDPAddr), nil } diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go index 6a828e1..b9ade48 100644 --- a/libgo/go/net/udpsock_plan9.go +++ b/libgo/go/net/udpsock_plan9.go @@ -19,8 +19,6 @@ type UDPConn struct { conn } -// UDP-specific methods. - // ReadFromUDP reads a UDP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address // that was on the packet. @@ -50,11 +48,11 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { h, buf := unmarshalUDPHeader(buf) n = copy(b, buf) - return n, &UDPAddr{h.raddr, int(h.rport)}, nil + return n, &UDPAddr{IP: h.raddr, Port: int(h.rport)}, nil } // ReadFrom implements the PacketConn ReadFrom method. -func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) { +func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } @@ -77,15 +75,16 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, // Timeout() == true after a fixed time limit; see SetDeadline and // SetWriteDeadline. On packet-oriented connections, write timeouts // are rare. -func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { +func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { if !c.ok() { return 0, syscall.EINVAL } if c.fd.data == nil { - c.fd.data, err = os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0) + f, err := os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0) if err != nil { return 0, err } + c.fd.data = f } h := new(udpHeader) h.raddr = addr.IP.To16() @@ -101,7 +100,7 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) { } // WriteTo implements the PacketConn WriteTo method. -func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) { +func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { if !c.ok() { return 0, syscall.EINVAL } @@ -122,7 +121,7 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er // DialUDP connects to the remote address raddr on the network net, // which must be "udp", "udp4", or "udp6". If laddr is not nil, it is // used as the local address for the connection. -func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) { +func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { return dialUDP(net, laddr, raddr, noDeadline) } diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go index d7329bf..b7de678 100644 --- a/libgo/go/net/udpsock_posix.go +++ b/libgo/go/net/udpsock_posix.go @@ -4,7 +4,7 @@ // +build darwin freebsd linux netbsd openbsd windows -// UDP sockets +// UDP sockets for POSIX package net @@ -16,9 +16,9 @@ import ( func sockaddrToUDP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: - return &UDPAddr{sa.Addr[0:], sa.Port} + return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: - return &UDPAddr{sa.Addr[0:], sa.Port} + return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } return nil } @@ -41,7 +41,7 @@ func (a *UDPAddr) isWildcard() bool { } func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, a.Port) + return ipToSockaddr(family, a.IP, a.Port, a.Zone) } func (a *UDPAddr) toAddr() sockaddr { @@ -59,8 +59,6 @@ type UDPConn struct { func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} } -// UDP-specific methods. - // ReadFromUDP reads a UDP packet from c, copying the payload into b. // It returns the number of bytes copied into b and the return address // that was on the packet. @@ -74,9 +72,9 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) { n, sa, err := c.fd.ReadFrom(b) switch sa := sa.(type) { case *syscall.SockaddrInet4: - addr = &UDPAddr{sa.Addr[0:], sa.Port} + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: - addr = &UDPAddr{sa.Addr[0:], sa.Port} + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } return } @@ -86,8 +84,8 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) { if !c.ok() { return 0, nil, syscall.EINVAL } - n, uaddr, err := c.ReadFromUDP(b) - return n, uaddr.toAddr(), err + n, addr, err := c.ReadFromUDP(b) + return n, addr.toAddr(), err } // ReadMsgUDP reads a packet from c, copying the payload into b and @@ -103,9 +101,9 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob) switch sa := sa.(type) { case *syscall.SockaddrInet4: - addr = &UDPAddr{sa.Addr[0:], sa.Port} + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port} case *syscall.SockaddrInet6: - addr = &UDPAddr{sa.Addr[0:], sa.Port} + addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))} } return } @@ -276,7 +274,7 @@ func listenIPv6MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error { func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { err := joinIPv4Group(c.fd, ifi, ip) if err != nil { - return &OpError{"joinipv4group", c.fd.net, &IPAddr{ip}, err} + return &OpError{"joinipv4group", c.fd.net, &IPAddr{IP: ip}, err} } return nil } @@ -284,7 +282,7 @@ func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error { err := joinIPv6Group(c.fd, ifi, ip) if err != nil { - return &OpError{"joinipv6group", c.fd.net, &IPAddr{ip}, err} + return &OpError{"joinipv6group", c.fd.net, &IPAddr{IP: ip}, err} } return nil } diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go index 342e26f..f7be5d2 100644 --- a/libgo/go/net/unixsock_plan9.go +++ b/libgo/go/net/unixsock_plan9.go @@ -14,67 +14,8 @@ import ( // UnixConn is an implementation of the Conn interface for connections // to Unix domain sockets. -type UnixConn bool - -// Implementation of the Conn interface - see Conn for documentation. - -// Read implements the Conn Read method. -func (c *UnixConn) Read(b []byte) (int, error) { - return 0, syscall.EPLAN9 -} - -// Write implements the Conn Write method. -func (c *UnixConn) Write(b []byte) (int, error) { - return 0, syscall.EPLAN9 -} - -// LocalAddr returns the local network address. -func (c *UnixConn) LocalAddr() Addr { - return nil -} - -// RemoteAddr returns the remote network address. -func (c *UnixConn) RemoteAddr() Addr { - return nil -} - -// SetDeadline implements the Conn SetDeadline method. -func (c *UnixConn) SetDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetReadDeadline implements the Conn SetReadDeadline method. -func (c *UnixConn) SetReadDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetWriteDeadline implements the Conn SetWriteDeadline method. -func (c *UnixConn) SetWriteDeadline(t time.Time) error { - return syscall.EPLAN9 -} - -// SetReadBuffer sets the size of the operating system's receive -// buffer associated with the connection. -func (c *UnixConn) SetReadBuffer(bytes int) error { - return syscall.EPLAN9 -} - -// SetWriteBuffer sets the size of the operating system's transmit -// buffer associated with the connection. -func (c *UnixConn) SetWriteBuffer(bytes int) error { - return syscall.EPLAN9 -} - -// File returns a copy of the underlying os.File, set to blocking -// mode. It is the caller's responsibility to close f when finished. -// Closing c does not affect f, and closing f does not affect c. -func (c *UnixConn) File() (f *os.File, err error) { - return nil, syscall.EPLAN9 -} - -// Close closes the Unix domain connection. -func (c *UnixConn) Close() error { - return syscall.EPLAN9 +type UnixConn struct { + conn } // ReadFromUnix reads a packet from c, copying the payload into b. It @@ -149,7 +90,7 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn // UnixListener is a Unix domain socket listener. Clients should // typically use variables of type Listener instead of assuming Unix // domain sockets. -type UnixListener bool +type UnixListener struct{} // ListenUnix announces on the Unix domain socket laddr and returns a // Unix listener. Net must be "unix" (stream sockets). diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go index d1fff89..692a7fd 100644 --- a/libgo/go/net/url/url.go +++ b/libgo/go/net/url/url.go @@ -224,7 +224,7 @@ type URL struct { Scheme string Opaque string // encoded opaque data User *Userinfo // username and password information - Host string + Host string // host or host:port Path string RawQuery string // encoded query values, without '?' Fragment string // fragment for references, without '#' diff --git a/libgo/go/os/dir_plan9.go b/libgo/go/os/dir_plan9.go index 060c0b2..8195c02 100644 --- a/libgo/go/os/dir_plan9.go +++ b/libgo/go/os/dir_plan9.go @@ -5,15 +5,11 @@ package os import ( - "errors" "io" "syscall" ) -var errShortStat = errors.New("short stat message") -var errBadStat = errors.New("bad stat message format") - -func (file *File) readdir(n int) (fi []FileInfo, err error) { +func (file *File) readdir(n int) ([]FileInfo, error) { // If this file has no dirinfo, create one. if file.dirinfo == nil { file.dirinfo = new(dirInfo) @@ -24,44 +20,47 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) { size = 100 n = -1 } - result := make([]FileInfo, 0, size) // Empty with room to grow. + fi := make([]FileInfo, 0, size) // Empty with room to grow. for n != 0 { - // Refill the buffer if necessary + // Refill the buffer if necessary. if d.bufp >= d.nbuf { - d.bufp = 0 - var e error - d.nbuf, e = file.Read(d.buf[:]) - if e != nil && e != io.EOF { - return result, &PathError{"readdir", file.name, e} - } - if e == io.EOF { - break + nb, err := file.Read(d.buf[:]) + + // Update the buffer state before checking for errors. + d.bufp, d.nbuf = 0, nb + + if err != nil { + if err == io.EOF { + break + } + return fi, &PathError{"readdir", file.name, err} } - if d.nbuf < syscall.STATFIXLEN { - return result, &PathError{"readdir", file.name, errShortStat} + if nb < syscall.STATFIXLEN { + return fi, &PathError{"readdir", file.name, syscall.ErrShortStat} } } - // Get a record from buffer - m, _ := gbit16(d.buf[d.bufp:]) - m += 2 + // Get a record from the buffer. + b := d.buf[d.bufp:] + m := int(uint16(b[0])|uint16(b[1])<<8) + 2 if m < syscall.STATFIXLEN { - return result, &PathError{"readdir", file.name, errShortStat} + return fi, &PathError{"readdir", file.name, syscall.ErrShortStat} } - dir, e := unmarshalDir(d.buf[d.bufp : d.bufp+int(m)]) - if e != nil { - return result, &PathError{"readdir", file.name, e} + + dir, err := syscall.UnmarshalDir(b[:m]) + if err != nil { + return fi, &PathError{"readdir", file.name, err} } - result = append(result, fileInfoFromStat(dir)) + fi = append(fi, fileInfoFromStat(dir)) - d.bufp += int(m) + d.bufp += m n-- } - if n >= 0 && len(result) == 0 { - return result, io.EOF + if n >= 0 && len(fi) == 0 { + return fi, io.EOF } - return result, nil + return fi, nil } func (file *File) readdirnames(n int) (names []string, err error) { @@ -72,205 +71,3 @@ func (file *File) readdirnames(n int) (names []string, err error) { } return } - -type dir struct { - // system-modified data - Type uint16 // server type - Dev uint32 // server subtype - // file data - Qid qid // unique id from server - Mode uint32 // permissions - Atime uint32 // last read time - Mtime uint32 // last write time - Length uint64 // file length - Name string // last element of path - Uid string // owner name - Gid string // group name - Muid string // last modifier name -} - -type qid struct { - Path uint64 // the file server's unique identification for the file - Vers uint32 // version number for given Path - Type uint8 // the type of the file (syscall.QTDIR for example) -} - -var nullDir = dir{ - ^uint16(0), - ^uint32(0), - qid{^uint64(0), ^uint32(0), ^uint8(0)}, - ^uint32(0), - ^uint32(0), - ^uint32(0), - ^uint64(0), - "", - "", - "", - "", -} - -// Null assigns members of d with special "don't care" values indicating -// they should not be written by syscall.Wstat. -func (d *dir) Null() { - *d = nullDir -} - -// pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b. -func pdir(b []byte, d *dir) []byte { - n := len(b) - b = pbit16(b, 0) // length, filled in later - b = pbit16(b, d.Type) - b = pbit32(b, d.Dev) - b = pqid(b, d.Qid) - b = pbit32(b, d.Mode) - b = pbit32(b, d.Atime) - b = pbit32(b, d.Mtime) - b = pbit64(b, d.Length) - b = pstring(b, d.Name) - b = pstring(b, d.Uid) - b = pstring(b, d.Gid) - b = pstring(b, d.Muid) - pbit16(b[0:n], uint16(len(b)-(n+2))) - return b -} - -// unmarshalDir reads a 9P Stat message from a 9P protocol message stored in b, -// returning the corresponding dir struct. -func unmarshalDir(b []byte) (d *dir, err error) { - n := uint16(0) - n, b = gbit16(b) - - if int(n) != len(b) { - return nil, errBadStat - } - - d = new(dir) - d.Type, b = gbit16(b) - d.Dev, b = gbit32(b) - d.Qid, b = gqid(b) - d.Mode, b = gbit32(b) - d.Atime, b = gbit32(b) - d.Mtime, b = gbit32(b) - d.Length, b = gbit64(b) - d.Name, b = gstring(b) - d.Uid, b = gstring(b) - d.Gid, b = gstring(b) - d.Muid, b = gstring(b) - - if len(b) != 0 { - return nil, errBadStat - } - - return d, nil -} - -// gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b, -// returning the corresponding qid struct and the remaining slice of b. -func gqid(b []byte) (qid, []byte) { - var q qid - q.Path, b = gbit64(b) - q.Vers, b = gbit32(b) - q.Type, b = gbit8(b) - return q, b -} - -// pqid appends a qid struct q to a 9P message b. -func pqid(b []byte, q qid) []byte { - b = pbit64(b, q.Path) - b = pbit32(b, q.Vers) - b = pbit8(b, q.Type) - return b -} - -// gbit8 reads a byte-sized numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit8(b []byte) (uint8, []byte) { - return uint8(b[0]), b[1:] -} - -// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit16(b []byte) (uint16, []byte) { - return uint16(b[0]) | uint16(b[1])<<8, b[2:] -} - -// gbit32 reads a 32-bit numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit32(b []byte) (uint32, []byte) { - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] -} - -// gbit64 reads a 64-bit numeric value from a 9P protocol message stored in b, -// returning the value and the remaining slice of b. -func gbit64(b []byte) (uint64, []byte) { - lo, b := gbit32(b) - hi, b := gbit32(b) - return uint64(hi)<<32 | uint64(lo), b -} - -// gstring reads a string from a 9P protocol message stored in b, -// returning the value as a Go string and the remaining slice of b. -func gstring(b []byte) (string, []byte) { - n, b := gbit16(b) - return string(b[0:n]), b[n:] -} - -// pbit8 appends a byte-sized numeric value x to a 9P message b. -func pbit8(b []byte, x uint8) []byte { - n := len(b) - if n+1 > cap(b) { - nb := make([]byte, n, 100+2*cap(b)) - copy(nb, b) - b = nb - } - b = b[0 : n+1] - b[n] = x - return b -} - -// pbit16 appends a 16-bit numeric value x to a 9P message b. -func pbit16(b []byte, x uint16) []byte { - n := len(b) - if n+2 > cap(b) { - nb := make([]byte, n, 100+2*cap(b)) - copy(nb, b) - b = nb - } - b = b[0 : n+2] - b[n] = byte(x) - b[n+1] = byte(x >> 8) - return b -} - -// pbit32 appends a 32-bit numeric value x to a 9P message b. -func pbit32(b []byte, x uint32) []byte { - n := len(b) - if n+4 > cap(b) { - nb := make([]byte, n, 100+2*cap(b)) - copy(nb, b) - b = nb - } - b = b[0 : n+4] - b[n] = byte(x) - b[n+1] = byte(x >> 8) - b[n+2] = byte(x >> 16) - b[n+3] = byte(x >> 24) - return b -} - -// pbit64 appends a 64-bit numeric value x to a 9P message b. -func pbit64(b []byte, x uint64) []byte { - b = pbit32(b, uint32(x)) - b = pbit32(b, uint32(x>>32)) - return b -} - -// pstring appends a Go string s to a 9P message b. -func pstring(b []byte, s string) []byte { - if len(s) >= 1<<16 { - panic(errors.New("string too long")) - } - b = pbit16(b, uint16(len(s))) - b = append(b, s...) - return b -} diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index db366a0..fb2f234 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -169,13 +169,18 @@ func (f *File) Stat() (fi FileInfo, err error) { // It does not change the I/O offset. // If there is an error, it will be of type *PathError. func (f *File) Truncate(size int64) error { - var d dir - d.Null() + var d syscall.Dir - d.Length = uint64(size) + d.Null() + d.Length = size - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { - return &PathError{"truncate", f.name, e} + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"truncate", f.name, err} + } + if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + return &PathError{"truncate", f.name, err} } return nil } @@ -185,7 +190,7 @@ const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | Mod // Chmod changes the mode of the file to mode. // If there is an error, it will be of type *PathError. func (f *File) Chmod(mode FileMode) error { - var d dir + var d syscall.Dir odir, e := dirstat(f) if e != nil { @@ -193,8 +198,14 @@ func (f *File) Chmod(mode FileMode) error { } d.Null() d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { - return &PathError{"chmod", f.name, e} + + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"chmod", f.name, err} + } + if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + return &PathError{"chmod", f.name, err} } return nil } @@ -206,12 +217,16 @@ func (f *File) Sync() (err error) { if f == nil { return ErrInvalid } - - var d dir + var d syscall.Dir d.Null() - if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil { - return NewSyscallError("fsync", e) + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return NewSyscallError("fsync", err) + } + if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + return NewSyscallError("fsync", err) } return nil } @@ -253,13 +268,18 @@ func (f *File) seek(offset int64, whence int) (ret int64, err error) { // If the file is a symbolic link, it changes the size of the link's target. // If there is an error, it will be of type *PathError. func Truncate(name string, size int64) error { - var d dir - d.Null() + var d syscall.Dir - d.Length = uint64(size) + d.Null() + d.Length = size - if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { - return &PathError{"truncate", name, e} + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"truncate", name, err} + } + if err = syscall.Wstat(name, buf[:n]); err != nil { + return &PathError{"truncate", name, err} } return nil } @@ -275,13 +295,18 @@ func Remove(name string) error { // Rename renames a file. func Rename(oldname, newname string) error { - var d dir - d.Null() + var d syscall.Dir + d.Null() d.Name = newname - if e := syscall.Wstat(oldname, pdir(nil, &d)); e != nil { - return &PathError{"rename", oldname, e} + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"rename", oldname, err} + } + if err = syscall.Wstat(oldname, buf[:n]); err != nil { + return &PathError{"rename", oldname, err} } return nil } @@ -290,7 +315,7 @@ func Rename(oldname, newname string) error { // If the file is a symbolic link, it changes the mode of the link's target. // If there is an error, it will be of type *PathError. func Chmod(name string, mode FileMode) error { - var d dir + var d syscall.Dir odir, e := dirstat(name) if e != nil { @@ -298,8 +323,14 @@ func Chmod(name string, mode FileMode) error { } d.Null() d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask - if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { - return &PathError{"chmod", name, e} + + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"chmod", name, err} + } + if err = syscall.Wstat(name, buf[:n]); err != nil { + return &PathError{"chmod", name, err} } return nil } @@ -311,14 +342,19 @@ func Chmod(name string, mode FileMode) error { // less precise time unit. // If there is an error, it will be of type *PathError. func Chtimes(name string, atime time.Time, mtime time.Time) error { - var d dir - d.Null() + var d syscall.Dir + d.Null() d.Atime = uint32(atime.Unix()) d.Mtime = uint32(mtime.Unix()) - if e := syscall.Wstat(name, pdir(nil, &d)); e != nil { - return &PathError{"chtimes", name, e} + var buf [syscall.STATFIXLEN]byte + n, err := d.Marshal(buf[:]) + if err != nil { + return &PathError{"chtimes", name, err} + } + if err = syscall.Wstat(name, buf[:n]); err != nil { + return &PathError{"chtimes", name, err} } return nil } diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 671c301..9f0bbdc 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -1093,3 +1093,25 @@ func TestLargeWriteToConsole(t *testing.T) { t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n) } } + +func TestStatDirModeExec(t *testing.T) { + const mode = 0111 + + path, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("Failed to create temp directory: %v", err) + } + defer RemoveAll(path) + + if err := Chmod(path, 0777); err != nil { + t.Fatalf("Chmod %q 0777: %v", path, err) + } + + dir, err := Stat(path) + if err != nil { + t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) + } + if dir.Mode()&mode != mode { + t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode) + } +} diff --git a/libgo/go/os/stat_plan9.go b/libgo/go/os/stat_plan9.go index b3dd188..6822cc0 100644 --- a/libgo/go/os/stat_plan9.go +++ b/libgo/go/os/stat_plan9.go @@ -10,12 +10,12 @@ import ( ) func sameFile(sys1, sys2 interface{}) bool { - a := sys1.(*dir) - b := sys2.(*dir) + a := sys1.(*syscall.Dir) + b := sys2.(*syscall.Dir) return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev } -func fileInfoFromStat(d *dir) FileInfo { +func fileInfoFromStat(d *syscall.Dir) FileInfo { fs := &fileStat{ name: d.Name, size: int64(d.Length), @@ -39,7 +39,7 @@ func fileInfoFromStat(d *dir) FileInfo { } // arg is an open *File or a path string. -func dirstat(arg interface{}) (d *dir, err error) { +func dirstat(arg interface{}) (*syscall.Dir, error) { var name string // This is big enough for most stat messages @@ -50,36 +50,40 @@ func dirstat(arg interface{}) (d *dir, err error) { buf := make([]byte, size) var n int + var err error switch a := arg.(type) { case *File: name = a.name n, err = syscall.Fstat(a.fd, buf) case string: name = a - n, err = syscall.Stat(name, buf) + n, err = syscall.Stat(a, buf) + default: + panic("phase error in dirstat") } if err != nil { return nil, &PathError{"stat", name, err} } if n < syscall.STATFIXLEN { - return nil, &PathError{"stat", name, errShortStat} + return nil, &PathError{"stat", name, syscall.ErrShortStat} } // Pull the real size out of the stat message. - s, _ := gbit16(buf) - size = int(s) + size = int(uint16(buf[0]) | uint16(buf[1])<<8) // If the stat message is larger than our buffer we will // go around the loop and allocate one that is big enough. - if size <= n { - d, err = unmarshalDir(buf[:n]) - if err != nil { - return nil, &PathError{"stat", name, err} - } - return + if size > n { + continue } + + d, err := syscall.UnmarshalDir(buf[:n]) + if err != nil { + return nil, &PathError{"stat", name, err} + } + return d, nil } - return nil, &PathError{"stat", name, errBadStat} + return nil, &PathError{"stat", name, syscall.ErrBadStat} } // Stat returns a FileInfo describing the named file. @@ -102,5 +106,5 @@ func Lstat(name string) (fi FileInfo, err error) { // For testing. func atime(fi FileInfo) time.Time { - return time.Unix(int64(fi.Sys().(*dir).Atime), 0) + return time.Unix(int64(fi.Sys().(*syscall.Dir).Atime), 0) } diff --git a/libgo/go/os/user/lookup.go b/libgo/go/os/user/lookup.go new file mode 100644 index 0000000..09f00c7 --- /dev/null +++ b/libgo/go/os/user/lookup.go @@ -0,0 +1,22 @@ +// 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 user + +// Current returns the current user. +func Current() (*User, error) { + return current() +} + +// Lookup looks up a user by username. If the user cannot be found, the +// returned error is of type UnknownUserError. +func Lookup(username string) (*User, error) { + return lookup(username) +} + +// LookupId looks up a user by userid. If the user cannot be found, the +// returned error is of type UnknownUserIdError. +func LookupId(uid string) (*User, error) { + return lookupId(uid) +} diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go index 415f869..ad06907 100644 --- a/libgo/go/os/user/lookup_stubs.go +++ b/libgo/go/os/user/lookup_stubs.go @@ -15,14 +15,14 @@ func init() { implemented = false } -func Current() (*User, error) { +func current() (*User, error) { return nil, fmt.Errorf("user: Current not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) } -func Lookup(username string) (*User, error) { +func lookup(username string) (*User, error) { return nil, fmt.Errorf("user: Lookup not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) } -func LookupId(string) (*User, error) { +func lookupId(uid string) (*User, error) { return nil, fmt.Errorf("user: LookupId not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) } diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index e0baae2..7e67495 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -44,28 +44,23 @@ func bytePtrToString(p *byte) string { return string(a[:i]) } -// Current returns the current user. -func Current() (*User, error) { - return lookup(syscall.Getuid(), "", false) +func current() (*User, error) { + return lookupUnix(syscall.Getuid(), "", false) } -// Lookup looks up a user by username. If the user cannot be found, -// the returned error is of type UnknownUserError. -func Lookup(username string) (*User, error) { - return lookup(-1, username, true) +func lookup(username string) (*User, error) { + return lookupUnix(-1, username, true) } -// LookupId looks up a user by userid. If the user cannot be found, -// the returned error is of type UnknownUserIdError. -func LookupId(uid string) (*User, error) { +func lookupId(uid string) (*User, error) { i, e := strconv.Atoi(uid) if e != nil { return nil, e } - return lookup(i, "", false) + return lookupUnix(i, "", false) } -func lookup(uid int, username string, lookupByName bool) (*User, error) { +func lookupUnix(uid int, username string, lookupByName bool) (*User, error) { var pwd syscall.Passwd var result *syscall.Passwd diff --git a/libgo/go/os/user/lookup_windows.go b/libgo/go/os/user/lookup_windows.go index 3626a4e..a0a8a4e 100644 --- a/libgo/go/os/user/lookup_windows.go +++ b/libgo/go/os/user/lookup_windows.go @@ -68,8 +68,7 @@ func newUser(usid *syscall.SID, gid, dir string) (*User, error) { return u, nil } -// Current returns the current user. -func Current() (*User, error) { +func current() (*User, error) { t, e := syscall.OpenCurrentProcessToken() if e != nil { return nil, e @@ -103,8 +102,7 @@ func newUserFromSid(usid *syscall.SID) (*User, error) { return newUser(usid, gid, dir) } -// Lookup looks up a user by username. -func Lookup(username string) (*User, error) { +func lookup(username string) (*User, error) { sid, _, t, e := syscall.LookupSID("", username) if e != nil { return nil, e @@ -115,8 +113,7 @@ func Lookup(username string) (*User, error) { return newUserFromSid(sid) } -// LookupId looks up a user by userid. -func LookupId(uid string) (*User, error) { +func lookupId(uid string) (*User, error) { sid, e := syscall.StringToSid(uid) if e != nil { return nil, e diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index 577b0a3..bee4d95 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -91,6 +91,7 @@ var wincleantests = []PathTest{ } func TestClean(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) tests := cleantests if runtime.GOOS == "windows" { for i := range tests { @@ -902,7 +903,7 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) { differently. func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486 - root, err := filepath.EvalSymlinks(os.Getenv("GOROOT")) + root, err := filepath.EvalSymlinks(runtime.GOROOT()) if err != nil { t.Fatal(err) } diff --git a/libgo/go/path/path_test.go b/libgo/go/path/path_test.go index 6909649..926b573 100644 --- a/libgo/go/path/path_test.go +++ b/libgo/go/path/path_test.go @@ -64,6 +64,7 @@ var cleantests = []PathTest{ } func TestClean(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) for _, test := range cleantests { if s := Clean(test.path); s != test.result { t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index dc2b5b4..8a36023 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -2020,6 +2020,7 @@ func TestAddr(t *testing.T) { /* gccgo does do allocations here. func noAlloc(t *testing.T, n int, f func(int)) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) // once to prime everything f(-1) memstats := new(runtime.MemStats) @@ -2029,12 +2030,9 @@ func noAlloc(t *testing.T, n int, f func(int)) { for j := 0; j < n; j++ { f(j) } - // A few allocs may happen in the testing package when GOMAXPROCS > 1, so don't - // require zero mallocs. - // A new thread, one of which will be created if GOMAXPROCS>1, does 6 allocations. runtime.ReadMemStats(memstats) mallocs := memstats.Mallocs - oldmallocs - if mallocs > 10 { + if mallocs > 0 { t.Fatalf("%d mallocs after %d iterations", mallocs, n) } } diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index fb0606d..9e65870 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -1334,7 +1334,7 @@ func cachePut(k cacheKey, t *rtype) Type { return t } -// ChanOf returns the channel type with the given direction and and element type. +// ChanOf returns the channel type with the given direction and element type. // For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int. // // The gc runtime imposes a limit of 64 kB on channel element types. diff --git a/libgo/go/regexp/all_test.go b/libgo/go/regexp/all_test.go index 39a28df..b7e4f04 100644 --- a/libgo/go/regexp/all_test.go +++ b/libgo/go/regexp/all_test.go @@ -5,6 +5,7 @@ package regexp_test import ( + "reflect" . "regexp" "strings" "testing" @@ -417,6 +418,59 @@ func TestSubexp(t *testing.T) { } } +var splitTests = []struct { + s string + r string + n int + out []string +}{ + {"foo:and:bar", ":", -1, []string{"foo", "and", "bar"}}, + {"foo:and:bar", ":", 1, []string{"foo:and:bar"}}, + {"foo:and:bar", ":", 2, []string{"foo", "and:bar"}}, + {"foo:and:bar", "foo", -1, []string{"", ":and:bar"}}, + {"foo:and:bar", "bar", -1, []string{"foo:and:", ""}}, + {"foo:and:bar", "baz", -1, []string{"foo:and:bar"}}, + {"baabaab", "a", -1, []string{"b", "", "b", "", "b"}}, + {"baabaab", "a*", -1, []string{"b", "b", "b"}}, + {"baabaab", "ba*", -1, []string{"", "", "", ""}}, + {"foobar", "f*b*", -1, []string{"", "o", "o", "a", "r"}}, + {"foobar", "f+.*b+", -1, []string{"", "ar"}}, + {"foobooboar", "o{2}", -1, []string{"f", "b", "boar"}}, + {"a,b,c,d,e,f", ",", 3, []string{"a", "b", "c,d,e,f"}}, + {"a,b,c,d,e,f", ",", 0, nil}, + {",", ",", -1, []string{"", ""}}, + {",,,", ",", -1, []string{"", "", "", ""}}, + {"", ",", -1, []string{""}}, + {"", ".*", -1, []string{""}}, + {"", ".+", -1, []string{""}}, + {"", "", -1, []string{}}, + {"foobar", "", -1, []string{"f", "o", "o", "b", "a", "r"}}, + {"abaabaccadaaae", "a*", 5, []string{"", "b", "b", "c", "cadaaae"}}, + {":x:y:z:", ":", -1, []string{"", "x", "y", "z", ""}}, +} + +func TestSplit(t *testing.T) { + for i, test := range splitTests { + re, err := Compile(test.r) + if err != nil { + t.Errorf("#%d: %q: compile error: %s", i, test.r, err.Error()) + continue + } + + split := re.Split(test.s, test.n) + if !reflect.DeepEqual(split, test.out) { + t.Errorf("#%d: %q: got %q; want %q", i, test.r, split, test.out) + } + + if QuoteMeta(test.r) == test.r { + strsplit := strings.SplitN(test.s, test.r, test.n) + if !reflect.DeepEqual(split, strsplit) { + t.Errorf("#%d: Split(%q, %q, %d): regexp vs strings mismatch\nregexp=%q\nstrings=%q", i, test.s, test.r, test.n, split, strsplit) + } + } + } +} + func BenchmarkLiteral(b *testing.B) { x := strings.Repeat("x", 50) + "y" b.StopTimer() diff --git a/libgo/go/regexp/example_test.go b/libgo/go/regexp/example_test.go index aa92e0b..b0ad9d3 100644 --- a/libgo/go/regexp/example_test.go +++ b/libgo/go/regexp/example_test.go @@ -20,3 +20,125 @@ func Example() { // false // false } + +func ExampleMatchString() { + matched, err := regexp.MatchString("foo.*", "seafood") + fmt.Println(matched, err) + matched, err = regexp.MatchString("bar.*", "seafood") + fmt.Println(matched, err) + matched, err = regexp.MatchString("a(b", "seafood") + fmt.Println(matched, err) + // Output: + // true <nil> + // false <nil> + // false error parsing regexp: missing closing ): `a(b` +} + +func ExampleRegexp_FindString() { + re := regexp.MustCompile("fo.?") + fmt.Printf("%q\n", re.FindString("seafood")) + fmt.Printf("%q\n", re.FindString("meat")) + // Output: + // "foo" + // "" +} + +func ExampleRegexp_FindStringIndex() { + re := regexp.MustCompile("ab?") + fmt.Println(re.FindStringIndex("tablett")) + fmt.Println(re.FindStringIndex("foo") == nil) + // Output: + // [1 3] + // true +} + +func ExampleRegexp_FindStringSubmatch() { + re := regexp.MustCompile("a(x*)b(y|z)c") + fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-")) + fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-")) + // Output: + // ["axxxbyc" "xxx" "y"] + // ["abzc" "" "z"] +} + +func ExampleRegexp_FindAllString() { + re := regexp.MustCompile("a.") + fmt.Println(re.FindAllString("paranormal", -1)) + fmt.Println(re.FindAllString("paranormal", 2)) + fmt.Println(re.FindAllString("graal", -1)) + fmt.Println(re.FindAllString("none", -1)) + // Output: + // [ar an al] + // [ar an] + // [aa] + // [] +} + +func ExampleRegexp_FindAllStringSubmatch() { + re := regexp.MustCompile("a(x*)b") + fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-", -1)) + fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-", -1)) + fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-axb-", -1)) + fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-ab-", -1)) + // Output: + // [["ab" ""]] + // [["axxb" "xx"]] + // [["ab" ""] ["axb" "x"]] + // [["axxb" "xx"] ["ab" ""]] +} + +func ExampleRegexp_FindAllStringSubmatchIndex() { + re := regexp.MustCompile("a(x*)b") + // Indices: + // 01234567 012345678 + // -ab-axb- -axxb-ab- + fmt.Println(re.FindAllStringSubmatchIndex("-ab-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-axxb-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-ab-axb-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-axxb-ab-", -1)) + fmt.Println(re.FindAllStringSubmatchIndex("-foo-", -1)) + // Output: + // [[1 3 2 2]] + // [[1 5 2 4]] + // [[1 3 2 2] [4 7 5 6]] + // [[1 5 2 4] [6 8 7 7]] + // [] +} + +func ExampleRegexp_ReplaceAllLiteralString() { + re := regexp.MustCompile("a(x*)b") + fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "T")) + fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "$1")) + fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "${1}")) + // Output: + // -T-T- + // -$1-$1- + // -${1}-${1}- +} + +func ExampleRegexp_ReplaceAllString() { + re := regexp.MustCompile("a(x*)b") + fmt.Println(re.ReplaceAllString("-ab-axxb-", "T")) + fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1")) + fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1W")) + fmt.Println(re.ReplaceAllString("-ab-axxb-", "${1}W")) + // Output: + // -T-T- + // --xx- + // --- + // -W-xxW- +} + +func ExampleRegexp_SubexpNames() { + re := regexp.MustCompile("(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)") + fmt.Println(re.MatchString("Alan Turing")) + fmt.Printf("%q\n", re.SubexpNames()) + reversed := fmt.Sprintf("${%s} ${%s}", re.SubexpNames()[2], re.SubexpNames()[1]) + fmt.Println(reversed) + fmt.Println(re.ReplaceAllString("Alan Turing", reversed)) + // Output: + // true + // ["" "first" "last"] + // ${last} ${first} + // Turing Alan +} diff --git a/libgo/go/regexp/regexp.go b/libgo/go/regexp/regexp.go index 2a1ae56..bcf354b 100644 --- a/libgo/go/regexp/regexp.go +++ b/libgo/go/regexp/regexp.go @@ -1048,3 +1048,52 @@ func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int { } return result } + +// Split slices s into substrings separated by the expression and returns a slice of +// the substrings between those expression matches. +// +// The slice returned by this method consists of all the substrings of s +// not contained in the slice returned by FindAllString. When called on an expression +// that contains no metacharacters, it is equivalent to strings.SplitN. +// +// Example: +// s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5) +// // s: ["", "b", "b", "c", "cadaaae"] +// +// The count determines the number of substrings to return: +// n > 0: at most n substrings; the last substring will be the unsplit remainder. +// n == 0: the result is nil (zero substrings) +// n < 0: all substrings +func (re *Regexp) Split(s string, n int) []string { + + if n == 0 { + return nil + } + + if len(re.expr) > 0 && len(s) == 0 { + return []string{""} + } + + matches := re.FindAllStringIndex(s, n) + strings := make([]string, 0, len(matches)) + + beg := 0 + end := 0 + for _, match := range matches { + if n > 0 && len(strings) >= n-1 { + break + } + + end = match[0] + if match[1] != 0 { + strings = append(strings, s[beg:end]) + } + beg = match[1] + } + + if end != len(s) { + strings = append(strings, s[beg:]) + } + + return strings +} diff --git a/libgo/go/runtime/debug.go b/libgo/go/runtime/debug.go index 9da71a7..bcdde4b 100644 --- a/libgo/go/runtime/debug.go +++ b/libgo/go/runtime/debug.go @@ -125,6 +125,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) // blocking until data is available. If profiling is turned off and all the profile // data accumulated while it was on has been returned, CPUProfile returns nil. // The caller must save the returned data before calling CPUProfile again. +// // Most clients should use the runtime/pprof package or // the testing package's -test.cpuprofile flag instead of calling // CPUProfile directly. @@ -133,6 +134,7 @@ func CPUProfile() []byte // SetCPUProfileRate sets the CPU profiling rate to hz samples per second. // If hz <= 0, SetCPUProfileRate turns off profiling. // If the profiler is on, the rate cannot be changed without first turning it off. +// // Most clients should use the runtime/pprof package or // the testing package's -test.cpuprofile flag instead of calling // SetCPUProfileRate directly. diff --git a/libgo/go/runtime/extern.go b/libgo/go/runtime/extern.go index 09d1391..2a90113 100644 --- a/libgo/go/runtime/extern.go +++ b/libgo/go/runtime/extern.go @@ -59,11 +59,7 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) { // implemented in symtab.c func funcline_go(*Func, uintptr) (string, int) -// A gccgo specific hook to use debug info to get file/line info. -func RegisterDebugLookup(func(pc uintptr, function *string, file *string, line *int) bool, - func(sym string, val *uintptr) bool) - -// mid returns the current os thread (m) id. +// mid returns the current OS thread (m) id. func mid() uint32 // SetFinalizer sets the finalizer associated with x to f. diff --git a/libgo/go/runtime/gc_test.go b/libgo/go/runtime/gc_test.go index 56dd938..283a681 100644 --- a/libgo/go/runtime/gc_test.go +++ b/libgo/go/runtime/gc_test.go @@ -10,6 +10,7 @@ import ( ) func TestGcSys(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) memstats := new(runtime.MemStats) runtime.GC() runtime.ReadMemStats(memstats) diff --git a/libgo/go/runtime/mallocrep1.go b/libgo/go/runtime/mallocrep1.go index 41c104c..bc33e3a 100644 --- a/libgo/go/runtime/mallocrep1.go +++ b/libgo/go/runtime/mallocrep1.go @@ -39,6 +39,7 @@ func OkAmount(size, n uintptr) bool { } func AllocAndFree(size, count int) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) if *chatty { fmt.Printf("size=%d count=%d ...\n", size, count) } diff --git a/libgo/go/strconv/strconv_test.go b/libgo/go/strconv/strconv_test.go index 5a3beae..b01aff1 100644 --- a/libgo/go/strconv/strconv_test.go +++ b/libgo/go/strconv/strconv_test.go @@ -48,6 +48,7 @@ var ( ) func TestCountMallocs(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) for _, mt := range mallocTest { const N = 100 memstats := new(runtime.MemStats) diff --git a/libgo/go/strings/reader.go b/libgo/go/strings/reader.go index 98325ce..11240ef 100644 --- a/libgo/go/strings/reader.go +++ b/libgo/go/strings/reader.go @@ -124,7 +124,7 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) { func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { r.prevRune = -1 if r.i >= len(r.s) { - return 0, io.EOF + return 0, nil } s := r.s[r.i:] m, err := io.WriteString(w, s) diff --git a/libgo/go/strings/reader_test.go b/libgo/go/strings/reader_test.go index bab91fc..4fdddcd 100644 --- a/libgo/go/strings/reader_test.go +++ b/libgo/go/strings/reader_test.go @@ -90,7 +90,7 @@ func TestReaderAt(t *testing.T) { func TestWriteTo(t *testing.T) { const str = "0123456789" - for i := 0; i < len(str); i++ { + for i := 0; i <= len(str); i++ { s := str[i:] r := strings.NewReader(s) var b bytes.Buffer @@ -99,7 +99,7 @@ func TestWriteTo(t *testing.T) { t.Errorf("got %v; want %v", n, expect) } if err != nil { - t.Errorf("got error = %v; want nil", err) + t.Errorf("for length %d: got error = %v; want nil", len(s), err) } if b.String() != s { t.Errorf("got string %q; want %q", b.String(), s) diff --git a/libgo/go/syscall/dir_plan9.go b/libgo/go/syscall/dir_plan9.go new file mode 100644 index 0000000..eee8be4 --- /dev/null +++ b/libgo/go/syscall/dir_plan9.go @@ -0,0 +1,205 @@ +// Copyright 2012 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 directory marshalling. See intro(5). + +package syscall + +import "errors" + +var ( + ErrShortStat = errors.New("stat buffer too short") + ErrBadStat = errors.New("malformed stat buffer") +) + +// A Qid represents a 9P server's unique identification for a file. +type Qid struct { + Path uint64 // the file server's unique identification for the file + Vers uint32 // version number for given Path + Type uint8 // the type of the file (syscall.QTDIR for example) +} + +// A Dir contains the metadata for a file. +type Dir struct { + // system-modified data + Type uint16 // server type + Dev uint32 // server subtype + + // file data + Qid Qid // unique id from server + Mode uint32 // permissions + Atime uint32 // last read time + Mtime uint32 // last write time + Length int64 // file length + Name string // last element of path + Uid string // owner name + Gid string // group name + Muid string // last modifier name +} + +var nullDir = Dir{ + Type: ^uint16(0), + Dev: ^uint32(0), + Qid: Qid{ + Path: ^uint64(0), + Vers: ^uint32(0), + Type: ^uint8(0), + }, + Mode: ^uint32(0), + Atime: ^uint32(0), + Mtime: ^uint32(0), + Length: ^int64(0), +} + +// Null assigns special "don't touch" values to members of d to +// avoid modifiying them during syscall.Wstat. +func (d *Dir) Null() { *d = nullDir } + +// Marshal encodes a 9P stat message corresponding to d into b +// +// If there isn't enough space in b for a stat message, ErrShortStat is returned. +func (d *Dir) Marshal(b []byte) (n int, err error) { + n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid) + if n > len(b) { + return n, ErrShortStat + } + + b = pbit16(b, uint16(n)-2) + b = pbit16(b, d.Type) + b = pbit32(b, d.Dev) + b = pbit64(b, d.Qid.Path) + b = pbit32(b, d.Qid.Vers) + b = pbit8(b, d.Qid.Type) + b = pbit32(b, d.Mode) + b = pbit32(b, d.Atime) + b = pbit32(b, d.Mtime) + b = pbit64(b, uint64(d.Length)) + b = pstring(b, d.Name) + b = pstring(b, d.Uid) + b = pstring(b, d.Gid) + b = pstring(b, d.Muid) + + return n, nil +} + +// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir. +// +// If b is too small to hold a valid stat message, ErrShortStat is returned. +// +// If the stat message itself is invalid, ErrBadStat is returned. +func UnmarshalDir(b []byte) (*Dir, error) { + if len(b) < STATFIXLEN { + return nil, ErrShortStat + } + size, buf := gbit16(b) + if len(b) != int(size)+2 { + return nil, ErrBadStat + } + b = buf + + var d Dir + d.Type, b = gbit16(b) + d.Dev, b = gbit32(b) + d.Qid.Path, b = gbit64(b) + d.Qid.Vers, b = gbit32(b) + d.Qid.Type, b = gbit8(b) + d.Mode, b = gbit32(b) + d.Atime, b = gbit32(b) + d.Mtime, b = gbit32(b) + + n, b := gbit64(b) + d.Length = int64(n) + + var ok bool + if d.Name, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + if d.Uid, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + if d.Gid, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + if d.Muid, b, ok = gstring(b); !ok { + return nil, ErrBadStat + } + + return &d, nil +} + +// pbit8 copies the 8-bit number v to b and returns the remaining slice of b. +func pbit8(b []byte, v uint8) []byte { + b[0] = byte(v) + return b[1:] +} + +// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b. +func pbit16(b []byte, v uint16) []byte { + b[0] = byte(v) + b[1] = byte(v >> 8) + return b[2:] +} + +// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b. +func pbit32(b []byte, v uint32) []byte { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + return b[4:] +} + +// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b. +func pbit64(b []byte, v uint64) []byte { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) + return b[8:] +} + +// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and +// returning the remaining slice of b.. +func pstring(b []byte, s string) []byte { + b = pbit16(b, uint16(len(s))) + n := copy(b, s) + return b[n:] +} + +// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b. +func gbit8(b []byte) (uint8, []byte) { + return uint8(b[0]), b[1:] +} + +// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b. +func gbit16(b []byte) (uint16, []byte) { + return uint16(b[0]) | uint16(b[1])<<8, b[2:] +} + +// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b. +func gbit32(b []byte) (uint32, []byte) { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:] +} + +// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b. +func gbit64(b []byte) (uint64, []byte) { + lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24 + return uint64(lo) | uint64(hi)<<32, b[8:] +} + +// gstring reads a string from b, prefixed with a 16-bit length in little-endian order. +// It returns the string with the remaining slice of b and a boolean. If the length is +// greater than the number of bytes in b, the boolean will be false. +func gstring(b []byte) (string, []byte, bool) { + n, b := gbit16(b) + if int(n) > len(b) { + return "", b, false + } + return string(b[:n]), b[n:], true +} diff --git a/libgo/go/time/example_test.go b/libgo/go/time/example_test.go index 944cc78..ea26710 100644 --- a/libgo/go/time/example_test.go +++ b/libgo/go/time/example_test.go @@ -56,3 +56,58 @@ func ExampleDate() { fmt.Printf("Go launched at %s\n", t.Local()) // Output: Go launched at 2009-11-10 15:00:00 -0800 PST } + +func ExampleTime_Round() { + t := time.Date(0, 0, 0, 12, 15, 30, 918273645, time.UTC) + round := []time.Duration{ + time.Nanosecond, + time.Microsecond, + time.Millisecond, + time.Second, + 2 * time.Second, + time.Minute, + 10 * time.Minute, + time.Hour, + } + + for _, d := range round { + fmt.Printf("t.Round(%6s) = %s\n", d, t.Round(d).Format("15:04:05.999999999")) + } + // Output: + // t.Round( 1ns) = 12:15:30.918273645 + // t.Round( 1us) = 12:15:30.918274 + // t.Round( 1ms) = 12:15:30.918 + // t.Round( 1s) = 12:15:31 + // t.Round( 2s) = 12:15:30 + // t.Round( 1m0s) = 12:16:00 + // t.Round( 10m0s) = 12:20:00 + // t.Round(1h0m0s) = 12:00:00 +} + +func ExampleTime_Truncate() { + t, _ := time.Parse("2006 Jan 02 15:04:05", "2012 Dec 07 12:15:30.918273645") + trunc := []time.Duration{ + time.Nanosecond, + time.Microsecond, + time.Millisecond, + time.Second, + 2 * time.Second, + time.Minute, + 10 * time.Minute, + time.Hour, + } + + for _, d := range trunc { + fmt.Printf("t.Truncate(%6s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999")) + } + + // Output: + // t.Truncate( 1ns) = 12:15:30.918273645 + // t.Truncate( 1us) = 12:15:30.918273 + // t.Truncate( 1ms) = 12:15:30.918 + // t.Truncate( 1s) = 12:15:30 + // t.Truncate( 2s) = 12:15:30 + // t.Truncate( 1m0s) = 12:15:00 + // t.Truncate( 10m0s) = 12:10:00 + // t.Truncate(1h0m0s) = 12:00:00 +} diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index 011a1e3..190cc37 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -1033,3 +1033,116 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T return Time{unix + unixToInternal, int32(nsec), loc} } + +// Truncate returns the result of rounding t down to a multiple of d (since the zero time). +// If d <= 0, Truncate returns t unchanged. +func (t Time) Truncate(d Duration) Time { + if d <= 0 { + return t + } + _, r := div(t, d) + return t.Add(-r) +} + +// Round returns the result of rounding t to the nearest multiple of d (since the zero time). +// The rounding behavior for halfway values is to round up. +// If d <= 0, Round returns t unchanged. +func (t Time) Round(d Duration) Time { + if d <= 0 { + return t + } + _, r := div(t, d) + if r+r < d { + return t.Add(-r) + } + return t.Add(d - r) +} + +// div divides t by d and returns the quotient parity and remainder. +// We don't use the quotient parity anymore (round half up instead of round to even) +// but it's still here in case we change our minds. +func div(t Time, d Duration) (qmod2 int, r Duration) { + neg := false + if t.sec < 0 { + // Operate on absolute value. + neg = true + t.sec = -t.sec + t.nsec = -t.nsec + if t.nsec < 0 { + t.nsec += 1e9 + t.sec-- // t.sec >= 1 before the -- so safe + } + } + + switch { + // Special case: 2d divides 1 second. + case d < Second && Second%(d+d) == 0: + qmod2 = int(t.nsec/int32(d)) & 1 + r = Duration(t.nsec % int32(d)) + + // Special case: d is a multiple of 1 second. + case d%Second == 0: + d1 := int64(d / Second) + qmod2 = int(t.sec/d1) & 1 + r = Duration(t.sec%d1)*Second + Duration(t.nsec) + + // General case. + // This could be faster if more cleverness were applied, + // but it's really only here to avoid special case restrictions in the API. + // No one will care about these cases. + default: + // Compute nanoseconds as 128-bit number. + sec := uint64(t.sec) + tmp := (sec >> 32) * 1e9 + u1 := tmp >> 32 + u0 := tmp << 32 + tmp = uint64(sec&0xFFFFFFFF) * 1e9 + u0x, u0 := u0, u0+tmp + if u0 < u0x { + u1++ + } + u0x, u0 = u0, u0+uint64(t.nsec) + if u0 < u0x { + u1++ + } + + // Compute remainder by subtracting r<<k for decreasing k. + // Quotient parity is whether we subtract on last round. + d1 := uint64(d) + for d1>>63 != 1 { + d1 <<= 1 + } + d0 := uint64(0) + for { + qmod2 = 0 + if u1 > d1 || u1 == d1 && u0 >= d0 { + // subtract + qmod2 = 1 + u0x, u0 = u0, u0-d0 + if u0 > u0x { + u1-- + } + u1 -= d1 + } + if d1 == 0 && d0 == uint64(d) { + break + } + d0 >>= 1 + d0 |= (d1 & 1) << 63 + d1 >>= 1 + } + r = Duration(u0) + } + + if neg && r != 0 { + // If input was negative and not an exact multiple of d, we computed q, r such that + // q*d + r = -t + // But the right answers are given by -(q-1), d-r: + // q*d + r = -t + // -q*d - r = t + // -(q-1)*d + (d - r) = t + qmod2 ^= 1 + r = d - r + } + return +} diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index 9888d0d..1fd575b 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -9,7 +9,9 @@ import ( "encoding/gob" "encoding/json" "fmt" + "math/big" "math/rand" + "runtime" "strconv" "strings" "testing" @@ -192,6 +194,184 @@ func TestNanosecondsToUTCAndBack(t *testing.T) { } } +// The time routines provide no way to get absolute time +// (seconds since zero), but we need it to compute the right +// answer for bizarre roundings like "to the nearest 3 ns". +// Compute as t - year1 = (t - 1970) + (1970 - 2001) + (2001 - 1). +// t - 1970 is returned by Unix and Nanosecond. +// 1970 - 2001 is -(31*365+8)*86400 = -978307200 seconds. +// 2001 - 1 is 2000*365.2425*86400 = 63113904000 seconds. +const unixToZero = -978307200 + 63113904000 + +// abs returns the absolute time stored in t, as seconds and nanoseconds. +func abs(t Time) (sec, nsec int64) { + unix := t.Unix() + nano := t.Nanosecond() + return unix + unixToZero, int64(nano) +} + +// absString returns abs as a decimal string. +func absString(t Time) string { + sec, nsec := abs(t) + if sec < 0 { + sec = -sec + nsec = -nsec + if nsec < 0 { + nsec += 1e9 + sec-- + } + return fmt.Sprintf("-%d%09d", sec, nsec) + } + return fmt.Sprintf("%d%09d", sec, nsec) +} + +var truncateRoundTests = []struct { + t Time + d Duration +}{ + {Date(-1, January, 1, 12, 15, 30, 5e8, UTC), 3}, + {Date(-1, January, 1, 12, 15, 31, 5e8, UTC), 3}, + {Date(2012, January, 1, 12, 15, 30, 5e8, UTC), Second}, + {Date(2012, January, 1, 12, 15, 31, 5e8, UTC), Second}, +} + +func TestTruncateRound(t *testing.T) { + var ( + bsec = new(big.Int) + bnsec = new(big.Int) + bd = new(big.Int) + bt = new(big.Int) + br = new(big.Int) + bq = new(big.Int) + b1e9 = new(big.Int) + ) + + b1e9.SetInt64(1e9) + + testOne := func(ti, tns, di int64) bool { + t0 := Unix(ti, int64(tns)).UTC() + d := Duration(di) + if d < 0 { + d = -d + } + if d <= 0 { + d = 1 + } + + // Compute bt = absolute nanoseconds. + sec, nsec := abs(t0) + bsec.SetInt64(sec) + bnsec.SetInt64(nsec) + bt.Mul(bsec, b1e9) + bt.Add(bt, bnsec) + + // Compute quotient and remainder mod d. + bd.SetInt64(int64(d)) + bq.DivMod(bt, bd, br) + + // To truncate, subtract remainder. + // br is < d, so it fits in an int64. + r := br.Int64() + t1 := t0.Add(-Duration(r)) + + // Check that time.Truncate works. + if trunc := t0.Truncate(d); trunc != t1 { + t.Errorf("Time.Truncate(%s, %s) = %s, want %s\n"+ + "%v trunc %v =\n%v want\n%v", + t0.Format(RFC3339Nano), d, trunc, t1.Format(RFC3339Nano), + absString(t0), int64(d), absString(trunc), absString(t1)) + return false + } + + // To round, add d back if remainder r > d/2 or r == exactly d/2. + // The commented out code would round half to even instead of up, + // but that makes it time-zone dependent, which is a bit strange. + if r > int64(d)/2 || r+r == int64(d) /*&& bq.Bit(0) == 1*/ { + t1 = t1.Add(Duration(d)) + } + + // Check that time.Round works. + if rnd := t0.Round(d); rnd != t1 { + t.Errorf("Time.Round(%s, %s) = %s, want %s\n"+ + "%v round %v =\n%v want\n%v", + t0.Format(RFC3339Nano), d, rnd, t1.Format(RFC3339Nano), + absString(t0), int64(d), absString(rnd), absString(t1)) + return false + } + return true + } + + // manual test cases + for _, tt := range truncateRoundTests { + testOne(tt.t.Unix(), int64(tt.t.Nanosecond()), int64(tt.d)) + } + + // exhaustive near 0 + for i := 0; i < 100; i++ { + for j := 1; j < 100; j++ { + testOne(unixToZero, int64(i), int64(j)) + testOne(unixToZero, -int64(i), int64(j)) + if t.Failed() { + return + } + } + } + + if t.Failed() { + return + } + + // randomly generated test cases + cfg := &quick.Config{MaxCount: 100000} + if testing.Short() { + cfg.MaxCount = 1000 + } + + // divisors of Second + f1 := func(ti int64, tns int32, logdi int32) bool { + d := Duration(1) + a, b := uint(logdi%9), (logdi>>16)%9 + d <<= a + for i := 0; i < int(b); i++ { + d *= 5 + } + return testOne(ti, int64(tns), int64(d)) + } + quick.Check(f1, cfg) + + // multiples of Second + f2 := func(ti int64, tns int32, di int32) bool { + d := Duration(di) * Second + if d < 0 { + d = -d + } + return testOne(ti, int64(tns), int64(d)) + } + quick.Check(f2, cfg) + + // halfway cases + f3 := func(tns, di int64) bool { + di &= 0xfffffffe + if di == 0 { + di = 2 + } + tns -= tns % di + if tns < 0 { + tns += di / 2 + } else { + tns -= di / 2 + } + return testOne(0, tns, di) + } + quick.Check(f3, cfg) + + // full generality + f4 := func(ti int64, tns int32, di int64) bool { + return testOne(ti, int64(tns), di) + } + quick.Check(f4, cfg) +} + type TimeFormatTest struct { time Time formattedValue string @@ -1037,9 +1217,47 @@ func TestParseDurationRoundTrip(t *testing.T) { } } +var ( + t Time + u int64 +) + +var mallocTest = []struct { + count int + desc string + fn func() +}{ + {0, `time.Now()`, func() { t = Now() }}, + {0, `time.Now().UnixNano()`, func() { u = Now().UnixNano() }}, +} + +func TestCountMallocs(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + for _, mt := range mallocTest { + const N = 100 + memstats := new(runtime.MemStats) + runtime.ReadMemStats(memstats) + mallocs := 0 - memstats.Mallocs + for i := 0; i < N; i++ { + mt.fn() + } + runtime.ReadMemStats(memstats) + mallocs += memstats.Mallocs + if mallocs/N > uint64(mt.count) { + t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N) + } + } +} + func BenchmarkNow(b *testing.B) { for i := 0; i < b.N; i++ { - Now() + t = Now() + } +} + +func BenchmarkNowUnixNano(b *testing.B) { + for i := 0; i < b.N; i++ { + u = Now().UnixNano() } } diff --git a/libgo/runtime/chan.c b/libgo/runtime/chan.c index de25e9f..ceee42c 100644 --- a/libgo/runtime/chan.c +++ b/libgo/runtime/chan.c @@ -197,7 +197,7 @@ runtime_chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc) runtime_lock(c); // TODO(dvyukov): add similar instrumentation to select. if(raceenabled) - runtime_racereadpc(c, pc); + runtime_racereadpc(c, pc, runtime_chansend); if(c->closed) goto closed; @@ -1271,7 +1271,7 @@ runtime_closechan(Hchan *c) } if(raceenabled) { - runtime_racewritepc(c, runtime_getcallerpc(&c)); + runtime_racewritepc(c, runtime_getcallerpc(&c), runtime_closechan); runtime_racerelease(c); } diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index 5ea456f..45f8a56 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -949,6 +949,7 @@ runtime_memorydump(void) dumpspan(spanidx); } } + void runtime_gchelper(void) { @@ -1025,16 +1026,21 @@ cachestats(GCStats *stats) mstats.stacks_sys = stacks_sys; } +// Structure of arguments passed to function gc(). +// This allows the arguments to be passed via reflect_call. +struct gc_args +{ + int32 force; +}; + +static void gc(struct gc_args *args); + void runtime_gc(int32 force) { M *m; - int64 t0, t1, t2, t3; - uint64 heap0, heap1, obj0, obj1; const byte *p; - GCStats stats; - M *m1; - uint32 i; + struct gc_args a, *ap; // The atomic operations are not atomic if the uint64s // are not aligned on uint64 boundaries. This has been @@ -1074,12 +1080,37 @@ runtime_gc(int32 force) if(gcpercent < 0) return; + // Run gc on a bigger stack to eliminate + // a potentially large number of calls to runtime_morestack. + // But not when using gccgo. + a.force = force; + ap = &a; + gc(ap); + + if(gctrace > 1 && !force) { + a.force = 1; + gc(&a); + } +} + +static void +gc(struct gc_args *args) +{ + M *m; + int64 t0, t1, t2, t3; + uint64 heap0, heap1, obj0, obj1; + GCStats stats; + M *m1; + uint32 i; + runtime_semacquire(&runtime_worldsema); - if(!force && mstats.heap_alloc < mstats.next_gc) { + if(!args->force && mstats.heap_alloc < mstats.next_gc) { runtime_semrelease(&runtime_worldsema); return; } + m = runtime_m(); + t0 = runtime_nanotime(); m->gcing = 1; @@ -1181,9 +1212,6 @@ runtime_gc(int32 force) // give the queued finalizers, if any, a chance to run if(finq != nil) runtime_gosched(); - - if(gctrace > 1 && !force) - runtime_gc(1); } void runtime_ReadMemStats(MStats *) diff --git a/libgo/runtime/race.h b/libgo/runtime/race.h index 2d8d095..9f3b3ec 100644 --- a/libgo/runtime/race.h +++ b/libgo/runtime/race.h @@ -20,8 +20,8 @@ void runtime_racemalloc(void *p, uintptr sz, void *pc); void runtime_racefree(void *p); void runtime_racegostart(int32 goid, void *pc); void runtime_racegoend(int32 goid); -void runtime_racewritepc(void *addr, void *pc); -void runtime_racereadpc(void *addr, void *pc); +void runtime_racewritepc(void *addr, void *callpc, void *pc); +void runtime_racereadpc(void *addr, void *callpc, void *pc); void runtime_racefingo(void); void runtime_raceacquire(void *addr); void runtime_raceacquireg(G *gp, void *addr); |